diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6056f16dd..a0d46bebf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,7 +31,8 @@ jobs: cache: 'npm' - run: npm install - run: npm run lint - - run: sudo npx playwright install-deps + - name: Install Playwright dependencies + run: npx playwright install --with-deps - run: npm run test build: diff --git a/package-lock.json b/package-lock.json index fee808d0e..3ddfb15b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@storybook/web-components": "^7.6.14", "@storybook/web-components-vite": "^7.6.14", "@types/chai": "4.3.12", - "@types/eslint__js": "^8.42.3", + "@types/eslint__js": "8.42.3", "@web/dev-server-esbuild": "0.4.3", "@web/test-runner": "0.18.2", "@web/test-runner-playwright": "0.11.0", @@ -43,7 +43,7 @@ "chromatic": "11.5.5", "element-internals-polyfill": "1.3.10", "esbuild": "0.21.5", - "eslint": "^9.7.0", + "eslint": "9.7.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-lit": "1.14.0", "eslint-plugin-local-rules": "3.0.2", @@ -25283,29 +25283,47 @@ } }, "node_modules/playwright": { - "version": "1.37.1", + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.37.1" + "playwright-core": "1.45.3" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.37.1", + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", "dev": true, - "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/plop": { diff --git a/package.json b/package.json index 1f15ac332..c94dbe446 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,9 @@ "vite-tsconfig-paths": "4.3.2", "web-component-analyzer": "2.0.0" }, + "overrides": { + "playwright": "^1.45.3" + }, "workspaces": [ "./packages/*" ], diff --git a/packages/uui-popover-container/lib/uui-popover-container.element.ts b/packages/uui-popover-container/lib/uui-popover-container.element.ts index 1ed78ebb4..7d37ce1bd 100644 --- a/packages/uui-popover-container/lib/uui-popover-container.element.ts +++ b/packages/uui-popover-container/lib/uui-popover-container.element.ts @@ -2,7 +2,6 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; import { findAncestorByAttributeValue } from '@umbraco-ui/uui-base/lib/utils'; import { css, html, LitElement } from 'lit'; import { property, state } from 'lit/decorators.js'; -import { polyfill } from './uui-popover-polyfill.js'; export type PopoverContainerPlacement = | 'top' @@ -72,10 +71,6 @@ export class UUIPopoverContainerElement extends LitElement { #scrollParents: Element[] = []; connectedCallback(): void { - //TODO: Remove this polyfill when firefox supports the new popover API - !Object.prototype.hasOwnProperty.call(HTMLElement, 'popover') && - polyfill.bind(this)(); - if (!this.hasAttribute('popover')) { this.setAttribute('popover', ''); } diff --git a/packages/uui-popover-container/lib/uui-popover-container.test.ts b/packages/uui-popover-container/lib/uui-popover-container.test.ts index e38fe1fea..a2ad641c8 100644 --- a/packages/uui-popover-container/lib/uui-popover-container.test.ts +++ b/packages/uui-popover-container/lib/uui-popover-container.test.ts @@ -6,7 +6,9 @@ describe('UUIPopoverContainerElement', () => { beforeEach(async () => { element = await fixture(html` - + + Hello world + `); }); @@ -17,4 +19,9 @@ describe('UUIPopoverContainerElement', () => { it('passes the a11y audit', async () => { await expect(element).shadowDom.to.be.accessible(); }); + + it('gets the popover attribute', async () => { + await element.updateComplete; + expect(element).to.have.attribute('popover'); + }); }); diff --git a/packages/uui-popover-container/lib/uui-popover-polyfill.ts b/packages/uui-popover-container/lib/uui-popover-polyfill.ts deleted file mode 100644 index f2fd7dced..000000000 --- a/packages/uui-popover-container/lib/uui-popover-polyfill.ts +++ /dev/null @@ -1,162 +0,0 @@ -// @ts-nocheck - -export function polyfill() { - const originalAddEventListener = this.addEventListener; - - // This is the only way to get access to the private functions onFocusOut and onBeforeToggle. - this.addEventListener = function (type, listener, options) { - if (type === 'focusout') { - // focusout doesn't work properly in firefox, so we ignore it. - return; - } - if (type === 'beforetoggle') { - // Intercept the beforetoggle event so we can dispatch our own event. - this.polyfill_onBeforeToggle = event => { - this.dispatchEvent( - new CustomEvent('polyfill-beforetoggle', { - bubbles: false, - composed: false, - detail: { - oldState: event.oldState, - newState: event.newState, - }, - }), - ); - - listener(event); - }; - return; - } - originalAddEventListener.call(this, type, listener, options); - }; - - this.polyfill_onFocusout = event => { - const target = event.relatedTarget; - if (!target) return; - - const isInsidePopoverContainer = target?.closest('uui-popover-container'); - const isInsidePopoverTarget = target?.closest('[popovertarget]'); - - if (!isInsidePopoverContainer && !isInsidePopoverTarget) { - this.hidePopover(); - } - }; - - this.polyfill_onClick = event => { - const path = event.composedPath(); - const isInsidePopoverContainer = path.some(element => { - return ( - element.tagName === 'UUI-POPOVER-CONTAINER' || - element.attributes?.popovertarget - ); - }); - - if (!isInsidePopoverContainer) { - this.hidePopover(); - } - }; - - this.polyfill_onParentPopoverUpdate = event => { - if (event.detail.newState === 'closed') { - this.hidePopover(); - } - }; - - const findParentPopover = element => { - if (!element.parentElement) return null; - if (element.parentElement?.tagName === 'UUI-POPOVER-CONTAINER') { - return element.parentElement; - } - return findParentPopover(element.parentElement); - }; - - if (!this.polyfill_hasBeenMovedToBody) { - this.style.display = 'none'; - this.style.position = 'fixed'; - this.style.inset = '0'; - this.style.zIndex = '9999'; - } - - this.showPopover = () => { - if (!this.polyfill_hasBeenMovedToBody) { - this.polyfill_parentPopoverContainer = findParentPopover(this); - } - - this.polyfill_onBeforeToggle({ - oldState: 'closed', - newState: 'open', - }); - this.style.display = 'block'; - this.polyfill_parentPopoverContainer?.addEventListener( - 'polyfill-beforetoggle', - this.polyfill_onParentPopoverUpdate, - ); - window.addEventListener('click', this.polyfill_onClick); - window.addEventListener('focusout', this.polyfill_onFocusout); - - //Find the dom position that the popover is currently at and save it so we can restore it later. - this.polyfill_originalParent = this.parentNode; - this.polyfill_originalNextSibling = this.nextSibling; - - //TODO: Find the render root of this component and get the stylesheet from there. - const renderRoot = this.getRootNode(); - - if (renderRoot && renderRoot.adoptedStyleSheets) { - // Styles from the parent are lost when moving the popover to the body, so we need to add them back. - const adoptedStyleSheets = renderRoot.adoptedStyleSheets; - const combinedStyles = adoptedStyleSheets.reduce((acc, styleSheet) => { - return ( - acc + - Object.values(styleSheet.cssRules).reduce((acc, rule) => { - return acc + `#${this.id} ${rule.cssText}`; - }, '') - ); - }, ''); - - const styleTag = document.createElement('style'); - styleTag.innerHTML = combinedStyles; - styleTag.id = 'uui-popover-polyfill-style'; - - //look in slot for existing style tag and remove it. - const existingStyleTag = this.shadowRoot.host.querySelector( - '#uui-popover-polyfill-style', - ); - - if (existingStyleTag) { - existingStyleTag.parentNode.removeChild(existingStyleTag); - } - - this.insertAdjacentElement('beforeend', styleTag); - } - - //Move the popover to the body so it sits on top of everything else. - if (this.parentNode !== document.body) { - this.parentNode?.removeChild(this); - this.polyfill_hasBeenMovedToBody = true; - document.body.appendChild(this); - } - }; - this.hidePopover = () => { - //Restore previous dom position. - if (this.polyfill_hasBeenMovedToBody) { - document.body.removeChild(this); - this.polyfill_hasBeenMovedToBody = false; - this.polyfill_originalParent?.insertBefore( - this, - this.polyfill_originalNextSibling, - ); - } - - window.removeEventListener('click', this.polyfill_onClick); - window.removeEventListener('focusout', this.polyfill_onFocusout); - this.polyfill_parentPopoverContainer?.removeEventListener( - 'polyfill-beforetoggle', - this.polyfill_onParentPopoverUpdate, - ); - this.polyfill_onBeforeToggle({ - oldState: 'open', - newState: 'closed', - }); - this.style.display = 'none'; - }; -}