Skip to content

Commit

Permalink
Upgrade Storybook to 8.5 (#2373)
Browse files Browse the repository at this point in the history
## Summary:

### Upgrading to v8.5.x

Upgrading Storybook to experiment with the new test addon.

We are doing this so we can test the new UI testing capabilities that work with
`vitest` + `Playwright` under the hood.

It will also get us closer to test the new a11y tooling that will be available
in the next days.

NOTE: This new feature is still under development by the Storybook team, so we
are not yet ready to use it in production.

You can see more information about this new testing approach here:

- [Storybook Test bootcamp](https://storybook.us18.list-manage.com/track/click?u=06a6fce3ab1327784d4342396&id=2a2ca202c1&e=50b9ba02d5)
- [Storybook Test addon docs](https://storybook.js.org/docs/writing-tests/test-addon)

### Integrating the new testing approach in CI

Now that we have upgraded to the experimental version, we can take advantage of its new features.

This PR adds a new step to our GH workflows: Running Storybook component tests in CI.

Issue: XXX-XXXX

## Test plan:

- Run `yarn storybook` and check if the new test addon is available.
- Also check that the Chromatic storybook environment still works.

Also verify how the storybook tests failed here:

https://github.com/Khan/wonder-blocks/actions/runs/12057096671/job/33620982112

<img width="1041" alt="Screenshot 2024-11-28 at 2 50 17 PM" src="https://github.com/user-attachments/assets/472fb90b-d98a-442f-9434-d55b3ae87f5f">

Now check that these tests were fixed here:

https://github.com/Khan/wonder-blocks/actions/runs/12074721182/job/33673231130#step:9:1024

<img width="1056" alt="Screenshot 2024-11-28 at 2 51 57 PM" src="https://github.com/user-attachments/assets/df2022d7-d64b-46f3-b740-bdfc052fce3b">

Author: jandrade

Reviewers: jandrade, jeremywiebe, kevinb-khan, beaesguerra, marcysutton

Required Reviewers:

Approved By: kevinb-khan

Checks: ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ Test / Test (ubuntu-latest, 20.x, 2/2), ✅ Lint / Lint (ubuntu-latest, 20.x), ✅ Test / Test (ubuntu-latest, 20.x, 1/2), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ Chromatic - Build and test on regular PRs / chromatic (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ⏭️  Chromatic - Skip on Release PR (changesets), ✅ gerald, ⏭️  dependabot

Pull Request URL: #2373
  • Loading branch information
jandrade authored Jan 22, 2025
1 parent 8d26588 commit a00747f
Show file tree
Hide file tree
Showing 13 changed files with 1,664 additions and 2,742 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/chromatic-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ jobs:
# Generate snapshots for only changed stories
# See: https://www.chromatic.com/docs/turbosnap
onlyChanged: true
# Install Playwright browsers so Vitest browser mode can run story tests
- name: Install playwright dependencies
if: ${{ inputs.target == 'review' }}
run: yarn exec playwright install chromium --with-deps
- name: Run Storybook tests
if: ${{ inputs.target == 'review' }}
run: yarn test:storybook
env:
SB_URL: '${{ steps.chromatic_publish.outputs.storybookUrl }}'

# (if) Run this step on dependabot or changesets PRs.
- name: Skip Chromatic builds (dependabot or changesets PRs)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/chromatic-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
if: |
github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' &&
!startsWith(github.head_ref, 'changeset-release/')
name: Chromatic - Build on regular PRs
name: Chromatic - Build and test on regular PRs
uses: ./.github/workflows/chromatic-build.yml
with:
target: 'review'
Expand Down
50 changes: 6 additions & 44 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import {resolve, dirname, join} from "path";
import {mergeConfig} from "vite";
import turbosnap from "vite-plugin-turbosnap";
import type {StorybookConfig} from "@storybook/react-vite";

const config: StorybookConfig = {
Expand All @@ -9,52 +6,17 @@ const config: StorybookConfig = {
"../__docs__/**/*.mdx",
],
addons: [
getAbsolutePath("@storybook/addon-essentials"),
getAbsolutePath("@storybook/addon-a11y"),
getAbsolutePath("@storybook/addon-designs"),
getAbsolutePath("@storybook/addon-interactions"),
getAbsolutePath("@storybook/addon-mdx-gfm"),
getAbsolutePath("storybook-addon-pseudo-states"),
"@storybook/addon-essentials",
"@storybook/addon-a11y",
"@storybook/addon-designs",
"storybook-addon-pseudo-states",
"@storybook/experimental-addon-test",
],
staticDirs: ["../static"],
core: {
disableTelemetry: true,
},
framework: getAbsolutePath("@storybook/react-vite"),
async viteFinal(config, {configType}) {
// Merge custom configuration into the default config
const mergedConfig = mergeConfig(config, {
resolve: {
// Allow us to detect changes from local wonder-blocks packages.
alias: [
{
find: /^@khanacademy\/wonder-blocks(-.*)$/,
replacement: resolve(
__dirname,
"../packages/wonder-blocks$1/src",
),
},
],
},
});

// Add turbosnap to production builds so we can let Chromatic take
// snapshots only to stories associated with the current PR.
if (configType === "PRODUCTION") {
config.plugins?.push(
turbosnap({rootDir: config.root || process.cwd()}),
);
}

return mergedConfig;
},
docs: {
autodocs: true,
},
framework: "@storybook/react-vite",
};

export default config;

function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, "package.json")));
}
43 changes: 21 additions & 22 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from "react";
import wonderBlocksTheme from "./wonder-blocks-theme";

import {Decorator} from "@storybook/react";
import {color} from "@khanacademy/wonder-blocks-tokens";
import Link from "@khanacademy/wonder-blocks-link";
import {ThemeSwitcherContext} from "@khanacademy/wonder-blocks-theming";
Expand Down Expand Up @@ -95,32 +95,29 @@ const parameters = {
},
};

const decorators = [
(Story, context) => {
const theme = context.globals.theme;
const enableRenderStateRootDecorator =
context.parameters.enableRenderStateRootDecorator;

if (enableRenderStateRootDecorator) {
return (
<RenderStateRoot>
<ThemeSwitcherContext.Provider value={theme}>
<Story />
</ThemeSwitcherContext.Provider>
</RenderStateRoot>
);
}
const withThemeSwitcher: Decorator = (
Story,
{globals: {theme}, parameters: {enableRenderStateRootDecorator}},
) => {
if (enableRenderStateRootDecorator) {
return (
<ThemeSwitcherContext.Provider value={theme}>
<Story />
</ThemeSwitcherContext.Provider>
<RenderStateRoot>
<ThemeSwitcherContext.Provider value={theme}>
<Story />
</ThemeSwitcherContext.Provider>
</RenderStateRoot>
);
},
];
}
return (
<ThemeSwitcherContext.Provider value={theme}>
<Story />
</ThemeSwitcherContext.Provider>
);
};

const preview: Preview = {
parameters,
decorators,
decorators: [withThemeSwitcher],
globalTypes: {
// Allow the user to select a theme from the toolbar.
theme: {
Expand All @@ -147,6 +144,8 @@ const preview: Preview = {
},
},
},

tags: ["autodocs"],
};

export default preview;
9 changes: 9 additions & 0 deletions .storybook/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { beforeAll } from 'vitest';
import { setProjectAnnotations } from '@storybook/react';
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);
2 changes: 1 addition & 1 deletion __docs__/wonder-blocks-dropdown/combobox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ export const ControlledMultilpleCombobox: Story = {
],

play: async ({canvasElement}) => {
const canvas = within(canvasElement);
const canvas = within(canvasElement.ownerDocument.body);

// Move to second option item
await userEvent.keyboard("{ArrowDown}");
Expand Down
72 changes: 38 additions & 34 deletions __docs__/wonder-blocks-switch/switch.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,46 +82,50 @@ export const Default: StoryComponentType = {
* The `onChange` prop is optional in case the toggle will
* be wrapped in a larger clickable component.
*/
export const Controlled: StoryComponentType = () => {
const [checkedOne, setCheckedOne] = React.useState(false);
const [checkedTwo, setCheckedTwo] = React.useState(false);
export const Controlled: StoryComponentType = {
render: function Render() {
const [checkedOne, setCheckedOne] = React.useState(false);
const [checkedTwo, setCheckedTwo] = React.useState(false);

return (
<View style={styles.column}>
<Switch checked={checkedOne} onChange={setCheckedOne} />

<Switch
testId="test-switch"
aria-label="test switch"
checked={checkedTwo}
onChange={setCheckedTwo}
icon={<PhosphorIcon icon={magnifyingGlassIcon} />}
/>
</View>
);
};

Controlled.play = async ({canvasElement}) => {
const canvas = within(canvasElement);
return (
<View style={styles.column}>
<Switch checked={checkedOne} onChange={setCheckedOne} />
<Switch
testId="test-switch"
aria-label="test switch"
checked={checkedTwo}
onChange={setCheckedTwo}
icon={<PhosphorIcon icon={magnifyingGlassIcon} />}
/>
</View>
);
},
play: async ({canvasElement}) => {
const canvas = within(canvasElement);

const switchWithIcon = canvas.getByTestId("test-switch");
const switchInput = canvas.getByRole("switch", {name: "test switch"});
const switchWithIcon = canvas.getByTestId("test-switch");
const switchInput = canvas.getByRole("switch", {name: "test switch"});

await userEvent.tab();
await userEvent.tab();
await userEvent.tab();
await userEvent.tab();

expect(switchWithIcon).toHaveStyle(
"background-color: rgba(33, 36, 44, 0.5)",
);
expect(switchWithIcon).toHaveStyle("outline: 2px solid rgb(24, 101, 242)");
expect(switchInput).toHaveProperty("checked", false);
expect(switchWithIcon).toHaveStyle(
"background-color: rgba(33, 36, 44, 0.5)",
);
expect(switchWithIcon).toHaveStyle(
"outline: 2px solid rgb(24, 101, 242)",
);
expect(switchInput).toHaveProperty("checked", false);

await userEvent.click(switchWithIcon);
// Wait for animations to finish
await new Promise((resolve) => setTimeout(resolve, 150));
await userEvent.click(switchWithIcon);
// Wait for animations to finish
await new Promise((resolve) => setTimeout(resolve, 150));

expect(switchInput).toHaveProperty("checked", true);
expect(switchWithIcon).toHaveStyle("background-color: rgb(24, 101, 242)");
expect(switchInput).toHaveProperty("checked", true);
expect(switchWithIcon).toHaveStyle(
"background-color: rgb(24, 101, 242)",
);
},
};

/**
Expand Down
34 changes: 17 additions & 17 deletions __docs__/wonder-blocks-tooltip/tooltip.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -375,23 +375,6 @@ export const AutoUpdate: StoryComponentType = {
} | null>(null);
return (
<View style={[styles.centered, styles.row, {position: "relative"}]}>
<Tooltip
content="This is a tooltip that auto-updates its position when the trigger element changes."
opened={true}
autoUpdate={true}
>
<View
style={[
position && {
position: "absolute",
top: position.y,
left: position.x,
},
]}
>
Trigger element
</View>
</Tooltip>
<Button
onClick={() => {
setPosition({
Expand All @@ -413,6 +396,23 @@ export const AutoUpdate: StoryComponentType = {
>
Click to update trigger position (fixed)
</Button>
<Tooltip
content="This is a tooltip that auto-updates its position when the trigger element changes."
opened={true}
autoUpdate={true}
>
<View
style={[
position && {
position: "absolute",
top: position.y,
left: position.x,
},
]}
>
Trigger element
</View>
</Tooltip>
</View>
);
},
Expand Down
Loading

0 comments on commit a00747f

Please sign in to comment.