diff --git a/config/jest.setup.js b/config/jest.setup.js index 5f93791..0166051 100644 --- a/config/jest.setup.js +++ b/config/jest.setup.js @@ -2,6 +2,7 @@ import '@testing-library/jest-dom/extend-expect' import FDBFactory from 'fake-indexeddb/build/FDBFactory' import FDBKeyRange from 'fake-indexeddb/build/FDBKeyRange' import { Crypto } from '@peculiar/webcrypto' +import { ResizeObserver } from 'd2l-resize-aware/resize-observer-module.js' jest.mock('node-fetch', () => require('fetch-mock-jest').sandbox()) jest.setTimeout(60000) @@ -9,6 +10,7 @@ jest.setTimeout(60000) global.fetch = require('node-fetch') global.Response = fetch.Response global.crypto = new Crypto() +global.ResizeObserver = ResizeObserver process.env.NODE_ENV = 'test' diff --git a/jest.config.cjs b/jest.config.cjs index 8203699..82f8563 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -15,7 +15,7 @@ module.exports = { }, moduleFileExtensions: ['js', 'svelte'], testPathIgnorePatterns: ['node_modules'], - transformIgnorePatterns: ['/node_modules/(?!lodash-es|if-emoji)'], + transformIgnorePatterns: ['/node_modules/(?!lodash-es|if-emoji|d2l-resize-aware)'], bail: true, verbose: true, silent: false, diff --git a/package.json b/package.json index d268e17..b9cd8c6 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "compression": "^1.7.4", "conventional-changelog-cli": "^2.1.1", "cssnano": "^4.1.10", + "d2l-resize-aware": "BrightspaceUI/resize-aware#semver:^1.2.2", "emoji-picker-element-data": "^1.0.0", "emojibase-data": "^5.1.1", "express": "^4.17.1", diff --git a/src/picker/components/Picker/Picker.js b/src/picker/components/Picker/Picker.js index 8598b36..4266bad 100644 --- a/src/picker/components/Picker/Picker.js +++ b/src/picker/components/Picker/Picker.js @@ -20,7 +20,7 @@ import { } from '../../constants' import { uniqBy } from '../../../shared/uniqBy' import { summarizeEmojisForUI } from '../../utils/summarizeEmojisForUI' -import { calculateWidth, resizeObserverSupported } from '../../utils/calculateWidth' +import * as widthCalculator from '../../utils/widthCalculator' import { checkZwjSupport } from '../../utils/checkZwjSupport' import { requestPostAnimationFrame } from '../../utils/requestPostAnimationFrame' import { stop } from '../../../shared/marks' @@ -253,7 +253,7 @@ $: { // eslint-disable-next-line no-unused-vars function calculateEmojiGridWidth (node) { - return calculateWidth(node, width => { + return widthCalculator.calculateWidth(node, width => { /* istanbul ignore next */ const newNumColumns = process.env.NODE_ENV === 'test' ? DEFAULT_NUM_COLUMNS @@ -280,7 +280,7 @@ $: currentGroup = groups[currentGroupIndex] // eslint-disable-next-line no-unused-vars function calculateIndicatorWidth (node) { - return calculateWidth(node, width => { + return widthCalculator.calculateWidth(node, width => { computedIndicatorWidth = width }) } @@ -290,14 +290,12 @@ function calculateIndicatorWidth (node) { // So we calculate of the indicator and use exact pixel values in the animation instead // (where ResizeObserver is supported). $: { - /* istanbul ignore if */ - if (resizeObserverSupported) { - // eslint-disable-next-line no-unused-vars - indicatorStyle = `transform: translateX(${currentGroupIndex * computedIndicatorWidth}px);` // exact pixels - } else { - // eslint-disable-next-line no-unused-vars - indicatorStyle = `transform: translateX(${currentGroupIndex * 100}%);`// fallback to percent-based - } + // eslint-disable-next-line no-unused-vars + indicatorStyle = `transform: translateX(${ + widthCalculator.resizeObserverSupported + ? `${currentGroupIndex * computedIndicatorWidth}px` // exact pixels + : `${currentGroupIndex * 100}%` // fallback to percent-based + })` } // diff --git a/src/picker/utils/calculateWidth.js b/src/picker/utils/widthCalculator.js similarity index 77% rename from src/picker/utils/calculateWidth.js rename to src/picker/utils/widthCalculator.js index ae6cf4f..9d5089d 100644 --- a/src/picker/utils/calculateWidth.js +++ b/src/picker/utils/widthCalculator.js @@ -4,11 +4,15 @@ import { requestAnimationFrame } from './requestAnimationFrame' -export const resizeObserverSupported = typeof ResizeObserver === 'function' +export let resizeObserverSupported = typeof ResizeObserver === 'function' + +// only used in jest tests +export const resetResizeObserverSupported = () => { + resizeObserverSupported = typeof ResizeObserver === 'function' +} export function calculateWidth (node, onUpdate) { let resizeObserver - /* istanbul ignore if */ if (resizeObserverSupported) { resizeObserver = new ResizeObserver(entries => ( onUpdate(entries[0].contentRect.width) @@ -22,7 +26,6 @@ export function calculateWidth (node, onUpdate) { return { destroy () { - /* istanbul ignore if */ if (resizeObserver) { resizeObserver.disconnect() } diff --git a/test/spec/picker/noResizeObserver.test.js b/test/spec/picker/noResizeObserver.test.js new file mode 100644 index 0000000..4ec51a7 --- /dev/null +++ b/test/spec/picker/noResizeObserver.test.js @@ -0,0 +1,65 @@ +import { groups } from '../../../src/picker/groups' +import * as testingLibrary from '@testing-library/dom' +import { + waitFor, getAllByRole, + getByRole, fireEvent +} from '@testing-library/dom' +import { ALL_EMOJI, basicAfterEach, basicBeforeEach, tick, truncatedEmoji } from '../shared' +import Picker from '../../../src/picker/PickerElement' +import Database from '../../../src/database/Database' +import { resetResizeObserverSupported } from '../../../src/picker/utils/widthCalculator' + +// TODO: we can remove these tests when/if we stop supporting browsers without ResizeObserver +// https://caniuse.com/resizeobserver + +describe('ResizeObserver unsupported', () => { + let picker + let container + let oldResizeObserver + + beforeEach(async () => { + basicBeforeEach() + + oldResizeObserver = global.ResizeObserver + delete global.ResizeObserver + resetResizeObserverSupported() + + picker = new Picker({ dataSource: ALL_EMOJI }) + document.body.appendChild(picker) + container = picker.shadowRoot.querySelector('.picker') + await tick(40) + }) + + afterEach(async () => { + await tick(40) + document.body.removeChild(picker) + await tick(40) + await new Database({ dataSource: ALL_EMOJI }).delete() + await tick(40) + basicAfterEach() + + global.ResizeObserver = oldResizeObserver + resetResizeObserverSupported() + }) + + test('basic picker test', async () => { + const numInGroup1 = truncatedEmoji.filter(_ => _.group === 0).length + const numInGroup2 = truncatedEmoji.filter(_ => _.group === 1).length + + await waitFor(() => expect(getByRole(container, 'button', { name: 'Choose a skin tone (currently Default)' })).toBeVisible()) + expect(getAllByRole(container, 'tab')).toHaveLength(groups.length) + + expect(getByRole(container, 'tab', { name: 'Smileys and emoticons', selected: true })).toBeVisible() + await waitFor(() => expect( + testingLibrary.getAllByRole(getByRole(container, 'tabpanel'), 'menuitem')).toHaveLength(numInGroup1) + ) + + expect(getByRole(container, 'tab', { name: 'People and body' })).toBeVisible() + fireEvent.click(getByRole(container, 'tab', { name: 'People and body' })) + + await waitFor(() => expect( + testingLibrary.getAllByRole(getByRole(container, 'tabpanel'), 'menuitem')).toHaveLength(numInGroup2)) + + expect(getByRole(container, 'tab', { name: 'People and body', selected: true })).toBeVisible() + }) +}) diff --git a/yarn.lock b/yarn.lock index 6145cbe..324ee36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1500,6 +1500,13 @@ tslib "^2.0.3" webcrypto-core "^1.1.8" +"@polymer/polymer@^3.0.0": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@polymer/polymer/-/polymer-3.4.1.tgz#333bef25711f8411bb5624fb3eba8212ef8bee96" + integrity sha512-KPWnhDZibtqKrUz7enIPOiO4ZQoJNOuLwqrhV2MXzIt3VVnUVJVG5ORz4Z2sgO+UZ+/UZnPD0jqY+jmw/+a9mQ== + dependencies: + "@webcomponents/shadycss" "^1.9.1" + "@rollup/plugin-commonjs@^16.0.0": version "16.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" @@ -1842,6 +1849,11 @@ dependencies: "@types/node" "*" +"@webcomponents/shadycss@^1.9.1": + version "1.10.2" + resolved "https://registry.yarnpkg.com/@webcomponents/shadycss/-/shadycss-1.10.2.tgz#40e03cab6dc5e12f199949ba2b79e02f183d1e7b" + integrity sha512-9Iseu8bRtecb0klvv+WXZOVZatsRkbaH7M97Z+f+Pt909R4lDfgUODAnra23DOZTpeMTAkVpf4m/FZztN7Ox1A== + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -3419,6 +3431,12 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" +"d2l-resize-aware@BrightspaceUI/resize-aware#semver:^1.2.2": + version "1.2.2" + resolved "https://codeload.github.com/BrightspaceUI/resize-aware/tar.gz/96ba3ac560380abfb753f128ac9a5c0992d7d723" + dependencies: + "@polymer/polymer" "^3.0.0" + dargs@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17"