Skip to content

Commit

Permalink
[Storybook] General consistency pass (#7245)
Browse files Browse the repository at this point in the history
  • Loading branch information
cee-chen authored Oct 3, 2023
1 parent afb99a4 commit ce51332
Show file tree
Hide file tree
Showing 29 changed files with 478 additions and 341 deletions.
21 changes: 15 additions & 6 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ import { writingModeStyles } from './writing_mode.styles';
// once all EUI components are converted to Emotion
import '../dist/eui_theme_light.css';

/**
* Prop controls
*/

import type { CommonProps } from '../src/components/common';
import { hideStorybookControls } from './utils';

const preview: Preview = {
decorators: [
(Story, context) => (
Expand Down Expand Up @@ -86,6 +93,7 @@ const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
backgrounds: { disable: true }, // Use colorMode instead
options: { showPanel: true }, // default to showing the controls panel
controls: {
expanded: true,
sort: 'requiredFirst',
Expand All @@ -100,12 +108,13 @@ const preview: Preview = {
},
// Due to CommonProps, these props appear on almost every Story, but generally
// aren't super useful to test - let's disable them by default and (if needed)
// individual stories can re-enable them
argTypes: {
css: { table: { disable: true } },
className: { table: { disable: true } },
'data-test-subj': { table: { disable: true } },
},
// individual stories can re-enable them, e.g. by passing
// `argTypes: { 'data-test-subj': { table: { disable: false } } }`
argTypes: hideStorybookControls<CommonProps>([
'css',
'className',
'data-test-subj',
]),
};

export default preview;
49 changes: 49 additions & 0 deletions .storybook/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { hideStorybookControls, disableStorybookControls } from './utils';

describe('hideStorybookControls', () => {
it('outputs the expected `argTypes` object when passed prop name strings', () => {
expect(
hideStorybookControls(['isDisabled', 'isLoading', 'isInvalid'])
).toEqual({
isDisabled: { table: { disable: true } },
isLoading: { table: { disable: true } },
isInvalid: { table: { disable: true } },
});
});

it('throws a typescript error if a generic is passed and the prop names do not match', () => {
type TestComponentProps = { hello: boolean; world: boolean };
// No typescript error
hideStorybookControls<TestComponentProps>(['hello', 'world']);
// @ts-expect-error - will fail `yarn lint` if a TS error is *not* produced
hideStorybookControls<TestComponentProps>(['hello', 'world', 'error']);
});
});

describe('disableStorybookControls', () => {
it('outputs the expected `argTypes` object when passed prop name strings', () => {
expect(
disableStorybookControls(['isDisabled', 'isLoading', 'isInvalid'])
).toEqual({
isDisabled: { control: false },
isLoading: { control: false },
isInvalid: { control: false },
});
});

it('throws a typescript error if a generic is passed and the prop names do not match', () => {
type TestComponentProps = { hello: boolean; world: boolean };
// No typescript error
disableStorybookControls<TestComponentProps>(['hello', 'world']);
// @ts-expect-error - will fail `yarn lint` if a TS error is *not* produced
disableStorybookControls<TestComponentProps>(['hello', 'world', 'error']);
});
});
64 changes: 64 additions & 0 deletions .storybook/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

/**
* argTypes configurations
*/

/**
* Completely hide props from Storybook's controls panel.
* Should be passed or spread to `argTypes`
*/
export const hideStorybookControls = <Props>(
propNames: Array<keyof Props>
): Record<keyof Props, typeof HIDE_CONTROL> | {} => {
return propNames.reduce(
(obj, name) => ({ ...obj, [name]: HIDE_CONTROL }),
{}
);
};
const HIDE_CONTROL = { table: { disable: true } };

/**
* Leave props visible in Storybook's controls panel, but disable them
* from being controllable (renders a `-`).
*
* Should be passed or spread to `argTypes`
*/
export const disableStorybookControls = <Props>(
propNames: Array<keyof Props>
): Record<keyof Props, typeof DISABLE_CONTROL> | {} => {
return propNames.reduce(
(obj, name) => ({ ...obj, [name]: DISABLE_CONTROL }),
{}
);
};
const DISABLE_CONTROL = { control: false };

/**
* parameters configurations
*/

/**
* Will hide all props/controls. Pass to `parameters`
*
* TODO: Figure out some way to not show Storybook's "setup" text?
*/
export const hideAllStorybookControls = {
controls: { exclude: /.*/g },
};

/**
* Will hide the control/addon panel entirely for a specific story.
* Should be passed or spread to to `parameters`.
*
* Note that users can choose to re-show the panel in the UI
*/
export const hidePanel = {
options: { showPanel: false },
};
1 change: 1 addition & 0 deletions scripts/jest/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const config = {
'<rootDir>/scripts/babel',
'<rootDir>/scripts/tests',
'<rootDir>/scripts/eslint-plugin',
'<rootDir>/.storybook',
],
collectCoverageFrom: [
'src/{components,services,global_styling}/**/*.{ts,tsx,js,jsx}',
Expand Down
17 changes: 10 additions & 7 deletions src/components/button/button_empty/button_empty.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,8 @@ const meta: Meta<EuiButtonEmptyProps> = {
},
iconType: { control: 'text' },
},
};

export default meta;
type Story = StoryObj<EuiButtonEmptyProps>;

export const Playground: Story = {
args: {
children: 'Tertiary action',
// Component defaults
color: 'primary',
size: 'm',
iconSize: 'm',
Expand All @@ -37,3 +31,12 @@ export const Playground: Story = {
isSelected: false,
},
};

export default meta;
type Story = StoryObj<EuiButtonEmptyProps>;

export const Playground: Story = {
args: {
children: 'Tertiary action',
},
};
49 changes: 27 additions & 22 deletions src/components/button/button_group/button_group.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { disableStorybookControls } from '../../../../.storybook/utils';

import {
EuiButtonGroup,
Expand All @@ -19,11 +20,6 @@ const meta: Meta<EuiButtonGroupProps> = {
title: 'EuiButtonGroup',
// @ts-ignore This still works for Storybook controls, even though Typescript complains
component: EuiButtonGroup,
parameters: {
controls: {
exclude: ['data-test-subj'],
},
},
argTypes: {
type: {
options: ['single', 'multi'],
Expand All @@ -44,6 +40,15 @@ const meta: Meta<EuiButtonGroupProps> = {
control: 'select',
},
},
args: {
// Component defaults
type: 'single',
buttonSize: 's',
color: 'text',
isDisabled: false,
isFullWidth: false,
isIconOnly: false,
},
};

export default meta;
Expand Down Expand Up @@ -76,6 +81,17 @@ const EuiButtonGroupSingle = (props: any) => {
);
};

export const SingleSelection: Story = {
render: ({ ...args }) => <EuiButtonGroupSingle {...args} />,
args: {
legend: 'EuiButtonGroup - single selection',
options,
type: 'single',
idSelected: 'button1',
},
argTypes: disableStorybookControls(['type']),
};

const EuiButtonGroupMulti = (props: any) => {
const [idToSelectedMap, setIdToSelectedMap] = useState<
Record<string, boolean>
Expand All @@ -100,24 +116,13 @@ const EuiButtonGroupMulti = (props: any) => {
);
};

export const Playground: Story = {
render: ({ ...args }) => {
if (args.type === 'multi') {
return <EuiButtonGroupMulti {...args} />;
} else {
return <EuiButtonGroupSingle {...args} />;
}
},
export const MultiSelection: Story = {
render: ({ ...args }) => <EuiButtonGroupMulti {...args} />,
args: {
legend: 'EuiButtonGroup demo',
type: 'single',
legend: 'EuiButtonGroup - multiple selections',
options,
idSelected: 'button1',
type: 'multi',
idToSelectedMap: { button1: true },
buttonSize: 's',
color: 'text',
isDisabled: false,
isFullWidth: false,
isIconOnly: false,
} as any,
},
argTypes: disableStorybookControls(['type']),
};
17 changes: 10 additions & 7 deletions src/components/button/button_icon/button_icon.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,8 @@ import { EuiButtonIcon, EuiButtonIconProps } from './button_icon';
const meta: Meta<EuiButtonIconProps> = {
title: 'EuiButtonIcon',
component: EuiButtonIcon,
};

export default meta;
type Story = StoryObj<EuiButtonIconProps>;

export const Playground: Story = {
args: {
iconType: 'faceHappy',
// Component defaults
color: 'primary',
display: 'empty',
size: 'xs',
Expand All @@ -30,3 +24,12 @@ export const Playground: Story = {
isSelected: false,
},
};

export default meta;
type Story = StoryObj<EuiButtonIconProps>;

export const Playground: Story = {
args: {
iconType: 'faceHappy',
},
};
11 changes: 7 additions & 4 deletions src/components/collapsible_nav/collapsible_nav.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import { EuiCollapsibleNav, EuiCollapsibleNavProps } from './collapsible_nav';
const meta: Meta<EuiCollapsibleNavProps> = {
title: 'EuiCollapsibleNav',
component: EuiCollapsibleNav,
args: {
// Component defaults
isDocked: false,
dockedBreakpoint: 'l',
showButtonIfDocked: false,
size: 320,
},
// TODO: Improve props inherited from EuiFlyout, ideally through
// a DRY import from `flyout.stories.tsx` once that's created
};
Expand Down Expand Up @@ -43,9 +50,5 @@ export const Playground: Story = {
args: {
children: 'Collapsible nav content',
isOpen: true,
isDocked: false,
dockedBreakpoint: 'l',
showButtonIfDocked: false,
size: 240,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ const meta: Meta<EuiCollapsibleNavGroupProps> = {
isDisabled: { if: { arg: 'isCollapsible' } },
element: { if: { arg: 'isCollapsible' } },
},
args: {
iconType: 'logoElastic',
// Component defaults
iconSize: 'l',
titleSize: 'xxs',
titleElement: 'h3',
background: 'none',
},
};

export default meta;
Expand All @@ -51,12 +59,7 @@ type Story = StoryObj<EuiCollapsibleNavGroupProps>;
export const Accordion: Story = {
args: {
children: 'This is an accordion group with a title',
background: 'none',
title: 'Nav group - accordion',
iconType: 'logoElastic',
iconSize: 'l',
titleElement: 'h3',
titleSize: 'xxs',
initialIsOpen: true,
isCollapsible: true,
},
Expand All @@ -65,19 +68,12 @@ export const Accordion: Story = {
export const NonAccordion: StoryObj<EuiCollapsibleNavGroupProps> = {
args: {
children: 'This is a group with a title',
background: 'none',
title: 'Nav group - non-accordion',
iconType: 'logoElastic',
iconSize: 'l',
titleElement: 'h3',
titleSize: 'xxs',
isCollapsible: false,
},
};

export const NoTitle: Story = {
args: {
children: 'This is a group without a title',
background: 'none',
},
};
Loading

0 comments on commit ce51332

Please sign in to comment.