Skip to content

Commit

Permalink
feat: Support optional target document for applyTheme (#73)
Browse files Browse the repository at this point in the history
Co-authored-by: Donita Almeida <[email protected]>
  • Loading branch information
almeidadonita and donitaa authored Apr 3, 2024
1 parent 2a31329 commit 5379a78
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 13 deletions.
154 changes: 154 additions & 0 deletions src/browser/__tests__/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,160 @@ exports[`with secondary theme > attaches one style node containing override 1`]
}"
`;

exports[`with targetDocument > attaches one style node containing override on the target document 1`] = `
":root:root{
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--shadow-css:yellow;
--buttonShadow-css:red;
--boxShadow-css:green;
--lineShadow-css:pink;
--small-css:1px;
--medium-css:3px;
--scaledSize-css:3px;
--appear-css:20ms;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}
.compact.compact:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--small-css:1px;
--medium-css:3px;
--scaledSize-css:1px;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}
@media not print {.dark.dark:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--shadow-css:orange;
--buttonShadow-css:red;
--boxShadow-css:brown;
--lineShadow-css:pink;
--small-css:1px;
--medium-css:3px;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}}
.disabled-motion.disabled-motion:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--small-css:1px;
--medium-css:3px;
--appear-css:0;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}
.navigation.navigation:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--shadow-css:pink;
--buttonShadow-css:red;
--boxShadow-css:purple;
--lineShadow-css:pink;
--small-css:1px;
--medium-css:3px;
--scaledSize-css:3px;
--appear-css:20ms;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}
.compact.compact .navigation:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--small-css:1px;
--medium-css:3px;
--scaledSize-css:1px;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}
.compact.compact.navigation:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--small-css:1px;
--medium-css:3px;
--scaledSize-css:1px;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}
@media not print {.dark.dark .navigation:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--shadow-css:brown;
--buttonShadow-css:green;
--boxShadow-css:purple;
--lineShadow-css:pink;
--small-css:1px;
--medium-css:3px;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}}
@media not print {.dark.dark.navigation:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--shadow-css:brown;
--buttonShadow-css:green;
--boxShadow-css:purple;
--lineShadow-css:pink;
--small-css:1px;
--medium-css:3px;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}}
.disabled-motion.disabled-motion .navigation:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--small-css:1px;
--medium-css:3px;
--appear-css:0;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}
.disabled-motion.disabled-motion.navigation:not(#\\9){
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
--fontFamilyBody-css:"Helvetica Neue", Arial, sans-serif;
--black-css:black;
--grey-css:grey;
--brown-css:brown;
--small-css:1px;
--medium-css:3px;
--appear-css:0;
--containerShadowBase-css:2px 3px orange, -1px 0 8px olive;
--modalShadowContainer-css:2px 3px orange, -1px 0 8px olive;
}"
`;

exports[`without secondary theme > attaches one style node containing override 1`] = `
":root:root{
--fontFamilyBase-css:"Helvetica Neue", Arial, sans-serif;
Expand Down
26 changes: 22 additions & 4 deletions src/browser/__tests__/dom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
import { describe, test, expect, afterEach } from 'vitest';
import { getNonce, createStyleNode, appendStyleNode } from '../dom';

function addMetaTag(name: string, content: string) {
function addMetaTag(name: string, content: string, targetDocument: Document = document) {
const node = document.createElement('meta');
node.name = name;
node.content = content;

document.head.appendChild(node);
targetDocument.head.appendChild(node);
}

function removeMetaTag(name: string) {
document.querySelector(`meta[name=${name}]`)?.remove();
function removeMetaTag(name: string, targetDocument: Document = document) {
targetDocument.querySelector(`meta[name=${name}]`)?.remove();
}

describe('getNonce', () => {
Expand All @@ -34,6 +34,16 @@ describe('getNonce', () => {

expect(actual).toEqual(nonce);
});

test('parses nonce from meta tag of the target document', () => {
const nonce = 'nonce-23safq34t';
const targetDocument = document.implementation.createHTMLDocument('');
addMetaTag('nonce', nonce, targetDocument);

const actual = getNonce(targetDocument);

expect(actual).toEqual(nonce);
});
});

describe('createStyleNode', () => {
Expand All @@ -58,4 +68,12 @@ describe('appendStyleNode', () => {

expect(document.contains(node)).toBeTruthy();
});

test('is attached to the passed document', () => {
const node = document.createElement('style');
const targetDocument = document.implementation.createHTMLDocument('');
appendStyleNode(node, targetDocument);

expect(targetDocument.contains(node)).toBeTruthy();
});
});
25 changes: 24 additions & 1 deletion src/browser/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,27 @@ describe('with baseThemeId', () => {
});
});

const allStyleNodes = () => document.head.querySelectorAll('style');
describe('with targetDocument', () => {
test('attaches one style node containing override on the target document', () => {
const targetDocument = document.implementation.createHTMLDocument();
applyTheme({ override, preset, targetDocument });

const styleNodes = allStyleNodes(targetDocument);

expect(styleNodes).toHaveLength(1);
const themeNode = styleNodes[0];

expect(themeNode.innerHTML).toMatchSnapshot();
});

test('removes style node on reset on the target document', () => {
const targetDocument = document.implementation.createHTMLDocument();
const { reset } = applyTheme({ override, preset, targetDocument });

reset();

expect(allStyleNodes(targetDocument)).toHaveLength(0);
});
});

const allStyleNodes = (targetDocument: Document = document) => targetDocument.head.querySelectorAll('style');
11 changes: 6 additions & 5 deletions src/browser/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

/**
* Creates a style elemenet from CSS String and an optional nonce value and returns it. The node
* Creates a style element from CSS String and an optional nonce value and returns it. The node
* will have an extra attribute for identification.
* @param content CSS string
* @param nonce optional nonce to be added to the element
Expand All @@ -19,15 +19,16 @@ export function createStyleNode(content: string, nonce?: string): HTMLStyleEleme
return node;
}

export function appendStyleNode(node: HTMLStyleElement): void {
document.head.appendChild(node);
export function appendStyleNode(node: HTMLStyleElement, targetDocument: Document = document): void {
targetDocument.head.appendChild(node);
}

/**
* Parses meta tags to find name="nonce" and returns the value
* @param targetDocument optional target HTML document to parse meta tags from. By default current document is used.
* @returns nonce from meta tag
*/
export function getNonce(): string | undefined {
const metaTag = document.querySelector<HTMLMetaElement>('meta[name="nonce"]');
export function getNonce(targetDocument: Document = document): string | undefined {
const metaTag = targetDocument.querySelector<HTMLMetaElement>('meta[name="nonce"]');
return metaTag?.content ?? undefined;
}
7 changes: 4 additions & 3 deletions src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ export interface ApplyThemeParams {
override: Override;
preset: ThemePreset;
baseThemeId?: string;
targetDocument?: Document;
}

export interface ApplyThemeResult {
reset: () => void;
}

export function applyTheme(params: ApplyThemeParams): ApplyThemeResult {
const { override, preset, baseThemeId } = params;
const { override, preset, baseThemeId, targetDocument } = params;

const availableContexts = getContexts(preset);

Expand All @@ -31,10 +32,10 @@ export function applyTheme(params: ApplyThemeParams): ApplyThemeResult {
preset.propertiesMap,
createMultiThemeCustomizer(preset.theme.selector)
);
const nonce = getNonce();
const nonce = getNonce(targetDocument);
const styleNode = createStyleNode(content, nonce);

appendStyleNode(styleNode);
appendStyleNode(styleNode, targetDocument);

return {
reset: () => {
Expand Down

0 comments on commit 5379a78

Please sign in to comment.