diff --git a/packages/core/src/elements/ResponsiveElement.ts b/packages/core/src/elements/ResponsiveElement.ts index e6bb324c07..3c34094f4a 100644 --- a/packages/core/src/elements/ResponsiveElement.ts +++ b/packages/core/src/elements/ResponsiveElement.ts @@ -1,5 +1,6 @@ import { BasicElement } from './BasicElement.js'; -import { ResizeObserver, ResizeObserverEntry } from '@juggle/resize-observer'; +// If ResizeObserver native API works fine, this package should be removed in future +import { ResizeObserver as PolyfillResizeObserver } from '@juggle/resize-observer'; export type ElementSize = { width: number; @@ -48,13 +49,21 @@ const triggerResize = (entry: ResizeObserverEntry): void => { entry.target.dispatchEvent(event); }; +/** + * Trigger Resize all entries from ResizeObserver + * @param entries array of ResizeObserverEntry + * @returns {void} + */ +const entriesResize = (entries: ResizeObserverEntry[]): void => { + entries.forEach(entry => triggerResize(entry)); +}; + /** * Global resize observer, * used to watch changes in element dimensions */ -const resizeObserver = new ResizeObserver(entries => { - entries.forEach(entry => triggerResize(entry)); -}); +const resizeObserver = typeof ResizeObserver === 'function' ? new ResizeObserver(entriesResize) : new PolyfillResizeObserver(entriesResize); + /** * Responsive element base class. diff --git a/packages/elements/src/canvas/__test__/canvas.test.js b/packages/elements/src/canvas/__test__/canvas.test.js index 562d863871..3b0e134ccc 100644 --- a/packages/elements/src/canvas/__test__/canvas.test.js +++ b/packages/elements/src/canvas/__test__/canvas.test.js @@ -45,7 +45,7 @@ describe('canvas/Canvas', () => { Object.defineProperty(window, 'devicePixelRatio', { value: null }); - await nextFrame(); + await nextFrame(2); // wait for resize observer & rendering completion expect(el.canvas.width).equal(Math.floor(el.width * dpr)); expect(el.canvas.height).equal(Math.floor(el.height * dpr)); }); @@ -55,7 +55,7 @@ describe('canvas/Canvas', () => { Object.defineProperty(window, 'devicePixelRatio', { value: 3 }); - await nextFrame(); + await nextFrame(2); // wait for resize observer & rendering completion expect(el.canvas.width).equal(Math.floor(el.width * devicePixelRatio)); expect(el.canvas.height).equal(Math.floor(el.height * devicePixelRatio)); Object.defineProperty(window, 'devicePixelRatio', { @@ -70,7 +70,7 @@ describe('canvas/Canvas', () => { it('Handles fractional pixelation', async () => { el.style.width = '300.5px'; - await elementUpdated(); + await elementUpdated(el); const listener = function () { el.removeEventListener('resize', listener); expect(el.style.width, 'ef-canvas\'s width should be fractional').equal('300.5px'); diff --git a/packages/elements/src/clock/__test__/clock.analogue.test.js b/packages/elements/src/clock/__test__/clock.analogue.test.js index ad305b48cf..fb10e2deb5 100644 --- a/packages/elements/src/clock/__test__/clock.analogue.test.js +++ b/packages/elements/src/clock/__test__/clock.analogue.test.js @@ -48,11 +48,11 @@ describe('clock/Analogue', () => { it('Shows small size clock when width is less than 130px', async () => { expect(el.shadowRoot.querySelector('[part="digital"]'), 'digital clock should display inside a default analog clock').not.to.be.null; - + // make size smaller than defined break point el.style.width = '129px'; await elementUpdated(el); - await nextFrame(); + await nextFrame(2); // wait for resize observer & rendering completion expect(el.shadowRoot.querySelector('[part="digital"]'), 'digital clock should not display inside small clock').to.be.null; expect(el.amPm, 'am-pm should be hidden by default on small clock').to.be.equal(false); @@ -63,18 +63,18 @@ describe('clock/Analogue', () => { el.style.width = '129px'; await elementUpdated(el); await nextFrame(); - + // test default behavior expect(el.hasAttribute('am-pm')).to.be.equal(false); expect(el.amPm).to.be.equal(false); expect(el.shadowRoot.querySelector('[part="segment am-pm"]')).to.be.null; - + // test when it has am-pm attribute el = await fixture(''); el.style.width = '129px'; await elementUpdated(el); await nextFrame(); - + expect(el.amPm, 'amPm property should be true if am-pm attribute is set').to.be.equal(true); expect(el.shadowRoot.querySelector('[part="segment am-pm"]'), 'AM/PM should display on clock').not.to.be.null; @@ -92,7 +92,7 @@ describe('clock/Analogue', () => { await nextFrame(); expect(el.hasAttribute('size'), 'size attribute should not show if not analog').to.be.equal(false); - + el.style.width = '129px'; await elementUpdated(el); await nextFrame(); diff --git a/packages/elements/src/clock/__test__/clock.test.js b/packages/elements/src/clock/__test__/clock.test.js index 2dbc0ac110..3a96b35e2b 100644 --- a/packages/elements/src/clock/__test__/clock.test.js +++ b/packages/elements/src/clock/__test__/clock.test.js @@ -25,7 +25,7 @@ describe('clock/Clock', () => { el = await fixture(''); el.style.width = '129px'; await elementUpdated(el); - await nextFrame(); + await nextFrame(2); // wait for resize observer & rendering completion expect(el).shadowDom.to.equalSnapshot(); }); diff --git a/packages/elements/src/datetime-field/__test__/utils.js b/packages/elements/src/datetime-field/__test__/utils.js index 826620bb12..8f366b6414 100644 --- a/packages/elements/src/datetime-field/__test__/utils.js +++ b/packages/elements/src/datetime-field/__test__/utils.js @@ -4,7 +4,7 @@ const inputValue = (el) => el.inputValue; // Access private property const inputElement = (el) => el.inputElement; // Access private property const focusInput = async (el) => { await triggerFocusFor(inputElement(el)); - await nextFrame(el); + await nextFrame(); }; const arrowRight = async (el) => { diff --git a/packages/elements/src/interactive-chart/__test__/interactive-chart.test.js b/packages/elements/src/interactive-chart/__test__/interactive-chart.test.js index 6998d1dc85..60b34fb05a 100644 --- a/packages/elements/src/interactive-chart/__test__/interactive-chart.test.js +++ b/packages/elements/src/interactive-chart/__test__/interactive-chart.test.js @@ -833,8 +833,8 @@ describe('interactive-chart/InteractiveChart', () => { it('Should has dynamic left position in legend when the chart set y axis at left', async () => { el.config = linePositionLeft; - await elementUpdated(); - await nextFrame(); + await elementUpdated(el); + await nextFrame(3); // wait for resize observer & rendering completion expect(el.chart).to.not.be.undefined; expect(el.chart).to.not.be.null; @@ -846,8 +846,8 @@ describe('interactive-chart/InteractiveChart', () => { it('Should has dynamic left position in legend when the chart set y axis at both edge', async () => { el.config = twoPriceScales; - await elementUpdated(); - await nextFrame(); + await elementUpdated(el); + await nextFrame(3); // wait for resize observer & rendering completion expect(el.chart).to.not.be.undefined; expect(el.chart).to.not.be.null; @@ -859,8 +859,8 @@ describe('interactive-chart/InteractiveChart', () => { it('Should has fixed left position in legend when the chart set y axis at right edge', async () => { el.config = line; - await elementUpdated(); - await nextFrame(); + await elementUpdated(el); + await nextFrame(3); // wait for resize observer & rendering completion expect(el.chart).to.not.be.undefined; expect(el.chart).to.not.be.null; @@ -873,32 +873,36 @@ describe('interactive-chart/InteractiveChart', () => { describe('Test deprecated attribute', () => { it('Switch attribute legendstyle horizontal to vertical, it should display vertical style', async () => { el = await fixture(''); - + el.config = line; - await elementUpdated(); + await elementUpdated(el); + await nextFrame(2); // wait for resize observer & rendering completion + expect(el.chart).to.not.be.undefined; expect(el.chart).to.not.be.null; - + el.setAttribute('legendstyle','vertical'); - + await nextFrame(); - await elementUpdated(); + await elementUpdated(el); expect(el.getAttribute('legend-style')).to.null; expect(el.shadowRoot.querySelector('[part=legend]').className).to.not.include('horizontal'); }); it('Set legend-style to vertical when legendstyle horizontal, it should display vertical style', async () => { el = await fixture(''); - + el.config = line; - await elementUpdated(); + await elementUpdated(el); + await nextFrame(2); // wait for resize observer & rendering completion + expect(el.chart).to.not.be.undefined; expect(el.chart).to.not.be.null; expect(el.getAttribute('legendstyle')).to.equal('horizontal'); - + el.setAttribute('legend-style','vertical'); - + await nextFrame(); - await elementUpdated(); + await elementUpdated(el); expect(el.shadowRoot.querySelector('[part=legend]').className).to.not.include('horizontal'); }); }); diff --git a/packages/elements/src/led-gauge/__test__/led-gauge.test.js b/packages/elements/src/led-gauge/__test__/led-gauge.test.js index 9bdb16b575..684ea23d29 100644 --- a/packages/elements/src/led-gauge/__test__/led-gauge.test.js +++ b/packages/elements/src/led-gauge/__test__/led-gauge.test.js @@ -56,7 +56,7 @@ describe('led-gauge/LedGauge', () => { el.setAttribute('top-label', 'NewTopLabel'); el.setAttribute('bottom-label', 'NewBottomLabel'); - await elementUpdated(); + await elementUpdated(el); top = el.shadowRoot.querySelector('#top'); bottom = el.shadowRoot.querySelector('#bottom'); @@ -72,7 +72,7 @@ describe('led-gauge/LedGauge', () => { el.topLabel = 'NewTopLabel'; el.bottomLabel = 'NewBottomLabel'; - await elementUpdated(); + await elementUpdated(el); top = el.shadowRoot.querySelector('#top'); bottom = el.shadowRoot.querySelector('#bottom'); @@ -87,7 +87,7 @@ describe('led-gauge/LedGauge', () => { el.removeAttribute('top-label'); el.removeAttribute('bottom-label'); - await elementUpdated(); + await elementUpdated(el); top = el.shadowRoot.querySelector('#top'); bottom = el.shadowRoot.querySelector('#bottom'); @@ -111,7 +111,7 @@ describe('led-gauge/LedGauge', () => { it('Should be able to set topValue and bottomValue via property', async () => { const el = await fixture(full); - await nextFrame(); + await nextFrame(2); // wait for resize observer & rendering completion top = el.shadowRoot.querySelector('#top'); bottom = el.shadowRoot.querySelector('#bottom'); let topTextPos = parseInt(window.getComputedStyle(top).getPropertyValue('left'), 10); @@ -122,7 +122,7 @@ describe('led-gauge/LedGauge', () => { el.topValue = 50; el.bottomValue = -50; - await elementUpdated(); + await elementUpdated(el); let newTopTextPos = parseInt(window.getComputedStyle(top).getPropertyValue('left'), 10); let newBottomTextPos = parseInt(window.getComputedStyle(bottom).getPropertyValue('left'), 10); @@ -133,7 +133,7 @@ describe('led-gauge/LedGauge', () => { it('Should be able to set topValue and bottomValue via attribute', async () => { const el = await fixture(full); - await nextFrame(); + await nextFrame(2); // wait for resize observer & rendering completion top = el.shadowRoot.querySelector('#top'); bottom = el.shadowRoot.querySelector('#bottom'); let topTextPos = parseInt(window.getComputedStyle(top).getPropertyValue('left'), 10); @@ -144,7 +144,7 @@ describe('led-gauge/LedGauge', () => { el.setAttribute('top-value', '50'); el.setAttribute('bottom-value', '-50'); - await elementUpdated(); + await elementUpdated(el); let newTopTextPos = parseInt(window.getComputedStyle(top).getPropertyValue('left'), 10); let newBottomTextPos = parseInt(window.getComputedStyle(bottom).getPropertyValue('left'), 10); @@ -161,7 +161,7 @@ describe('led-gauge/LedGauge', () => { it('Should update range label when range-label changed by attribute', async () => { const el = await fixture(rangeFixture); el.setAttribute('range-label', 'NewRangeLabel'); - await elementUpdated(); + await elementUpdated(el); range = el.shadowRoot.querySelector('#range'); expect(range).to.not.equal(null); @@ -171,7 +171,7 @@ describe('led-gauge/LedGauge', () => { it('Should update range label when range-label changed by property', async () => { const el = await fixture(rangeFixture); el.rangeLabel = 'NewRangeLabel'; - await elementUpdated(); + await elementUpdated(el); range = el.shadowRoot.querySelector('#range'); expect(range).to.not.equal(null); @@ -186,7 +186,7 @@ describe('led-gauge/LedGauge', () => { expect(range).to.not.equal(null); el.setAttribute('bottom-label', 'Bottom Text'); - await elementUpdated(); + await elementUpdated(el); expect(bottom).to.equal(null); expect(range).to.not.equal(null); }); @@ -200,20 +200,20 @@ describe('led-gauge/LedGauge', () => { it('Should have min=0 and max=100 when set zero=left by property', async () => { const el = await fixture(zero); el.zero = 'left'; - await elementUpdated(); + await elementUpdated(el); expect(el.zero).to.equal('left'); }); it('Should set zero to center when invalid value is set', async () => { const el = await fixture(zero); el.zero = 'left'; - await elementUpdated(); + await elementUpdated(el); expect(el.zero).to.equal('left'); expect(el.min).to.equal(0); expect(el.max).to.equal(100); el.zero = 'invalid'; - await elementUpdated(); + await elementUpdated(el); expect(el.zero).to.equal('center'); expect(el.min).to.equal(-100); expect(el.max).to.equal(100); diff --git a/packages/elements/src/select/__test__/select.events.test.js b/packages/elements/src/select/__test__/select.events.test.js index c76441ef7d..5f5c921771 100644 --- a/packages/elements/src/select/__test__/select.events.test.js +++ b/packages/elements/src/select/__test__/select.events.test.js @@ -110,7 +110,7 @@ describe('select/Events', () => { it('opened-changed event on keyboard pressed', async () => { const el = await fixture(`${getOptions()}`); el.focus(); - await nextFrame(el); + await nextFrame(); let counter = 0; let opened = false; diff --git a/packages/elements/src/sparkline/__test__/sparkline.test.js b/packages/elements/src/sparkline/__test__/sparkline.test.js index e5e8500a42..650cc41479 100644 --- a/packages/elements/src/sparkline/__test__/sparkline.test.js +++ b/packages/elements/src/sparkline/__test__/sparkline.test.js @@ -1,4 +1,4 @@ -import { fixture, expect, elementUpdated } from '@refinitiv-ui/test-helpers'; +import { fixture, expect, elementUpdated, nextFrame } from '@refinitiv-ui/test-helpers'; // import element and theme import '@refinitiv-ui/elements/sparkline'; @@ -75,7 +75,8 @@ describe('sparkline/Sparkline', () => { expect(isCanvasBlank(canvas)).to.be.true; el.data = data; - await elementUpdated(); + await elementUpdated(el); + await nextFrame(2); // wait for rendering completion expect(countDataChanged).to.equal(1); expect(countDataError).to.equal(0); expect(isCanvasBlank(canvas)).to.be.false; diff --git a/packages/elements/src/tab-bar/__test__/tab-bar.template.test.js b/packages/elements/src/tab-bar/__test__/tab-bar.template.test.js index c4f9da444d..a428c66614 100644 --- a/packages/elements/src/tab-bar/__test__/tab-bar.template.test.js +++ b/packages/elements/src/tab-bar/__test__/tab-bar.template.test.js @@ -1,4 +1,4 @@ -import { fixture, expect, elementUpdated, aTimeout } from '@refinitiv-ui/test-helpers'; +import { fixture, expect, elementUpdated, aTimeout, nextFrame } from '@refinitiv-ui/test-helpers'; import '@refinitiv-ui/elements/tab-bar'; import '@refinitiv-ui/elemental-theme/light/ef-tab-bar'; @@ -100,6 +100,7 @@ describe('tab-bar/TabBar', () => { }); it('Should show only right scroll button', async () => { + await nextFrame(); // wait for resize observer & rendering completion expect(getElementStyle(leftScrollBtn, 'display')).equal('none'); expect(getElementStyle(rightScrollBtn, 'display')).equal('flex'); }); diff --git a/packages/elements/src/tooltip/__demo__/index.html b/packages/elements/src/tooltip/__demo__/index.html index 4693162029..6f78c9865a 100644 --- a/packages/elements/src/tooltip/__demo__/index.html +++ b/packages/elements/src/tooltip/__demo__/index.html @@ -110,6 +110,26 @@ + + side by side buttons: testing tooltip display + + +
+ Tooltip1 + Tooltip2 +
+
+
Tooltip can have different transition styles, which may be overridden by changing `transitionStyle`.

diff --git a/packages/elements/src/tornado-chart/__test__/tornado-item.test.js b/packages/elements/src/tornado-chart/__test__/tornado-chart-item.test.js similarity index 100% rename from packages/elements/src/tornado-chart/__test__/tornado-item.test.js rename to packages/elements/src/tornado-chart/__test__/tornado-chart-item.test.js diff --git a/packages/elements/src/tornado-chart/__test__/tornado-chart.test.js b/packages/elements/src/tornado-chart/__test__/tornado-chart.test.js index 56e788efa1..b07c2d5be5 100644 --- a/packages/elements/src/tornado-chart/__test__/tornado-chart.test.js +++ b/packages/elements/src/tornado-chart/__test__/tornado-chart.test.js @@ -48,7 +48,7 @@ describe('tornado-chart/TornadoChart', () => { const legend = element.shadowRoot.querySelector('[part="legend"]'); await oneEvent(element, 'resize'); - await elementUpdated(); + await elementUpdated(element); expect(legend.hasAttribute('vertical')).to.equal(true); }); @@ -62,7 +62,7 @@ describe('tornado-chart/TornadoChart', () => { ); await oneEvent(element, 'resize'); - await elementUpdated(); + await elementUpdated(element); const items = element.querySelectorAll('ef-tornado-item'); items.forEach((item) => { diff --git a/packages/elements/src/tree-select/__test__/tree-select.interaction.test.js b/packages/elements/src/tree-select/__test__/tree-select.interaction.test.js index 38d25f8b4a..02c9fa6ff4 100644 --- a/packages/elements/src/tree-select/__test__/tree-select.interaction.test.js +++ b/packages/elements/src/tree-select/__test__/tree-select.interaction.test.js @@ -153,7 +153,7 @@ describe('tree-select/Interaction', () => { el.data = flatData; el.opened = true; changeItemSelection(el, flatSelection); - await nextFrame(); + await openedUpdated(el); const elementToRemove = [...el.shadowRoot.querySelectorAll('ef-pill')].find(el => el.value === itemToRemove.value); // Austria elementToRemove.dispatchEvent(new CustomEvent('clear', { detail: { diff --git a/packages/elements/src/tree-select/__test__/utils.js b/packages/elements/src/tree-select/__test__/utils.js index 5c9823ee81..34860b84e0 100644 --- a/packages/elements/src/tree-select/__test__/utils.js +++ b/packages/elements/src/tree-select/__test__/utils.js @@ -3,7 +3,7 @@ import { elementUpdated, expect, nextFrame } from '@refinitiv-ui/test-helpers'; /** * Cross browser function to wait while select element becomes opened/closed and resized * @param {TreeSelect} el Tree select - * @returns {void} + * @returns {Promise} */ export const openedUpdated = async (el) => { await elementUpdated(el); diff --git a/packages/test-helpers/__test__/test-helpers.test.js b/packages/test-helpers/__test__/test-helpers.test.js index 32a2e6416b..bca94d8c70 100644 --- a/packages/test-helpers/__test__/test-helpers.test.js +++ b/packages/test-helpers/__test__/test-helpers.test.js @@ -1,4 +1,5 @@ -import { fixture, expect, keyboardEvent, oneEvent } from '../lib/test-helpers'; +import { fixture, expect, keyboardEvent, oneEvent, isNear, nextFrame, replaceWhitespace } from '../lib/test-helpers'; +import { createSandbox } from 'sinon'; describe('TestHelpersTest', () => { @@ -48,4 +49,63 @@ describe('TestHelpersTest', () => { }); }); + describe('Test nextFrame helper', () => { + const sandbox = createSandbox(); + + beforeEach(async () => { + sandbox.spy(window, "requestAnimationFrame"); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('Calling nextFrame without param', async () => { + await nextFrame(); + expect(window.requestAnimationFrame.calledOnce).to.equal(true, 'requestAnimationFrame should be called once'); + }); + it('Calling nextFrame with 1 as param', async () => { + await nextFrame(1); + expect(window.requestAnimationFrame.calledOnce).to.equal(true, 'requestAnimationFrame should be called once'); + }); + it('Calling nextFrame with 2 as param', async () => { + await nextFrame(2); + expect(window.requestAnimationFrame.calledTwice).to.equal(true, 'requestAnimationFrame should be called twice'); + }); + }); + + describe('Test isNear helper', () => { + it('Calling isNear with numbers & distance', async () => { + expect(isNear(10, 10, 0)).to.equal(true, 'isNear at boundary distance of 0 should be true'); + expect(isNear(10, 10.1, 0)).to.equal(false, 'isNear beyond boundary distance of 0 should be false'); + expect(isNear(10, 14.9, 5)).to.equal(true, 'isNear within boundary distance greater than 0 should be true'); + expect(isNear(10, 15, 5)).to.equal(true, 'isNear at boundary distance greater than 0 should be true'); + expect(isNear(10, 15.1, 5)).to.equal(false, 'isNear beyond boundary distance greater than 0 should be true'); + }); + + it('Calling isNear with numbers, distance & inclusive as true', async () => { + expect(isNear(10, 10, 0, true)).to.equal(true, 'isNear at boundary distance of 0 should be true'); + expect(isNear(10, 10.1, 0, true)).to.equal(false, 'isNear beyond boundary distance of 0 should be false'); + expect(isNear(10, 14.9, 5, true)).to.equal(true, 'isNear within boundary distance greater than 0 should be true'); + expect(isNear(10, 15, 5, true)).to.equal(true, 'isNear at boundary distance greater than 0 should be true'); + expect(isNear(10, 15.1, 5, true)).to.equal(false, 'isNear beyond boundary distance greater than 0 should be true'); + }); + + it('Calling isNear with numbers, distance & inclusive as false', async () => { + expect(isNear(10, 10, 0, false)).to.equal(true, 'isNear at boundary distance of 0 should be true'); + expect(isNear(10, 10.1, 0, false)).to.equal(false, 'isNear beyond boundary distance of 0 should be false'); + expect(isNear(10, 14.9, 5, false)).to.equal(true, 'isNear within boundary distance greater than 0 should be true'); + expect(isNear(10, 15, 5, false)).to.equal(false, 'isNear at boundary distance greater than 0 should be true'); + expect(isNear(10, 15.1, 5, false)).to.equal(false, 'isNear beyond boundary distance greater than 0 should be true'); + }); + }); + + describe('Test Method helper', () => { + it('Replace spacial whitespace to normal whitespace correctly', () => { + // Remove whitespace character U+202F from Chrome 111 and U+00A0 from Safari + const specialWhitespace = '  '; + expect(replaceWhitespace(specialWhitespace)).to.equal(' ', 'Remove whitespace should work correctly'); + }); + }); + }); diff --git a/packages/test-helpers/src/test-helpers.ts b/packages/test-helpers/src/test-helpers.ts index d13e39d967..3a643772ea 100644 --- a/packages/test-helpers/src/test-helpers.ts +++ b/packages/test-helpers/src/test-helpers.ts @@ -1,5 +1,4 @@ -/* istanbul ignore file */ -import { isIE } from '@open-wc/testing'; +import { isIE, nextFrame as _nextFrame } from '@open-wc/testing'; export { html, @@ -14,7 +13,6 @@ export { defineCE, aTimeout, waitUntil, - nextFrame, litFixture, litFixtureSync, fixture, @@ -38,23 +36,56 @@ export interface CustomKeyboardEvent extends CustomEvent { * @returns {KeyboardEvent|CustomKeyboardEvent} keyboard event */ export const keyboardEvent = (type: string, init: KeyboardEventInit = {}): KeyboardEvent|CustomKeyboardEvent => { + /* istanbul ignore else */ if (!isIE()) { return new KeyboardEvent(type, init); } + else { + const event = new CustomEvent(type, { + detail: 0, + bubbles: true, + cancelable: true, + composed: true + }) as CustomKeyboardEvent; + event.key = init.key || ''; + event.shiftKey = init.shiftKey || false; + event.altKey = init.altKey || false; + event.ctrlKey = init.ctrlKey || false; + event.metaKey = init.metaKey || false; - const event = new CustomEvent(type, { - detail: 0, - bubbles: true, - cancelable: true, - composed: true - }) as CustomKeyboardEvent; - event.key = init.key || ''; - event.shiftKey = init.shiftKey || false; - event.altKey = init.altKey || false; - event.ctrlKey = init.ctrlKey || false; - event.metaKey = init.metaKey || false; + return event; + } +}; + +/** + * Resolves after requestAnimationFrame. + * @param [frameCount = 1] number of animationFrame to be requested + * + * @returns {Promise} Promise that resolved after requestAnimationFrame + */ +export const nextFrame = async (frameCount = 1): Promise => { + for (let i = 0; i < frameCount; i++) { + await _nextFrame(); + } +}; - return event; +/** + * Check value difference between 2 number. + * If it's within `distance` value, they are near. + * @param a 1 of the 2 numbers to be checked + * @param b 1 of the 2 numbers to be checked + * @param distance maximum value difference of `a` & `b` to be considered near, must equal or greater than 0 + * @param [inclusive = true] `true`: value difference must be smaller or equal to `distance` , `false`: value difference must be smaller than `distance` + * If `distance` is 0, inclusive would be overwritten as `true`. + * + * @returns {boolean} equality result + */ +export const isNear = (a: number, b: number, distance: number, inclusive = true): boolean => { + if (distance === 0) { + inclusive = true; + } + const diff = Math.abs(a - b); + return inclusive ? diff <= distance : diff < distance; }; /** diff --git a/packages/translate/__test__/elf-translate-element.test.js b/packages/translate/__test__/elf-translate-element.test.js index 338ca379e3..a4f23228ce 100644 --- a/packages/translate/__test__/elf-translate-element.test.js +++ b/packages/translate/__test__/elf-translate-element.test.js @@ -47,7 +47,7 @@ describe('Elf Translate Element Lang Test', () => { expect(el.defaultEl.innerText).to.equal('Региональные настройки: ru'); el.lang = 'en'; await elementUpdated(el); - await nextFrame(el); // need for IE11 + await nextFrame(); // need for IE11 expect(el.defaultEl.innerText).to.equal('This is en locale'); }); @@ -56,11 +56,11 @@ describe('Elf Translate Element Lang Test', () => { expect(el.numberEl.innerText).to.equal('Long number: 0'); el.number = 1000; await elementUpdated(el); - await nextFrame(el); + await nextFrame(); expect(el.numberEl.innerText).to.equal('Long number: 1,000'); el.number = 1000000; await elementUpdated(el); - await nextFrame(el); + await nextFrame(); expect(el.numberEl.innerText).to.equal('Long number: 1,000,000'); }); diff --git a/packages/translate/__test__/elf-translate.navigator.test.js b/packages/translate/__test__/elf-translate.navigator.test.js index 42460baf66..f56bcc8f07 100644 --- a/packages/translate/__test__/elf-translate.navigator.test.js +++ b/packages/translate/__test__/elf-translate.navigator.test.js @@ -20,13 +20,13 @@ describe('Elf Translate Navigator Test', () => { } document.documentElement.lang = 'en-US'; - await nextFrame(el); + await nextFrame(); expect(el.defaultEl.innerText).to.equal('This is en-US locale', 'Document locale should take priority over navigator'); el.lang = 'en-GB'; await elementUpdated(el); - await nextFrame(el); // need for IE11 + await nextFrame(); // need for IE11 expect(el.defaultEl.innerText).to.equal('This is en locale', 'Element locale should take priority over document locale'); }); });