Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Storybook/Button documentation #19298

Merged
merged 16 commits into from
Aug 23, 2021
8 changes: 7 additions & 1 deletion .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ export const decorators = [withFluentProvider, withStrictMode];
/** @type {import('@storybook/react').Parameters} */
export const parameters = {
controls: {
disable: true,
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved
hideNoControlsWarning: true,
expanded: true,
},
viewMode: 'docs',
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved
docs: {
source: {
excludeDecorators: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

what is this config for ? I'm unable to find any related docs - can you provide those pls?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this config, the source code for the main story wasn’t being shown properly - only the decorator was visible and not the story code itself. This config option isn’t present in the docs, I found it in this comment: storybookjs/storybook#12596 (comment)

},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Button docs",
"packageName": "@fluentui/react-button",
"email": "peter@draxler.ml",
"dependentChangeType": "none"
}
1 change: 1 addition & 0 deletions packages/react-button/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as rootPreview from '../../../.storybook/preview';

export const decorators = [...rootPreview.decorators];
export const parameters = rootPreview.parameters;
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved
118 changes: 0 additions & 118 deletions packages/react-button/src/Button.stories.tsx

This file was deleted.

169 changes: 169 additions & 0 deletions packages/react-button/src/components/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import * as React from 'react';
import { Button, ButtonProps } from '../../Button';
import { Meta } from '@storybook/react';
import { CalendarMonth24Regular } from '@fluentui/react-icons';

export const Default = (props: ButtonProps) => <Button {...props}>Button</Button>;
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved

export const Emphasis = () => (
<>
<Button primary>Primary button</Button>
<Button>Default button</Button>
<Button outline>Outline button</Button>
<Button subtle>Subtle button</Button>
<Button transparent>Transparent button</Button>
</>
);
Emphasis.parameters = {
docs: {
description: {
story:
'- `primary` button is used for the most important action on the page or in a view\n' +
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved
'- `default` button is used for subordinate actions\n' +
'- `outline` has no background styling and is emphasized through the styling of its content and borders\n' +
'- `transparent` has no background or border styling and is just emphasized through its content styling\n' +
'- `subtle` button blends into its background and becomes less emphasized\n',
},
},
};

export const ButtonWithIcon = () => (
<>
<Button icon={<CalendarMonth24Regular />}>Text</Button>
<Button icon={<CalendarMonth24Regular />} iconPosition="after">
Text
</Button>
<Button icon={<CalendarMonth24Regular />} />
</>
);
ButtonWithIcon.parameters = {
docs: {
description: {
story:
'Button has an `icon` slot that, if specified, renders an icon either `before` or `after` the children, ' +
'as specified by the `iconPosition` prop.',
},
},
};

export const CircularButton = () => (
<>
<Button circular>Button</Button>
<Button circular outline icon={<CalendarMonth24Regular />} />
<Button circular subtle icon={<CalendarMonth24Regular />} />
<Button circular transparent icon={<CalendarMonth24Regular />} />
</>
);
CircularButton.parameters = {
docs: {
description: {
story: 'A button can have completely rounded corners.',
},
},
};

export const ButtonSize = () => {
return (
<>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1em' }}>
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved
<h4 style={{ width: '100%', margin: 0 }}>small</h4>
<Button size="small">Text</Button>
<Button size="small" icon={<CalendarMonth24Regular />}>
Text
</Button>
<Button size="small" icon={<CalendarMonth24Regular />} />
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1em' }}>
<h4 style={{ width: '100%', margin: 0 }}>medium</h4>
<Button>Text</Button>
<Button icon={<CalendarMonth24Regular />}>Text</Button>
<Button icon={<CalendarMonth24Regular />} />
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1em' }}>
<h4 style={{ width: '100%', margin: 0 }}>large</h4>
<Button size="large">Text</Button>
<Button size="large" icon={<CalendarMonth24Regular />}>
Text
</Button>
<Button size="large" icon={<CalendarMonth24Regular />} />
</div>
</>
);
};
ButtonSize.parameters = {
docs: {
description: {
story: 'A button supports `small`, `medium` and `large` size. Default size is `medium`.',
},
},
};

export const BlockButton = () => (
<>
<Button block>Block button</Button>
</>
);
BlockButton.parameters = {
docs: {
description: {
story: 'A button can fill the width of its container.',
},
},
};

export const DisabledButton = () => (
<>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5em' }}>
<Button>Default</Button>
<Button disabled>Disabled</Button>
<Button disabledFocusable>Disabled focusable</Button>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5em' }}>
<Button primary icon={<CalendarMonth24Regular />}>
Primary
</Button>
<Button primary disabled icon={<CalendarMonth24Regular />}>
Primary disabled
</Button>
<Button primary disabledFocusable>
Primary disabled focusable
</Button>
</div>
</>
);
DisabledButton.parameters = {
docs: {
description: {
story: `A button can be \`disabled\` or \`disabledFocusable\`.
\`disabledFocusable\` is used in scenarios where it is important to keep a consistent tab order
for screen reader and keyboard users. The primary example of this pattern is when
the disabled button is in a menu or a commandbar and is seldom used for standalone buttons.`,
},
},
};

export const ButtonWithLongText = () => (
<>
<Button>Text</Button>
<Button>Text truncates after it hits the max width token value</Button>
</>
);
ButtonWithLongText.parameters = {
docs: {
description: {
story: 'Text truncates after it hits the max width theme token value.',
},
},
};

export default {
title: 'Components/Button',
component: Button,
decorators: [
Story => (
<div style={{ display: 'flex', justifyContent: 'space-evenly' }}>
<Story />
</div>
),
],
} as Meta;
33 changes: 29 additions & 4 deletions packages/react-button/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,35 @@ import { renderButton } from './renderButton';
import { useButtonStyles } from './useButtonStyles';

/**
* Define a styled Button, using the `useButton` hook.
* {@docCategory Button}
* Buttons give people a way to trigger an action.
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved
*
* They’re typically found in forms, dialog panels, and dialogs. Some buttons are specialized for particular tasks,
Copy link
Contributor

Choose a reason for hiding this comment

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

from experience authoring any kind of lengthy docs as JSDoc for storybook becomes hard to maintain.

  • this applies for all inline "docs" in this story within this PR

The approach I'd recommend is to embrace markdown -> https://storybook.js.org/docs/react/writing-docs/docs-page#with-mdx-documentation

Also markdown is more universal format which can be used later for different docs approaches if we choose so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I’ve thought about using MDX before I started. I think it has the following advantages:

  • syntax highlighting when editing
  • allows for multiple stories within one canvas, so users would see less boilerplate code
  • possibility of later use for Fluent UI docs for all platforms(?)

On the other hand, it gives too much freedom to the writer of individual docs page. With MDX, each page is independent and can have a completely different structure.

With CSF, the docs writer creates pieces of documentation, which are then composed together to a docs page according to customizable rules. This enforces consistency between pages and makes it easier to change docs structure in the future - for example if we want to remove Storybook Controls from the docs page and move it to a different tab (like there’s Canvas and Docs now), we can do that without the need to modify each page.

Regarding reuse for future documentation, I think this is tricky. We’d need to closely coordinate our plans with the team working on the new docs page, who’s building the new docs to achieve compatibility. If we don’t do that, it’s likely that those MDX pages will need modifications anyway for use in the new docs. If we do coordinate, it’ll extend the scope of this project (as well as of the new docs project) and therefore limit the quality of docs we’ll have ready for beta. I think it’s better to keep us focused now and deal with requirements of the new docs site later.

That’s why I went with CSF.

Thoughts?

cc @ecraig12345

* such as navigation, repeated actions, or presenting menus.
*
* See also:
* [MenuButton](/?path=/docs/components-menubutton--menu-button-playground),
* [ToggleButton](/?path=/docs/components-togglebutton--toggle-button-playground),
* [CompoundButton](/?path=/docs/components-compoundbutton--compound-button-playground)
*
* ## Best practices
* ### Layout
* - For dialog boxes and panels, where people are moving through a sequence of screens, right-align buttons with the
* container.
* - For single-page forms and focused tasks, left-align buttons with the container.
* - Always place the primary button on the left, the secondary button just to the right of it.
* - Show only one primary button that inherits theme color at rest state. If there are more than two buttons with
* equal priority, all buttons should have neutral backgrounds.
* - Don't use a button to navigate to another place; use a link instead. The exception is in a wizard where "Back" and
* "Next" buttons may be used.
* - Don't place the default focus on a button that destroys data. Instead, place the default focus on the button that
* performs the "safe act" and retains the content (such as "Save") or cancels the action (such as "Cancel").
*
* ### Content
* - Use sentence-style capitalization—only capitalize the first word. For more info, see
* [Capitalization](https://docs.microsoft.com/en-us/style-guide/capitalization) in the Microsoft Writing Style Guide.
* - Make sure it's clear what will happen when people interact with the button. Be concise; usually a single verb
* is best. Include a noun if there is any room for interpretation about what the verb means.
* For example, "Delete folder" or "Create account".
*/
export const Button: React.FunctionComponent<ButtonProps & React.RefAttributes<HTMLElement>> = React.forwardRef<
HTMLElement,
Expand All @@ -18,5 +45,3 @@ export const Button: React.FunctionComponent<ButtonProps & React.RefAttributes<H

return renderButton(state);
});

Button.displayName = 'Button';
23 changes: 23 additions & 0 deletions packages/react-button/src/tmp-icons.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';

export const CalendarIcon = () => (
PeterDraex marked this conversation as resolved.
Show resolved Hide resolved
<span
role="presentation"
style={{
width: '1em',
height: '1em',
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 2048 2048"
style={{
height: '100%',
fill: 'currentColor',
verticalAlign: 'top',
}}
>
<path d="M768 768h128v128H768V768zm384 768h128v128h-128v-128zm384-768h128v128h-128V768zm-384 0h128v128h-128V768zm-384 256h128v128H768v-128zm-384 0h128v128H384v-128zm1152 0h128v128h-128v-128zm-384 0h128v128h-128v-128zm-384 256h128v128H768v-128zm-384 0h128v128H384v-128zm1152 0h128v128h-128v-128zm-384 0h128v128h-128v-128zm-384 256h128v128H768v-128zm-384 0h128v128H384v-128zM2048 128v1792H0V128h384V0h128v128h1024V0h128v128h384zM128 256v256h1792V256h-256v128h-128V256H512v128H384V256H128zm1792 1536V640H128v1152h1792z" />
</svg>
</span>
);