Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions pages/theming/global-theme-iframes-inner.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useEffect } from 'react';

import { preset } from '~components/internal/generated/theming';
import { applyGlobalTheme } from '~components/theming';

const colorProperty = preset.propertiesMap.colorTextAccent;

export default function GlobalThemeIframesContentPage() {
useEffect(() => {
applyGlobalTheme();
}, []);

return (
<div data-testid="iframe-content">
<h1>Inner iframe</h1>
<span data-testid="themed-element" style={{ color: `var(${colorProperty})` }}>
Themed text
</span>
</div>
);
}
52 changes: 52 additions & 0 deletions pages/theming/global-theme-iframes.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';

import { setGlobalTheme, Theme } from '~components/theming';

const themeA: Theme = {
tokens: {
colorTextAccent: '#ff0000',
},
};

const themeB: Theme = {
tokens: {
colorTextAccent: '#0000ff',
},
};

export default function GlobalThemeIframesPage() {
const [themeApplied, setThemeApplied] = useState<string | null>(null);
const iframeHref = window.location.href.replace('global-theme-iframes', 'global-theme-iframes-inner');

return (
<div>
<h1>Global Theme with Iframes</h1>

<output data-testid="current-theme">{themeApplied ?? 'none'}</output>

<button
data-testid="set-theme-a"
onClick={() => {
setGlobalTheme(themeA);
setThemeApplied('theme-a');
}}
>
Set Theme A
</button>
<button
data-testid="set-theme-b"
onClick={() => {
setGlobalTheme(themeB);
setThemeApplied('theme-b');
}}
>
Set Theme B
</button>

<iframe id="iframe-1" src={iframeHref} title="iframe-1" />
<iframe id="iframe-2" src={iframeHref} title="iframe-2" />
</div>
);
}
107 changes: 107 additions & 0 deletions src/theming/__integ__/global-theme-iframes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objects';
import useBrowser from '@cloudscape-design/browser-test-tools/use-browser';

class GlobalThemeIframesPage extends BasePageObject {
async getAppliedColorInsideIframe(iframeSelector: string): Promise<string> {
let color = '';
await this.runInsideIframe(iframeSelector, true, async () => {
await this.waitForVisible('[data-testid="themed-element"]');
color = await this.browser.execute(() => {
return getComputedStyle(document.querySelector('[data-testid="themed-element"]')!)
.getPropertyValue('color')
.trim();
});
});
return color;
}

async waitForIframeColor(iframeSelector: string, expectedColor: string): Promise<void> {
await this.browser.waitUntil(
async () => {
const color = await this.getAppliedColorInsideIframe(iframeSelector);
return color === expectedColor;
},
{ timeout: 5000, timeoutMsg: `Expected color "${expectedColor}" in ${iframeSelector} but did not match in time` }
);
}

setThemeA() {
return this.click('[data-testid="set-theme-a"]');
}

setThemeB() {
return this.click('[data-testid="set-theme-b"]');
}

getCurrentTheme(): Promise<string> {
return this.getText('[data-testid="current-theme"]');
}

async waitForIframesDisplay(): Promise<void> {
await this.runInsideIframe('#iframe-1', true, async () => {
await this.waitForVisible('[data-testid="themed-element"]');
});
await this.runInsideIframe('#iframe-2', true, async () => {
await this.waitForVisible('[data-testid="themed-element"]');
});
}
}

const setupTest = (testFn: (page: GlobalThemeIframesPage) => Promise<void>) => {
return useBrowser(async browser => {
const page = new GlobalThemeIframesPage(browser);
await browser.url('#/light/theming/global-theme-iframes');
await page.waitForVisible('[data-testid="set-theme-a"]');
await page.waitForIframesDisplay();
await testFn(page);
});
};

describe('Global theme with multiple iframes', () => {
test(
'applies theme to iframes after setGlobalTheme is called',
setupTest(async page => {
await page.setThemeA();

await page.waitForIframeColor('#iframe-1', 'rgb(255, 0, 0)');
await page.waitForIframeColor('#iframe-2', 'rgb(255, 0, 0)');
})
);

test(
'propagates theme changes to all iframes',
setupTest(async page => {
await page.setThemeA();
await page.waitForIframeColor('#iframe-1', 'rgb(255, 0, 0)');
await page.waitForIframeColor('#iframe-2', 'rgb(255, 0, 0)');

await page.setThemeB();

await page.waitForIframeColor('#iframe-1', 'rgb(0, 0, 255)');
await page.waitForIframeColor('#iframe-2', 'rgb(0, 0, 255)');
})
);

test(
'both iframes receive the same theme value',
setupTest(async page => {
await page.setThemeA();
await page.waitForIframeColor('#iframe-1', 'rgb(255, 0, 0)');
await page.waitForIframeColor('#iframe-2', 'rgb(255, 0, 0)');
})
);

test(
'switching themes multiple times applies the latest theme',
setupTest(async page => {
await page.setThemeA();
await page.setThemeB();
await page.setThemeA();

await page.waitForIframeColor('#iframe-1', 'rgb(255, 0, 0)');
await page.waitForIframeColor('#iframe-2', 'rgb(255, 0, 0)');
})
);
});
37 changes: 37 additions & 0 deletions src/theming/__tests__/index-ssr.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* @jest-environment node
*/
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { applyGlobalTheme, setGlobalTheme, Theme } from '../../../lib/components/theming';

const theme: Theme = {
tokens: {
colorTextAccent: {
light: 'red',
dark: 'orange',
},
},
};

test('window is not defined in this environment', () => {
expect(typeof window).toBe('undefined');
});

describe('setGlobalTheme', () => {
test('does not throw when window is undefined', () => {
expect(() => setGlobalTheme(theme)).not.toThrow();
});
});

describe('applyGlobalTheme', () => {
test('does not throw when window is undefined', () => {
expect(() => applyGlobalTheme()).not.toThrow();
});

test('returns a no-op reset function when window is undefined', () => {
const { reset } = applyGlobalTheme();
expect(reset).toBeInstanceOf(Function);
expect(() => reset()).not.toThrow();
});
});
Loading
Loading