From d1f1d4496bc671032e7a8c5709f792fca98fb15e Mon Sep 17 00:00:00 2001 From: Anton Gilgur <4970083+agilgur5@users.noreply.github.com> Date: Thu, 22 Aug 2024 23:16:23 -0400 Subject: [PATCH] chore(storybook): update v1 syntax to use Storybook v6 (#568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(storybook): update v1 syntax to use Storybook v6 - while the deps used Storybook v6, the configuration and stories themselves were still on a legacy v5 format - v6 had backward compat for v5, but v7 does not, so this needs upgrading/migrating - or well, v7 has some legacy mode for it that can be enabled with some config, but it is entirely gone in v8 - migrate `storiesOf` to CSF per https://storybook.js.org/docs/7/migration-guide#storiesof-to-csf - then had to do a bunch of manual changes to get back mostly the same previous indentation (with some minor differences where there were mistakes or inconsistencies) - tried to keep the diff as small as possible - CSF is still valid through to latest v8 Signed-off-by: Anton Gilgur * chore(storybook): convert v1 config to use Storybook v6 - modified version of v2's config Signed-off-by: Anton Gilgur * fixup config a bit -- take more from prev webpack config - `ts-loader` is necessary to handle the `import type` syntax - custom `tsconfig.json` doesn't seem necessary though - use same SASS config - plain CSS config not needed though bc PostCSS already runs by default Signed-off-by: Anton Gilgur * rename to `.stories.tsx` and remove `index` - the conventional format - also had some problems finding stories without this change Signed-off-by: Anton Gilgur * fix deprecation warning re: `storyName` - per https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#hoisted-csf-annotations - migrate `.story.name` -> `.storyName` - not sure why the automigration didn't do this and instead used the deprecated name 😕 Signed-off-by: Anton Gilgur * also fix history deprecation warning Signed-off-by: Anton Gilgur * attempt to load CSS properly but still fail Signed-off-by: Anton Gilgur * improve perf with built in loader for TS - remove `ts-loader` as not necessary - plus some settings for Storybook's TS loader preset Signed-off-by: Anton Gilgur * diff reduction Signed-off-by: Anton Gilgur * more diff reduction Signed-off-by: Anton Gilgur * more diff reduction Signed-off-by: Anton Gilgur * attempt to reduce diff a bit more hackily Signed-off-by: Anton Gilgur * one more diff reduction with one hackishness Signed-off-by: Anton Gilgur * diff reduction with older wacky syntax Signed-off-by: Anton Gilgur * slightly better syntax than previous while retaining indentation Signed-off-by: Anton Gilgur * last diff reduction/indentation change Signed-off-by: Anton Gilgur * missing trailing comma Signed-off-by: Anton Gilgur --------- Signed-off-by: Anton Gilgur --- .storybook/config.js | 9 - .storybook/main.js | 21 +++ .storybook/preview.js | 1 + .storybook/webpack.config.js | 26 --- package.json | 1 - ...ata-loader.tsx => data-loader.stories.tsx} | 17 +- .../{dropdown.tsx => dropdown.stories.tsx} | 24 ++- stories/{forms.tsx => forms.stories.tsx} | 15 +- stories/index.stories.tsx | 10 - ...ogs-viewer.tsx => logs-viewer.stories.tsx} | 10 +- ...ications.tsx => notifications.stories.tsx} | 36 ++-- stories/{page.tsx => page.stories.tsx} | 120 +++++++----- stories/{popup.tsx => popup.stories.tsx} | 177 +++++++++++------- stories/{select.tsx => select.stories.tsx} | 28 +-- stories/{table.tsx => table.stories.tsx} | 9 +- stories/{tabs.tsx => tabs.stories.tsx} | 14 +- stories/tsconfig.json | 22 --- yarn.lock | 15 +- 18 files changed, 311 insertions(+), 244 deletions(-) delete mode 100644 .storybook/config.js create mode 100644 .storybook/main.js create mode 100644 .storybook/preview.js delete mode 100644 .storybook/webpack.config.js rename stories/{data-loader.tsx => data-loader.stories.tsx} (74%) rename stories/{dropdown.tsx => dropdown.stories.tsx} (59%) rename stories/{forms.tsx => forms.stories.tsx} (79%) delete mode 100644 stories/index.stories.tsx rename stories/{logs-viewer.tsx => logs-viewer.stories.tsx} (80%) rename stories/{notifications.tsx => notifications.stories.tsx} (50%) rename stories/{page.tsx => page.stories.tsx} (54%) rename stories/{popup.tsx => popup.stories.tsx} (64%) rename stories/{select.tsx => select.stories.tsx} (74%) rename stories/{table.tsx => table.stories.tsx} (92%) rename stories/{tabs.tsx => tabs.stories.tsx} (70%) delete mode 100644 stories/tsconfig.json diff --git a/.storybook/config.js b/.storybook/config.js deleted file mode 100644 index 94f6c467..00000000 --- a/.storybook/config.js +++ /dev/null @@ -1,9 +0,0 @@ -import { configure } from '@storybook/react'; - -// automatically import all files ending in *.stories.js -const req = require.context('../stories', true, /.stories.tsx$/); -function loadStories() { - req.keys().forEach((filename) => req(filename)); -} - -configure(loadStories, module); diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 00000000..15846543 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,21 @@ +const path = require('path'); + +module.exports = { + stories: ['../stories/*.stories.tsx'], + addons: ['@storybook/addon-essentials'], + typescript: { + check: false, // typecheck separately + reactDocgen: false, // substantially improves performance: https://github.com/storybookjs/storybook/issues/22164#issuecomment-1603627308 + }, + webpackFinal: async (config, {configType}) => { + config.devtool = false; // perf per: https://github.com/storybookjs/storybook/issues/19736#issuecomment-1478103817 + config.module.rules.push({ + test: /\.scss$/, + exclude: /node_modules/, + include: path.resolve(__dirname, '../'), + sideEffects: true, // get side-effect styles to load per: https://github.com/storybookjs/storybook/issues/4690#issuecomment-435909433 + loader: 'style-loader!raw-loader!sass-loader' + }); + return config; + }, +}; diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 00000000..6dd02725 --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1 @@ +// import '../src/styles/main.scss'; -- this seems to not work and also makes the Storybook freeze for multiple minutes 😕 diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js deleted file mode 100644 index 37365165..00000000 --- a/.storybook/webpack.config.js +++ /dev/null @@ -1,26 +0,0 @@ -const path = require('path'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); - -module.exports = ({config}) => { - config.module.rules = [{ - test: /\.(ts|tsx)$/, - loader: `ts-loader?configFile=${path.resolve('./stories/tsconfig.json')}` - }, { - test: /\.scss$/, - exclude: /node_modules/, - loader: 'style-loader!raw-loader!sass-loader' - }, { - test: /\.css$/, - loader: 'style-loader!raw-loader' - }]; - config.resolve = { - extensions: ['.ts', '.tsx', '.js', '.json'] - }; - config.plugins.push(new CopyWebpackPlugin([{ - from: 'src/assets', to: 'assets' - }, { - from: 'node_modules/@fortawesome/fontawesome-free/webfonts', to: 'assets/fonts' - }])); - - return config; -}; diff --git a/package.json b/package.json index e540b36b..02a898f7 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,6 @@ "storybook": "6.5.0-beta.1", "style-loader": "^2.0.0", "ts-jest": "^26.5.6", - "ts-loader": "^8.3.0", "ts-node": "^10.9.1", "webfonts-generator": "^0.4.0", "webpack": "^4.46.0" diff --git a/stories/data-loader.tsx b/stories/data-loader.stories.tsx similarity index 74% rename from stories/data-loader.tsx rename to stories/data-loader.stories.tsx index 6c4b3b6b..f91ec85d 100644 --- a/stories/data-loader.tsx +++ b/stories/data-loader.stories.tsx @@ -1,4 +1,3 @@ -import { storiesOf } from '@storybook/react'; import * as React from 'react'; import { App } from './utils'; @@ -9,10 +8,14 @@ function loadData(input: string): Promise { return new Promise((resolve) => window.setTimeout(() => resolve(`hello ${input}`), 50)); } -storiesOf('Data Loader', module) - .add('loading data asynchronously', () => { - const [input, setInput] = React.useState('world'); - return +export default { + title: 'Data Loader', +}; + +export const LoadingDataAsynchronously = () => { + const [input, setInput] = React.useState('world'); + return ( + {() => ( setInput(e.target.value)}/> @@ -26,4 +29,6 @@ storiesOf('Data Loader', module) )} - }); + ); +}; +LoadingDataAsynchronously.storyName = 'loading data asynchronously'; diff --git a/stories/dropdown.tsx b/stories/dropdown.stories.tsx similarity index 59% rename from stories/dropdown.tsx rename to stories/dropdown.stories.tsx index 07a285f2..a7811855 100644 --- a/stories/dropdown.tsx +++ b/stories/dropdown.stories.tsx @@ -1,21 +1,33 @@ -import { storiesOf } from '@storybook/react'; import * as React from 'react'; import { DropDown } from '../src/components/dropdown/dropdown'; import { DropDownMenu } from '../src/components/dropdown-menu'; -storiesOf('Dropdown', module) - .add('default', () => Click me}>

Dropdown content here

) - .add('menu', () => ( +export default { + title: 'Dropdown', +}; + +export const Default = () => ( Click me}>

Dropdown content here

); +Default.storyName = 'default'; + +export const Menu = () => { + return ( Click me}> - )).add('menu wrapper', () => ( + ); +} +Menu.storyName = 'menu'; + +export const MenuWrapper = () => { + return ( Click me} items={[{ title: 'menu item 1', action: () => window.alert('Clicked!'), }]} /> - )); + ); +} +MenuWrapper.storyName = 'menu wrapper'; diff --git a/stories/forms.tsx b/stories/forms.stories.tsx similarity index 79% rename from stories/forms.tsx rename to stories/forms.stories.tsx index 9227ab4c..5084d1a1 100644 --- a/stories/forms.tsx +++ b/stories/forms.stories.tsx @@ -1,11 +1,14 @@ -import { storiesOf } from '@storybook/react'; import * as React from 'react'; import { Form, Text } from 'react-form'; import { FormField, FormSelect } from '../src/components/form-field/form-field'; -storiesOf('Forms', module) - .add('default', () => ( +export default { + title: 'Forms', +}; + +export const Default = () => { + return (
{(api) => ( @@ -16,9 +19,11 @@ storiesOf('Forms', module)
- +
)} - )); + ); +} +Default.storyName = 'default'; diff --git a/stories/index.stories.tsx b/stories/index.stories.tsx deleted file mode 100644 index 3ba52f29..00000000 --- a/stories/index.stories.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import './data-loader'; -import './dropdown'; -import './forms'; -import './logs-viewer'; -import './notifications'; -import './page'; -import './popup'; -import './select'; -import './table'; -import './tabs'; diff --git a/stories/logs-viewer.tsx b/stories/logs-viewer.stories.tsx similarity index 80% rename from stories/logs-viewer.tsx rename to stories/logs-viewer.stories.tsx index 9354285c..97175b7d 100644 --- a/stories/logs-viewer.tsx +++ b/stories/logs-viewer.stories.tsx @@ -1,10 +1,13 @@ -import { storiesOf } from '@storybook/react'; import * as React from 'react'; import { Observable } from 'rxjs'; import { LogsViewer } from '../src/components/logs-viewer/logs-viewer'; -storiesOf('LogsViewer', module).add('default', () => ( +export default { + title: 'LogsViewer', +}; + +export const Default = () => (
( shouldRepeat: () => false, }}/>
-)); +); +Default.storyName = 'default'; diff --git a/stories/notifications.tsx b/stories/notifications.stories.tsx similarity index 50% rename from stories/notifications.tsx rename to stories/notifications.stories.tsx index 939ccc75..f0ce70b4 100644 --- a/stories/notifications.tsx +++ b/stories/notifications.stories.tsx @@ -1,4 +1,3 @@ -import { storiesOf } from '@storybook/react'; import * as React from 'react'; import { NotificationType } from '../src/components/notifications/notifications'; @@ -15,20 +14,25 @@ function getMessage() { return messages[Math.floor(Math.random() * messages.length)]; } -storiesOf('Notifications', module) - .add('default', () => ( +export default { + title: 'Notifications', +}; + +export const Default = () => { + return ( - {(apis) => ( - [ - {type: NotificationType.Success, title: 'Success'}, - {type: NotificationType.Warning, title: 'Warning'}, - {type: NotificationType.Error, title: 'Error'}, - ].map((item) => ( - - )) - )} + {(apis) => [ + {type: NotificationType.Success, title: 'Success'}, + {type: NotificationType.Warning, title: 'Warning'}, + {type: NotificationType.Error, title: 'Error'}, + ].map((item) => ( + + ))} - )); + ); +} +Default.storyName = 'default'; diff --git a/stories/page.tsx b/stories/page.stories.tsx similarity index 54% rename from stories/page.tsx rename to stories/page.stories.tsx index 6e86c55e..5437f5b4 100644 --- a/stories/page.tsx +++ b/stories/page.stories.tsx @@ -1,5 +1,4 @@ -import { storiesOf } from '@storybook/react'; -import createHistory from 'history/createBrowserHistory'; +import { createBrowserHistory } from 'history'; import * as React from 'react'; import { Route, Router } from 'react-router'; import { timer } from 'rxjs'; @@ -46,7 +45,7 @@ const actionMenu = { }], }; -const history = createHistory(); +const history = createBrowserHistory(); function ensureSelected(vals: string[], selected: string[]): string[] { const res = new Set(selected); @@ -54,33 +53,41 @@ function ensureSelected(vals: string[], selected: string[]): string[] { return Array.from(res); } -storiesOf('Page', module) - .add('default', () => { - const [selectedFilter, setSelectedFilter] = React.useState([]); - return +export default { + title: 'Page', +}; + +export const Default = () => { + const [selectedFilter, setSelectedFilter] = React.useState([]); + return ( + - ( - - Filter type one: changeSelection(ensureSelected(['1', '2'], selectedFilter))}>all - - )}, - {label: 'filter 1', value: '1' }, - {label: 'filter 2', value: '2' }, - { content: (changeSelection) => ( - - Filter type two: changeSelection(ensureSelected(['3', '4'], selectedFilter))}>all - - )}, - {label: 'filter 3', value: '3' }, - {label: 'filter 4', value: '4' }, + { + content: (changeSelection) => ( + + Filter type one: changeSelection(ensureSelected(['1', '2'], selectedFilter))}>all + + ), + }, + { label: 'filter 1', value: '1' }, + { label: 'filter 2', value: '2' }, + { + content: (changeSelection) => ( + + Filter type two: changeSelection(ensureSelected(['3', '4'], selectedFilter))}>all + + ), + }, + { label: 'filter 3', value: '3' }, + { label: 'filter 4', value: '4' }, ], selectedValues: selectedFilter, selectionChanged: setSelectedFilter, }}}> -
+
Hello world!
@@ -89,31 +96,41 @@ storiesOf('Page', module) - }).add('dynamic toolbar', () => ( + ) +}; +Default.storyName = 'default'; + +export const DynamicToolbar = () => { + return ( - ({ breadcrumbs: [{title: 'hello ' + new Date().toLocaleTimeString()}] })))}> -
-
- Hello world! -
+ ({breadcrumbs: [ + { title: 'hello ' + new Date().toLocaleTimeString() }, + ]})))}> +
+
Hello world!
- )).add('compact nav bar', () => { - const manyNavItems = []; - for (let i = 0; i < 10; i++) { - manyNavItems.push({ path: location.pathname + '/' + i, title: 'Sample', iconClassName: 'argo-icon-docs' }); - } - return ( + ); +} +DynamicToolbar.storyName = 'dynamic toolbar'; + +export const CompactNavBar = () => { + const manyNavItems = []; + for (let i = 0; i < 10; i++) { + manyNavItems.push({path: location.pathname + '/' + i, title: 'Sample', iconClassName: 'argo-icon-docs'}); + } + return ( + ( -
+
Hello world!
@@ -122,13 +139,21 @@ storiesOf('Page', module) - ); - }).add('custom top bar title', () => ( + ) + ); +}; +CompactNavBar.storyName = 'compact nav bar'; + +export const CustomTopBarTitle = () => { + return ( - -
+ +
Test
@@ -137,12 +162,17 @@ storiesOf('Page', module) - )).add('background color', () => ( + ); +} +CustomTopBarTitle.storyName = 'custom top bar title'; + +export const BackgroundColor = () => { + return ( - + -
+
Hello world!
@@ -151,4 +181,6 @@ storiesOf('Page', module) - )); + ); +} +BackgroundColor.storyName = 'background color'; diff --git a/stories/popup.tsx b/stories/popup.stories.tsx similarity index 64% rename from stories/popup.tsx rename to stories/popup.stories.tsx index da6124c7..4f3bb178 100644 --- a/stories/popup.tsx +++ b/stories/popup.stories.tsx @@ -1,15 +1,18 @@ import { action } from '@storybook/addon-actions'; -import { storiesOf } from '@storybook/react'; import * as React from 'react'; -import { Checkbox as ReactCheckbox} from 'react-form'; +import { Checkbox as ReactCheckbox } from 'react-form'; import { Text } from 'react-form'; import { Checkbox } from '../src/components/checkbox'; import { FormField } from '../src/components/form-field/form-field'; import { App } from './utils'; -storiesOf('Popup', module) - .add('confirmation', () => ( +export default { + title: 'Popup', +}; + +export const Confirmation = () => { + return ( {(apis) => ( )} - )).add('confirmation with custom form inside', () => { - const [checked, setChecked] = React.useState(false); - return ( + ); +} +Confirmation.storyName = 'confirmation'; + +export const ConfirmationWithCustomFormInside = () => { + const [checked, setChecked] = React.useState(false); + return ( + ( {(apis) => (
@@ -37,40 +45,52 @@ storiesOf('Popup', module) )} ) - }).add('prompt', () => ( + ); +} +ConfirmationWithCustomFormInside.storyName = 'confirmation with custom form inside'; + +export const Prompt = () => { + return ( {(apis) => ( )} - )).add('prompt with custom submit', () => ( + ); +} +Prompt.storyName = 'prompt'; + +export const PromptWithCustomSubmit = () => { + return ( {(apis) => ( )} - )).add('prompt with red title and icon, with custom submit', () => ( + ); +} +PromptWithCustomSubmit.storyName = 'prompt with custom submit'; + +export const PromptWithRedTitleAndIconWithCustomSubmit = () => { + return ( {(apis) => ( )} - )).add('prompt with yellow title and icon, three fields and custom submit. Vertical center layout of icon', () => ( + ); +} +PromptWithRedTitleAndIconWithCustomSubmit.storyName = 'prompt with red title and icon, with custom submit'; + +export const PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCenterLayoutOfIcon = () => { + return ( {(apis) => ( )} - )).add('prompt with green clock icon and custom submit', () => ( + ); +} +PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCenterLayoutOfIcon.storyName = 'prompt with yellow title and icon, three fields and custom submit. Vertical center layout of icon'; + +export const PromptWithGreenClockIconAndCustomSubmit = () => { + return ( {(apis) => ( )} - )).add('prompt with just headers and paragraphs', () => ( + ); +} +PromptWithGreenClockIconAndCustomSubmit.storyName = 'prompt with green clock icon and custom submit'; + +export const PromptWithJustHeadersAndParagraphs = () => { + return ( {(apis) => (
- ), - ); + )); action('Prompt values')(values); }}>Click me )}
- )).add('prompt with only paragraphs. Additional top padding is optional for the first paragraph', () => ( + ); +} +PromptWithJustHeadersAndParagraphs.storyName = 'prompt with just headers and paragraphs'; + +export const PromptWithOnlyParagraphsAdditionalTopPaddingIsOptionalForTheFirstParagraph = () => { + return ( {(apis) => (
- ), - ); + )); action('Prompt values')(values); }}>Click me )} - )).add('prompt with React Checkbox that is checked by default; Username default set to admin', () => ( + ); +} +PromptWithOnlyParagraphsAdditionalTopPaddingIsOptionalForTheFirstParagraph.storyName = 'prompt with only paragraphs. Additional top padding is optional for the first paragraph'; + +export const PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAdmin = () => { + return ( {(apis) => (
- + {' '} +
- ), - { + ), { validate: (vals) => ({ username: !vals.username && 'Username is required', password: !vals.password && 'Password is required', @@ -264,4 +311,6 @@ storiesOf('Popup', module) }}>Click me )} - )); + ); +} +PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAdmin.storyName = 'prompt with React Checkbox that is checked by default; Username default set to admin'; diff --git a/stories/select.tsx b/stories/select.stories.tsx similarity index 74% rename from stories/select.tsx rename to stories/select.stories.tsx index 4b2fe51e..4906f1d2 100644 --- a/stories/select.tsx +++ b/stories/select.stories.tsx @@ -1,12 +1,14 @@ -import { storiesOf } from '@storybook/react'; import * as React from 'react'; import { Select } from '../src/components/select/select'; -storiesOf('Select', module) - .add('default', () => { - const [selected, setSelected] = React.useState('option1'); - return ( +export default { + title: 'Select', +}; + +export const Default = () => { + const [selected, setSelected] = React.useState('option1'); + return (

Selected option value: {selected} @@ -19,10 +21,13 @@ storiesOf('Select', module) onChange={(option) => setSelected(option.value)} />

- )}, - ).add('multi-select', () => { - const [selected, setSelected] = React.useState(['option1']); - return ( + ) +}; +Default.storyName = 'default'; + +export const MultiSelect = () => { + const [selected, setSelected] = React.useState(['option1']); + return (