-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prevent regressions in our custom components by adding Storybook testing. Copy Storybook tests from the archived `gravitational/docs` repo. Add a rough-and-ready Storybook configuration to enable tests to pass. One significant complication is that Docusaurus generates a Webpack configuration when building a docs site. There are Storybook frameworks and add-ons for Docusaurus that take advantage of Docusaurus's asset-loading logic, but none are currently being maintained. The quickest thing we can do is add a separate Webpack configuration that renders components in Storybook similarly (but not identically) to the Docusaurus site. A separate change can refine this approach by, for example: - Vendoring a Storybook Docusaurus framework - Migrating Storybook tests to `react-testing-library`
- Loading branch information
Showing
15 changed files
with
2,504 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
# Generated files | ||
.docusaurus | ||
.cache-loader | ||
storybook-static | ||
|
||
# Misc | ||
.env | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Declare modules so Storybook can import assets as expected. Docusaurus | ||
// handles this on its own, so we need to redeclare these modules here for | ||
// Storybook. | ||
|
||
declare module "*.css"; | ||
|
||
declare module "*.svg"; | ||
|
||
declare module "*.svg?react" { | ||
const Component: React.StatelessComponent<React.SVGAttributes<SVGElement>>; | ||
|
||
export default Component; | ||
} | ||
|
||
declare module "*.png" { | ||
const value: string; | ||
export default value; | ||
} | ||
|
||
declare module "*.webp" { | ||
const value: string; | ||
export default value; | ||
} | ||
|
||
declare module "*.jpg" { | ||
const value: string; | ||
export default value; | ||
} | ||
|
||
declare module "*.woff2" { | ||
const value: string; | ||
export default value; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import type { StorybookConfig } from "@storybook/react-webpack5"; | ||
|
||
const config: StorybookConfig = { | ||
framework: "@storybook/react-webpack5", | ||
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"], | ||
addons: ["@storybook/addon-essentials"], | ||
webpackFinal: async (config) => { | ||
config.module?.rules?.push({ | ||
test: /\.css$/, | ||
use: { | ||
loader: "postcss-loader", | ||
}, | ||
}); | ||
|
||
const imageRule = config.module?.rules?.find((rule) => { | ||
const test = (rule as { test: RegExp }).test; | ||
|
||
if (!test) { | ||
return false; | ||
} | ||
|
||
return test.test(".svg"); | ||
}) as { [key: string]: any }; | ||
|
||
imageRule.exclude = /\.svg$/; | ||
|
||
config.module?.rules?.push({ | ||
test: /\.svg$/, | ||
use: ["@svgr/webpack"], | ||
}); | ||
|
||
config.module?.rules?.push({ | ||
test: /\.tsx?$/, | ||
use: [ | ||
{ | ||
loader: "ts-loader", | ||
options: { | ||
logLevel: "INFO", | ||
logInfoToStdOut: true, | ||
configFile: "tsconfig.storybook.json", | ||
// Otherwise, properties added by Storybook trip the TypeScript | ||
// checker. | ||
transpileOnly: true, | ||
}, | ||
}, | ||
], | ||
exclude: /node_modules/, | ||
}); | ||
|
||
return config; | ||
}, | ||
}; | ||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Preview } from "@storybook/react-webpack5"; | ||
|
||
// See the following documentation for how this configuration loads CSS styles | ||
// for Storybook stories: | ||
// https://storybook.js.org/docs/configure/styling-and-css#import-bundled-css-recommended | ||
import "../src/styles/variables.css"; | ||
import "../src/styles/fonts-ubuntu.css"; | ||
import "../src/styles/global.css"; | ||
import "../src/styles/media.css"; | ||
import "../src/styles/fonts-lato.css"; | ||
|
||
const preview: Preview = { | ||
parameters: {}, | ||
}; | ||
|
||
export default preview; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { userEvent, within } from "@storybook/test"; | ||
import { replaceClipboardWithCopyBuffer } from "/src/utils/clipboard"; | ||
|
||
import Command from "./Command"; | ||
|
||
const commandText = "yarn install"; | ||
|
||
const meta: Meta<typeof Command> = { | ||
title: "components/Command", | ||
component: Command, | ||
args: { | ||
children: <span>{commandText}</span>, | ||
}, | ||
}; | ||
export default meta; | ||
type Story = StoryObj<typeof Command>; | ||
|
||
export const SimpleCommand: Story = { | ||
args: { | ||
children: <span>{commandText}</span>, | ||
}, | ||
}; | ||
|
||
export const CopyButton: Story = { | ||
play: async ({ canvasElement, step }) => { | ||
replaceClipboardWithCopyBuffer(); | ||
const canvas = within(canvasElement); | ||
await step("Hover and click on copy button", async () => { | ||
await userEvent.hover(canvas.getByTestId("copy-button")); | ||
await userEvent.click(canvas.getByTestId("copy-button")); | ||
}); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { userEvent, within } from "@storybook/test"; | ||
import { expect } from "@storybook/test"; | ||
|
||
import { Var } from "../Variables/Var"; | ||
import { default as Snippet } from "./Snippet"; | ||
import Command, { CommandLine, CommandComment } from "../Command/Command"; | ||
import { CodeLine } from "/src/theme/MDXComponents/Code"; | ||
import { replaceClipboardWithCopyBuffer } from "/src/utils/clipboard"; | ||
|
||
export const SimpleCommand = () => ( | ||
<Snippet> | ||
<Command> | ||
<CommandLine data-content="$ ">echo Hello world!</CommandLine> | ||
</Command> | ||
</Snippet> | ||
); | ||
|
||
const meta: Meta<typeof Snippet> = { | ||
title: "components/Snippet", | ||
component: SimpleCommand, | ||
}; | ||
export default meta; | ||
type Story = StoryObj<typeof Snippet>; | ||
|
||
export const CopyCommandVar: Story = { | ||
render: () => { | ||
return ( | ||
<Snippet> | ||
<Command> | ||
<CommandLine data-content="$ "> | ||
curl https:// | ||
<Var name="example.com" isGlobal={false} description="" /> | ||
/v1/webapi/saml/acs/azure-saml | ||
</CommandLine> | ||
</Command> | ||
</Snippet> | ||
); | ||
}, | ||
play: async ({ canvasElement, step }) => { | ||
replaceClipboardWithCopyBuffer(); | ||
const canvas = within(canvasElement); | ||
|
||
await step("Copy the content", async () => { | ||
await userEvent.hover(canvas.getByText("example.com")); | ||
await userEvent.click(canvas.getByTestId("copy-button")); | ||
expect(navigator.clipboard.readText()).toEqual( | ||
"curl https://example.com/v1/webapi/saml/acs/azure-saml", | ||
); | ||
await userEvent.click(canvas.getByTestId("copy-button-all")); | ||
expect(navigator.clipboard.readText()).toEqual( | ||
"curl https://example.com/v1/webapi/saml/acs/azure-saml", | ||
); | ||
}); | ||
}, | ||
}; | ||
|
||
// A code snippet with commands should only copy the commands. | ||
export const CopyCommandVarWithOutput: Story = { | ||
render: () => { | ||
return ( | ||
<Snippet> | ||
<Command> | ||
<CommandLine data-content="$ "> | ||
curl https:// | ||
<Var name="example.com" isGlobal={false} description="" /> | ||
/v1/webapi/saml/acs/azure-saml | ||
</CommandLine> | ||
</Command> | ||
<CodeLine> | ||
The output of curling <Var name="example.com" /> | ||
</CodeLine> | ||
</Snippet> | ||
); | ||
}, | ||
play: async ({ canvasElement, step }) => { | ||
replaceClipboardWithCopyBuffer(); | ||
const canvas = within(canvasElement); | ||
|
||
await step("Copy the content", async () => { | ||
await userEvent.click(canvas.getByTestId("copy-button-all")); | ||
expect(navigator.clipboard.readText()).toEqual( | ||
"curl https://example.com/v1/webapi/saml/acs/azure-saml", | ||
); | ||
}); | ||
}, | ||
}; | ||
|
||
// A code snippet with no commands should copy all content within the snippet. | ||
export const CopyCodeLineVar: Story = { | ||
render: () => { | ||
return ( | ||
<Snippet> | ||
<CodeLine> | ||
curl https:// | ||
<Var name="example.com" isGlobal={false} description="" /> | ||
/v1/webapi/saml/acs/azure-saml | ||
</CodeLine> | ||
</Snippet> | ||
); | ||
}, | ||
play: async ({ canvasElement, step }) => { | ||
replaceClipboardWithCopyBuffer(); | ||
|
||
const canvas = within(canvasElement); | ||
|
||
await step("Copy the content", async () => { | ||
await userEvent.hover(canvas.getByText("example.com")); | ||
await userEvent.click(canvas.getByTestId("copy-button-all")); | ||
expect(navigator.clipboard.readText()).toEqual( | ||
"curl https://example.com/v1/webapi/saml/acs/azure-saml", | ||
); | ||
}); | ||
}, | ||
}; |
Oops, something went wrong.