diff --git a/labs/gb/styles/adopt-styles.ts b/labs/gb/styles/adopt-styles.ts index ad756f49ea..fb02fe7707 100644 --- a/labs/gb/styles/adopt-styles.ts +++ b/labs/gb/styles/adopt-styles.ts @@ -68,8 +68,15 @@ function adopt( stylesheets: CSSStyleSheet[], ): node is DocumentOrShadowRoot { if (node && 'adoptedStyleSheets' in node) { + const unadopted = stylesheets.filter( + (stylesheet) => !node.adoptedStyleSheets.includes(stylesheet), + ); + if (!unadopted.length) { + // All styles are already adopted. + return true; + } node.adoptedStyleSheets = Array.from( - new Set([...node.adoptedStyleSheets, ...stylesheets]), + new Set([...node.adoptedStyleSheets, ...unadopted]), ); return true; } diff --git a/labs/gb/styles/adopt-styles_test.ts b/labs/gb/styles/adopt-styles_test.ts index e190ea283e..d972904f59 100644 --- a/labs/gb/styles/adopt-styles_test.ts +++ b/labs/gb/styles/adopt-styles_test.ts @@ -34,6 +34,40 @@ describe('adoptStyles()', () => { .toContain(sheet); }); + it('should not duplicate stylesheets if already adopted', () => { + adoptStyles(document, sheet); + adoptStyles(document, sheet); + + expect(document.adoptedStyleSheets) + .withContext('document.adoptedStyleSheets') + .toHaveSize(1); + }); + + it('should only adopt new stylesheets when given an array that includes adopted sheets', () => { + const alreadyAdopted = new CSSStyleSheet(); + adoptStyles(document, alreadyAdopted); + adoptStyles(document, [sheet, alreadyAdopted]); + + expect(document.adoptedStyleSheets) + .withContext('document.adoptedStyleSheets') + .toHaveSize(2); + }); + + it('should not mutate adoptedStyleSheets if the sheets are already adopted', () => { + adoptStyles(document, sheet); + const previousAdoptedStyleSheets = document.adoptedStyleSheets; + const previousSize = previousAdoptedStyleSheets.length; + + adoptStyles(document, sheet); + + expect(document.adoptedStyleSheets) + .withContext('document.adoptedStyleSheets') + .toBe(previousAdoptedStyleSheets); + expect(document.adoptedStyleSheets) + .withContext('document.adoptedStyleSheets') + .toHaveSize(previousSize); + }); + it("should adopt to an Element's document", () => { const element = document.createElement('div'); adoptStyles(element, sheet); diff --git a/labs/gb/styles/icon/md-icon.ts b/labs/gb/styles/icon/md-icon.ts index 09ce2984be..bdba886204 100644 --- a/labs/gb/styles/icon/md-icon.ts +++ b/labs/gb/styles/icon/md-icon.ts @@ -7,6 +7,7 @@ import {type CSSResultOrNative} from 'lit'; import {customElement} from 'lit/decorators.js'; import {Icon as IconBase} from '../../../../icon/internal/icon.js'; +import {adoptStyles} from '../adopt-styles.js'; import iconStyles from './md-icon.css' with {type: 'css'}; // github-only // import iconStyles from './md-icon.cssresult.js'; // google3-only @@ -27,6 +28,8 @@ export class Icon extends IconBase { override connectedCallback() { super.connectedCallback(); + // Adopt stylesheet to ensure global CSS @property variables are registered. + adoptStyles(this, iconStyles); this.classList.add('md-icon'); } }