diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d1d5a6658f..efdc6ef077 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,7 @@ on: - .github/workflows/test.yml - "packages/**" - "apps/storybook/**" + - "apps/www/**" permissions: checks: write diff --git a/apps/www/app/_components/css-attributes/css-attributes.test.ts b/apps/www/app/_components/css-attributes/css-attributes.test.ts new file mode 100644 index 0000000000..da5cb82438 --- /dev/null +++ b/apps/www/app/_components/css-attributes/css-attributes.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'vitest'; + +import { getAttributes } from './css-attributes'; + +describe('getAttributes', () => { + it('returns public data attributes', () => { + const attributes = getAttributes(` + .ds-chip[data-variant='primary'] {} + .ds-chip[data-removable] {} + `); + + expect(attributes).toEqual({ + removable: '', + variant: "'primary'", + }); + }); + + it('filters internal floating UI selectors', () => { + const attributes = getAttributes(` + .ds-popover[data-floating] {} + .ds-tooltip[data-floating|='top'] {} + .ds-combobox [data-floating-ui-portal] {} + .ds-chip[data-removable] {} + `); + + expect(attributes).toEqual({ removable: '' }); + }); + + it('continues to filter global attributes', () => { + const attributes = getAttributes(` + [data-color='neutral'] {} + [data-size='sm'] {} + [data-color-scheme='dark'] {} + [data-removable] {} + `); + + expect(attributes).toEqual({ removable: '' }); + }); + + it('returns an empty object when no data attributes match', () => { + expect(getAttributes('.ds-button:hover {}')).toEqual({}); + }); +}); diff --git a/apps/www/app/_components/css-attributes/css-attributes.tsx b/apps/www/app/_components/css-attributes/css-attributes.tsx index 6e8227da98..c7aedb93cf 100644 --- a/apps/www/app/_components/css-attributes/css-attributes.tsx +++ b/apps/www/app/_components/css-attributes/css-attributes.tsx @@ -13,6 +13,15 @@ type CssAttributesProps = { }; } & TableProps; +const EXCLUDED_ATTRIBUTES = new Set([ + 'color', + 'size', + 'color-scheme', + // Floating UI positioning hooks are internal and not authored by consumers. + 'floating', + 'floating-ui-portal', +]); + export const CssAttributes = forwardRef( function CssAttributes({ vars, className, ...rest }, ref) { const { t } = useTranslation(); @@ -62,8 +71,6 @@ export const CssAttributes = forwardRef( /* returns data-attributes and their possible values as key value pairs*/ export function getAttributes(css: string) { const res: { [key: string]: Set | string } = {}; - //filter out global attributes referenced locally - const globals = ['color', 'size', 'color-scheme']; const allAttrs = Array.from( css.matchAll(/\[data-([^=\]|$~*^]+)(?:([|$~*^]?=)([^\]]+))?\]/g), @@ -71,7 +78,7 @@ export function getAttributes(css: string) { for (const attr of allAttrs) { for (const [key, value] of Object.entries(attr)) { - if (globals.includes(key)) continue; + if (EXCLUDED_ATTRIBUTES.has(key)) continue; if (!res[key]) { res[key] = new Set(); } diff --git a/apps/www/vite.config.ts b/apps/www/vite.config.ts index 211eedf338..9fb9f06289 100644 --- a/apps/www/vite.config.ts +++ b/apps/www/vite.config.ts @@ -1,6 +1,6 @@ import { fileURLToPath } from 'node:url'; import { reactRouter } from '@react-router/dev/vite'; -import { defineConfig } from 'vite'; +import { defineConfig } from 'vitest/config'; import { envOnlyMacros } from 'vite-env-only'; import tsconfigPaths from 'vite-tsconfig-paths'; @@ -51,4 +51,8 @@ export default defineConfig(({ isSsrBuild, command }) => ({ clientFiles: ['./app/root.tsx', './app/entry.client.tsx'], }, }, + test: { + environment: 'node', + globals: true, + }, })); diff --git a/vitest.config.mjs b/vitest.config.mjs index 33cfeb65cb..27586ccaa1 100644 --- a/vitest.config.mjs +++ b/vitest.config.mjs @@ -3,7 +3,10 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - projects: ['{packages,internal}/*/vitest.config.mjs'], + projects: [ + '{packages,internal}/*/vitest.config.mjs', + 'apps/www/vite.config.ts', + ], reporters: [ 'default', ['junit', { suiteName: 'Unit tests', addFileAttribute: true }],