From aedcb58d535a4bba5f1ba6632d3034f5bbc7a235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DUNGLER?= Date: Sun, 15 Nov 2020 22:31:47 +0100 Subject: [PATCH 1/6] Args: Add capacity to sort ArgsTable --- .../src/blocks/ArgsTable/ArgsTable.tsx | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx index 98c1fcad9f71..36a0052c13ce 100644 --- a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx @@ -226,6 +226,8 @@ export enum ArgsTableError { ARGS_UNSUPPORTED = 'Args unsupported. See Args documentation for your framework.', } +type SortType = 'ascending' | 'descending' | 'requiredFirst'; + export interface ArgsTableRowProps { rows: ArgTypes; args?: Args; @@ -234,6 +236,7 @@ export interface ArgsTableRowProps { compact?: boolean; inAddonPanel?: boolean; initialExpandedArgs?: boolean; + sort?: SortType; } export interface ArgsTableErrorProps { @@ -254,7 +257,7 @@ type Sections = { sections: Record; }; -const groupRows = (rows: ArgType) => { +const groupRows = (rows: ArgType, sort: SortType) => { const sections: Sections = { ungrouped: [], ungroupedSubsections: {}, sections: {} }; if (!rows) return sections; @@ -278,7 +281,47 @@ const groupRows = (rows: ArgType) => { sections.ungrouped.push({ key, ...row }); } }); - return sections; + + // apply sort + const sortFn = (a: ArgType, b: ArgType) => { + const sortFns: Record number> = { + ascending: () => a.name.localeCompare(b.name), + descending: () => b.name.localeCompare(a.name), + requiredFirst: () => { + return ( + Number(!!b.type?.required) - Number(!!a.type?.required) || a.name.localeCompare(b.name) + ); + }, + }; + return sortFns[sort](); + }; + + const sortSubsection = (record: Record) => { + return Object.keys(record).reduce>( + (acc, cur) => ({ + ...acc, + [cur]: record[cur].sort(sortFn), + }), + {} + ); + }; + + const sorted = { + ungrouped: sections.ungrouped.sort(sortFn), + ungroupedSubsections: sortSubsection(sections.ungroupedSubsections), + sections: Object.keys(sections.sections).reduce>( + (acc, cur) => ({ + ...acc, + [cur]: { + ungrouped: sections.sections[cur].ungrouped.sort(sortFn), + subsections: sortSubsection(sections.sections[cur].subsections), + }, + }), + {} + ), + }; + + return sorted; }; /** @@ -306,9 +349,13 @@ export const ArgsTable: FC = (props) => { compact, inAddonPanel, initialExpandedArgs, + sort = 'ascending', } = props as ArgsTableRowProps; - const groups = groupRows(pickBy(rows, (row) => !row?.table?.disable)); + const groups = groupRows( + pickBy(rows, (row) => !row?.table?.disable), + sort + ); if ( groups.ungrouped.length === 0 && From da4f95309b1dd7db973fb45e0be314afc4c9a51b Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 15 Mar 2021 17:18:33 +0800 Subject: [PATCH 2/6] Controls: Tune up sorting --- addons/controls/src/ControlsPanel.tsx | 6 ++-- addons/docs/src/blocks/ArgsTable.tsx | 18 +++++++--- .../stories/controls-sort.stories.tsx | 34 +++++++++++++++++++ .../src/blocks/ArgsTable/ArgsTable.tsx | 27 +++++++-------- 4 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 examples/official-storybook/stories/controls-sort.stories.tsx diff --git a/addons/controls/src/ControlsPanel.tsx b/addons/controls/src/ControlsPanel.tsx index c32e9f6b3091..4eed46c31e6e 100644 --- a/addons/controls/src/ControlsPanel.tsx +++ b/addons/controls/src/ControlsPanel.tsx @@ -1,10 +1,11 @@ import React, { FC } from 'react'; import { useArgs, useArgTypes, useParameter } from '@storybook/api'; -import { ArgsTable, NoControlsWarning } from '@storybook/components'; +import { ArgsTable, NoControlsWarning, SortType } from '@storybook/components'; import { PARAM_KEY } from './constants'; interface ControlsParameters { + sort?: SortType; expanded?: boolean; hideNoControlsWarning?: boolean; } @@ -13,7 +14,7 @@ export const ControlsPanel: FC = () => { const [args, updateArgs, resetArgs] = useArgs(); const rows = useArgTypes(); const isArgsStory = useParameter('__isArgsStory', false); - const { expanded, hideNoControlsWarning = false } = useParameter( + const { expanded, sort, hideNoControlsWarning = false } = useParameter( PARAM_KEY, {} ); @@ -32,6 +33,7 @@ export const ControlsPanel: FC = () => { updateArgs, resetArgs, inAddonPanel: true, + sort, }} /> diff --git a/addons/docs/src/blocks/ArgsTable.tsx b/addons/docs/src/blocks/ArgsTable.tsx index 999cd20415be..49f0286d0536 100644 --- a/addons/docs/src/blocks/ArgsTable.tsx +++ b/addons/docs/src/blocks/ArgsTable.tsx @@ -6,6 +6,7 @@ import { ArgsTableProps as PureArgsTableProps, ArgsTableError, ArgTypes, + SortType, TabbedArgsTable, } from '@storybook/components'; import { Args } from '@storybook/addons'; @@ -22,6 +23,7 @@ import { lookupStoryId } from './Story'; interface BaseProps { include?: PropDescriptor; exclude?: PropDescriptor; + sort?: SortType; } type OfProps = BaseProps & { @@ -111,11 +113,13 @@ const addComponentTabs = ( components: Record, context: DocsContextProps, include?: PropDescriptor, - exclude?: PropDescriptor + exclude?: PropDescriptor, + sort?: SortType ) => ({ ...tabs, ...mapValues(components, (comp) => ({ rows: extractComponentArgTypes(comp, context, include, exclude), + sort, })), }); @@ -199,14 +203,17 @@ export const ComponentsTable: FC = (props) => { export const ArgsTable: FC = (props) => { const context = useContext(DocsContext); - const { parameters: { subcomponents } = {} } = context; + const { parameters: { subcomponents, controls } = {} } = context; - const { include, exclude, components } = props as ComponentsProps; + const { include, exclude, components, sort: sortProp } = props as ComponentsProps; const { story } = props as StoryProps; + const sort = sortProp || controls?.sort; + console.log('preparing', { sort }); + const main = getComponent(props, context); if (story) { - return ; + return ; } if (!components && !subcomponents) { @@ -220,7 +227,7 @@ export const ArgsTable: FC = (props) => { } if (components) { - return ; + return ; } const mainLabel = getComponentName(main); @@ -228,6 +235,7 @@ export const ArgsTable: FC = (props) => { ); }; diff --git a/examples/official-storybook/stories/controls-sort.stories.tsx b/examples/official-storybook/stories/controls-sort.stories.tsx new file mode 100644 index 000000000000..cea006752526 --- /dev/null +++ b/examples/official-storybook/stories/controls-sort.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import Button from '../components/TsButton'; + +export default { + title: 'Addons/Controls-Sort', + argTypes: { + x: { type: { required: true } }, + y: { type: { required: true }, table: { category: 'foo' } }, + z: {}, + a: { type: { required: true } }, + b: { table: { category: 'foo' } }, + c: {}, + }, + args: { + x: 'x', + y: 'y', + z: 'z', + a: 'a', + b: 'b', + c: 'c', + }, + parameters: { chromatic: { disable: true } }, +}; + +const Template = (args: any) =>
{args &&
{JSON.stringify(args, null, 2)}
}
; + +export const None = Template.bind({}); +None.parameters = { controls: { sort: 'none' } }; + +export const Alpha = Template.bind({}); +Alpha.parameters = { controls: { sort: 'alpha' } }; + +export const RequiredFirst = Template.bind({}); +RequiredFirst.parameters = { controls: { sort: 'requiredFirst' } }; diff --git a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx index 36a0052c13ce..030c723c0015 100644 --- a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx @@ -226,8 +226,15 @@ export enum ArgsTableError { ARGS_UNSUPPORTED = 'Args unsupported. See Args documentation for your framework.', } -type SortType = 'ascending' | 'descending' | 'requiredFirst'; - +export type SortType = 'alpha' | 'requiredFirst' | 'none'; +type SortFn = (a: ArgType, b: ArgType) => number; + +const sortFns: Record = { + alpha: (a: ArgType, b: ArgType) => a.name.localeCompare(b.name), + requiredFirst: (a: ArgType, b: ArgType) => + Number(!!b.type?.required) - Number(!!a.type?.required) || a.name.localeCompare(b.name), + none: undefined, +}; export interface ArgsTableRowProps { rows: ArgTypes; args?: Args; @@ -283,20 +290,10 @@ const groupRows = (rows: ArgType, sort: SortType) => { }); // apply sort - const sortFn = (a: ArgType, b: ArgType) => { - const sortFns: Record number> = { - ascending: () => a.name.localeCompare(b.name), - descending: () => b.name.localeCompare(a.name), - requiredFirst: () => { - return ( - Number(!!b.type?.required) - Number(!!a.type?.required) || a.name.localeCompare(b.name) - ); - }, - }; - return sortFns[sort](); - }; + const sortFn = sortFns[sort]; const sortSubsection = (record: Record) => { + if (!sortFn) return record; return Object.keys(record).reduce>( (acc, cur) => ({ ...acc, @@ -349,7 +346,7 @@ export const ArgsTable: FC = (props) => { compact, inAddonPanel, initialExpandedArgs, - sort = 'ascending', + sort = 'requiredFirst', } = props as ArgsTableRowProps; const groups = groupRows( From 140721c03eea113263bc8b7085b1fe21822336df Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 15 Mar 2021 17:29:18 +0800 Subject: [PATCH 3/6] Document controls sorting --- docs/essentials/controls.md | 70 ++++++++++++------- .../component-story-sort-controls.js.mdx | 11 +++ .../component-story-sort-controls.mdx.mdx | 12 ++++ .../src/blocks/ArgsTable/ArgsTable.tsx | 2 +- 4 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 docs/snippets/common/component-story-sort-controls.js.mdx create mode 100644 docs/snippets/common/component-story-sort-controls.mdx.mdx diff --git a/docs/essentials/controls.md b/docs/essentials/controls.md index 0bbc296af077..8e5b50a6cf4f 100644 --- a/docs/essentials/controls.md +++ b/docs/essentials/controls.md @@ -61,7 +61,6 @@ By default, Storybook will render a free text input for the `variant` arg: ![Essential addon Controls using a string](addon-controls-args-variant-string.png) - This works as long as you type a valid string into the auto-generated text control, but it's not the best UI for our scenario, given that the component only accepts `primary` or `secondary` as variants. Let’s replace it with Storybook’s radio component. We can specify which controls get used by declaring a custom [argType](../api/argtypes.md) for the `variant` property. ArgTypes encode basic metadata for args, such as name, description, defaultValue for an arg. These get automatically filled in by Storybook Docs. @@ -87,13 +86,13 @@ This replaces the input with a radio group for a more intuitive experience. For a few types, Controls will automatically infer them by using [regex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp). You can change the matchers for a regex that suits you better. -| Data type | Default regex | Description | -| :---------: | :-----------------------: | :-------------------------------------------------------: | -| **color** | `/(background\|color)$/i` | Will display a color picker UI for the args that match it | -| **date** | `/Date$/` | Will display a date picker UI for the args that match it | - +| Data type | Default regex | Description | +| :-------: | :-----------------------: | :-------------------------------------------------------: | +| **color** | `/(background\|color)$/i` | Will display a color picker UI for the args that match it | +| **date** | `/Date$/` | Will display a date picker UI for the args that match it | To do so, use the `matchers` property in `controls` parameter: + + + + +## Sorting controls + +By default, controls are sorted alphabetically by arg name (`alpha`). This can be configured to sort alphabetically required args first (`requiredFirst`), or to not sort at all and use whatever order the args data is processed in (`none`). + +Consider the following snippet to force required args first: + + + + diff --git a/docs/snippets/common/component-story-sort-controls.js.mdx b/docs/snippets/common/component-story-sort-controls.js.mdx new file mode 100644 index 000000000000..6a816951b85a --- /dev/null +++ b/docs/snippets/common/component-story-sort-controls.js.mdx @@ -0,0 +1,11 @@ +```js +// YourComponent.stories.js | YourComponent.stories.ts + +import { YourComponent } from './your-component' + +export default { + title:'My Story', + component: YourComponent, + parameters: { controls: { sort: 'requiredFirst' } }, +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/component-story-sort-controls.mdx.mdx b/docs/snippets/common/component-story-sort-controls.mdx.mdx new file mode 100644 index 000000000000..a5aca9d87504 --- /dev/null +++ b/docs/snippets/common/component-story-sort-controls.mdx.mdx @@ -0,0 +1,12 @@ +```md + + +import { Meta } from '@storybook/addon-docs/blocks'; +import { YourComponent } from './your-component' + + +``` \ No newline at end of file diff --git a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx index 030c723c0015..3648ebd5841b 100644 --- a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx @@ -346,7 +346,7 @@ export const ArgsTable: FC = (props) => { compact, inAddonPanel, initialExpandedArgs, - sort = 'requiredFirst', + sort = 'alpha', } = props as ArgsTableRowProps; const groups = groupRows( From 1ea6c43f2155d53151a5234a563305e484915437 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 15 Mar 2021 17:29:51 +0800 Subject: [PATCH 4/6] remove console.log --- addons/docs/src/blocks/ArgsTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/docs/src/blocks/ArgsTable.tsx b/addons/docs/src/blocks/ArgsTable.tsx index 49f0286d0536..a942488fa831 100644 --- a/addons/docs/src/blocks/ArgsTable.tsx +++ b/addons/docs/src/blocks/ArgsTable.tsx @@ -209,7 +209,6 @@ export const ArgsTable: FC = (props) => { const { story } = props as StoryProps; const sort = sortProp || controls?.sort; - console.log('preparing', { sort }); const main = getComponent(props, context); if (story) { From 6135d4b8d7996f8c2e91289054d29247ce1e58a3 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 15 Mar 2021 17:31:59 +0800 Subject: [PATCH 5/6] Fix deepscan --- examples/official-storybook/stories/controls-sort.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/official-storybook/stories/controls-sort.stories.tsx b/examples/official-storybook/stories/controls-sort.stories.tsx index cea006752526..46dc611795d1 100644 --- a/examples/official-storybook/stories/controls-sort.stories.tsx +++ b/examples/official-storybook/stories/controls-sort.stories.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import Button from '../components/TsButton'; export default { title: 'Addons/Controls-Sort', From 08ed3928329c069b45dffa908955c21ab8bcdac1 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 15 Mar 2021 17:56:04 +0800 Subject: [PATCH 6/6] Change default sort to none --- docs/essentials/controls.md | 2 +- lib/components/src/blocks/ArgsTable/ArgsTable.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/essentials/controls.md b/docs/essentials/controls.md index 8e5b50a6cf4f..45058692aaae 100644 --- a/docs/essentials/controls.md +++ b/docs/essentials/controls.md @@ -312,7 +312,7 @@ Consider the following story snippets: ## Sorting controls -By default, controls are sorted alphabetically by arg name (`alpha`). This can be configured to sort alphabetically required args first (`requiredFirst`), or to not sort at all and use whatever order the args data is processed in (`none`). +By default, controls are unsorted and use whatever order the args data is processed in (`none`). It can also be configured to sort alphabetically by arg name (`alpha`) or alphabetically required args first (`requiredFirst`). Consider the following snippet to force required args first: diff --git a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx index 3648ebd5841b..f9e85eee1b71 100644 --- a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx @@ -346,7 +346,7 @@ export const ArgsTable: FC = (props) => { compact, inAddonPanel, initialExpandedArgs, - sort = 'alpha', + sort = 'none', } = props as ArgsTableRowProps; const groups = groupRows(