|
| 1 | +# Garden demos |
| 2 | + |
| 3 | +Garden leverages [Storybook](https://storybook.js.org/) for generating component |
| 4 | +demos with story controls. Run `yarn start` to build and serve package demos in |
| 5 | +development mode (with hot reloading). |
| 6 | + |
| 7 | +The development goal for demos is to render components in isolation with the |
| 8 | +least amount of structural scaffolding together with the greatest amount of |
| 9 | +control for content and flexibility. Along with global toolbar items (preview |
| 10 | +size, locale direction, CSS Bedrock, etc), controls should stress every aspect |
| 11 | +of a component's layout and behavior. |
| 12 | + |
| 13 | +## Best practice |
| 14 | + |
| 15 | +The following list of dos and don'ts outline demo code expectations. |
| 16 | + |
| 17 | +### Do this |
| 18 | + |
| 19 | +- Expose controls for all essentials – inherent props, conditional subcomponent |
| 20 | + renders, child content |
| 21 | +- Allow natural component layout – full width for block, partial width for inline |
| 22 | +- Use the Garden `Grid` to manage layout for components that respond to |
| 23 | + placement values (start, end, top, bottom, etc) |
| 24 | +- Rely on `useArgs` from Storybook's client API in order to keep component state |
| 25 | + connected with it's associated Storybook control |
| 26 | + |
| 27 | +### Not this |
| 28 | + |
| 29 | +- Avoid `styled-components`. The addition of demo CSS is an indicator that the |
| 30 | + underlying component may not offer comprehensive styling. |
| 31 | +- Refrain from adding component documentation. The Storybook demos are not a |
| 32 | + substitute for the [garden.zendesk.com](https://garden.zendesk.com/) website. |
| 33 | + The website is the source of truth for component documenation. |
| 34 | +- Avoid `useState`; prefer Storybook `useArgs` instead |
| 35 | +- Do not write "Default" vs "Advanced" stories. A well-written component story |
| 36 | + will render as expected with defaults and offer controls to push into advanced |
| 37 | + component capabilities. See the [patterns](#patterns) section below for cases |
| 38 | + that extend beyond the consideration of an isolated component. |
| 39 | +- Do not use numeric file naming. By following the [naming](#naming) conventions |
| 40 | + listed below, component stories will be naturally sorted alphabetically. |
| 41 | + |
| 42 | +## Conventions |
| 43 | + |
| 44 | +Stick to the following conventions for authoring demo stories that are |
| 45 | +consistent with the existing codebase. You may find that running `yarn new` is |
| 46 | +helpful for auto-generating component demo scaffolding. |
| 47 | + |
| 48 | +### Structure |
| 49 | + |
| 50 | +- Place MDX stories under the package `demo` directory. |
| 51 | +- All `Story` TSX files are placed under a `demo/stories` directory. |
| 52 | +- Move all boilerplate data constants (i.e. control data for a component's |
| 53 | + `children`) into `demo/stories/data.ts` |
| 54 | +- Capture all types and interfaces specific to story data under |
| 55 | + `demo/stories/types.ts` |
| 56 | +- If a package has patterns (see [section](#patterns) below), tuck all pattern |
| 57 | + demo code under a `demo/~patterns` directory – repeating the directory structure |
| 58 | + as needed for `Story` TSX, `data.ts`, and `types.ts` files noted above. Prefix |
| 59 | + with a tilde to ensure these stories are ordered last. |
| 60 | + |
| 61 | +### Meta |
| 62 | + |
| 63 | +Story meta appears in the MDX file with the following structure: |
| 64 | + |
| 65 | +```mdx |
| 66 | +<Meta |
| 67 | + title="Packages/Package/Component" |
| 68 | + component={Component} |
| 69 | + subcomponents={{...}} /> |
| 70 | +``` |
| 71 | + |
| 72 | +- The `Component` is the element-level component that this story is focused on |
| 73 | +- Use the optional `subcomponents` object whenever subcomponents exist in |
| 74 | + support of the main element component. Keep this list in alphabetical order. |
| 75 | + |
| 76 | +### ArgsTable |
| 77 | + |
| 78 | +The next simple section of MDX generates prop tables for the component and |
| 79 | +subcomponents identied by the story meta. These tables can be viewed under the |
| 80 | +Storybook "Docs" tab. |
| 81 | + |
| 82 | +```mdx |
| 83 | +# API |
| 84 | + |
| 85 | +<ArgsTable /> |
| 86 | +``` |
| 87 | + |
| 88 | +### Canvas story (or stories) |
| 89 | + |
| 90 | +The canvas story specifies a name, args, argTypes, parameters, and the rendered |
| 91 | +story itself. In its most basic form: |
| 92 | + |
| 93 | +```mdx |
| 94 | +# Demo |
| 95 | + |
| 96 | +<Canvas> |
| 97 | + <Story name="Component">{args => <Component {...args} />}</Story> |
| 98 | +</Canvas> |
| 99 | +``` |
| 100 | + |
| 101 | +Often a single story is enough to fulfill the development goal for an isolated |
| 102 | +and flexible component demo. But sometimes, in the case of uncontrolled vs. |
| 103 | +controlled, there is a need to render multiple stories. In this case, the basic |
| 104 | +MDX form is modified to be: |
| 105 | + |
| 106 | +```mdx |
| 107 | +# Demo |
| 108 | + |
| 109 | +## Uncontrolled |
| 110 | + |
| 111 | +<Canvas> |
| 112 | + <Story name="Uncontrolled">{args => <ComponentStory {...args} />}</Story> |
| 113 | +</Canvas> |
| 114 | + |
| 115 | +## Controlled |
| 116 | + |
| 117 | +<Canvas> |
| 118 | + <Story name="Controlled"> |
| 119 | + {args => { |
| 120 | + const updateArgs = useArgs()[1]; |
| 121 | + const handleEvent = argName => updateArgs({ argName }); |
| 122 | + return <ComponentStory {...args} onEvent={handleEvent} />; |
| 123 | + }} |
| 124 | + </Story> |
| 125 | +</Canvas> |
| 126 | +``` |
| 127 | + |
| 128 | +Note how the `ComponentStory` is reused for both canvas stories. See |
| 129 | +[Story](#story) below for additional details. Be sure to move common `args`, |
| 130 | +`argTypes`, and `parameters` under the MDX `<Meta>` rather than repeating these |
| 131 | +under each story. |
| 132 | + |
| 133 | +#### Story `args` |
| 134 | + |
| 135 | +- Storybook automatically provides main component props as `args`. Usually there |
| 136 | + is no need to specify these values (double-check for missing prop controls). One |
| 137 | + notable exception is for Garden boolean prop values that default to `true`. In |
| 138 | + this case, the `args` should be specified to match. |
| 139 | +- Select smart defaults for the remaining subcomponent or "Story" `args`. |
| 140 | + Sometimes the best choice is to leave an arg undefined. |
| 141 | + |
| 142 | +#### Story `argTypes` |
| 143 | + |
| 144 | +Standard convention for `argTypes` exists across dozens of existing stories. |
| 145 | +Study demo code for details. Most importantly, main component props that are |
| 146 | +_not_ auto-generated must be properly categorized. The two acceptable story |
| 147 | +categories are: |
| 148 | + |
| 149 | +- Subcomponent. All subcomponent props (including `children`) are specified |
| 150 | + under the associated subcomponent name. |
| 151 | +- "Story". This category includes all ancillary controls that are needed to |
| 152 | + stress the full flexibility of the rendered component(s). |
| 153 | + |
| 154 | +#### Story `parameters` |
| 155 | + |
| 156 | +Currently, story `parameters` are used to link to the Figma page(s) or frame(s) |
| 157 | +that best exhibits associated component designs. Note that the parameter URLs |
| 158 | +are internal-only and point to designs that exist in the main Garden Figma |
| 159 | +branch. Therefore, `parameters` may need to be added as a follow-on for new |
| 160 | +components. |
| 161 | + |
| 162 | +### Story |
| 163 | + |
| 164 | +A `Story` type component is the best way to develop demo code for non-trivial |
| 165 | +Garden components. Doing so removes complexity of coding in MDX, placing |
| 166 | +component rendering and application of story args into TypeScript where code can |
| 167 | +be properly maintained. In its basic form, a `Story` looks like this: |
| 168 | + |
| 169 | +```tsx |
| 170 | +import React from 'react'; |
| 171 | +import { Story } from '@storybook/react'; |
| 172 | +import { Component, IComponentProps } from '@zendeskgarden/react-package'; |
| 173 | + |
| 174 | +interface IArgs extends IComponentProps { |
| 175 | + /* Subcomponent and "Story" arg definitions go here */ |
| 176 | +} |
| 177 | + |
| 178 | +export const ComponentStory: Story<IArgs> = args => <Component {...args} />; |
| 179 | +``` |
| 180 | + |
| 181 | +Take time to ensure Story `args` and `argTypes` are defined well. Often you will |
| 182 | +find that the majority of demo development time is spent on `args` and |
| 183 | +`argTypes` and the `Story` itself essentially falls out of the goal to |
| 184 | +demonstrate component flexibility in isolation. |
| 185 | + |
| 186 | +## Naming |
| 187 | + |
| 188 | +- Follow the existing Storybook naming hierarcy: |
| 189 | + `Packages/{{PackageName}}/{{ComponentName}}` |
| 190 | +- The first story in every package demo should be a `#readme.stories.mdx` file |
| 191 | + that renders the package's README.md. Prefix with a hashtag to ensure this story |
| 192 | + is ordered first. |
| 193 | +- Subsequent component story files are named `componentName.stories.mdx` |
| 194 | + (camelCase) |
| 195 | +- Use Garden's standard prop naming for story args (i.e. isXxx/hasXxx for |
| 196 | + boolean args), but use `argTypes` to rename with subcomponent notation. For |
| 197 | + example: |
| 198 | + |
| 199 | +```js |
| 200 | +args={{ |
| 201 | + hasHint: true |
| 202 | +}} |
| 203 | +argTypes={{ |
| 204 | + hasHint: { name: Hint, table: { category: 'Story' } } |
| 205 | +}} |
| 206 | +``` |
| 207 | + |
| 208 | +## Patterns |
| 209 | + |
| 210 | +A pattern is a demo story that highlights a component's ability to work together |
| 211 | +with other Garden components or in conjunction with external libraries. |
| 212 | +Oftentimes, a pattern ends up violating the best practice of duplicating example |
| 213 | +documentation that belongs on the website. However, due to component development |
| 214 | +sequencing or a need to persist a visual test, a pattern can be a helpful tool |
| 215 | +for (temporarily) demonstrating component layout or behavior that supercedes the |
| 216 | +isolation of the component itself. |
| 217 | + |
| 218 | +Follow [structure](#structure) conventions to keep patterns collected in a |
| 219 | +(potentially) short-lived location. It is the component developer's |
| 220 | +responsibility to track example pattern movement to the website and subsequently |
| 221 | +remove the pattern from Storybook – keeping the website as the focal source of |
| 222 | +truth. |
0 commit comments