diff --git a/packages/calcite-components/src/demos/_assets/demo-theme.ts b/packages/calcite-components/src/demos/_assets/demo-theme.ts new file mode 100644 index 00000000000..55c02899401 --- /dev/null +++ b/packages/calcite-components/src/demos/_assets/demo-theme.ts @@ -0,0 +1,112 @@ +/** + * + * Sets the value of a CSS variable to a test value. + * This is useful for testing themed components. + * + * @param token - the token as a CSS variable + * @returns string - the new value for the token + */ +export function getTokenValue(token: string): string { + const tokenValueMap = { + background$: "rgb(252, 244, 52)", + "text-color$": "rgb(239,118,39)", + "border-color$": "rgb(156, 89, 209)", + "background-color$": "rgb(44, 44, 44)", + color$: "rgb(0, 191, 255)", + highlighted$: "rgb(255, 105, 180)", + selected$: "rgb(255, 255, 255)", + shadow$: + "rgb(255, 255, 255) 0px 0px 0px 4px, rgb(255, 105, 180) 0px 0px 0px 5px inset, rgb(0, 191, 255) 0px 0px 0px 9px", + "z-index$": "42", + "(size|space)$": "42px", + } as const; + + const match = Object.entries(tokenValueMap).find(([regexStr]) => { + return new RegExp(regexStr, "g").test(token); + }); + + if (!match) { + console.warn("token not found in tokenValueMap", token); + return tokenValueMap["color$"]; + } + + return match[1]; +} + +/* + * @prop tokens - an array of CSS variables + * @returns a string of CSS variables with their new values. + */ +export function setCSSVariables(tokens: string[]): string { + return tokens + .map((token) => { + return `${token}: ${getTokenValue(token)};`; + }) + .join("\n"); +} + +/** + * + * @example + * Button + */ +export class DemoTheme extends HTMLElement { + _slot: HTMLSlotElement; + + _el: HTMLElement; + + static observedAttributes = ["tokens"]; + + constructor() { + super(); + const shadow = this.attachShadow({ mode: "open" }); + const slot = document.createElement("slot"); + shadow.append(slot); + this._slot = slot; + if (this._slot.assignedNodes().length === 1 && this._slot.assignedNodes()[0].nodeName.includes("calcite")) { + this._el = this._slot.assignedNodes()[0] as HTMLElement; + } + } + + attributeChangedCallback(name: string, oldValue: string, newValue: string): void { + if (newValue !== oldValue && name === "tokens") { + this.updateTheme(newValue); + } + } + + updateTheme(newValue: string): void { + if (typeof newValue === "string") { + let tokensList; + + try { + tokensList = JSON.parse(newValue); + } catch (error) { + tokensList = newValue.split(",").map((t) => t.trim()); + } + + if (Array.isArray(tokensList)) { + const stringifiedTheme = setCSSVariables(tokensList); + + if (this._el) { + this._el.style.cssText = stringifiedTheme; + } else { + this.setAttribute("style", stringifiedTheme); + } + } + } + } +} + +customElements.define("demo-theme", DemoTheme); diff --git a/packages/calcite-components/src/demos/_assets/head.ts b/packages/calcite-components/src/demos/_assets/head.ts index eeda4a8ab55..b82226531b6 100644 --- a/packages/calcite-components/src/demos/_assets/head.ts +++ b/packages/calcite-components/src/demos/_assets/head.ts @@ -18,6 +18,10 @@ { src: "demos/_assets/demo-dom-swapper.js", }, + { + src: "demos/_assets/demo-theme.js", + type: "module", + }, ]; const parseTemplate = (text: string): HTMLTemplateElement | null => { diff --git a/packages/calcite-components/src/tests/commonTests/themed.ts b/packages/calcite-components/src/tests/commonTests/themed.ts index 00524d01382..363577eb8f4 100644 --- a/packages/calcite-components/src/tests/commonTests/themed.ts +++ b/packages/calcite-components/src/tests/commonTests/themed.ts @@ -2,6 +2,7 @@ import { E2EElement, E2EPage } from "@stencil/core/testing"; import { toHaveNoViolations } from "jest-axe"; import { ElementHandle } from "puppeteer"; import type { RequireExactlyOne } from "type-fest"; +import { getTokenValue } from "../utils/cssTokenValues"; import type { ComponentTestSetup } from "./interfaces"; import { getTagAndPage } from "./utils"; @@ -101,7 +102,7 @@ export function themed(componentTestSetup: ComponentTestSetup, tokens: Component // Set test values for each token if (!setTokens[token]) { - setTokens[token] = assignTestTokenThemeValues(token); + setTokens[token] = getTokenValue(token); } // Set up styleTargets and testTargets @@ -426,21 +427,3 @@ async function assertThemedProps(page: E2EPage, options: TestTarget): Promise { + return new RegExp(regexStr, "g").test(token); + }); + + if (!match) { + console.warn("token not found in tokenValueMap", token); + return tokenValueMap["color$"]; + } + + return match[1]; +} + +/** + * + * @param tokens - an array of CSS variables + * @returns a string of CSS variables with their new values. + */ +export function setCSSVariables(tokens: string[]): string { + return tokens + .map((token) => { + return `${token}: ${getTokenValue(token)};`; + }) + .join("\n"); +}