Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocks: Story stories #19805

Merged
merged 8 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@
"publish:debug": "npm run publish:latest -- --npm-tag=debug --no-push",
"publish:latest": "lerna publish --exact --concurrency 1 --force-publish",
"publish:next": "npm run publish:latest -- --npm-tag=next",
"storybook:blocks": "BLOCKS_ONLY=true yarn storybook:ui",
"storybook:blocks:build": "BLOCKS_ONLY=true yarn storybook:ui:build",
"storybook:blocks:chromatic": "BLOCKS_ONLY=true yarn storybook:ui:chromatic --project-token=${CHROMATIC_TOKEN_STORYBOOK_BLOCKS:-MISSING_PROJECT_TOKEN}",
"storybook:blocks": "STORYBOOK_BLOCKS_ONLY=true yarn storybook:ui",
"storybook:blocks:build": "STORYBOOK_BLOCKS_ONLY=true yarn storybook:ui:build",
"storybook:blocks:chromatic": "STORYBOOK_BLOCKS_ONLY=true yarn storybook:ui:chromatic --project-token=${CHROMATIC_TOKEN_STORYBOOK_BLOCKS:-MISSING_PROJECT_TOKEN}",
"storybook:ui": "NODE_OPTIONS=\"--preserve-symlinks --preserve-symlinks-main\" ./lib/cli/bin/index.js dev --port 6006 --config-dir ./ui/.storybook --no-manager-cache",
"storybook:ui:build": "NODE_OPTIONS=\"--preserve-symlinks --preserve-symlinks-main\" ./lib/cli/bin/index.js build --config-dir ./ui/.storybook",
"storybook:ui:chromatic": "yarn chromatic --build-script-name storybook:ui:build --storybook-config-dir ./ui/.storybook --storybook-base-dir ./code/ui --project-token=${CHROMATIC_TOKEN_STORYBOOK_UI:-MISSING_PROJECT_TOKEN} --only-changed --exit-zero-on-changes --exit-once-uploaded",
Expand Down
5 changes: 4 additions & 1 deletion code/ui/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { vite as csfPlugin } from '@storybook/csf-plugin';
import pluginTurbosnap from 'vite-plugin-turbosnap';
import type { StorybookConfig } from '../../frameworks/react-vite/dist';

const isBlocksOnly = process.env.BLOCKS_ONLY === 'true';
const isBlocksOnly = process.env.STORYBOOK_BLOCKS_ONLY === 'true';

const allStories = [
{
Expand Down Expand Up @@ -56,6 +56,9 @@ const config: StorybookConfig = {
core: {
disableTelemetry: true,
},
features: {
interactionsDebugger: true,
},
viteFinal: (viteConfig, { configType }) => ({
...viteConfig,
plugins: [
Expand Down
100 changes: 98 additions & 2 deletions code/ui/blocks/src/blocks/Story.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/// <reference types="vite/client" />
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';

import { Story as StoryComponent } from './Story';
Expand All @@ -6,15 +8,109 @@ import * as BooleanStories from '../controls/Boolean.stories';
const meta: Meta<typeof StoryComponent> = {
component: StoryComponent,
parameters: {
relativeCsfPaths: ['../controls/Boolean.stories'],
relativeCsfPaths: ['../controls/Boolean.stories', '../blocks/Story.stories'],
},
};
export default meta;

type Story = StoryObj<typeof meta>;

export const BasicOf: Story = {
export const Of: Story = {
args: {
of: BooleanStories.Undefined,
},
};

export const OfWithMeta: Story = {
args: {
of: BooleanStories.True,
meta: BooleanStories.default,
},
};

const blocksAwareId = `${
import.meta.env.STORYBOOK_BLOCKS_ONLY === 'true' ? '' : 'storybook-blocks-'
}controls-boolean--false`;

export const Id: Story = {
args: {
id: blocksAwareId,
},
};

export const Name: Story = {
args: {
name: 'True',
},
};

export const SimpleSizeTest: Story = {
render: () => {
return (
<div
style={{
background: '#fd5c9355',
padding: '3rem',
height: '1000px',
width: '800px',
// a global decorator is applying a default padding that we want to negate here
margin: '-4rem -20px',
}}
>
<p>
This story does nothing. Its only purpose is to show how its size renders in different
conditions (inline/iframe/fixed height) when used in a <code>{'<Story />'}</code> block.
</p>
<p>
It has a fixed <code>height</code> of <code>1000px</code> and a fixed <code>width</code>{' '}
of <code>800px</code>
</p>
</div>
);
},
};

export const Inline: Story = {
args: {
of: SimpleSizeTest,
inline: true,
},
};
export const InlineWithHeight: Story = {
...Inline,
args: {
of: SimpleSizeTest,
inline: true,
height: '300px',
},
};
export const Iframe: Story = {
...Inline,
args: {
of: SimpleSizeTest,
inline: false,
},
};
export const IframeWithHeight: Story = {
...Inline,
args: {
of: SimpleSizeTest,
inline: false,
height: '300px',
},
};

export const WithDefaultInteractions: Story = {
args: {
of: BooleanStories.Toggling,
},
};
export const WithInteractionsAutoplayInStory: Story = {
args: {
of: BooleanStories.TogglingInDocs,
},
};

// TODO: types suggest that <Story /> can take ProjectAnnotations, but it doesn't seem to do anything with them
// Such as parameters, decorators, etc.
// they seem to be taken from the story itself, and not from the <Story /> call
Comment on lines +114 to +116
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably for portable docs, and wouldn't apply in non-external. We should probably talk this through.

30 changes: 12 additions & 18 deletions code/ui/blocks/src/blocks/Story.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FC, ReactNode, ElementType, ComponentProps } from 'react';
import type { FC, ComponentProps } from 'react';
import React, { useContext, useRef, useEffect, useState } from 'react';
import type {
Renderer,
Expand All @@ -25,7 +25,6 @@ type CommonProps = StoryAnnotations & {

type StoryDefProps = {
name: string;
children: ReactNode;
};

type StoryRefProps = {
Expand All @@ -36,7 +35,6 @@ type StoryRefProps = {

type StoryImportProps = {
name: string;
story: ElementType;
};

export type StoryProps = (StoryDefProps | StoryRefProps | StoryImportProps) & CommonProps;
Expand All @@ -49,8 +47,7 @@ export const getStoryId = (props: StoryProps, context: DocsContextProps): StoryI
}

const { name } = props as StoryDefProps;
const inputId = id;
return inputId || context.storyIdByName(name);
return id || context.storyIdByName(name);
};

export const getStoryProps = <TFramework extends Renderer>(
Expand Down Expand Up @@ -87,14 +84,16 @@ const Story: FC<StoryProps> = (props) => {
const [showLoader, setShowLoader] = useState(true);

JReinhold marked this conversation as resolved.
Show resolved Hide resolved
useEffect(() => {
let cleanup: () => void;
if (story && storyRef.current) {
const element = storyRef.current as HTMLElement;
const { autoplay } = story.parameters.docs || {};
cleanup = context.renderStoryToElement(story, element, { autoplay });
setShowLoader(false);
if (!(story && storyRef.current)) {
return () => {};
}
return () => cleanup && cleanup();
const element = storyRef.current as HTMLElement;
const { autoplay } = story.parameters.docs || {};
const cleanup = context.renderStoryToElement(story, element, { autoplay });
setShowLoader(false);
return () => {
cleanup();
};
}, [context, story]);

if (!story) {
Expand All @@ -115,7 +114,7 @@ const Story: FC<StoryProps> = (props) => {
return (
<div id={storyBlockIdFromId(story.id)}>
{height ? (
<style>{`#story--${story.id} { min-height: ${height}px; transform: translateZ(0); overflow: auto }`}</style>
<style>{`#story--${story.id} { min-height: ${height}; transform: translateZ(0); overflow: auto }`}</style>
) : null}
{showLoader && <StorySkeleton />}
<div
Expand All @@ -134,9 +133,4 @@ const Story: FC<StoryProps> = (props) => {
);
};

Story.defaultProps = {
children: null,
name: null,
};

export { Story };
54 changes: 53 additions & 1 deletion code/ui/blocks/src/controls/Boolean.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { expect } from '@storybook/jest';
import type { Meta, StoryObj } from '@storybook/react';
import { within, fireEvent } from '@storybook/testing-library';
import { addons } from '@storybook/addons';
import { RESET_STORY_ARGS, STORY_ARGS_UPDATED } from '@storybook/core-events';
import { BooleanControl } from './Boolean';

const meta = {
component: BooleanControl,
tags: ['docsPage'],
parameters: { withRawArg: 'value', controls: { include: ['value'] } },
parameters: {
withRawArg: 'value',
controls: { include: ['value'] },
notes: 'These are notes for the Boolean control stories',
info: 'This is info for the Boolean control stories',
jsx: { useBooleanShorthandSyntax: false },
},
args: { name: 'boolean' },
} as Meta<typeof BooleanControl>;

Expand All @@ -26,3 +36,45 @@ export const Undefined: StoryObj<typeof BooleanControl> = {
value: undefined,
},
};

export const Toggling: StoryObj<typeof BooleanControl> = {
args: {
value: undefined,
},
play: async ({ canvasElement, id }) => {
const channel = addons.getChannel();

channel.emit(RESET_STORY_ARGS, { storyId: id });
await new Promise<void>((resolve) => {
channel.once(STORY_ARGS_UPDATED, resolve);
});

const canvas = within(canvasElement);

// from Undefined to False
const setBooleanControl = canvas.getByText('Set boolean');
await fireEvent.click(setBooleanControl);

let toggle = await canvas.findByTitle('Change to true');
expect(toggle).toBeInTheDocument();

// from False to True
await fireEvent.click(toggle);
toggle = await canvas.findByTitle('Change to false');
expect(toggle).toBeInTheDocument();

// from True to False
await fireEvent.click(toggle);
toggle = await canvas.findByTitle('Change to true');
expect(toggle).toBeInTheDocument();
},
};

export const TogglingInDocs: StoryObj<typeof BooleanControl> = {
...Toggling,
parameters: {
docs: {
autoplay: true,
},
},
};
10 changes: 8 additions & 2 deletions code/ui/blocks/src/controls/Boolean.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ const Label = styled.label(({ theme }) => ({
const parse = (value: string | null): boolean => value === 'true';

export type BooleanProps = ControlProps<BooleanValue> & BooleanConfig;
/**
* # Boolean control
* Renders a switch toggle with "True" or "False".
* or if the value is `undefined`, renders a button to set the boolean.
*/
export const BooleanControl: FC<BooleanProps> = ({ name, value, onChange, onBlur, onFocus }) => {
const onSetFalse = useCallback(() => onChange(false), [onChange]);
if (value === undefined) {
Expand All @@ -95,13 +100,14 @@ export const BooleanControl: FC<BooleanProps> = ({ name, value, onChange, onBlur
</Form.Button>
);
}
const controlId = getControlId(name);

const parsedValue = typeof value === 'string' ? parse(value) : value;

return (
<Label htmlFor={name} title={parsedValue ? 'Change to false' : 'Change to true'}>
<Label htmlFor={controlId} title={parsedValue ? 'Change to false' : 'Change to true'}>
<input
id={getControlId(name)}
id={controlId}
type="checkbox"
onChange={(e) => onChange(e.target.checked)}
checked={parsedValue}
Expand Down
1 change: 1 addition & 0 deletions code/ui/blocks/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "esnext",
Copy link
Contributor Author

@JReinhold JReinhold Nov 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ndelangen I changed this so TS wouldn't complain that I was referencing import.meta.env, do you know if changing this will break something somewhere else? Or does tsup ignore this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is completely ignored 👍

"rootDir": "./src",
"types": ["jest"]
},
Expand Down