diff --git a/components/.storybook/main.ts b/components/.storybook/main.ts index 5354b4c7..a03f9136 100644 --- a/components/.storybook/main.ts +++ b/components/.storybook/main.ts @@ -1,14 +1,14 @@ -import { dirname, join } from "path"; -import type { StorybookConfig } from "@storybook/sveltekit"; +import { dirname, join } from 'path'; +import type { StorybookConfig } from '@storybook/sveltekit'; function getAbsolutePath(value: string): any { - return dirname(require.resolve(join(value, "package.json"))); + return dirname(require.resolve(join(value, 'package.json'))); } const config: StorybookConfig = { stories: [ - "../src/**/*.stories.@(js|ts|svelte)", - "../src/**/*.mdx", + '../src/**/*.stories.@(js|ts|svelte)', + '../src/**/*.mdx' ], addons: [ @@ -18,20 +18,17 @@ const config: StorybookConfig = { legacyTemplate: true } }, - getAbsolutePath("@storybook/addon-links"), - getAbsolutePath("@storybook/addon-essentials"), - getAbsolutePath("@chromatic-com/storybook"), - getAbsolutePath("@storybook/addon-interactions"), - getAbsolutePath("@storybook/addon-mdx-gfm") + getAbsolutePath('@storybook/addon-links'), + getAbsolutePath('@storybook/addon-essentials'), + getAbsolutePath('@chromatic-com/storybook'), + getAbsolutePath('@storybook/experimental-addon-test') ], framework: { - name: getAbsolutePath("@storybook/sveltekit"), - options: {}, + name: '@storybook/sveltekit', + options: {} }, - docs: { - autodocs: true - } + docs: {} }; -export default config; \ No newline at end of file +export default config; diff --git a/components/.storybook/preview.ts b/components/.storybook/preview.ts index 80325931..ee59718c 100644 --- a/components/.storybook/preview.ts +++ b/components/.storybook/preview.ts @@ -2,6 +2,9 @@ import type { Preview } from "@storybook/svelte"; const preview: Preview = { parameters: { + options: { + storySort: { order: ['About', 'Design Tokens', "Display", "Chart", ["ChartHeader"], "Form", "Deprecated"] }, + }, controls: { matchers: { color: /(background|color)$/i, diff --git a/components/.storybook/vitest.setup.ts b/components/.storybook/vitest.setup.ts new file mode 100644 index 00000000..e9000fa2 --- /dev/null +++ b/components/.storybook/vitest.setup.ts @@ -0,0 +1,9 @@ +import { beforeAll } from 'vitest'; +import { setProjectAnnotations } from '@storybook/sveltekit'; +import * as projectAnnotations from './preview'; + +// This is an important step to apply the right configuration when testing your stories. +// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations +const project = setProjectAnnotations([projectAnnotations]); + +beforeAll(project.beforeAll); \ No newline at end of file diff --git a/components/package.json b/components/package.json index 9a533b84..5a4206db 100644 --- a/components/package.json +++ b/components/package.json @@ -14,7 +14,7 @@ "storybook": "storybook dev -p 6006", "start": "storybook dev -p 6006", "build-storybook": "storybook build --disable-telemetry", - "test-storybook": "test-storybook --browsers chromium firefox", + "test-storybook": "vitest run --project=storybook", "test-storybook:ci": "concurrently -k -s first -n \"Storybook,Test\" -c \"magenta,blue\" \"npm run build-storybook --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && npm run test-storybook\"", "semantic-release": "semantic-release" }, @@ -27,21 +27,23 @@ "@semantic-release/git": "^10.0.1", "@semantic-release/npm": "^12.0.1", "@storybook/addon-essentials": "^8.6.4", - "@storybook/addon-interactions": "^8.6.4", "@storybook/addon-links": "^8.6.4", - "@storybook/addon-mdx-gfm": "^8.6.4", "@storybook/addon-svelte-csf": "^5.0.0-next.27", "@storybook/blocks": "^8.6.4", "@storybook/docs-tools": "^8.6.4", + "@storybook/experimental-addon-test": "^8.6.12", "@storybook/svelte": "^8.6.4", "@storybook/sveltekit": "^8.6.4", "@storybook/test": "^8.6.4", - "@storybook/test-runner": "^0.19.1", + "@storybook/test-runner": "^0.22.0", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@vitest/browser": "^3.1.1", + "@vitest/coverage-v8": "^3.1.1", "concurrently": "^9.0.1", "http-server": "^14.1.1", + "playwright": "^1.51.1", "sass-embedded": "^1.78.0", "semantic-release": "^24.1.2", "storybook": "^8.6.4", @@ -49,6 +51,7 @@ "svelte-check": "^4.0.0", "typescript": "^5.0.0", "vite": "^6.0.0", + "vitest": "^3.1.1", "wait-on": "^8.0.1" }, "type": "module", @@ -65,4 +68,4 @@ } }, "version": "1.0.6" -} +} \ No newline at end of file diff --git a/components/src/Autocomplete/Autocomplete.stories.js b/components/src/Autocomplete/Autocomplete.stories.js deleted file mode 100644 index 54910146..00000000 --- a/components/src/Autocomplete/Autocomplete.stories.js +++ /dev/null @@ -1,61 +0,0 @@ -import { userEvent, within, expect, fn } from "@storybook/test" -import AutoComplete from "./index.js" - -export default { - title: "Example/Autocomplete", - tags: ["autodocs"], - component: AutoComplete, -} - -const testData = ["Apples", "Oranges", "Pears", "Peaches", "Bananas"].map((el) => { - return { - value: el, - label: el, - details: {}, - } -}) - -export const Basic = { - args: { - data: testData, - query: "", - placeholder: "Select a fruit", - }, -} - -export const Test = { - parameters: { - docs: { - story: { - autoplay: true, - }, - }, - }, - args: { - data: testData, - query: "", - placeholder: "Select a fruit", - }, - play: async ({ canvasElement, step }) => { - const canvas = within(canvasElement) - const input = canvas.getByTestId("autocomplete-input") - await step("Select using the mouse", async () => { - await userEvent.click(input) - expect(input).toHaveFocus() - await userEvent.keyboard("ba") - const bananasOption = canvas.getByText("Bananas") - await userEvent.click(bananasOption) - expect(input).toHaveValue("Bananas") - }) - await userEvent.clear(input) - await step("Select using the keyboard", async () => { - await userEvent.click(input) - expect(input).toHaveFocus() - await userEvent.keyboard("ap") - await userEvent.keyboard("{ArrowDown}") - await userEvent.keyboard("{Enter}") - expect(input).toHaveValue("Apples") - }) - await userEvent.clear(input) - }, -} diff --git a/components/src/Autocomplete/Autocomplete.stories.svelte b/components/src/Autocomplete/Autocomplete.stories.svelte new file mode 100644 index 00000000..2832ba00 --- /dev/null +++ b/components/src/Autocomplete/Autocomplete.stories.svelte @@ -0,0 +1,61 @@ + + + { + const canvas = within(canvasElement); + const input = canvas.getByTestId('autocomplete-input'); + await step('Select using the mouse', async () => { + await userEvent.click(input); + expect(input).toHaveFocus(); + await userEvent.keyboard('ba'); + const bananasOption = canvas.getByText('Bananas'); + await userEvent.click(bananasOption); + expect(input).toHaveValue('Bananas'); + }); + await userEvent.clear(input); + await step('Select using the keyboard', async () => { + await userEvent.click(input); + expect(input).toHaveFocus(); + await userEvent.keyboard('ap'); + await userEvent.keyboard('{ArrowDown}'); + await userEvent.keyboard('{Enter}'); + expect(input).toHaveValue('Apples'); + }); + await userEvent.clear(input); + }} +> + +
+ +
+
+
+ + diff --git a/components/src/Autocomplete/Autocomplete.svelte b/components/src/Autocomplete/Autocomplete.svelte index fa3bf769..ce301d64 100644 --- a/components/src/Autocomplete/Autocomplete.svelte +++ b/components/src/Autocomplete/Autocomplete.svelte @@ -108,8 +108,8 @@ Data should be provided as array of objects. Each object contains the informatio autocapitalize="off" data-testid="autocomplete-input" bind:this={inputRef} - on:keydown={handleKeyDown} bind:value={query} + on:keydown={handleKeyDown} on:input={handleInput} on:focus={() => { isActive = true; @@ -150,28 +150,30 @@ Data should be provided as array of objects. Each object contains the informatio diff --git a/components/src/Card/Card.stories.svelte b/components/src/Card/Card.stories.svelte index df2f9e4f..2170d61d 100644 --- a/components/src/Card/Card.stories.svelte +++ b/components/src/Card/Card.stories.svelte @@ -1,27 +1,12 @@ - - - diff --git a/components/src/Card/Card.svelte b/components/src/Card/Card.svelte index e4a6f988..0ee5c537 100644 --- a/components/src/Card/Card.svelte +++ b/components/src/Card/Card.svelte @@ -1,18 +1,31 @@ + +
- + {#if children} + {@render children()} + {/if}
diff --git a/components/src/ChartHeader/ChartHeader.mdx b/components/src/ChartHeader/ChartHeader.mdx new file mode 100644 index 00000000..006a52c7 --- /dev/null +++ b/components/src/ChartHeader/ChartHeader.mdx @@ -0,0 +1,15 @@ +import { Story, Meta, Primary, Controls, Stories } from '@storybook/blocks'; + +import * as ChartHeaderStories from './ChartHeader.stories.svelte'; + + + +# Chart header + +Standard chart header. + +- Any nested HTML will render under the description (useful for i.e colour keys and interactives controls). + + + + \ No newline at end of file diff --git a/components/src/ChartHeader/ChartHeader.stories.svelte b/components/src/ChartHeader/ChartHeader.stories.svelte index 0694fcd5..3cc4e0eb 100644 --- a/components/src/ChartHeader/ChartHeader.stories.svelte +++ b/components/src/ChartHeader/ChartHeader.stories.svelte @@ -1,19 +1,22 @@ - - - - + + + + Arbitrary HTML content can go here + + diff --git a/components/src/ChartHeader/ChartHeader.svelte b/components/src/ChartHeader/ChartHeader.svelte index d842d97c..007f7e59 100644 --- a/components/src/ChartHeader/ChartHeader.svelte +++ b/components/src/ChartHeader/ChartHeader.svelte @@ -1,21 +1,27 @@ - - -
-

{title}

+
+

{title}

+ {#if subtitle}

{subtitle}

- -
- - - diff --git a/components/src/Container/index.js b/components/src/Container/index.js deleted file mode 100644 index 27e630cd..00000000 --- a/components/src/Container/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import Container from './Container.svelte'; -export default Container; diff --git a/components/src/DesignTokens/DesignTokens.mdx b/components/src/DesignTokens/DesignTokens.mdx index 811c8075..7d795c9d 100644 --- a/components/src/DesignTokens/DesignTokens.mdx +++ b/components/src/DesignTokens/DesignTokens.mdx @@ -1,5 +1,5 @@ import { Title, Story, ColorPalette, ColorItem, Meta, Typeset } from '@storybook/blocks'; -import {shades, scales} from "./Tokens" +import { shades, scales } from "./Tokens" @@ -7,11 +7,13 @@ import {shades, scales} from "./Tokens" ## Usage +### Web + The `` component makes colours, type sizes and other design tokens available to its children as CSS variables. -```jsx +```html @@ -25,14 +27,18 @@ The `` component makes colours, type sizes and other design tokens ``` -You can also import from `Tokens` directly to use colours in maps and charts: +You can also import `tokens` directly to use them in Javascript: -```js -import { scales } from 'Tokens' +```jsx +import tokens from "@swr-data-lab/components" import { scaleThreshold } from 'd3-scale'; -const sampleScale = scaleThreshold([0, 10], scales.red_blue); +const sampleScale = scaleThreshold([0, 10], tokens.scales.red_blue); ``` +### Figma + +The tokens on this page are available as a [Figma library](https://www.figma.com/design/Wrd7uUV3GxpNXXkM5ozuHk/Datalab-Design-Tokens?node-id=0-1&t=iOqDxhuMdvEyY8VK-1). This is enabled by default for any file in the [SWR Data Lab team](https://www.figma.com/files/1125823985461580916/team/1415988138343592768). + ## Available Tokens ### Typography @@ -40,7 +46,7 @@ const sampleScale = scaleThreshold([0, 10], scales.red_blue); ### Colours -{["violet", "red", "teal", "yellow", "forest", "pink", "orange", "plum", "apple", "blue", "gray"].map((el, i) => { +{["violet", "plum", "pink", "red", "orange", "yellow", "apple" , "forest","teal", "blue", "gray"].map((el, i) => { return( {return c[0].toUpperCase() + c.slice(1)}).join("-")}`} subtitle={el} colors={scales[el]} /> diff --git a/components/src/DesignTokens/DesignTokens.svelte b/components/src/DesignTokens/DesignTokens.svelte index 45376200..6fc98478 100644 --- a/components/src/DesignTokens/DesignTokens.svelte +++ b/components/src/DesignTokens/DesignTokens.svelte @@ -16,12 +16,13 @@ 'display: contents', '--fast: 150ms', '--slow: 300ms', + '--app-max-width: 40rem', '--br-large: 8px', '--br-small: 4px', - '--ratio: 1.15', '--input-height: 2.5rem', '--swr-sans: "SWR-VAR-Sans", sans-serif', '--swr-text: "SWR-VAR-Text", sans-serif', + '--ratio: 1.15', '--fs-small-3: calc(var(--fs-small-2) / var(--ratio))', '--fs-small-2: calc(var(--fs-small-1) / var(--ratio))', '--fs-small-1: calc(var(--fs-base) / var(--ratio))', diff --git a/components/src/DesignTokens/index.js b/components/src/DesignTokens/index.js index 38d9285d..824d9bbc 100644 --- a/components/src/DesignTokens/index.js +++ b/components/src/DesignTokens/index.js @@ -1,2 +1,9 @@ import DesignTokens from './DesignTokens.svelte'; -export default DesignTokens; +import { shades, scales } from './Tokens'; + +const tokens = { + shades, + scales +}; + +export { DesignTokens, tokens }; diff --git a/components/src/HighlightCard/HighlightCard.stories.svelte b/components/src/HighlightCard/HighlightCard.stories.svelte index 5fb8c548..f79dc69d 100644 --- a/components/src/HighlightCard/HighlightCard.stories.svelte +++ b/components/src/HighlightCard/HighlightCard.stories.svelte @@ -1,26 +1,12 @@ - - - @@ -63,7 +49,6 @@ :global(.highlight-cards > *) { margin: 1rem 0; - @media (min-width: 900px) { margin: 2rem 1rem; flex: 1; diff --git a/components/src/HighlightCard/HighlightCard.svelte b/components/src/HighlightCard/HighlightCard.svelte index fdf7caf5..80a98a7a 100644 --- a/components/src/HighlightCard/HighlightCard.svelte +++ b/components/src/HighlightCard/HighlightCard.svelte @@ -1,10 +1,14 @@ - @@ -20,15 +24,15 @@ {unit} -
- {@html subline} -
+ {#if subline} +
+ {@html subline} +
+ {/if}
diff --git a/components/src/Select/Select.types.ts b/components/src/Select/Select.types.ts new file mode 100644 index 00000000..0c6e812d --- /dev/null +++ b/components/src/Select/Select.types.ts @@ -0,0 +1,8 @@ +interface SelectItem { + value: string; + label: string; + group?: string; + details?: any; +} + +export { type SelectItem } \ No newline at end of file diff --git a/components/src/Select/SelectStoriesTemplate.svelte b/components/src/Select/SelectStoriesTemplate.svelte new file mode 100644 index 00000000..b9454cdd --- /dev/null +++ b/components/src/Select/SelectStoriesTemplate.svelte @@ -0,0 +1,35 @@ + + + + {#if demoComponent} + {@render demoComponent()} + {:else} + +