Skip to content
Merged
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
64 changes: 64 additions & 0 deletions packages/elements/src/flag/__test__/flag.cdn-prefix.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import sinon from 'sinon';

// import element and theme
import '@refinitiv-ui/elements/flag';
import { FlagLoader } from '@refinitiv-ui/elements/flag';

import '@refinitiv-ui/halo-theme/light/ef-flag.js';
import { expect, nextFrame } from '@refinitiv-ui/test-helpers';

import {
checkRequestedUrl,
createAndWaitForLoad,
createFakeResponse,
flagName,
gbSvg,
generateUniqueName,
isEqualSvg,
responseConfigSuccess
} from './helpers/helpers.js';

// Must match DefaultStyle.CDN_PREFIX in src/flag/const.ts
const DEFAULT_CDN_PREFIX = 'https://cdn.refinitiv.net/public/libs/elf/assets/elf-theme-halo/resources/flags/';

// CDN prefix is resolved once per page by the FlagLoader singleton, so this case needs its own test file
describe('flag/cdn-prefix', function () {
let fetch;
const resetLoaders = async () => {
FlagLoader.reset();
await nextFrame(5);
};
beforeEach(async function () {
await resetLoaders();
fetch = sinon.stub(window, 'fetch');
});
afterEach(async function () {
window.fetch.restore(); // remove stub
await resetLoaders();
});

it('Should fall back to the hardcoded CDN prefix when --cdn-prefix resolves to an empty value', async function () {
createFakeResponse(gbSvg, responseConfigSuccess);
const uniqueFlagName = generateUniqueName(flagName); // to avoid caching
// `initial` makes the custom property guaranteed-invalid, so getComputedStyle returns an empty value
const el = await createAndWaitForLoad(
`<ef-flag style="--cdn-prefix: initial" flag="${uniqueFlagName}"></ef-flag>`
);
const expectedSrc = `${DEFAULT_CDN_PREFIX}${uniqueFlagName}.svg`;

expect(el.getComputedVariable('--cdn-prefix')).to.equal(
'',
'CSS variable should resolve to an empty value'
);
expect(fetch.callCount).to.equal(1, 'Should make one request');
expect(checkRequestedUrl(fetch.args, expectedSrc)).to.equal(
true,
`Requested URL should use the fallback prefix: ${expectedSrc}`
);

const svg = el.shadowRoot.querySelector('svg');

expect(svg).to.not.equal(null, 'SVG element should exist when falling back to the default prefix');
expect(isEqualSvg(svg.outerHTML, gbSvg)).to.equal(true, 'Should render SVG, from the mock response');
});
});
4 changes: 4 additions & 0 deletions packages/elements/src/flag/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Default to Halo theme as Elemental and Solar theme are deprecated.
export enum DefaultStyle {
CDN_PREFIX = 'https://cdn.refinitiv.net/public/libs/elf/assets/elf-theme-halo/resources/flags/'
}
21 changes: 15 additions & 6 deletions packages/elements/src/flag/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ import { property } from '@refinitiv-ui/core/decorators/property.js';
import { unsafeHTML } from '@refinitiv-ui/core/directives/unsafe-html.js';

import { VERSION } from '../version.js';
import { DefaultStyle } from './const.js';
import { FlagLoader } from './utils/FlagLoader.js';

export { preload } from './utils/FlagLoader.js';

export { FlagLoader } from './utils/FlagLoader.js';

const EmptyTemplate = svg``;

@customElement('ef-flag')
Expand Down Expand Up @@ -131,11 +134,17 @@ export class Flag extends BasicElement {
*/
protected override firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
/**
* We have to call this here because
* polyfilled browsers only get variables at this point.
*/
this.setPrefix();
// Chromium only issue: starting from version 151,
// when Flag is slotted into an unregistered custom element,
// getComputedStyle() will always return empty string as a style value.
// It needs to wait for the registration of the parent custom element first.
// In practice, this happens when Flag class and its style are imported before the parent's ones as following:
// import '@refinitiv-ui/elements/flag';
// import '@refinitiv-ui/elements/panel';

// import '@refinitiv-ui/elements/flag/themes/halo/dark';
// import '@refinitiv-ui/elements/panel/themes/halo/dark';
setTimeout(() => this.setPrefix());
}

/**
Expand Down Expand Up @@ -167,7 +176,7 @@ export class Flag extends BasicElement {
*/
private setPrefix(): void {
if (!FlagLoader.isPrefixSet) {
const CDNPrefix = this.getComputedVariable('--cdn-prefix').replace(/^('|")|('|")$/g, '');
const CDNPrefix = this.getComputedVariable('--cdn-prefix', DefaultStyle.CDN_PREFIX);

FlagLoader.setCdnPrefix(CDNPrefix);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import sinon from 'sinon';

// import element and theme
import '@refinitiv-ui/elements/configuration';
import { IconLoader, SpriteLoader, iconTemplateCache } from '@refinitiv-ui/elements/icon';

import '@refinitiv-ui/halo-theme/light/ef-icon.js';
import { expect, nextFrame } from '@refinitiv-ui/test-helpers';

import {
checkRequestedUrl,
createAndWaitForLoad,
createFakeResponse,
iconName,
isEqualSvg,
responseConfigSuccess,
tickSvgSprite
} from './helpers/helpers.js';

// Must match DefaultStyle.CDN_SPRITE_PREFIX in src/icon/const.ts
const DEFAULT_CDN_SPRITE_PREFIX =
'https://cdn.refinitiv.net/public/libs/elf/assets/elf-theme-halo/resources/sprites/icons.svg';

// CDN prefixes and the sprite are resolved once per page by the loader singletons,
// so they must be reset to make this case independent of other tests sharing the same page
describe('icon/cdn-prefix-fallback', function () {
let fetch;
const resetLoaders = async () => {
IconLoader.reset();
SpriteLoader.reset();
iconTemplateCache.clear();
await nextFrame(5);
};
beforeEach(async function () {
await resetLoaders();
fetch = sinon.stub(window, 'fetch');
});
afterEach(async function () {
window.fetch.restore(); // remove stub
await resetLoaders();
});

it('Should fall back to the sprite when CDN prefixes resolve to empty values', async function () {
createFakeResponse(tickSvgSprite, responseConfigSuccess);
// `initial` makes the custom properties guaranteed-invalid, so getComputedStyle returns empty values
const el = await createAndWaitForLoad(
`<ef-icon style="--cdn-prefix: initial; --cdn-sprite-prefix: initial" icon="${iconName}"></ef-icon>`
);

expect(el.getComputedVariable('--cdn-prefix')).to.equal(
'',
'CSS variable should resolve to an empty value'
);
expect(el.getComputedVariable('--cdn-sprite-prefix')).to.equal(
'',
'CSS variable should resolve to an empty value'
);
expect(fetch.callCount).to.equal(1, 'Should make one request');
expect(checkRequestedUrl(fetch.args, DEFAULT_CDN_SPRITE_PREFIX)).to.equal(
true,
`Requested URL should use the fallback sprite prefix: ${DEFAULT_CDN_SPRITE_PREFIX}`
);

const svg = el.shadowRoot.querySelector('svg');

expect(svg).to.not.equal(null, 'SVG element should exist when falling back to the sprite');
expect(isEqualSvg(svg.outerHTML, tickSvgSprite)).to.equal(
true,
'Should render SVG, from the mock response'
);
});
});
5 changes: 5 additions & 0 deletions packages/elements/src/icon/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Default to Halo theme as Elemental and Solar theme are deprecated.
export enum DefaultStyle {
CDN_PREFIX = '',
CDN_SPRITE_PREFIX = 'https://cdn.refinitiv.net/public/libs/elf/assets/elf-theme-halo/resources/sprites/icons.svg'
}
27 changes: 18 additions & 9 deletions packages/elements/src/icon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Deferred, isBase64svg, isUrl } from '@refinitiv-ui/utils/loader.js';
import { efConfig } from '../configuration/index.js';
import type { Config } from '../configuration/index.js';
import { VERSION } from '../version.js';
import { DefaultStyle } from './const.js';
import { IconLoader } from './utils/IconLoader.js';
import { SpriteLoader } from './utils/SpriteLoader.js';

Expand Down Expand Up @@ -94,7 +95,10 @@ export class Icon extends BasicElement {
if (oldValue !== value) {
this.deferIconReady();
this._icon = value;
requestAnimationFrame(() => this.updateRenderer());
// Wait for setPrefix() resolving both sprite & icon CDN prefix value before updating the renderer
void Promise.all([SpriteLoader.getCdnPrefix(), IconLoader.getCdnPrefix()]).then(() => {
this.updateRenderer();
});
this.requestUpdate('icon', oldValue);
}
}
Expand Down Expand Up @@ -180,12 +184,17 @@ export class Icon extends BasicElement {
*/
protected override firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);

/**
* We have to call this here because
* polyfilled browsers only get variables at this point.
*/
this.setPrefix();
// Chromium only issue: starting from version 151,
// when Icon is slotted into an unregistered custom element,
// getComputedStyle() will always return empty string as a style value.
// It needs to wait for the registration of the parent custom element first.
// In practice, this happens when Icon class and its style are imported before the parent's ones as following:
// import '@refinitiv-ui/elements/icon';
// import '@refinitiv-ui/elements/panel';

// import '@refinitiv-ui/elements/icon/themes/halo/dark';
// import '@refinitiv-ui/elements/panel/themes/halo/dark';
setTimeout(() => this.setPrefix());
}

protected override async getUpdateComplete(): Promise<boolean> {
Expand Down Expand Up @@ -284,12 +293,12 @@ export class Icon extends BasicElement {
private setPrefix(): void {
// This prefix for individual icons allows supporting custom prefix of self-managed icons.
if (IconLoader.isPrefixPending) {
const CDNPrefix = this.getComputedVariable('--cdn-prefix');
const CDNPrefix = this.getComputedVariable('--cdn-prefix', DefaultStyle.CDN_PREFIX);
IconLoader.setCdnPrefix(CDNPrefix);
}

if (SpriteLoader.isPrefixPending) {
const CDNSpritePrefix = this.getComputedVariable('--cdn-sprite-prefix');
const CDNSpritePrefix = this.getComputedVariable('--cdn-sprite-prefix', DefaultStyle.CDN_SPRITE_PREFIX);
SpriteLoader.setCdnPrefix(CDNSpritePrefix);
}
}
Expand Down
Loading