From a9e70d4a1d3ee9d2c7cbbeb89daa99660e9a256c Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 8 Jul 2025 13:37:09 +0100 Subject: [PATCH] Storybook: Rearrange and tidy stories (#107270) * Tidy up storybook a little bit * change sort order, delete some stories * More tidy up of actions * More tidy up of actions * tweak story sorting, again * Make all internal stories public * fix sort * Add ESLint rule to enforce storybook titles * update verify storybook test * simplify glob --- .betterer.results | 42 ++++++ e2e/storybook/verify.spec.ts | 2 +- eslint.config.js | 7 + packages/grafana-eslint-rules/README.md | 27 ++++ packages/grafana-eslint-rules/index.cjs | 2 + .../rules/consistent-story-titles.cjs | 106 +++++++++++++ .../tests/consistent-stories.test.js | 117 +++++++++++++++ packages/grafana-ui/.storybook/main.ts | 8 +- packages/grafana-ui/.storybook/preview.ts | 82 ++++++++-- ...InlineBanner.story.tsx => Alert.story.tsx} | 13 +- .../src/components/Alert/Toast.story.tsx | 126 ---------------- .../AutoSaveField/AutoSaveField.story.tsx | 2 +- .../src/components/Badge/Badge.story.tsx | 2 +- .../components/BarGauge/BarGauge.story.tsx | 2 +- .../components/BigValue/BigValue.story.tsx | 2 +- .../src/components/Button/Button.story.tsx | 2 +- .../ButtonCascader/ButtonCascader.story.tsx | 2 +- ...l.story.tsx => CallToActionCard.story.tsx} | 2 +- .../CallToActionCard/CallToActionCard.tsx | 1 + .../src/components/Card/Card.story.tsx | 5 +- .../components/Carousel/Carousel.story.tsx | 2 +- .../components/Cascader/Cascader.story.tsx | 2 +- .../ClickOutsideWrapper.story.tsx | 2 +- .../ClipboardButton/ClipboardButton.story.tsx | 2 +- .../ColorPicker/ColorPicker.story.tsx | 38 +---- .../ColorPicker/ColorPickerInput.story.tsx | 32 ++++ .../ColorPicker/ColorPickerPopover.story.tsx | 52 ------- .../components/ColorPicker/Palettes.story.tsx | 43 ------ .../ColorPicker/SeriesColorPicker.story.tsx | 39 +++++ .../components/Combobox/Combobox.story.tsx | 2 +- .../Combobox/MultiCombobox.story.tsx | 2 +- .../ConfirmButton/ConfirmButton.story.tsx | 2 +- .../components/ContextMenu/ContextMenu.mdx | 2 + .../ContextMenu/ContextMenu.story.tsx | 2 +- .../DataSourceHttpSettings.story.tsx | 2 +- .../DatePicker/DatePicker.story.tsx | 2 +- .../DatePickerWithInput.story.tsx | 2 +- .../DateTimePicker/DateTimePicker.story.tsx | 2 +- .../RelativeTimeRangePicker.story.tsx | 2 +- .../DateTimePickers/TimeOfDayPicker.story.tsx | 2 +- .../DateTimePickers/TimeRangeInput.story.tsx | 2 +- .../DateTimePickers/TimeRangePicker.story.tsx | 2 +- .../DateTimePickers/TimeZonePicker.story.tsx | 2 +- .../DateTimePickers/WeekStartPicker.story.tsx | 2 +- .../src/components/Divider/Divider.story.tsx | 2 +- ...ernal.story.tsx => ButtonSelect.story.tsx} | 2 +- .../src/components/Dropdown/ButtonSelect.tsx | 3 +- .../EmptySearchResult.story.tsx | 2 +- .../EmptySearchResult/EmptySearchResult.tsx | 1 + .../EmptyState/EmptyState.story.tsx | 2 +- .../ErrorBoundary/ErrorBoundary.story.tsx | 2 +- .../FeatureBadge/FeatureBadge.story.tsx | 2 +- .../FileDropzone/FileDropzone.story.tsx | 2 +- .../FileDropzone/FileListItem.story.tsx | 2 +- .../FileUpload/FileUpload.story.tsx | 2 +- .../FilterPill/FilterPill.story.tsx | 2 +- ...internal.story.tsx => FormField.story.tsx} | 2 +- .../FormattedValueDisplay.story.tsx | 2 +- .../src/components/Forms/Checkbox.story.tsx | 2 +- .../Legacy/Input/Input.internal.story.tsx | 58 -------- .../Legacy/Select/Select.internal.story.tsx | 113 -------------- .../Legacy/Switch/Switch.internal.story.tsx | 27 ---- .../RadioButtonGroup.story.tsx | 2 +- .../RadioButtonList/RadioButtonList.story.tsx | 2 +- .../src/components/Icon/Icon.story.tsx | 2 +- .../IconButton/IconButton.story.tsx | 2 +- .../src/components/InfoBox/InfoBox.story.tsx | 2 +- ...ternal.story.tsx => InfoTooltip.story.tsx} | 2 +- .../components/InfoTooltip/InfoTooltip.tsx | 1 + ...ternal.story.tsx => InlineToast.story.tsx} | 3 +- .../components/Input/AutoSizeInput.story.tsx | 2 +- .../src/components/Input/Input.story.tsx | 2 +- .../InteractiveTable.story.tsx | 2 +- .../src/components/Layout/Box/Box.story.tsx | 2 +- .../src/components/Layout/Grid/Grid.story.tsx | 2 +- .../src/components/Layout/Layout.story.tsx | 6 +- .../src/components/Layout/Space.story.tsx | 2 +- .../components/Layout/Stack/Stack.story.tsx | 20 +-- .../src/components/Link/TextLink.story.tsx | 2 +- .../src/components/List/InlineList.tsx | 1 + ...List.internal.story.tsx => List.story.tsx} | 2 +- .../grafana-ui/src/components/List/List.tsx | 1 + .../LoadingBar/LoadingBar.story.tsx | 2 +- .../LoadingPlaceholder.story.tsx | 2 +- .../grafana-ui/src/components/Menu/Menu.mdx | 2 +- .../src/components/Menu/Menu.story.tsx | 2 +- ...nternal.story.tsx => CodeEditor.story.tsx} | 2 +- .../PageLayout/PageToolbar.story.tsx | 2 +- .../src/components/PageLayout/PageToolbar.tsx | 2 +- .../Pagination/Pagination.story.tsx | 2 +- .../PanelChrome/PanelChrome.story.tsx | 2 +- .../PanelContainer/PanelContainer.story.tsx | 2 +- .../PanelContainer/PanelContainer.tsx | 2 + .../PluginSignatureBadge.story.tsx | 2 +- .../QueryField/QueryField.story.tsx | 2 +- .../RefreshPicker/RefreshPicker.story.tsx | 2 +- .../RenderUserContentAsHTML.story.tsx | 2 +- .../ScrollContainer/ScrollContainer.story.tsx | 2 +- ...al.story.tsx => SecretFormField.story.tsx} | 2 +- .../SecretInput/SecretInput.story.tsx | 2 +- .../SecretTextArea/SecretTextArea.story.tsx | 2 +- .../src/components/Segment/Segment.story.tsx | 2 +- .../components/Segment/SegmentAsync.story.tsx | 2 +- .../components/Segment/SegmentInput.story.tsx | 2 +- .../src/components/Select/Select.story.tsx | 6 +- ...nternal.story.tsx => SelectPerf.story.tsx} | 2 +- .../components/Slider/RangeSlider.story.tsx | 2 +- .../src/components/Slider/Slider.story.tsx | 2 +- .../src/components/Spinner/Spinner.story.tsx | 2 +- .../components/Splitter/useSplitter.story.tsx | 2 +- .../StatsPicker/StatsPicker.story.tsx | 2 +- .../src/components/Switch/Switch.story.tsx | 2 +- .../src/components/Table/Table.story.tsx | 2 +- ...rnal.story.tsx => TableInputCSV.story.tsx} | 2 +- .../TableInputCSV/TableInputCSV.tsx | 1 + .../src/components/Tabs/Tabs.story.tsx | 2 +- .../src/components/Tags/Tag.story.tsx | 2 +- .../src/components/Tags/TagList.story.tsx | 2 +- .../components/TagsInput/TagsInput.story.tsx | 2 +- .../src/components/Text/Text.story.tsx | 2 +- .../components/TextArea/TextArea.story.tsx | 2 +- ...ernal.story.tsx => BorderRadius.story.tsx} | 2 +- .../ThemeDemos/EmotionPerfTest.story.tsx | 20 +++ .../components/ThemeDemos/ThemeDemo.story.tsx | 7 +- .../Typography.story.tsx} | 5 +- .../ToolbarButton/ToolbarButton.story.tsx | 2 +- .../ToolbarButton/ToolbarButtonRow.story.tsx | 2 +- .../UnitPicker/UnitPicker.story.tsx | 2 +- .../UsersIndicator/Avatar.story.tsx | 2 +- .../UsersIndicator/UserIcon.story.tsx | 2 +- .../UsersIndicator/UsersIndicator.story.tsx | 2 +- .../ValuePicker/ValuePicker.story.tsx | 2 +- .../components/VizLayout/VizLayout.story.tsx | 2 +- .../components/VizLegend/VizLegend.story.tsx | 2 +- .../VizTooltip/SeriesTable.story.tsx | 2 +- .../Graph/GraphWithLegend.internal.story.tsx | 140 ------------------ ...l.story.tsx => useDelayedSwitch.story.tsx} | 2 +- 137 files changed, 610 insertions(+), 749 deletions(-) create mode 100644 packages/grafana-eslint-rules/rules/consistent-story-titles.cjs create mode 100644 packages/grafana-eslint-rules/tests/consistent-stories.test.js rename packages/grafana-ui/src/components/Alert/{InlineBanner.story.tsx => Alert.story.tsx} (87%) delete mode 100644 packages/grafana-ui/src/components/Alert/Toast.story.tsx rename packages/grafana-ui/src/components/CallToActionCard/{CallToActionCard.internal.story.tsx => CallToActionCard.story.tsx} (96%) create mode 100644 packages/grafana-ui/src/components/ColorPicker/ColorPickerInput.story.tsx delete mode 100644 packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx delete mode 100644 packages/grafana-ui/src/components/ColorPicker/Palettes.story.tsx create mode 100644 packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.story.tsx rename packages/grafana-ui/src/components/Dropdown/{ButtonSelect.internal.story.tsx => ButtonSelect.story.tsx} (95%) rename packages/grafana-ui/src/components/FormField/{FormField.internal.story.tsx => FormField.story.tsx} (95%) delete mode 100644 packages/grafana-ui/src/components/Forms/Legacy/Input/Input.internal.story.tsx delete mode 100644 packages/grafana-ui/src/components/Forms/Legacy/Select/Select.internal.story.tsx delete mode 100644 packages/grafana-ui/src/components/Forms/Legacy/Switch/Switch.internal.story.tsx rename packages/grafana-ui/src/components/InfoTooltip/{InfoTooltip.internal.story.tsx => InfoTooltip.story.tsx} (87%) rename packages/grafana-ui/src/components/InlineToast/{InlineToast.internal.story.tsx => InlineToast.story.tsx} (93%) rename packages/grafana-ui/src/components/List/{List.internal.story.tsx => List.story.tsx} (98%) rename packages/grafana-ui/src/components/Monaco/{CodeEditor.internal.story.tsx => CodeEditor.story.tsx} (97%) rename packages/grafana-ui/src/components/SecretFormField/{SecretFormField.internal.story.tsx => SecretFormField.story.tsx} (96%) rename packages/grafana-ui/src/components/Select/{SelectPerf.internal.story.tsx => SelectPerf.story.tsx} (98%) rename packages/grafana-ui/src/components/TableInputCSV/{TableInputCSV.internal.story.tsx => TableInputCSV.story.tsx} (92%) rename packages/grafana-ui/src/components/ThemeDemos/{BorderRadius.internal.story.tsx => BorderRadius.story.tsx} (95%) create mode 100644 packages/grafana-ui/src/components/ThemeDemos/EmotionPerfTest.story.tsx rename packages/grafana-ui/src/components/{Text/Typography.internal.story.tsx => ThemeDemos/Typography.story.tsx} (97%) delete mode 100644 packages/grafana-ui/src/graveyard/Graph/GraphWithLegend.internal.story.tsx rename packages/grafana-ui/src/utils/{useDelayedSwitch.internal.story.tsx => useDelayedSwitch.story.tsx} (90%) diff --git a/.betterer.results b/.betterer.results index fa1406d7eb5..01a8e96aa9f 100644 --- a/.betterer.results +++ b/.betterer.results @@ -4128,6 +4128,15 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], + "packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/components/ColorPicker/ColorPickerInput.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], "packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], @@ -4143,12 +4152,27 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/DateTimePickers/WeekStartPicker.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], + "packages/grafana-ui/src/components/Dropdown/ButtonSelect.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/components/FormField/FormField.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/components/InfoTooltip/InfoTooltip.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/components/List/List.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], "packages/grafana-ui/src/components/PageLayout/PageToolbar.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], "packages/grafana-ui/src/components/QueryField/QueryField.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], + "packages/grafana-ui/src/components/SecretFormField/SecretFormField.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], "packages/grafana-ui/src/components/SecretTextArea/SecretTextArea.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], @@ -4161,6 +4185,9 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/Segment/SegmentInput.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], + "packages/grafana-ui/src/components/Select/SelectPerf.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], "packages/grafana-ui/src/components/Slider/RangeSlider.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], @@ -4170,9 +4197,21 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], + "packages/grafana-ui/src/components/TableInputCSV/TableInputCSV.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/components/ThemeDemos/BorderRadius.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/components/ThemeDemos/EmotionPerfTest.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], "packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], + "packages/grafana-ui/src/components/ThemeDemos/Typography.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], "packages/grafana-ui/src/components/UnitPicker/UnitPicker.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], @@ -4184,6 +4223,9 @@ exports[`no undocumented stories`] = { ], "packages/grafana-ui/src/components/VizTooltip/SeriesTable.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], + "packages/grafana-ui/src/utils/useDelayedSwitch.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ] }` }; diff --git a/e2e/storybook/verify.spec.ts b/e2e/storybook/verify.spec.ts index 2610b1d5bf6..f6210d890d1 100644 --- a/e2e/storybook/verify.spec.ts +++ b/e2e/storybook/verify.spec.ts @@ -3,7 +3,7 @@ // NOTE: storybook must already be running (`yarn storybook`) for this test to work describe('Verify storybook', () => { it('Loads the button story correctly', () => { - cy.visit('?path=/story/buttons-button--basic'); + cy.visit('?path=/story/inputs-button--basic'); getIframeBody().find('button:contains("Example button")').should('be.visible'); }); }); diff --git a/eslint.config.js b/eslint.config.js index 055bb6e4158..9f5bcc9da52 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -176,6 +176,13 @@ module.exports = [ 'react/react-in-jsx-scope': 'off', }, }, + { + name: 'grafana/story-rules', + files: ['packages/grafana-ui/src/**/*.story.tsx'], + rules: { + '@grafana/consistent-story-titles': 'error', + }, + }, { name: 'grafana/public-dashboards-overrides', files: ['public/dashboards/scripted*.js'], diff --git a/packages/grafana-eslint-rules/README.md b/packages/grafana-eslint-rules/README.md index 3fb9a053670..be49122b758 100644 --- a/packages/grafana-eslint-rules/README.md +++ b/packages/grafana-eslint-rules/README.md @@ -113,3 +113,30 @@ const getStyles = (theme: GrafanaTheme2) => ({ ### `theme-token-usage` Used to find all instances of `theme` tokens being used in the codebase and emit the counts as metrics. Should **not** be used as an actual lint rule! + +### `consistent-story-titles` + +Enforce consistent Storybook titles in `.story.tsx` files. + +Storybook titles should not contain more than one `/` for sections (resulting in maximum 2 parts), unless one of the sections is 'Deprecated'. This helps maintain a clean and organized Storybook structure. + +#### Examples + +```tsx +// Bad ❌ +export default { title: 'Components/Forms/Button' }; + +// Good ✅ +export default { title: 'Components/Button' }; + +// Good ✅ - Deprecated allows any number of sections +export default { title: 'Components/Deprecated/Forms/Button/Extra' }; + +// Good ✅ - Variable assignment pattern +const storyConfig = { title: 'Components/Button' }; +export default storyConfig; + +// Bad ❌ - Variable assignment with too many sections +const storyConfig = { title: 'Components/Forms/Button' }; +export default storyConfig; +``` diff --git a/packages/grafana-eslint-rules/index.cjs b/packages/grafana-eslint-rules/index.cjs index 2babd165372..438d88332bc 100644 --- a/packages/grafana-eslint-rules/index.cjs +++ b/packages/grafana-eslint-rules/index.cjs @@ -3,6 +3,7 @@ const noBorderRadiusLiteral = require('./rules/no-border-radius-literal.cjs'); const noUnreducedMotion = require('./rules/no-unreduced-motion.cjs'); const themeTokenUsage = require('./rules/theme-token-usage.cjs'); const noRestrictedImgSrcs = require('./rules/no-restricted-img-srcs.cjs'); +const consistentStoryTitles = require('./rules/consistent-story-titles.cjs'); module.exports = { rules: { @@ -11,5 +12,6 @@ module.exports = { 'no-border-radius-literal': noBorderRadiusLiteral, 'theme-token-usage': themeTokenUsage, 'no-restricted-img-srcs': noRestrictedImgSrcs, + 'consistent-story-titles': consistentStoryTitles, }, }; diff --git a/packages/grafana-eslint-rules/rules/consistent-story-titles.cjs b/packages/grafana-eslint-rules/rules/consistent-story-titles.cjs new file mode 100644 index 00000000000..9fe077d699b --- /dev/null +++ b/packages/grafana-eslint-rules/rules/consistent-story-titles.cjs @@ -0,0 +1,106 @@ +// @ts-check +const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils'); + +const createRule = ESLintUtils.RuleCreator( + (name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}` +); + +/** + * @param {string} title + * @returns {boolean} + */ +const isValidStorybookTitle = (title) => { + if (typeof title !== 'string') { + return true; // Skip non-string titles + } + + const sections = title.split('/'); + + // Allow up to 3 sections if one of them is 'Deprecated' + if (sections.some((section) => section.trim() === 'Deprecated')) { + return sections.length <= 3; + } + + // Otherwise, limit to maximum 2 sections (1 slash) + return sections.length <= 2; +}; + +/** + * @param {import('@typescript-eslint/utils').TSESTree.ObjectExpression} objectNode + * @param {import('@typescript-eslint/utils/ts-eslint').RuleContext<'invalidTitle', []>} context + */ +const checkObjectForTitle = (objectNode, context) => { + const titleProperty = objectNode.properties.find( + (prop) => + prop.type === AST_NODE_TYPES.Property && prop.key.type === AST_NODE_TYPES.Identifier && prop.key.name === 'title' + ); + + if ( + titleProperty && + titleProperty.type === AST_NODE_TYPES.Property && + titleProperty.value.type === AST_NODE_TYPES.Literal + ) { + const titleValue = titleProperty.value.value; + + if (typeof titleValue === 'string' && !isValidStorybookTitle(titleValue)) { + context.report({ + node: titleProperty.value, + messageId: 'invalidTitle', + data: { + title: titleValue, + }, + }); + } + } +}; + +const consistentStoryTitlesRule = createRule({ + create(context) { + return { + ExportDefaultDeclaration(node) { + // Only check .story.tsx files + const filename = context.filename; + if (!filename || !filename.endsWith('.story.tsx')) { + return; + } + + if (node.declaration.type === AST_NODE_TYPES.ObjectExpression) { + // Handle direct object export: export default { title: '...' } + checkObjectForTitle(node.declaration, context); + } else if (node.declaration.type === AST_NODE_TYPES.Identifier) { + // Handle variable reference export: export default storyConfig + const variableName = node.declaration.name; + const scope = context.sourceCode.getScope(node); + const variable = scope.set.get(variableName); + + if (variable) { + // Find the variable declaration + const declaration = variable.defs.find((def) => def.type === 'Variable'); + if ( + declaration && + declaration.node.init && + declaration.node.init.type === AST_NODE_TYPES.ObjectExpression + ) { + checkObjectForTitle(declaration.node.init, context); + } + } + } + }, + }; + }, + name: 'consistent-story-titles', + meta: { + type: 'problem', + docs: { + description: 'Enforce consistent Storybook titles with maximum two sections (1 slash) unless one is "Deprecated"', + }, + messages: { + invalidTitle: + 'Storybook title "{{ title }}" has too many sections. Use maximum 2 sections (1 slash) unless one section is "Deprecated".', + }, + schema: [], + }, + defaultOptions: [], +}); + +module.exports = consistentStoryTitlesRule; diff --git a/packages/grafana-eslint-rules/tests/consistent-stories.test.js b/packages/grafana-eslint-rules/tests/consistent-stories.test.js new file mode 100644 index 00000000000..004cd0aeeb3 --- /dev/null +++ b/packages/grafana-eslint-rules/tests/consistent-stories.test.js @@ -0,0 +1,117 @@ +import { RuleTester } from 'eslint'; + +import consistentStories from '../rules/consistent-story-titles.cjs'; + +RuleTester.setDefaultConfig({ + languageOptions: { + ecmaVersion: 2018, + sourceType: 'module', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, +}); + +const ruleTester = new RuleTester(); + +ruleTester.run('eslint consistent-stories', consistentStories, { + valid: [ + { + name: 'simple title', + code: `export default { title: 'Button' };`, + filename: 'Button.story.tsx', + }, + { + name: 'one section', + code: `export default { title: 'Components/Button' };`, + filename: 'Button.story.tsx', + }, + { + name: 'deprecated can have 3 sections', + code: `export default { title: 'Components/Deprecated/Button' };`, + filename: 'Button.story.tsx', + }, + { + name: 'not a story file', + code: `export default { title: 'Components/Forms/Button/Extra/Section' };`, + filename: 'Button.tsx', + }, + { + name: 'non-string title', + code: `export default { title: 123 };`, + filename: 'Button.story.tsx', + }, + { + name: 'no title property', + code: `export default { component: Button };`, + filename: 'Button.story.tsx', + }, + { + name: 'variable assignment - simple title', + code: ` +const storyConfig = { title: 'Button' }; +export default storyConfig;`, + filename: 'Button.story.tsx', + }, + { + name: 'variable assignment - one section', + code: ` +const storyConfig = { title: 'Components/Button' }; +export default storyConfig;`, + filename: 'Button.story.tsx', + }, + { + name: 'variable assignment - with Deprecated', + code: ` +const storyConfig = { title: 'Components/Deprecated/Button' }; +export default storyConfig;`, + filename: 'Button.story.tsx', + }, + ], + invalid: [ + { + name: 'too many sections without Deprecated', + code: `export default { title: 'Components/Forms/Button' };`, + filename: 'Button.story.tsx', + errors: [ + { + messageId: 'invalidTitle', + }, + ], + }, + { + name: 'too many sections without Deprecated', + code: `export default { title: 'Components/Forms/Button/Extra' };`, + filename: 'Button.story.tsx', + errors: [ + { + messageId: 'invalidTitle', + }, + ], + }, + { + name: 'with spaces around sections', + code: `export default { title: 'Components / Forms / Button' };`, + filename: 'Button.story.tsx', + errors: [ + { + messageId: 'invalidTitle', + }, + ], + }, + { + name: 'variable assignment - too many sections', + code: ` +const storyConfig = { title: 'Components/Forms/Button' }; +export default storyConfig;`, + filename: 'Button.story.tsx', + errors: [ + { + messageId: 'invalidTitle', + }, + ], + }, + ], +}); diff --git a/packages/grafana-ui/.storybook/main.ts b/packages/grafana-ui/.storybook/main.ts index 1c75daef688..29258c4a228 100644 --- a/packages/grafana-ui/.storybook/main.ts +++ b/packages/grafana-ui/.storybook/main.ts @@ -2,13 +2,7 @@ import path, { dirname, join } from 'node:path'; import type { StorybookConfig } from '@storybook/react-webpack5'; import { copyAssetsSync } from './copyAssets'; -// Internal stories should only be visible during development -const coreComponentsGlobs: StorybookConfig['stories'] = [ - '../src/Intro.mdx', - process.env.NODE_ENV === 'production' - ? '../src/components/**/!(*.internal).story.tsx' - : '../src/components/**/*.story.tsx', -]; +const coreComponentsGlobs: StorybookConfig['stories'] = ['../src/Intro.mdx', '../src/**/*.story.tsx']; const alertingComponentsGlobs: StorybookConfig['stories'] = [ { diff --git a/packages/grafana-ui/.storybook/preview.ts b/packages/grafana-ui/.storybook/preview.ts index 93892fae443..803d45315e3 100644 --- a/packages/grafana-ui/.storybook/preview.ts +++ b/packages/grafana-ui/.storybook/preview.ts @@ -71,20 +71,84 @@ const preview: Preview = { // Sort stories first by Docs Overview, then alphabetically // We should be able to use the builtin alphabetical sort, but is broken in SB 7.0 // https://github.com/storybookjs/storybook/issues/22470 + + // Story sorting is weird - All stories are sorted as a single 1D list, but then grouped in the UI. + // Story titles are generally in the format of [Category]/[Component]/[Story]. However, some categories + // will have an additional `Deprecated` sub folder before the [Component] + // + // We want to have multi-level sorting where: + // - The top level category has an explicit order + // - Components are sorted alphabetically within their category + // - Except the Deprecated folder, which is sorted to the bottom + // - Stories per component use the default file sort order storySort: (a, b) => { - // Skip sorting for stories with nosort tag - if (a.tags.includes('nosort') || b.tags.includes('nosort')) { - return 0; + const CATEGORY_ORDER = [ + // Should all be lowercase + 'docs overview', + 'foundations', + 'iconography', + 'layout', + + 'forms', + 'inputs', + 'pickers', + 'date time pickers', + + 'information', + 'overlays', + 'utilities', + 'navigation', + + 'plugins', + 'alerting', + 'developers', + ]; + + const aTitle = a.title.toLowerCase(); + const bTitle = b.title.toLowerCase(); + const [aCategory, aComponent] = aTitle.split('/'); + const [bCategory, bComponent] = bTitle.split('/'); + + // + // Sort by category order first + const aCategoryIndex = CATEGORY_ORDER.indexOf(aCategory); + const bCategoryIndex = CATEGORY_ORDER.indexOf(bCategory); + + if (aCategoryIndex === -1 || bCategoryIndex === -1) { + const category = aCategoryIndex === -1 ? aCategory : bCategory; + throw new Error( + `Category ${category} not found in CATEGORY_ORDER. Prefer reusing the existing categories, or add to CATEGORY_ORDER.` + ); } - if (a.title.startsWith('Docs Overview')) { - if (b.title.startsWith('Docs Overview')) { - return 0; - } + + if (aCategoryIndex !== bCategoryIndex) { + return aCategoryIndex - bCategoryIndex; + } + + // + // Sort 'Deprecated' subfolders to the bottom + if (aTitle.includes('deprecated') && !bTitle.includes('deprecated')) { + return 1; + } else if (bTitle.includes('deprecated') && !aTitle.includes('deprecated')) { return -1; - } else if (b.title.startsWith('Docs Overview')) { + } + + // + // Sort Docs to the top + if (a.type === 'docs' && b.type !== 'docs') { + return -1; + } else if (a.type !== 'docs' && b.type === 'docs') { return 1; } - return a.id === b.id ? 0 : a.id.localeCompare(b.id, undefined, { numeric: true }); + + // + // If sorting different components, sort alphabetically + if (aComponent !== bComponent) { + return aComponent.localeCompare(bComponent, undefined, { numeric: true }); + } + + // Otherwise, sort stories within componmments according to source order + return 0; }, }, }, diff --git a/packages/grafana-ui/src/components/Alert/InlineBanner.story.tsx b/packages/grafana-ui/src/components/Alert/Alert.story.tsx similarity index 87% rename from packages/grafana-ui/src/components/Alert/InlineBanner.story.tsx rename to packages/grafana-ui/src/components/Alert/Alert.story.tsx index 61d573d0f1f..2c5ffba4f06 100644 --- a/packages/grafana-ui/src/components/Alert/InlineBanner.story.tsx +++ b/packages/grafana-ui/src/components/Alert/Alert.story.tsx @@ -10,7 +10,7 @@ import mdx from './Alert.mdx'; const severities: AlertVariant[] = ['error', 'warning', 'info', 'success']; const meta: Meta = { - title: 'Overlays/Alert/InlineBanner', + title: 'Information/Alert', component: Alert, parameters: { docs: { @@ -78,4 +78,15 @@ export const Examples: StoryFn = () => { ); }; +export const Toast: StoryFn = (args) => { + return To use as a toast, set the elevated and onRemove props.; +}; + +Toast.args = { + title: 'Toast', + severity: 'error', + onRemove: action('Remove button clicked'), + elevated: true, +}; + export default meta; diff --git a/packages/grafana-ui/src/components/Alert/Toast.story.tsx b/packages/grafana-ui/src/components/Alert/Toast.story.tsx deleted file mode 100644 index 12991adcb03..00000000000 --- a/packages/grafana-ui/src/components/Alert/Toast.story.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { StoryFn, Meta } from '@storybook/react'; - -import { StoryExample } from '../../utils/storybook/StoryExample'; -import { Stack } from '../Layout/Stack/Stack'; - -import { Alert, AlertVariant } from './Alert'; -import mdx from './Alert.mdx'; - -const severities: AlertVariant[] = ['error', 'warning', 'info', 'success']; - -const meta: Meta = { - title: 'Overlays/Alert/Toast', - component: Alert, - parameters: { - docs: { - page: mdx, - }, - controls: { exclude: ['onRemove'] }, - }, - argTypes: { - severity: { control: { type: 'select', options: severities } }, - }, - args: { - title: 'Toast', - severity: 'error', - onRemove: action('Remove button clicked'), - }, -}; - -export const Basic: StoryFn = (args) => { - return ( - - Child content that includes some alert details, like maybe what actually happened. - - ); -}; - -export function Examples() { - return ( - - - - {severities.map((severity) => ( - - ))} - - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam metus urna, aliquam eu scelerisque non, - facilisis eget est. Morbi eleifend egestas massa id vulputate. Fusce dignissim magna lacus, ut molestie odio - feugiat sed. Cras fringilla justo sit amet turpis scelerisque, a volutpat purus iaculis. Nunc sagittis - molestie faucibus. Curabitur at neque luctus, pellentesque urna eget, posuere urna. Nunc malesuada elit in - ipsum dictum egestas. Praesent convallis mauris massa, porta mattis ex gravida ut. Proin consectetur ultrices - tortor sit amet efficitur. Suspendisse nec turpis dapibus mauris venenatis maximus quis eget orci. Ut semper - enim magna, ullamcorper elementum sapien pharetra vitae. Vivamus at nulla ut metus bibendum ornare et ut leo. - Proin ante turpis, ornare a malesuada et, rutrum nec lorem. Maecenas vestibulum orci vel nibh convallis - eleifend. Quisque vitae consectetur massa, vitae elementum mauris. Pellentesque sit amet ligula lorem. Fusce - sit amet lorem non augue rutrum varius. Donec sed imperdiet libero, eget venenatis elit. Fusce porttitor - dapibus urna. Duis fringilla ante vel tempor tincidunt. In euismod vestibulum odio sit amet iaculis. Donec vel - dapibus libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi lacinia commodo lectus. Aenean - in magna eget lectus luctus suscipit et vitae erat. Pellentesque quis ligula id lorem egestas sollicitudin sit - amet sed sem. Nullam et nibh a odio rhoncus efficitur sed nec est. Sed commodo lacus vitae sem congue, - accumsan dignissim metus iaculis. Praesent in dignissim nisl. Aliquam facilisis, sapien eget porttitor - ultrices, massa libero bibendum odio, at ornare diam arcu ac massa. Vestibulum egestas leo eget lorem congue - condimentum. Praesent egestas, neque id gravida vehicula, augue ex scelerisque lectus, finibus pellentesque - enim dolor vel ante. Cras convallis, sem at malesuada tincidunt, diam urna auctor leo, sed laoreet est ex in - libero. Ut condimentum ante eget ex gravida, id tempus metus ultricies. Pellentesque placerat, massa id - laoreet molestie, justo nisl varius metus, maximus vehicula erat libero vitae nulla. Mauris rhoncus ligula - vitae volutpat auctor. Suspendisse potenti. Quisque quis orci faucibus, ullamcorper dolor eget, mollis massa. - Etiam eu molestie ipsum. Sed laoreet diam metus, luctus maximus erat viverra quis. Ut eu felis dictum, - tincidunt erat sit amet, scelerisque neque. Orci varius natoque penatibus et magnis dis parturient montes, - nascetur ridiculus mus. Phasellus sit amet est tristique, fermentum massa ut, viverra metus. Interdum et - malesuada fames ac ante ipsum primis in faucibus. Nunc iaculis nunc elit, ut feugiat ipsum egestas eget. - Vestibulum pulvinar ligula mi, quis lacinia diam suscipit eget. Etiam consectetur vel nunc at hendrerit. - Pellentesque blandit eleifend aliquam. Etiam et malesuada purus, et bibendum sapien. Phasellus tincidunt - consequat eros consequat sodales. Vestibulum quis viverra neque. Integer sit amet lacinia nunc. Ut cursus, - elit id faucibus elementum, elit nunc dapibus tellus, non ornare nisi sapien et eros. Nunc sit amet suscipit - arcu. Nulla ut nunc tempor, auctor massa sed, consectetur orci. Pellentesque erat ante, placerat eget dictum - elementum, dapibus et ipsum. Nunc sit amet nulla gravida, finibus felis vel, tempus sem. In urna purus, - accumsan quis aliquam et, condimentum ac urna. Nullam volutpat ullamcorper sapien, quis ultricies purus - dignissim aliquam. Mauris quis enim ante. Etiam vulputate faucibus placerat. Ut pellentesque, purus vitae - euismod cursus, lacus enim vulputate sapien, in porttitor erat dui eu lectus. Duis eleifend, massa vel - vehicula gravida, magna urna rutrum ligula, vitae mollis ipsum neque id enim. Donec varius tristique nisi, et - vestibulum dolor efficitur eget. Cras mauris leo, bibendum eget pretium a, tincidunt faucibus massa. - Vestibulum hendrerit arcu magna, vel consequat est euismod nec. Vestibulum non lacus porttitor, congue tortor - ut, venenatis elit. Duis at lectus arcu. Nunc quis sapien eu ipsum rutrum accumsan. Orci varius natoque - penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus quis sapien luctus, volutpat nulla - eget, gravida nunc. Aenean placerat a felis quis imperdiet. Sed sapien tellus, ultrices non ipsum eget, - pretium rhoncus quam. Aliquam erat volutpat. Maecenas at interdum turpis, eu mattis ligula. Class aptent - taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In lobortis felis a leo - ultricies, venenatis mollis felis lobortis. Suspendisse placerat vel ante vel euismod. Aenean sit amet - ullamcorper mauris, id consectetur est. Ut ultricies enim non quam condimentum, et congue arcu commodo. - Praesent convallis eleifend turpis, vitae feugiat turpis imperdiet sit amet. Class aptent taciti sociosqu ad - litora torquent per conubia nostra, per inceptos himenaeos. Quisque vulputate porttitor mattis. Pellentesque - sed ullamcorper lectus. Suspendisse velit tortor, viverra eget facilisis condimentum, accumsan sit amet felis. - Cras lobortis mi fermentum ligula consectetur, vitae tincidunt mauris scelerisque. Aenean ac condimentum erat, - quis lacinia lacus. Ut magna nibh, tempor et ligula suscipit, placerat laoreet ipsum. In semper semper nisl. - Donec risus lorem, tempor sed sollicitudin vitae, fringilla et mi. Vivamus pulvinar quam nisl, et tincidunt - justo tempus quis. Duis semper magna nunc, vitae faucibus lectus facilisis sed. Phasellus consequat arcu vel - interdum fermentum. In condimentum euismod neque, sed aliquet mauris posuere nec. Etiam metus eros, - pellentesque eget scelerisque id, porttitor at ligula. Curabitur eget nibh maximus enim lobortis sodales. - Etiam vulputate ligula lobortis vestibulum pulvinar. Curabitur eros justo, accumsan sed elit ac, mattis - lacinia nisi. Suspendisse ullamcorper lectus sit amet tellus condimentum porttitor. Duis cursus, neque et - aliquam congue, odio lectus porta elit, id lacinia dolor justo non leo. Aliquam vehicula at tellus ullamcorper - tincidunt. Phasellus neque nibh, convallis sit amet arcu sit amet, convallis egestas tortor. Etiam sit amet - vehicula quam. Praesent id consequat lacus, ac facilisis quam. Integer tristique lorem eros, id consequat - lorem lobortis vitae. Aliquam luctus purus eget sem molestie iaculis. Duis nisl risus, sodales sit amet nunc - vitae, volutpat cursus augue. Pellentesque congue massa eu metus pellentesque consectetur at vel neque. Donec - bibendum hendrerit erat, vitae dictum enim lobortis a. Quisque ac dapibus tellus, sit amet facilisis orci. - Cras pretium tortor non condimentum semper. Phasellus mollis condimentum blandit. Pellentesque at arcu risus. - Vivamus sit amet dui semper, suscipit est nec, elementum arcu. Praesent ante turpis, convallis ac leo eget, - - - - ); -} - -export default meta; diff --git a/packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.story.tsx b/packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.story.tsx index 1f033fcf69c..eb6cb427eba 100644 --- a/packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.story.tsx +++ b/packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.story.tsx @@ -12,7 +12,7 @@ import { AutoSaveField } from './AutoSaveField'; import mdx from './AutoSaveField.mdx'; const meta: Meta = { - title: 'Forms/AutoSaveField', + title: 'Inputs/AutoSaveField', component: AutoSaveField, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/Badge/Badge.story.tsx b/packages/grafana-ui/src/components/Badge/Badge.story.tsx index 173ba2ab36d..69ee4c37d94 100644 --- a/packages/grafana-ui/src/components/Badge/Badge.story.tsx +++ b/packages/grafana-ui/src/components/Badge/Badge.story.tsx @@ -6,7 +6,7 @@ import { Badge } from './Badge'; import mdx from './Badge.mdx'; const meta: Meta = { - title: 'Data Display/Badge', + title: 'Information/Badge', component: Badge, parameters: { docs: { page: mdx }, diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx index 4c5575563d0..f5f42236828 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx @@ -9,7 +9,7 @@ import { BarGauge, Props } from './BarGauge'; import mdx from './BarGauge.mdx'; const meta: Meta = { - title: 'Visualizations/BarGauge', + title: 'Plugins/BarGauge', component: BarGauge, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/BigValue/BigValue.story.tsx b/packages/grafana-ui/src/components/BigValue/BigValue.story.tsx index 816a377b25a..48c45f9843c 100644 --- a/packages/grafana-ui/src/components/BigValue/BigValue.story.tsx +++ b/packages/grafana-ui/src/components/BigValue/BigValue.story.tsx @@ -15,7 +15,7 @@ import { import mdx from './BigValue.mdx'; const meta: Meta = { - title: 'Visualizations/BigValue', + title: 'Plugins/BigValue', component: BigValue, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/Button/Button.story.tsx b/packages/grafana-ui/src/components/Button/Button.story.tsx index c87d6709567..5f26875852e 100644 --- a/packages/grafana-ui/src/components/Button/Button.story.tsx +++ b/packages/grafana-ui/src/components/Button/Button.story.tsx @@ -12,7 +12,7 @@ import { ButtonGroup } from './ButtonGroup'; const sizes: ComponentSize[] = ['lg', 'md', 'sm']; export default { - title: 'Buttons/Button', + title: 'Inputs/Button', component: Button, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx b/packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx index edb861bbfad..b9870ad11b2 100644 --- a/packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx +++ b/packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx @@ -3,7 +3,7 @@ import { StoryFn, Meta } from '@storybook/react'; import { ButtonCascader } from './ButtonCascader'; const meta: Meta = { - title: 'Forms/Cascader/ButtonCascader', + title: 'Inputs/ButtonCascader', component: ButtonCascader, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.internal.story.tsx b/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.story.tsx similarity index 96% rename from packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.internal.story.tsx rename to packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.story.tsx index 5d42373e1cc..8c107a20129 100644 --- a/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.internal.story.tsx +++ b/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.story.tsx @@ -6,7 +6,7 @@ import { Button } from '../Button/Button'; import { CallToActionCard, CallToActionCardProps } from './CallToActionCard'; const meta: Meta = { - title: 'Layout/CallToActionCard', + title: 'Information/Deprecated/CallToActionCard', component: CallToActionCard, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.tsx b/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.tsx index e71c688f3e7..ca3348c2b54 100644 --- a/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.tsx +++ b/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.tsx @@ -11,6 +11,7 @@ export interface CallToActionCardProps { className?: string; } +/** @deprecated Use instead */ export const CallToActionCard = ({ message, callToActionElement, footer, className }: CallToActionCardProps) => { const css = useStyles2(getStyles); diff --git a/packages/grafana-ui/src/components/Card/Card.story.tsx b/packages/grafana-ui/src/components/Card/Card.story.tsx index 8f2a9668dd2..34b25d96a05 100644 --- a/packages/grafana-ui/src/components/Card/Card.story.tsx +++ b/packages/grafana-ui/src/components/Card/Card.story.tsx @@ -9,10 +9,9 @@ import { Card } from './Card'; const logo = 'https://grafana.com/static/assets/img/apple-touch-icon.png'; const meta: Meta = { - title: 'General/Card', + title: 'Layout/Card', component: Card, - // nosort is a custom tag used so the stories shown in docs keep the order they are defined in the file - tags: ['autodocs', 'nosort'], + tags: ['autodocs'], parameters: { controls: { exclude: ['onClick', 'href', 'heading', 'description', 'className', 'noMargin'], diff --git a/packages/grafana-ui/src/components/Carousel/Carousel.story.tsx b/packages/grafana-ui/src/components/Carousel/Carousel.story.tsx index ffed8b27098..33a2c762582 100644 --- a/packages/grafana-ui/src/components/Carousel/Carousel.story.tsx +++ b/packages/grafana-ui/src/components/Carousel/Carousel.story.tsx @@ -15,7 +15,7 @@ const sampleImages = [ ]; const meta: Meta = { - title: 'Data Display/Carousel', + title: 'Overlays/Carousel', component: Carousel, parameters: { docs: { page: mdx }, diff --git a/packages/grafana-ui/src/components/Cascader/Cascader.story.tsx b/packages/grafana-ui/src/components/Cascader/Cascader.story.tsx index 43d8c337589..761235d5a22 100644 --- a/packages/grafana-ui/src/components/Cascader/Cascader.story.tsx +++ b/packages/grafana-ui/src/components/Cascader/Cascader.story.tsx @@ -31,7 +31,7 @@ const options = [ ]; const meta: Meta = { - title: 'Forms/Cascader', + title: 'Inputs/Cascader', component: Cascader, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/ClickOutsideWrapper/ClickOutsideWrapper.story.tsx b/packages/grafana-ui/src/components/ClickOutsideWrapper/ClickOutsideWrapper.story.tsx index c2fc4d41a24..53f56e7d128 100644 --- a/packages/grafana-ui/src/components/ClickOutsideWrapper/ClickOutsideWrapper.story.tsx +++ b/packages/grafana-ui/src/components/ClickOutsideWrapper/ClickOutsideWrapper.story.tsx @@ -5,7 +5,7 @@ import { ClickOutsideWrapper } from './ClickOutsideWrapper'; import mdx from './ClickOutsideWrapper.mdx'; const meta: Meta = { - title: 'Layout/ClickOutsideWrapper', + title: 'Utilities/ClickOutsideWrapper', component: ClickOutsideWrapper, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/ClipboardButton/ClipboardButton.story.tsx b/packages/grafana-ui/src/components/ClipboardButton/ClipboardButton.story.tsx index 5089786ae97..64c0489590a 100644 --- a/packages/grafana-ui/src/components/ClipboardButton/ClipboardButton.story.tsx +++ b/packages/grafana-ui/src/components/ClipboardButton/ClipboardButton.story.tsx @@ -7,7 +7,7 @@ import { ClipboardButton as ClipboardButtonImpl, Props } from './ClipboardButton import mdx from './ClipboardButton.mdx'; const meta: Meta = { - title: 'Buttons/ClipboardButton', + title: 'Inputs/ClipboardButton', component: ClipboardButtonImpl, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx index 196c8d65c97..c3967136503 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx @@ -5,12 +5,11 @@ import { Meta, StoryFn } from '@storybook/react'; import { useStyles2 } from '../../themes/ThemeContext'; import { clearButtonStyles } from '../Button/Button'; -import { ColorPicker, SeriesColorPicker } from './ColorPicker'; +import { ColorPicker } from './ColorPicker'; import mdx from './ColorPicker.mdx'; -import { ColorPickerInput } from './ColorPickerInput'; const meta: Meta = { - title: 'Pickers and Editors/ColorPicker', + title: 'Pickers/ColorPicker', component: ColorPicker, parameters: { docs: { @@ -43,24 +42,6 @@ export const Basic: StoryFn = ({ color, enableNamedColors }) ); }; -export const SeriesPicker: StoryFn = ({ color, enableNamedColors }) => { - const [, updateArgs] = useArgs(); - return ( -
- {}} - color={color} - onChange={(color) => { - action('Color changed')(color); - updateArgs({ color }); - }} - /> -
- ); -}; - export const CustomTrigger: StoryFn = ({ color, enableNamedColors }) => { const [, updateArgs] = useArgs(); const clearButton = useStyles2(clearButtonStyles); @@ -89,19 +70,4 @@ export const CustomTrigger: StoryFn = ({ color, enableNamedC ); }; -export const Input: StoryFn = ({ color }) => { - const [, updateArgs] = useArgs(); - return ( -
- { - action('Color changed')(color); - updateArgs({ color }); - }} - /> -
- ); -}; - export default meta; diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerInput.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerInput.story.tsx new file mode 100644 index 00000000000..0be0684ca33 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerInput.story.tsx @@ -0,0 +1,32 @@ +import { action } from '@storybook/addon-actions'; +import { useArgs } from '@storybook/preview-api'; +import { Meta, StoryFn } from '@storybook/react'; + +import { ColorPickerInput } from './ColorPickerInput'; + +const meta: Meta = { + title: 'Pickers/ColorPickerInput', + component: ColorPickerInput, + parameters: { + controls: { + exclude: ['onChange', 'onColorChange'], + }, + }, +}; + +export const Basic: StoryFn = ({ color }) => { + const [, updateArgs] = useArgs(); + return ( +
+ { + action('Color changed')(color); + updateArgs({ color }); + }} + /> +
+ ); +}; + +export default meta; diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx deleted file mode 100644 index a8ffbc6d129..00000000000 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.story.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Meta } from '@storybook/react'; -import { useState } from 'react'; - -import { useTheme2 } from '../../themes/ThemeContext'; - -import mdx from './ColorPicker.mdx'; -import { ColorPickerPopover } from './ColorPickerPopover'; -import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; - -const meta: Meta = { - title: 'Pickers and Editors/ColorPicker/Popovers', - component: ColorPickerPopover, - parameters: { - docs: { - page: mdx, - }, - }, -}; - -export const Basic = () => { - return ( -
- { - console.log(color); - }} - /> -
- ); -}; - -export const SeriesColorPickerPopoverExample = () => { - const theme = useTheme2(); - const [yAxis, setYAxis] = useState(0); - - return ( -
- (yAxis ? setYAxis(0) : setYAxis(2))} - color="#BC67E6" - onChange={(color: string) => { - console.log(color); - }} - /> -
- ); -}; - -export default meta; diff --git a/packages/grafana-ui/src/components/ColorPicker/Palettes.story.tsx b/packages/grafana-ui/src/components/ColorPicker/Palettes.story.tsx deleted file mode 100644 index 06042a9a30d..00000000000 --- a/packages/grafana-ui/src/components/ColorPicker/Palettes.story.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { useArgs } from '@storybook/preview-api'; -import { Meta, StoryFn } from '@storybook/react'; -import { useState } from 'react'; - -import mdx from './ColorPicker.mdx'; -import { NamedColorsPalette } from './NamedColorsPalette'; -import SpectrumPalette from './SpectrumPalette'; - -const meta: Meta = { - title: 'Pickers and Editors/ColorPicker/Palettes', - parameters: { - docs: { - page: mdx, - }, - controls: { - exclude: ['theme', 'color'], - }, - }, - args: { - color: 'green', - }, -}; - -export const NamedColors: StoryFn = ({ color }) => { - const [colorVal, setColor] = useState(color); - return ; -}; - -export const Spectrum: StoryFn = ({ color }) => { - const [, updateArgs] = useArgs(); - return ( - { - action('Color changed')(color); - updateArgs({ color }); - }} - /> - ); -}; - -export default meta; diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.story.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.story.tsx new file mode 100644 index 00000000000..a66b0b951dd --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPicker.story.tsx @@ -0,0 +1,39 @@ +import { action } from '@storybook/addon-actions'; +import { useArgs } from '@storybook/preview-api'; +import { Meta, StoryFn } from '@storybook/react'; + +import { SeriesColorPicker } from './ColorPicker'; + +const meta: Meta = { + title: 'Pickers/SeriesColorPicker', + component: SeriesColorPicker, + parameters: { + controls: { + exclude: ['onChange', 'onColorChange'], + }, + }, + args: { + enableNamedColors: false, + color: '#00ff00', + }, +}; + +export const Basic: StoryFn = ({ color, enableNamedColors }) => { + const [, updateArgs] = useArgs(); + return ( +
+ {}} + color={color} + onChange={(color) => { + action('Color changed')(color); + updateArgs({ color }); + }} + /> +
+ ); +}; + +export default meta; diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx index d8dc2ff2b7c..eea67016ada 100644 --- a/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx +++ b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx @@ -16,7 +16,7 @@ type PropsAndCustomArgs = ComboboxProps & type Story = StoryObj>; const meta: Meta = { - title: 'Forms/Combobox', + title: 'Inputs/Combobox', component: Combobox, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/Combobox/MultiCombobox.story.tsx b/packages/grafana-ui/src/components/Combobox/MultiCombobox.story.tsx index 6c99190c64a..9456242aa8f 100644 --- a/packages/grafana-ui/src/components/Combobox/MultiCombobox.story.tsx +++ b/packages/grafana-ui/src/components/Combobox/MultiCombobox.story.tsx @@ -11,7 +11,7 @@ import { generateOptions, fakeSearchAPI, generateGroupingOptions } from './story import { ComboboxOption } from './types'; const meta: Meta = { - title: 'Forms/MultiCombobox', + title: 'Inputs/MultiCombobox', component: MultiCombobox, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.story.tsx b/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.story.tsx index 6d138a7cb90..106dfc19af6 100644 --- a/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.story.tsx +++ b/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.story.tsx @@ -8,7 +8,7 @@ import mdx from './ConfirmButton.mdx'; import { DeleteButton } from './DeleteButton'; const meta: Meta = { - title: 'Buttons/ConfirmButton', + title: 'Inputs/ConfirmButton', component: ConfirmButton, // SB7 has broken subcomponent types due to dropping support for the feature // https://github.com/storybookjs/storybook/issues/20782 diff --git a/packages/grafana-ui/src/components/ContextMenu/ContextMenu.mdx b/packages/grafana-ui/src/components/ContextMenu/ContextMenu.mdx index 353636c7a21..a50659a3f0e 100644 --- a/packages/grafana-ui/src/components/ContextMenu/ContextMenu.mdx +++ b/packages/grafana-ui/src/components/ContextMenu/ContextMenu.mdx @@ -6,6 +6,8 @@ import { WithContextMenu } from './WithContextMenu'; A menu displaying additional options when it's not possible to show them at all times due to a space constraint. +`ContextMenu` wraps `Menu` to supply options as a list, and display at absolute coordinates. + ### Usage There are controlled and uncontrolled versions of the component available. With the controlled component (`ContextMenu`) the open/close logic needs to be handled separately. Uncontrolled component (`WithContextMenu`) handles this logic internally. diff --git a/packages/grafana-ui/src/components/ContextMenu/ContextMenu.story.tsx b/packages/grafana-ui/src/components/ContextMenu/ContextMenu.story.tsx index 3834bccbedf..ebafcf95e03 100644 --- a/packages/grafana-ui/src/components/ContextMenu/ContextMenu.story.tsx +++ b/packages/grafana-ui/src/components/ContextMenu/ContextMenu.story.tsx @@ -10,7 +10,7 @@ import { renderMenuItems } from './ContextMenuStoryHelper'; import { WithContextMenu, WithContextMenuProps } from './WithContextMenu'; const meta: Meta = { - title: 'General/ContextMenu', + title: 'Overlays/ContextMenu', component: ContextMenu, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.story.tsx b/packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.story.tsx index 3557ef84dd6..b198f976280 100644 --- a/packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.story.tsx +++ b/packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.story.tsx @@ -36,7 +36,7 @@ const settingsMock: HttpSettingsProps['dataSourceConfig'] = { }; const meta: Meta = { - title: 'Data Source/DataSourceHttpSettings', + title: 'Plugins/DataSourceHttpSettings', component: DataSourceHttpSettings, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/DatePicker/DatePicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/DatePicker/DatePicker.story.tsx index 29b10d03148..3f12563318a 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/DatePicker/DatePicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/DatePicker/DatePicker.story.tsx @@ -7,7 +7,7 @@ import { DatePicker, DatePickerProps } from './DatePicker'; import mdx from './DatePicker.mdx'; const meta: Meta = { - title: 'Pickers and Editors/TimePickers/Pickers And Editors/DatePicker', + title: 'Date time pickers/DatePicker', component: DatePicker, argTypes: { minDate: { control: 'date' }, diff --git a/packages/grafana-ui/src/components/DateTimePickers/DatePickerWithInput/DatePickerWithInput.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/DatePickerWithInput/DatePickerWithInput.story.tsx index 66b067846dd..158aa681b2d 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/DatePickerWithInput/DatePickerWithInput.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/DatePickerWithInput/DatePickerWithInput.story.tsx @@ -13,7 +13,7 @@ const minimumDate = new Date(); minimumDate.setMonth(minimumDate.getMonth() - 1); const meta: Meta = { - title: 'Pickers and Editors/TimePickers/DatePickerWithInput', + title: 'Date time pickers/DatePickerWithInput', component: DatePickerWithInput, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.story.tsx index a7a8d87961f..19acd203c0a 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.story.tsx @@ -15,7 +15,7 @@ const minimumDate = new Date(); minimumDate.setDate(minimumDate.getDate() - 7); const meta: Meta = { - title: 'Pickers and Editors/TimePickers/DateTimePicker', + title: 'Date time pickers/DateTimePicker', component: DateTimePicker, argTypes: { date: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx index a6a0813e210..cacf19faf92 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx @@ -5,7 +5,7 @@ import { Meta, StoryFn } from '@storybook/react'; import { RelativeTimeRangePicker } from './RelativeTimeRangePicker'; const meta: Meta = { - title: 'Pickers and Editors/TimePickers/RelativeTimeRangePicker', + title: 'Date time pickers/RelativeTimeRangePicker', component: RelativeTimeRangePicker, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx index abd37242483..d3678e8dc84 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx @@ -7,7 +7,7 @@ import { dateTime } from '@grafana/data'; import { TimeOfDayPicker } from './TimeOfDayPicker'; const meta: Meta = { - title: 'Pickers and Editors/TimePickers/TimeOfDayPicker', + title: 'Date time pickers/TimeOfDayPicker', component: TimeOfDayPicker, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx index ef2ce7d2923..c70656bc070 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.story.tsx @@ -30,7 +30,7 @@ const nullRange = { }; const meta: Meta = { - title: 'Pickers and Editors/TimePickers/TimeRangeInput', + title: 'Date time pickers/TimeRangeInput', component: TimeRangeInput, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.story.tsx index f7ae73b010c..dadb184d716 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.story.tsx @@ -10,7 +10,7 @@ const to = dateTime(); const from = to.subtract(6, 'h'); const meta: Meta = { - title: 'Pickers and Editors/TimePickers/TimeRangePicker', + title: 'Date time pickers/TimeRangePicker', component: TimeRangePicker, args: { value: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeZonePicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeZonePicker.story.tsx index ee1b5e7e9d5..da95bb70c81 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeZonePicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeZonePicker.story.tsx @@ -5,7 +5,7 @@ import { Meta, StoryFn } from '@storybook/react'; import { TimeZonePicker } from './TimeZonePicker'; const meta: Meta = { - title: 'Pickers and Editors/TimePickers/TimeZonePicker', + title: 'Date time pickers/TimeZonePicker', component: TimeZonePicker, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/DateTimePickers/WeekStartPicker.story.tsx b/packages/grafana-ui/src/components/DateTimePickers/WeekStartPicker.story.tsx index cf80764789c..cc41f958d7b 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/WeekStartPicker.story.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/WeekStartPicker.story.tsx @@ -5,7 +5,7 @@ import { Meta, StoryFn } from '@storybook/react'; import { WeekStartPicker } from './WeekStartPicker'; const meta: Meta = { - title: 'Pickers and Editors/TimePickers/WeekStartPicker', + title: 'Date time pickers/WeekStartPicker', component: WeekStartPicker, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/Divider/Divider.story.tsx b/packages/grafana-ui/src/components/Divider/Divider.story.tsx index f7757d80d4f..37317a0ee05 100644 --- a/packages/grafana-ui/src/components/Divider/Divider.story.tsx +++ b/packages/grafana-ui/src/components/Divider/Divider.story.tsx @@ -4,7 +4,7 @@ import { Divider } from './Divider'; import mdx from './Divider.mdx'; const meta: Meta = { - title: 'General/Divider', + title: 'Layout/Divider', component: Divider, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/Dropdown/ButtonSelect.internal.story.tsx b/packages/grafana-ui/src/components/Dropdown/ButtonSelect.story.tsx similarity index 95% rename from packages/grafana-ui/src/components/Dropdown/ButtonSelect.internal.story.tsx rename to packages/grafana-ui/src/components/Dropdown/ButtonSelect.story.tsx index edc03ecf4a4..a943ab93b73 100644 --- a/packages/grafana-ui/src/components/Dropdown/ButtonSelect.internal.story.tsx +++ b/packages/grafana-ui/src/components/Dropdown/ButtonSelect.story.tsx @@ -5,7 +5,7 @@ import { Meta, StoryFn } from '@storybook/react'; import { ButtonSelect } from './ButtonSelect'; const meta: Meta = { - title: 'Forms/Select/ButtonSelect', + title: 'Inputs/Deprecated/ButtonSelect', component: ButtonSelect, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx b/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx index 7268565239e..3db07bcfcea 100644 --- a/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx +++ b/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx @@ -33,8 +33,7 @@ export interface Props extends HTMLAttributes { } /** - * @internal - * A temporary component until we have a proper dropdown component + * @deprecated Use Combobox or Dropdown instead */ const ButtonSelectComponent = (props: Props) => { const { className, options, value, onChange, narrow, variant, ...restProps } = props; diff --git a/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.story.tsx b/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.story.tsx index c44444597e0..c32e91e9777 100644 --- a/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.story.tsx +++ b/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.story.tsx @@ -4,7 +4,7 @@ import { EmptySearchResult } from './EmptySearchResult'; import mdx from './EmptySearchResult.mdx'; const meta: Meta = { - title: 'Visualizations/EmptySearchResult', + title: 'Information/Deprecated/EmptySearchResult', component: EmptySearchResult, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.tsx b/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.tsx index 23a794908bf..2ddafbf865b 100644 --- a/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.tsx +++ b/packages/grafana-ui/src/components/EmptySearchResult/EmptySearchResult.tsx @@ -8,6 +8,7 @@ export interface Props { children: JSX.Element | string; } +/** @deprecated Use instead */ const EmptySearchResult = ({ children }: Props) => { const styles = useStyles2(getStyles); return
{children}
; diff --git a/packages/grafana-ui/src/components/EmptyState/EmptyState.story.tsx b/packages/grafana-ui/src/components/EmptyState/EmptyState.story.tsx index c483f4ed4a4..d9d5b67f526 100644 --- a/packages/grafana-ui/src/components/EmptyState/EmptyState.story.tsx +++ b/packages/grafana-ui/src/components/EmptyState/EmptyState.story.tsx @@ -6,7 +6,7 @@ import { EmptyState } from './EmptyState'; import mdx from './EmptyState.mdx'; const meta: Meta = { - title: 'General/EmptyState', + title: 'Information/EmptyState', component: EmptyState, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.story.tsx b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.story.tsx index 474cfc400fb..b4dce6ba569 100644 --- a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.story.tsx +++ b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.story.tsx @@ -9,7 +9,7 @@ import mdx from './ErrorBoundary.mdx'; import { ErrorWithStack } from './ErrorWithStack'; const meta: Meta = { - title: 'General/ErrorBoundary', + title: 'Utilities/ErrorBoundary', component: ErrorBoundary, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/FeatureBadge/FeatureBadge.story.tsx b/packages/grafana-ui/src/components/FeatureBadge/FeatureBadge.story.tsx index e3e24bf77a9..e2a8d70dc0d 100644 --- a/packages/grafana-ui/src/components/FeatureBadge/FeatureBadge.story.tsx +++ b/packages/grafana-ui/src/components/FeatureBadge/FeatureBadge.story.tsx @@ -6,7 +6,7 @@ import { FeatureBadge } from './FeatureBadge'; import mdx from './FeatureBadge.mdx'; const meta: Meta = { - title: 'Data Display/FeatureBadge', + title: 'Information/FeatureBadge', component: FeatureBadge, parameters: { docs: { page: mdx }, diff --git a/packages/grafana-ui/src/components/FileDropzone/FileDropzone.story.tsx b/packages/grafana-ui/src/components/FileDropzone/FileDropzone.story.tsx index de3ef47612e..ac9b70eb14c 100644 --- a/packages/grafana-ui/src/components/FileDropzone/FileDropzone.story.tsx +++ b/packages/grafana-ui/src/components/FileDropzone/FileDropzone.story.tsx @@ -4,7 +4,7 @@ import { FileDropzone } from './FileDropzone'; import mdx from './FileDropzone.mdx'; const meta: Meta = { - title: 'Forms/FileDropzone', + title: 'Inputs/FileDropzone', component: FileDropzone, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/FileDropzone/FileListItem.story.tsx b/packages/grafana-ui/src/components/FileDropzone/FileListItem.story.tsx index 3e10caf11f4..1b5d2eed5d2 100644 --- a/packages/grafana-ui/src/components/FileDropzone/FileListItem.story.tsx +++ b/packages/grafana-ui/src/components/FileDropzone/FileListItem.story.tsx @@ -4,7 +4,7 @@ import { FileListItem as FileListItemComponent, FileListItemProps } from './File import mdx from './FileListItem.mdx'; const meta: Meta = { - title: 'Forms/FileListItem', + title: 'Inputs/FileListItem', component: FileListItemComponent, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/FileUpload/FileUpload.story.tsx b/packages/grafana-ui/src/components/FileUpload/FileUpload.story.tsx index f6e7025553a..bdd445316ae 100644 --- a/packages/grafana-ui/src/components/FileUpload/FileUpload.story.tsx +++ b/packages/grafana-ui/src/components/FileUpload/FileUpload.story.tsx @@ -4,7 +4,7 @@ import { FileUpload } from './FileUpload'; import mdx from './FileUpload.mdx'; const meta: Meta = { - title: 'Forms/FileUpload', + title: 'Inputs/FileUpload', component: FileUpload, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/FilterPill/FilterPill.story.tsx b/packages/grafana-ui/src/components/FilterPill/FilterPill.story.tsx index 54dec2ec7ea..eb4c48deae9 100644 --- a/packages/grafana-ui/src/components/FilterPill/FilterPill.story.tsx +++ b/packages/grafana-ui/src/components/FilterPill/FilterPill.story.tsx @@ -8,7 +8,7 @@ import { FilterPill } from './FilterPill'; import mdx from './FilterPill.mdx'; const meta: Meta = { - title: 'General/FilterPill', + title: 'Inputs/FilterPill', component: FilterPill, argTypes: { icon: { control: { type: 'select', options: getAvailableIcons() } }, diff --git a/packages/grafana-ui/src/components/FormField/FormField.internal.story.tsx b/packages/grafana-ui/src/components/FormField/FormField.story.tsx similarity index 95% rename from packages/grafana-ui/src/components/FormField/FormField.internal.story.tsx rename to packages/grafana-ui/src/components/FormField/FormField.story.tsx index efddb8d7f3c..845001dcacc 100644 --- a/packages/grafana-ui/src/components/FormField/FormField.internal.story.tsx +++ b/packages/grafana-ui/src/components/FormField/FormField.story.tsx @@ -3,7 +3,7 @@ import { Meta, StoryFn } from '@storybook/react'; import { FormField } from './FormField'; const meta: Meta = { - title: 'Forms/Legacy/FormField', + title: 'Forms/Deprecated/FormField', component: FormField, parameters: { controls: { diff --git a/packages/grafana-ui/src/components/FormattedValueDisplay/FormattedValueDisplay.story.tsx b/packages/grafana-ui/src/components/FormattedValueDisplay/FormattedValueDisplay.story.tsx index 3358a90f179..5f7cad8d3ec 100644 --- a/packages/grafana-ui/src/components/FormattedValueDisplay/FormattedValueDisplay.story.tsx +++ b/packages/grafana-ui/src/components/FormattedValueDisplay/FormattedValueDisplay.story.tsx @@ -4,7 +4,7 @@ import { FormattedValueDisplay } from './FormattedValueDisplay'; import mdx from './FormattedValueDisplay.mdx'; const meta: Meta = { - title: 'Visualizations/FormattedValueDisplay', + title: 'Plugins/FormattedValueDisplay', component: FormattedValueDisplay, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx b/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx index 8cdf3daa56d..fa4d1fb1d8e 100644 --- a/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx +++ b/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx @@ -9,7 +9,7 @@ import mdx from './Checkbox.mdx'; import { Field } from './Field'; const meta: Meta = { - title: 'Forms/Checkbox', + title: 'Inputs/Checkbox', component: Checkbox, parameters: { docs: { diff --git a/packages/grafana-ui/src/components/Forms/Legacy/Input/Input.internal.story.tsx b/packages/grafana-ui/src/components/Forms/Legacy/Input/Input.internal.story.tsx deleted file mode 100644 index a837949c744..00000000000 --- a/packages/grafana-ui/src/components/Forms/Legacy/Input/Input.internal.story.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Meta, StoryFn } from '@storybook/react'; -import { zip, fromPairs } from 'lodash'; -import { useState } from 'react'; - -import { EventsWithValidation } from '../../../../utils/validate'; - -import { Input } from './Input'; - -const meta: Meta = { - title: 'Forms/Legacy/Input', - component: Input, - parameters: { - controls: { - exclude: ['inputRef', 'onBlur', 'onFocus', 'onChange'], - }, - }, - argTypes: { - validationEvents: { - control: { - type: 'select', - options: fromPairs(zip(Object.keys(EventsWithValidation), Object.values(EventsWithValidation))), - }, - }, - validation: { name: 'Validation regex (will do a partial match if you do not anchor it)' }, - }, -}; - -const Wrapper: StoryFn = (args) => { - const [value, setValue] = useState(''); - const validations = { - [args.validationEvents]: [ - { - rule: (value: string) => { - return !!value.match(args.validation); - }, - errorMessage: args.validationErrorMessage, - }, - ], - }; - return ( - setValue(e.currentTarget.value)} - validationEvents={validations} - hideErrorMessage={args.hideErrorMessage} - /> - ); -}; - -export const Basic = Wrapper.bind({}); -Basic.args = { - validation: '', - validationErrorMessage: 'Input not valid', - validationEvents: EventsWithValidation.onBlur, - hideErrorMessage: false, -}; - -export default meta; diff --git a/packages/grafana-ui/src/components/Forms/Legacy/Select/Select.internal.story.tsx b/packages/grafana-ui/src/components/Forms/Legacy/Select/Select.internal.story.tsx deleted file mode 100644 index 5e156ab8f9c..00000000000 --- a/packages/grafana-ui/src/components/Forms/Legacy/Select/Select.internal.story.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { useArgs } from '@storybook/preview-api'; -import { Meta, StoryFn } from '@storybook/react'; -import { useCallback } from 'react'; - -import { SelectableValue } from '@grafana/data'; - -import { Select, AsyncSelect as AsyncSelectComponent } from './Select'; - -const meta: Meta = { - title: 'Forms/Legacy/Select', - component: Select, - parameters: { - controls: { - exclude: [ - 'className', - 'menuPlacement', - 'menuPosition', - 'maxMenuHeight', - 'minMenuHeight', - 'maxVisibleValues', - 'prefix', - 'renderControl', - 'value', - 'tooltipContent', - 'components', - 'inputValue', - 'id', - 'inputId', - 'defaultValue', - 'aria-label', - 'noOptionsMessage', - 'onChange', - 'onBlur', - 'onKeyDown', - 'filterOption', - 'formatCreateLabel', - 'getOptionLabel', - 'getOptionValue', - 'onCloseMenu', - 'onCreateOption', - 'onInputChange', - 'onOpenMenu', - 'isOptionDisabled', - ], - }, - }, - argTypes: { - width: { control: { type: 'range', min: 5, max: 30 } }, - }, -}; - -const initialValue: SelectableValue = { label: 'A label', value: 'A value' }; - -const options = [ - initialValue, - { label: 'Another label', value: 'Another value 1' }, - { label: 'Another label', value: 'Another value 2' }, - { label: 'Another label', value: 'Another value 3' }, - { label: 'Another label', value: 'Another value 4' }, - { label: 'Another label', value: 'Another value 5' }, - { label: 'Another label', value: 'Another value ' }, -]; - -export const Basic: StoryFn = (args) => { - const [, updateArgs] = useArgs(); - return ( -