♻️(react) migrate to Storybook 7

This new release comes with breaking changes for stories and mdx docs.
This commit is contained in:
Nathan Vasse
2023-05-12 16:03:16 +02:00
committed by NathanVss
parent e7fc782b1c
commit 77721c3b6d
24 changed files with 2639 additions and 4187 deletions

View File

@@ -1,34 +0,0 @@
const viteTsconfig = require('vite-tsconfig-paths');
const tsconfigPaths = viteTsconfig.default;
const { mergeConfig } = require('vite');
module.exports = {
'stories': [
'../src/**/*.stories.mdx',
'../src/**/*.stories.@(js|jsx|ts|tsx)',
],
'addons': [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
'@storybook/preset-scss'
],
'framework': '@storybook/react',
'core': {
'builder': '@storybook/builder-vite',
},
staticDirs: [
'../src',
],
'features': {
'storyStoreV7': true,
},
async viteFinal(config) {
const finalConfig = mergeConfig(config, {
plugins: [tsconfigPaths()],
});
finalConfig.base = 'https://openfun.github.io/cunningham';
return finalConfig;
},
};

View File

@@ -0,0 +1,33 @@
import { StorybookConfig } from "@storybook/react-vite";
import remarkGfm from "remark-gfm";
const config: StorybookConfig = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"@storybook/addon-a11y",
"@storybook/preset-scss",
{
name: "@storybook/addon-docs",
options: {
mdxPluginOptions: {
mdxCompileOptions: {
remarkPlugins: [remarkGfm],
},
},
},
},
],
framework: {
name: "@storybook/react-vite",
options: {},
},
staticDirs: ["../src"],
features: {
storyStoreV7: true,
},
};
export default config;

View File

@@ -34,7 +34,7 @@
"test": "FORCE_COLOR=1 vitest run",
"test-watch": "vitest",
"coverage": "vitest run --coverage",
"storybook": "start-storybook -p 6006",
"storybook": "storybook dev -p 6006",
"deploy-storybook": "storybook-to-ghpages"
},
"dependencies": {
@@ -58,14 +58,14 @@
"@faker-js/faker": "8.0.0",
"@openfun/cunningham-tokens": "*",
"@openfun/typescript-configs": "*",
"@storybook/addon-a11y": "7.0.10",
"@storybook/addon-actions": "7.0.10",
"@storybook/addon-essentials": "7.0.10",
"@storybook/addon-interactions": "7.0.10",
"@storybook/addon-links": "7.0.10",
"@storybook/builder-vite": "0.3.0",
"@storybook/addon-a11y": "7.0.11",
"@storybook/addon-actions": "7.0.11",
"@storybook/addon-essentials": "7.0.11",
"@storybook/addon-interactions": "7.0.11",
"@storybook/addon-links": "7.0.11",
"@storybook/preset-scss": "1.0.3",
"@storybook/react": "7.0.11",
"@storybook/react-vite": "7.0.11",
"@storybook/storybook-deployer": "2.8.16",
"@storybook/testing-library": "0.1.0",
"@testing-library/dom": "9.2.0",
@@ -82,8 +82,10 @@
"css-loader": "6.7.3",
"glob": "10.2.3",
"jsdom": "22.0.0",
"remark-gfm": "3.0.1",
"sass": "1.62.1",
"sass-loader": "13.2.2",
"storybook": "7.0.11",
"style-loader": "3.3.2",
"typescript": "5.0.4",
"vite": "4.3.5",

View File

@@ -29,17 +29,17 @@ You can use icons within the button by passing the icon name as a prop.
> Use the attribute `iconPosition` to position the icon on the left or right side of the button. The default is `left`.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-button--icon-left"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-button--icon-right"/>
</Canvas>
You can also use button with only an icon.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-button--icon-only"/>
</Canvas>
@@ -49,7 +49,7 @@ The button can be disabled. The disabled button will render the same no matter w
> Keep in the mind that a disabled button will never call `onClick` if it is provided.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-button--disabled"/>
</Canvas>

View File

@@ -1,4 +1,4 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta } from "@storybook/react";
import React from "react";
import { Button } from "./index";
@@ -10,111 +10,118 @@ export default {
control: "boolean",
},
},
} as ComponentMeta<typeof Button>;
} as Meta<typeof Button>;
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = {
children: "Primary",
color: "primary",
export const Primary = {
args: {
children: "Primary",
color: "primary",
},
};
export const Secondary = Template.bind({});
Secondary.args = {
children: "Secondary",
color: "secondary",
export const Secondary = {
args: {
children: "Secondary",
color: "secondary",
},
};
export const Tertiary = Template.bind({});
Tertiary.args = {
children: "Tertiary",
color: "tertiary",
export const Tertiary = {
args: {
children: "Tertiary",
color: "tertiary",
},
};
export const Disabled = Template.bind({});
Disabled.args = {
children: "Disabled",
color: "primary",
disabled: true,
export const Disabled = {
args: {
children: "Disabled",
color: "primary",
disabled: true,
},
};
export const Danger = Template.bind({});
Danger.args = {
children: "Danger",
color: "danger",
export const Danger = {
args: {
children: "Danger",
color: "danger",
},
};
export const Small = Template.bind({});
Small.args = {
children: "Primary",
color: "primary",
size: "small",
export const Small = {
args: {
children: "Primary",
color: "primary",
size: "small",
},
};
export const IconLeft = Template.bind({});
IconLeft.args = {
children: "Icon",
icon: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M17.8724 10.0166H13.248L16.6251 4.07749C16.9258 3.54846 16.2447 3.01176 15.8005 3.42755L6.57343 12.0655C6.22192 12.3946 6.45489 12.9838 6.93615 12.9838H11.5606L8.18353 18.9229C7.88275 19.4519 8.56335 19.9886 9.00746 19.5728L18.2352 10.9349C18.5867 10.6058 18.3537 10.0166 17.8724 10.0166Z"
/>
</svg>
),
color: "primary",
export const IconLeft = {
args: {
children: "Icon",
icon: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M17.8724 10.0166H13.248L16.6251 4.07749C16.9258 3.54846 16.2447 3.01176 15.8005 3.42755L6.57343 12.0655C6.22192 12.3946 6.45489 12.9838 6.93615 12.9838H11.5606L8.18353 18.9229C7.88275 19.4519 8.56335 19.9886 9.00746 19.5728L18.2352 10.9349C18.5867 10.6058 18.3537 10.0166 17.8724 10.0166Z"
/>
</svg>
),
color: "primary",
},
};
export const IconRight = Template.bind({});
IconRight.args = {
children: "Icon",
iconPosition: "right",
icon: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M17.8724 10.0166H13.248L16.6251 4.07749C16.9258 3.54846 16.2447 3.01176 15.8005 3.42755L6.57343 12.0655C6.22192 12.3946 6.45489 12.9838 6.93615 12.9838H11.5606L8.18353 18.9229C7.88275 19.4519 8.56335 19.9886 9.00746 19.5728L18.2352 10.9349C18.5867 10.6058 18.3537 10.0166 17.8724 10.0166Z"
/>
</svg>
),
color: "primary",
export const IconRight = {
args: {
children: "Icon",
iconPosition: "right",
icon: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M17.8724 10.0166H13.248L16.6251 4.07749C16.9258 3.54846 16.2447 3.01176 15.8005 3.42755L6.57343 12.0655C6.22192 12.3946 6.45489 12.9838 6.93615 12.9838H11.5606L8.18353 18.9229C7.88275 19.4519 8.56335 19.9886 9.00746 19.5728L18.2352 10.9349C18.5867 10.6058 18.3537 10.0166 17.8724 10.0166Z"
/>
</svg>
),
color: "primary",
},
};
export const IconOnly = Template.bind({});
IconOnly.args = {
"aria-label": "Button with only an icon",
icon: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M17.8724 10.0166H13.248L16.6251 4.07749C16.9258 3.54846 16.2447 3.01176 15.8005 3.42755L6.57343 12.0655C6.22192 12.3946 6.45489 12.9838 6.93615 12.9838H11.5606L8.18353 18.9229C7.88275 19.4519 8.56335 19.9886 9.00746 19.5728L18.2352 10.9349C18.5867 10.6058 18.3537 10.0166 17.8724 10.0166Z"
/>
</svg>
),
color: "primary",
export const IconOnly = {
args: {
"aria-label": "Button with only an icon",
icon: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M17.8724 10.0166H13.248L16.6251 4.07749C16.9258 3.54846 16.2447 3.01176 15.8005 3.42755L6.57343 12.0655C6.22192 12.3946 6.45489 12.9838 6.93615 12.9838H11.5606L8.18353 18.9229C7.88275 19.4519 8.56335 19.9886 9.00746 19.5728L18.2352 10.9349C18.5867 10.6058 18.3537 10.0166 17.8724 10.0166Z"
/>
</svg>
),
color: "primary",
},
};

View File

@@ -40,7 +40,7 @@ This component is a wrapper around the more complicated DataGrid component. It i
Here a quick usage example
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-datagrid--data-list-only"/>
</Canvas>
@@ -63,7 +63,7 @@ Sorting etc ...
Take a look at the following example that renders a table of users.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-datagrid--client-side-without-pagination"/>
</Canvas>
@@ -74,7 +74,7 @@ with `enableRowSelection`, `rowSelection` and `onRowSelectionChange` props.
> Please click on checkboxes to select rows to see hows the `onRowSelectionChange` prop works, selected ids are displayed
below the table.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-datagrid--client-side-with-pagination"/>
</Canvas>
@@ -94,7 +94,7 @@ pagination, sorting etc ...
Please take a look at the following example that simulates a server side data that's re-fetched on each page and sorting
change.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-datagrid--full-server-side"/>
</Canvas>
@@ -152,7 +152,7 @@ outside the grid component ( for example to delete selected rows ).
The component provides out of the box a loading state that can be enabled by setting the `isLoading` props to `true`.
So feel free to use it between page or sorting changes when you are fetching data from a server.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-datagrid--loading"/>
</Canvas>
@@ -160,7 +160,7 @@ So feel free to use it between page or sorting changes when you are fetching dat
The component automatically displays an empty state when there is no data to display and it is not loading.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-datagrid--empty"/>
</Canvas>

View File

@@ -1,4 +1,4 @@
import { ComponentMeta } from "@storybook/react";
import { Meta } from "@storybook/react";
import React, { useEffect, useMemo, useState } from "react";
import { faker } from "@faker-js/faker";
import { DataGrid, SortModel } from ":/components/DataGrid/index";
@@ -11,7 +11,7 @@ import { DataList } from ":/components/DataGrid/DataList";
export default {
title: "Components/DataGrid",
component: DataGrid,
} as ComponentMeta<typeof DataGrid>;
} as Meta<typeof DataGrid>;
export const Empty = () => {
return (

View File

@@ -17,13 +17,13 @@ The `label` props is optional, but you can use it to provide a description of th
**Without label**
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--default"/>
</Canvas>
**With label**
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--with-label"/>
</Canvas>
@@ -31,7 +31,7 @@ The `label` props is optional, but you can use it to provide a description of th
You can set the value of the checkbox in 3 different ways.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--default"/>
<Story id="components-forms-checkbox--indeterminate"/>
<Story id="components-forms-checkbox--checked"/>
@@ -41,7 +41,7 @@ You can set the value of the checkbox in 3 different ways.
As the component uses [Field](?path=/story/components-forms-field-doc--page), you can use the `text` props to provide a description of the checkbox.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--with-texts"/>
</Canvas>
@@ -49,7 +49,7 @@ As the component uses [Field](?path=/story/components-forms-field-doc--page), yo
As a regular checkbox, you can disable it by using the `disabled` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--disabled"/>
</Canvas>
@@ -57,15 +57,15 @@ As a regular checkbox, you can disable it by using the `disabled` props.
You can use the following props to change the state of the Input component by using the `state` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--with-texts"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--success"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--error"/>
</Canvas>
@@ -73,13 +73,13 @@ You can use the following props to change the state of the Input component by us
It will happen often that you will need to use multiple grouped checkboxes. You can use the `CheckboxGroup` component to do so.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--group"/>
</Canvas>
You can also define `state`, `text` props on the group component
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-checkbox--group-error"/>
<Story id="components-forms-checkbox--group-success"/>
</Canvas>

View File

@@ -1,74 +1,103 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta, StoryFn } from "@storybook/react";
import React from "react";
import { Checkbox, CheckboxGroup } from ":/components/Forms/Checkbox/index";
export default {
title: "Components/Forms/Checkbox",
component: Checkbox,
} as ComponentMeta<typeof Checkbox>;
} as Meta<typeof Checkbox>;
const Template: ComponentStory<typeof Checkbox> = (args) => (
const Template: StoryFn<typeof Checkbox> = (args) => (
<Checkbox {...args} aria-label="Checkbox" />
);
export const Default = Template.bind({});
Default.args = {};
export const Checked = Template.bind({});
Checked.args = {
checked: true,
export const Default = {
render: Template,
args: {},
};
export const Indeterminate = Template.bind({});
Indeterminate.args = {
indeterminate: true,
export const Checked = {
render: Template,
args: {
checked: true,
},
};
export const WithLabel = Template.bind({});
WithLabel.args = {
label: "Label",
export const Indeterminate = {
render: Template,
args: {
indeterminate: true,
},
};
export const LabelChecked = Template.bind({});
LabelChecked.args = {
checked: true,
label: "Label",
export const WithLabel = {
render: Template,
args: {
label: "Label",
},
};
export const WithTexts = Template.bind({});
WithTexts.args = {
checked: true,
label: "Label",
text: "This is an optional text",
export const LabelChecked = {
render: Template,
args: {
checked: true,
label: "Label",
},
};
export const Disabled = Template.bind({});
Disabled.args = {
disabled: true,
label: "Label",
export const WithTexts = {
render: Template,
args: {
checked: true,
label: "Label",
text: "This is an optional text",
},
};
export const DisabledChecked = Template.bind({});
DisabledChecked.args = {
checked: true,
disabled: true,
label: "Label",
export const Disabled = {
render: Template,
args: {
disabled: true,
label: "Label",
},
};
export const Error = Template.bind({});
Error.args = {
checked: true,
label: "Label",
text: "This is an optional text",
state: "error",
export const DisabledChecked = {
render: Template,
args: {
checked: true,
disabled: true,
label: "Label",
},
};
export const Success = Template.bind({});
Success.args = {
checked: true,
label: "Label",
text: "This is an optional text",
state: "success",
export const Error = {
render: Template,
args: {
checked: true,
label: "Label",
text: "This is an optional text",
state: "error",
},
};
export const Success = {
render: Template,
args: {
checked: true,
label: "Label",
text: "This is an optional text",
state: "success",
},
};
export const Group = () => (

View File

@@ -1,34 +1,43 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta, StoryFn } from "@storybook/react";
import React from "react";
import { Field } from ":/components/Forms/Field/index";
export default {
title: "Components/Forms/Field",
component: Field,
} as ComponentMeta<typeof Field>;
} as Meta<typeof Field>;
const Template: ComponentStory<typeof Field> = (args) => (
const Template: StoryFn<typeof Field> = (args) => (
<Field {...args}>
<strong>Any children</strong>
</Field>
);
export const Default = Template.bind({});
Default.args = {
text: "This is an optional text",
rightText: "Right text",
export const Default = {
render: Template,
args: {
text: "This is an optional text",
rightText: "Right text",
},
};
export const Success = Template.bind({});
Success.args = {
state: "success",
text: "This is an optional success message",
rightText: "Right text",
export const Success = {
render: Template,
args: {
state: "success",
text: "This is an optional success message",
rightText: "Right text",
},
};
export const Error = Template.bind({});
Error.args = {
state: "error",
text: "This is an optional error message",
rightText: "Right text",
export const Error = {
render: Template,
args: {
state: "error",
text: "This is an optional error message",
rightText: "Right text",
},
};

View File

@@ -24,15 +24,15 @@ Cunningham provides a versatile Input component that you can use in your forms.
You can use the following props to change the state of the Input component by using the `state` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--default"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--success"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--error"/>
</Canvas>
@@ -40,7 +40,7 @@ You can use the following props to change the state of the Input component by us
As a regular input, you can disable it by using the `disabled` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--disabled-empty"/>
<Story id="components-forms-input--disabled-filled"/>
</Canvas>
@@ -49,13 +49,13 @@ As a regular input, you can disable it by using the `disabled` props.
You can define an icon that will appear on the left side of the input by using the `icon` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--icon"/>
</Canvas>
You can also independently add an icon on the right side by using the `rightIcon` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--icon-right"/>
</Canvas>
@@ -63,13 +63,13 @@ You can also independently add an icon on the right side by using the `rightIcon
You can define a text that will appear below the input by using the `text` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--with-text"/>
</Canvas>
You can also independently add a text on the right side by using the `rightText` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--with-both-texts"/>
</Canvas>
@@ -77,7 +77,7 @@ You can also independently add a text on the right side by using the `rightText`
By default, the input has a default width, like all inputs. But you can force it to take the full width of its container by using the `fullWidth` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--full-width"/>
</Canvas>
@@ -86,7 +86,7 @@ By default, the input has a default width, like all inputs. But you can force it
You can display a counter of the number of characters entered in the input by using the `charsCounter` props. Please bare
in mind to also define `charCounterMax`.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--char-counter"/>
</Canvas>
@@ -95,7 +95,7 @@ in mind to also define `charCounterMax`.
Like a native input, you can use the Input component in a controlled or non controlled way. You can see the example below
using the component in a controlled way.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--controlled"/>
</Canvas>
@@ -103,7 +103,7 @@ using the component in a controlled way.
You can use the `ref` props to get a reference to the input element. The ref to the native input is nested inside the `input` attribute.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-input--with-ref"/>
</Canvas>

View File

@@ -1,4 +1,4 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta } from "@storybook/react";
import React, { useRef } from "react";
import { Input, InputRefType } from ":/components/Forms/Input/index";
import { Button } from ":/components/Button";
@@ -6,121 +6,134 @@ import { Button } from ":/components/Button";
export default {
title: "Components/Forms/Input",
component: Input,
} as ComponentMeta<typeof Input>;
} as Meta<typeof Input>;
const Template: ComponentStory<typeof Input> = (args) => <Input {...args} />;
export const Default = Template.bind({});
Default.args = {
defaultValue: "Hello world",
label: "Your name",
export const Default = {
args: {
defaultValue: "Hello world",
label: "Your name",
},
};
export const Success = Template.bind({});
Success.args = {
defaultValue: "Hello world",
label: "Your name",
state: "success",
icon: <span className="material-icons">person</span>,
text: "This is an optional success message",
export const Success = {
args: {
defaultValue: "Hello world",
label: "Your name",
state: "success",
icon: <span className="material-icons">person</span>,
text: "This is an optional success message",
},
};
export const Error = Template.bind({});
Error.args = {
defaultValue: "Hello world",
label: "Your name",
state: "error",
icon: <span className="material-icons">person</span>,
text: "This is an optional error message",
export const Error = {
args: {
defaultValue: "Hello world",
label: "Your name",
state: "error",
icon: <span className="material-icons">person</span>,
text: "This is an optional error message",
},
};
export const DisabledEmpty = Template.bind({});
DisabledEmpty.args = {
label: "Your name",
icon: <span className="material-icons">person</span>,
disabled: true,
export const DisabledEmpty = {
args: {
label: "Your name",
icon: <span className="material-icons">person</span>,
disabled: true,
},
};
export const DisabledFilled = Template.bind({});
DisabledFilled.args = {
label: "Your name",
defaultValue: "John Doe",
icon: <span className="material-icons">person</span>,
disabled: true,
export const DisabledFilled = {
args: {
label: "Your name",
defaultValue: "John Doe",
icon: <span className="material-icons">person</span>,
disabled: true,
},
};
export const Empty = Template.bind({});
Empty.args = {
label: "Your email",
export const Empty = {
args: {
label: "Your email",
},
};
export const Icon = Template.bind({});
Icon.args = {
label: "Account balance",
icon: <span className="material-icons">attach_money</span>,
defaultValue: "1000",
export const Icon = {
args: {
label: "Account balance",
icon: <span className="material-icons">attach_money</span>,
defaultValue: "1000",
},
};
export const IconRight = Template.bind({});
IconRight.args = {
label: "Account balance",
rightIcon: <span className="material-icons">attach_money</span>,
defaultValue: "1000",
export const IconRight = {
args: {
label: "Account balance",
rightIcon: <span className="material-icons">attach_money</span>,
defaultValue: "1000",
},
};
export const IconBoth = Template.bind({});
IconBoth.args = {
label: "Not a recommended use",
icon: <span className="material-icons">attach_money</span>,
rightIcon: <span className="material-icons">attach_money</span>,
defaultValue: "Is isn't recommended to use both icons",
export const IconBoth = {
args: {
label: "Not a recommended use",
icon: <span className="material-icons">attach_money</span>,
rightIcon: <span className="material-icons">attach_money</span>,
defaultValue: "Is isn't recommended to use both icons",
},
};
export const OverflowingText = Template.bind({});
OverflowingText.args = {
label: "Your name",
icon: <span className="material-icons">attach_money</span>,
defaultValue: "John Dave Mike Smith Doe Junior Senior Yoda Skywalker",
export const OverflowingText = {
args: {
label: "Your name",
icon: <span className="material-icons">attach_money</span>,
defaultValue: "John Dave Mike Smith Doe Junior Senior Yoda Skywalker",
},
};
export const WithText = Template.bind({});
WithText.args = {
defaultValue: "Hello world",
label: "Your name",
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
export const WithText = {
args: {
defaultValue: "Hello world",
label: "Your name",
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
},
};
export const WithTextRight = Template.bind({});
WithTextRight.args = {
defaultValue: "Hello world",
label: "Your name",
rightText: "0/300",
export const WithTextRight = {
args: {
defaultValue: "Hello world",
label: "Your name",
rightText: "0/300",
},
};
export const WithBothTexts = Template.bind({});
WithBothTexts.args = {
defaultValue: "Hello world",
label: "Your name",
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
rightText: "0/300",
export const WithBothTexts = {
args: {
defaultValue: "Hello world",
label: "Your name",
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
rightText: "0/300",
},
};
export const FullWidth = Template.bind({});
FullWidth.args = {
defaultValue: "Hello world",
label: "Your name",
fullWidth: true,
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
rightText: "0/300",
export const FullWidth = {
args: {
defaultValue: "Hello world",
label: "Your name",
fullWidth: true,
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
rightText: "0/300",
},
};
export const CharCounter = Template.bind({});
CharCounter.args = {
defaultValue: "CEO",
label: "Job title",
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
charCounter: true,
charCounterMax: 30,
export const CharCounter = {
args: {
defaultValue: "CEO",
label: "Job title",
text: "This is a text, you can display anything you want here like warnings, informations or errors.",
charCounter: true,
charCounterMax: 30,
},
};
export const Controlled = () => {

View File

@@ -1,20 +1,23 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta, StoryFn } from "@storybook/react";
import React from "react";
import { LabelledBox } from ":/components/Forms/LabelledBox/index";
export default {
title: "Components/Forms/LabelledBox",
component: LabelledBox,
} as ComponentMeta<typeof LabelledBox>;
} as Meta<typeof LabelledBox>;
const Template: ComponentStory<typeof LabelledBox> = (args) => (
const Template: StoryFn<typeof LabelledBox> = (args) => (
<div style={{ height: "3.5rem" }}>
<LabelledBox {...args} />
</div>
);
export const Default = Template.bind({});
Default.args = {
label: "Your label here",
children: <span className="clr-greyscale-800">Hello world</span>,
export const Default = {
render: Template,
args: {
label: "Your label here",
children: <span className="clr-greyscale-800">Hello world</span>,
},
};

View File

@@ -17,13 +17,13 @@ The `label` props is optional, but you can use it to provide a description of th
**Without label**
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--default"/>
</Canvas>
**With label**
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--with-label"/>
</Canvas>
@@ -31,7 +31,7 @@ The `label` props is optional, but you can use it to provide a description of th
You can set the value of the radio with the `checked` attribute.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--default"/>
<Story id="components-forms-radio--checked"/>
</Canvas>
@@ -40,7 +40,7 @@ You can set the value of the radio with the `checked` attribute.
As the component uses [Field](?path=/story/components-forms-field-doc--page), you can use the `text` props to provide a description of the radio.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--with-text"/>
</Canvas>
@@ -48,7 +48,7 @@ As the component uses [Field](?path=/story/components-forms-field-doc--page), yo
As a regular radio, you can disable it by using the `disabled` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--disabled"/>
</Canvas>
@@ -56,15 +56,15 @@ As a regular radio, you can disable it by using the `disabled` props.
You can use the following props to change the state of the Input component by using the `state` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--with-text"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--success"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--error"/>
</Canvas>
@@ -72,13 +72,13 @@ You can use the following props to change the state of the Input component by us
Here is how you can leverage the `RadioGroup` component to create a group of radio buttons.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--group"/>
</Canvas>
You can also define `state`, `text` props on the group component
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-radio--group-error"/>
<Story id="components-forms-radio--group-success"/>
</Canvas>

View File

@@ -1,69 +1,95 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta, StoryFn } from "@storybook/react";
import React from "react";
import { Radio, RadioGroup } from ":/components/Forms/Radio/index";
export default {
title: "Components/Forms/Radio",
component: Radio,
} as ComponentMeta<typeof Radio>;
} as Meta<typeof Radio>;
const Template: ComponentStory<typeof Radio> = (args) => (
const Template: StoryFn<typeof Radio> = (args) => (
<Radio {...args} aria-label="Radio" />
);
export const Default = Template.bind({});
Default.args = {};
export const Checked = Template.bind({});
Checked.args = {
checked: true,
export const Default = {
render: Template,
args: {},
};
export const WithLabel = Template.bind({});
WithLabel.args = {
label: "Label",
export const Checked = {
render: Template,
args: {
checked: true,
},
};
export const WithLabelChecked = Template.bind({});
WithLabelChecked.args = {
label: "Label",
checked: true,
export const WithLabel = {
render: Template,
args: {
label: "Label",
},
};
export const WithText = Template.bind({});
WithText.args = {
label: "Label",
text: "Some optional text",
export const WithLabelChecked = {
render: Template,
args: {
label: "Label",
checked: true,
},
};
export const Disabled = Template.bind({});
Disabled.args = {
label: "Label",
disabled: true,
export const WithText = {
render: Template,
args: {
label: "Label",
text: "Some optional text",
},
};
export const DisabledChecked = Template.bind({});
DisabledChecked.args = {
label: "Label",
disabled: true,
checked: true,
text: "Some optional text",
export const Disabled = {
render: Template,
args: {
label: "Label",
disabled: true,
},
};
export const Error = Template.bind({});
Error.args = {
checked: true,
label: "Label",
text: "This is an optional text",
state: "error",
export const DisabledChecked = {
render: Template,
args: {
label: "Label",
disabled: true,
checked: true,
text: "Some optional text",
},
};
export const Success = Template.bind({});
Success.args = {
checked: true,
label: "Label",
text: "This is an optional text",
state: "success",
export const Error = {
render: Template,
args: {
checked: true,
label: "Label",
text: "This is an optional text",
state: "error",
},
};
export const Success = {
render: Template,
args: {
checked: true,
label: "Label",
text: "This is an optional text",
state: "success",
},
};
export const Group = () => {

View File

@@ -36,7 +36,7 @@ As you can see the `value` is optional, if not provided, the `label` will be use
You can enable the text live filtering simply by using the `searchable` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--searchable-uncontrolled"/>
</Canvas>
@@ -44,11 +44,11 @@ You can enable the text live filtering simply by using the `searchable` props.
You can use the following props to change the state of the Select component by using the `state` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--success"/>
</Canvas>
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--error"/>
</Canvas>
@@ -56,7 +56,7 @@ You can use the following props to change the state of the Select component by u
As a regular select, you can disable it by using the `disabled` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--disabled"/>
</Canvas>
@@ -64,7 +64,7 @@ As a regular select, you can disable it by using the `disabled` props.
As the component uses [Field](?path=/story/components-forms-field-doc--page), you can use the `text` props to provide a description of the checkbox.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--with-text"/>
</Canvas>
@@ -72,7 +72,7 @@ As the component uses [Field](?path=/story/components-forms-field-doc--page), yo
By default, the select has a default width, like all inputs. But you can force it to take the full width of its container by using the `fullWidth` props.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--full-width"/>
</Canvas>
@@ -81,7 +81,7 @@ By default, the select has a default width, like all inputs. But you can force i
Like a native select, you can use the Select component in a controlled or non controlled way. You can see the example below
using the component in a controlled way.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--controlled"/>
</Canvas>

View File

@@ -1,4 +1,4 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta, StoryFn } from "@storybook/react";
import React, { useState } from "react";
import { faker } from "@faker-js/faker";
import { Select } from ":/components/Forms/Select";
@@ -8,9 +8,9 @@ import { CunninghamProvider } from ":/components/Provider";
export default {
title: "Components/Forms/Select/Mono",
component: Select,
} as ComponentMeta<typeof Select>;
} as Meta<typeof Select>;
const Template: ComponentStory<typeof Select> = (args) => (
const Template: StoryFn<typeof Select> = (args) => (
<div style={{ paddingBottom: "200px" }}>
<CunninghamProvider>
<Select {...args} />
@@ -24,28 +24,38 @@ const OPTIONS = CITIES.map((city) => ({
value: city.toLowerCase(),
}));
export const Uncontrolled = Template.bind({});
Uncontrolled.args = {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
export const Uncontrolled = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
},
};
export const Disabled = Template.bind({});
Disabled.args = {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
disabled: true,
export const Disabled = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
disabled: true,
},
};
export const WithText = Template.bind({});
WithText.args = {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
text: "This is a text, you can display anything you want here like warnings, information or errors.",
export const WithText = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
text: "This is a text, you can display anything you want here like warnings, information or errors.",
},
};
export const Controlled = () => {
const [value, setValue] = useState(OPTIONS[8].value);
return (
@@ -66,40 +76,52 @@ export const Controlled = () => {
);
};
export const Overflow = Template.bind({});
Overflow.args = {
label: "Select a city",
options: [
{
value: "1",
label: "Very long long long long long long long city name",
},
],
defaultValue: "1",
export const Overflow = {
render: Template,
args: {
label: "Select a city",
options: [
{
value: "1",
label: "Very long long long long long long long city name",
},
],
defaultValue: "1",
},
};
export const SearchableEmpty = Template.bind({});
SearchableEmpty.args = {
label: "Select a city",
options: OPTIONS,
searchable: true,
export const SearchableEmpty = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
searchable: true,
},
};
export const SearchableUncontrolled = Template.bind({});
SearchableUncontrolled.args = {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
searchable: true,
export const SearchableUncontrolled = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
searchable: true,
},
};
export const SearchableDisabled = Template.bind({});
SearchableDisabled.args = {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
searchable: true,
disabled: true,
export const SearchableDisabled = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
searchable: true,
disabled: true,
},
};
export const SearchableControlled = () => {
@@ -123,27 +145,36 @@ export const SearchableControlled = () => {
);
};
export const FullWidth = Template.bind({});
FullWidth.args = {
label: "Select a city",
options: OPTIONS,
fullWidth: true,
export const FullWidth = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
fullWidth: true,
},
};
export const Success = Template.bind({});
Success.args = {
label: "Select a city",
options: OPTIONS,
state: "success",
text: "Well done",
export const Success = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
state: "success",
text: "Well done",
},
};
export const Error = Template.bind({});
Error.args = {
label: "Select a city",
options: OPTIONS,
state: "error",
text: "Something went wrong",
export const Error = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS,
state: "error",
text: "Something went wrong",
},
};
export const FormExample = () => {

View File

@@ -1,13 +1,15 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Meta, StoryFn } from "@storybook/react";
import React from "react";
import { Loader } from ":/components/Loader/index";
export default {
title: "Components/Loader (WIP)",
component: Loader,
} as ComponentMeta<typeof Loader>;
} as Meta<typeof Loader>;
const Template: ComponentStory<typeof Loader> = () => <Loader />;
const Template: StoryFn<typeof Loader> = () => <Loader />;
export const Default = Template.bind({});
Default.args = {};
export const Default = {
render: Template,
args: {},
};

View File

@@ -10,7 +10,7 @@ for synchronous loading as well as asynchronous loading. You can paginate your a
fetch it from a server, the component is really versatile.
<Canvas withSource={"none"}>
<Canvas sourceState="none">
<Story id="components-pagination--basic"/>
</Canvas>
@@ -30,7 +30,7 @@ The most basic usage you can make of it is this one, defining a pagination with
### Basic
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-pagination--basic"/>
</Canvas>
@@ -40,7 +40,7 @@ The most basic usage you can make of it is this one, defining a pagination with
But this won't make you really happy if you want to paginate your list of items, so you can wire things a bit better.
Let's make a component that paginate a list of random number.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-pagination--list"/>
</Canvas>
@@ -48,7 +48,7 @@ Let's make a component that paginate a list of random number.
You can also set the page programmatically, for example, if you want to use a query parameter to set the page.
<Canvas withSource="open">
<Canvas sourceState="shown">
<Story id="components-pagination--force-page"/>
</Canvas>

View File

@@ -1,4 +1,4 @@
import { ComponentMeta } from "@storybook/react";
import { Meta } from "@storybook/react";
import React, { useEffect, useMemo, useState } from "react";
import { Pagination, usePagination } from ":/components/Pagination/index";
import { CunninghamProvider } from ":/components/Provider";
@@ -6,7 +6,7 @@ import { CunninghamProvider } from ":/components/Provider";
export default {
title: "Components/Pagination",
component: Pagination,
} as ComponentMeta<typeof Pagination>;
} as Meta<typeof Pagination>;
export const Basic = () => {
const pagination = usePagination({

View File

@@ -26,13 +26,13 @@ You can use custom utility classes to set the background color of an element. Th
You can find all existing classes below.
<Canvas isColumn={true} withSource="none">
<Canvas sourceState="none">
{colors.map(color => (
<div key={color} style={{display: 'flex', gap: '5px'}}>
{tints.map(tint => (
<div key={color + tint} style={{display: 'flex', flexDirection: 'column', flexShrink: 0,flexBasis: '120px', alignItems: 'center', flexGrow: 1}}>
<div style={{width: '72px', height: '48px'}} className={"bg-" + color + "-" + tint}></div>
<code className="fs-s mt-st">bg-{color}-{tint}</code>
<pre className="fs-s mt-st">bg-{color}-{tint}</pre>
</div>
))}
</div>
@@ -54,7 +54,7 @@ You can use custom utility classes to set the color attribute of an element. The
You can find all existing classes below.
<Canvas isColumn={true} withSource="none">
<Canvas sourceState="none">
{colors.map(color => (
<div key={color} style={{display: 'flex', gap: '10px'}}>
{['text', ...tints].map(tint => {
@@ -70,7 +70,7 @@ You can find all existing classes below.
alignItems: 'center',
flexGrow: 1
}}>
<code className={classes.join(' ')}>clr-{color}-{tint}</code>
<pre className={classes.join(' ')}>clr-{color}-{tint}</pre>
</div>
})}
</div>
@@ -80,16 +80,10 @@ You can find all existing classes below.
Pay attention the special `{color}-text` variation. Each color must have a text color variation, which is supposed to
render readable text on top of a -500 background of its own color variation. Please check out the following examples:
<Canvas withSource="open">
<span className="bg-primary-500 clr-primary-text fw-medium p-t">
I'm a text on top of a primary-500 background 👋
</span>
<span className="bg-secondary-500 clr-secondary-text fw-medium p-t">
I'm a text on top of a secondary-500 background 👋
</span>
<span className="bg-danger-500 clr-danger-text fw-medium p-t">
I'm a text on top of a danger-500 background 👋
</span>
<Canvas sourceState="none">
<div className="bg-primary-500 clr-primary-text fw-medium p-t">I'm a text on top of a primary-500 background 👋</div>
<div className="bg-secondary-500 clr-secondary-text fw-medium p-t">I'm a text on top of a secondary-500 background 👋</div>
<div className="bg-danger-500 clr-danger-text fw-medium p-t">I'm a text on top of a danger-500 background 👋</div>
</Canvas>
## Customize

View File

@@ -9,7 +9,7 @@ Cunningham comes with an existing toolkit to deal with spacings. 📏
Here are the existing spacings:
<Canvas isColumn={true} withSource="none">
<Canvas sourceState="none">
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
{Object.keys(tokens.theme.spacings).map(key => (
<div key={key} style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
@@ -29,11 +29,11 @@ You can use the following classes to add margins to your elements:
| Class | Equivalent |
|--------------|----------------------------|
| m-{spacing} | `margin: {spacing}` |
| mt-{spacing} | `margin-top: {spacing}` |
| mr-{spacing} | `margin-right: {spacing}` |
| mb-{spacing} | `margin-bottom: {spacing}` |
| ml-{spacing} | `margin-left: {spacing}` |
| m-[spacing] | `margin: [spacing]` |
| mt-[spacing] | `margin-top: [spacing]` |
| mr-[spacing] | `margin-right: [spacing]` |
| mb-[spacing] | `margin-bottom: [spacing]` |
| ml-[spacing] | `margin-left: [spacing]` |
## Paddings
@@ -41,22 +41,18 @@ You can use the following classes to add paddings to your elements:
| Class | Equivalent |
|--------------|----------------------------|
| p-{spacing} | `padding: {spacing}` |
| pt-{spacing} | `padding-top: {spacing}` |
| pr-{spacing} | `padding-right: {spacing}` |
| pb-{spacing} | `padding-bottom: {spacing}` |
| pl-{spacing} | `padding-left: {spacing}` |
| p-[spacing] | `padding: [spacing]` |
| pt-[spacing] | `padding-top: [spacing]` |
| pr-[spacing] | `padding-right: [spacing]` |
| pb-[spacing] | `padding-bottom: [spacing]` |
| pl-[spacing] | `padding-left: [spacing]` |
## Example
<Canvas isColumn={true} withSource="open">
<Canvas sourceState="shown">
<div className="bg-danger-100">
<div className="bg-primary-500 clr-primary-text fw-medium p-t mb-l">
Tiny padding + Large margin bottom
</div>
<div className="bg-secondary-500 clr-secondary-text fw-medium p-l ml-b">
Large padding + Base margin left
</div>
</div>
<div className="bg-primary-500 clr-primary-text fw-medium p-t mb-l">Tiny padding + Large margin bottom</div>
<div className="bg-secondary-500 clr-secondary-text fw-medium p-l ml-b">Large padding + Base margin left</div>
</div>
</Canvas>

View File

@@ -20,10 +20,12 @@ You can use custom utility classes to set the font size of an element. These cla
`}
/>
<Canvas isColumn={true} withSource="none">
{Object.keys(tokens.theme.font.sizes).map(key => (
<div key={key} className={"fs-" + key}>Using the <code>fs-{key}</code> class</div>
))}
<Canvas sourceState="none">
<div style={{display: "flex", flexDirection: "column", gap: "20px"}}>
{Object.keys(tokens.theme.font.sizes).map(key => (
<div key={key} className={"fs-" + key}>Using the <code>fs-{key}</code> class</div>
))}
</div>
</Canvas>
You can customize the values of the font size design tokens with the configuration file this way:
@@ -61,10 +63,12 @@ You can use custom utility classes to set the font weight of an element. These c
`}
/>
<Canvas isColumn={true} withSource="none">
{Object.keys(tokens.theme.font.weights).map(key => (
<div key={key} className={"fs-l fw-" + key}>Using the <code>fw-{key}</code> class</div>
))}
<Canvas sourceState="none">
<div style={{display: "flex", flexDirection: "column", gap: "20px"}}>
{Object.keys(tokens.theme.font.weights).map(key => (
<div key={key} className={"fs-l fw-" + key}>Using the <code>fw-{key}</code> class</div>
))}
</div>
</Canvas>
You can customize the values of the font weight design tokens with the configuration file this way:
@@ -106,10 +110,12 @@ class on every dom element.
`}
/>
<Canvas isColumn={true} withSource="none">
{Object.keys(tokens.theme.font.families).map(key => (
<div key={key} className={"f-" + key}>Using the <code>f-{key}</code> class</div>
))}
<Canvas sourceState="none">
<div style={{display: "flex", flexDirection: "column", gap: "20px"}}>
{Object.keys(tokens.theme.font.families).map(key => (
<div key={key} className={"f-" + key}>Using the <code>f-{key}</code> class</div>
))}
</div>
</Canvas>
You can customize the values of the font family design tokens with the configuration file this way:

5705
yarn.lock

File diff suppressed because it is too large Load Diff