@mitodl/smoot-design 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +142 -0
- package/.github/workflows/ci.yml +48 -0
- package/.github/workflows/publish-pages.yml +50 -0
- package/.github/workflows/release.yml +34 -0
- package/.github/workflows/validate-pr.yml +49 -0
- package/.pre-commit-config.yaml +90 -0
- package/.prettierignore +1 -0
- package/.prettierrc.json +4 -0
- package/.releaserc.json +40 -0
- package/.secrets.baseline +113 -0
- package/.storybook/main.ts +46 -0
- package/.storybook/manager-head.html +1 -0
- package/.storybook/preview-head.html +5 -0
- package/.storybook/preview.tsx +15 -0
- package/.storybook/public/pexels-photo-1851188.webp +0 -0
- package/.yarn/releases/yarn-4.5.1.cjs +934 -0
- package/.yarnrc.yml +23 -0
- package/LICENSE +28 -0
- package/README.md +13 -0
- package/jest.config.ts +22 -0
- package/package.json +110 -0
- package/src/components/Button/ActionButton.stories.tsx +186 -0
- package/src/components/Button/Button.stories.tsx +275 -0
- package/src/components/Button/Button.test.tsx +56 -0
- package/src/components/Button/Button.tsx +418 -0
- package/src/components/LinkAdapter/LinkAdapter.tsx +38 -0
- package/src/components/ThemeProvider/ThemeProvider.stories.tsx +94 -0
- package/src/components/ThemeProvider/ThemeProvider.tsx +127 -0
- package/src/components/ThemeProvider/Typography.stories.tsx +74 -0
- package/src/components/ThemeProvider/breakpoints.ts +20 -0
- package/src/components/ThemeProvider/buttons.ts +22 -0
- package/src/components/ThemeProvider/chips.tsx +167 -0
- package/src/components/ThemeProvider/colors.ts +33 -0
- package/src/components/ThemeProvider/typography.ts +174 -0
- package/src/index.ts +24 -0
- package/src/jest-setup.ts +0 -0
- package/src/story-utils/index.ts +28 -0
- package/src/types/theme.d.ts +106 -0
- package/src/types/typography.d.ts +54 -0
- package/tsconfig.json +26 -0
package/.yarnrc.yml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
compressionLevel: mixed
|
|
2
|
+
|
|
3
|
+
enableGlobalCache: false
|
|
4
|
+
|
|
5
|
+
nodeLinker: node-modules
|
|
6
|
+
|
|
7
|
+
yarnPath: .yarn/releases/yarn-4.5.1.cjs
|
|
8
|
+
|
|
9
|
+
# https://github.com/vitejs/vite-plugin-react-swc/issues/74#issuecomment-1520484130
|
|
10
|
+
# https://github.com/swc-project/swc/issues/5616#issuecomment-1265639797
|
|
11
|
+
# This setting ensures we always install the Linux binaries when running `yarn install`. This is needed for running
|
|
12
|
+
# swc natively in Docker (addresses swc-loader bindings not found error).
|
|
13
|
+
supportedArchitectures:
|
|
14
|
+
os:
|
|
15
|
+
- current
|
|
16
|
+
- linux
|
|
17
|
+
cpu:
|
|
18
|
+
- current
|
|
19
|
+
- x64
|
|
20
|
+
- arm64
|
|
21
|
+
libc:
|
|
22
|
+
- current
|
|
23
|
+
- glibc
|
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, MIT Office of Digital Learning
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# smoot-design
|
|
2
|
+
|
|
3
|
+
Design system components for MITODL Projects
|
|
4
|
+
|
|
5
|
+
## Development and Release
|
|
6
|
+
|
|
7
|
+
All PR titles and commits to `main` should use the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) format. During release, the types of commits included since the last release inform what sort of version bump should be made. For example, bugfixes yield a new patch version, whereas breaking changes trigger a major version bump.
|
|
8
|
+
|
|
9
|
+
To trigger a release, run the "Release" github action. Using [semantic-release](https://semantic-release.gitbook.io/semantic-release), this action will:
|
|
10
|
+
|
|
11
|
+
1. Inspect the commit history since previous release for [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) an
|
|
12
|
+
2. Determine whether the version bump should be major, minor, or patch based on commit types. Breaking changes (e.g., `feat!: remove Button variant 'outlined'`) will result in major version bumps.
|
|
13
|
+
3. Publish the package to NPM and the repository's [Github Releases](https://github.com/mitodl/smoot-design/releases).
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Config } from "@jest/types"
|
|
2
|
+
|
|
3
|
+
const config: Config.InitialOptions = {
|
|
4
|
+
collectCoverage: true,
|
|
5
|
+
coverageDirectory: "coverage",
|
|
6
|
+
watchPlugins: [
|
|
7
|
+
"jest-watch-typeahead/filename",
|
|
8
|
+
"jest-watch-typeahead/testname",
|
|
9
|
+
],
|
|
10
|
+
setupFilesAfterEnv: ["./jest-setup.ts"],
|
|
11
|
+
testEnvironment: "jsdom",
|
|
12
|
+
transform: {
|
|
13
|
+
"^.+\\.(t|j)sx?$": "@swc/jest",
|
|
14
|
+
},
|
|
15
|
+
moduleNameMapper: {
|
|
16
|
+
"\\.(svg|jpg|jpeg|png)$": "ol-test-utilities/filemocks/imagemock.js",
|
|
17
|
+
"\\.(css|scss)$": "ol-test-utilities/filemocks/filemock.js",
|
|
18
|
+
},
|
|
19
|
+
rootDir: "./src",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default config
|
package/package.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mitodl/smoot-design",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"packageManager": "yarn@4.5.1",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/mitodl/smoot-design"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"storybook": "storybook dev -p 6006",
|
|
11
|
+
"build-storybook": "storybook build --docs",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"build:esm": "tsc",
|
|
15
|
+
"build:cjs": "tsc --module commonjs --outDir dist/cjs",
|
|
16
|
+
"build": "rm -rf dist && rm -f .tsbuildinfo && npm run build:esm && npm run build:cjs",
|
|
17
|
+
"lint-check": "eslint src/ .storybook/",
|
|
18
|
+
"lint-fix": "yarn lint-check --fix",
|
|
19
|
+
"fmt-check": "prettier --ignore-path .gitignore --ignore-path .prettierignore --check .",
|
|
20
|
+
"fmt-fix": "prettier --ignore-path .gitignore --ignore-path .prettierignore --write ."
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/esm/index.d.ts",
|
|
26
|
+
"default": "./dist/esm/index.js"
|
|
27
|
+
},
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./dist/cjs/index.d.ts",
|
|
30
|
+
"default": "./dist/cjs/index.js"
|
|
31
|
+
},
|
|
32
|
+
"default": {
|
|
33
|
+
"types": "./dist/esm/index.d.ts",
|
|
34
|
+
"default": "./dist/esm/index.js"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@emotion/react": "^11.11.1",
|
|
40
|
+
"@emotion/styled": "^11.11.0",
|
|
41
|
+
"@mui/base": "5.0.0-beta.61",
|
|
42
|
+
"@mui/lab": "6.0.0-beta.14",
|
|
43
|
+
"@mui/material": "^6.1.6",
|
|
44
|
+
"@mui/material-nextjs": "^6.1.6",
|
|
45
|
+
"@mui/system": "^6.1.6",
|
|
46
|
+
"@remixicon/react": "^4.2.0",
|
|
47
|
+
"@types/jest": "^29.5.14",
|
|
48
|
+
"classnames": "^2.5.1",
|
|
49
|
+
"lodash": "^4.17.21",
|
|
50
|
+
"material-ui-popup-state": "^5.1.0",
|
|
51
|
+
"tiny-invariant": "^1.3.1"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@chromatic-com/storybook": "^1.9.0",
|
|
55
|
+
"@faker-js/faker": "^9.0.0",
|
|
56
|
+
"@storybook/addon-actions": "^8.4.2",
|
|
57
|
+
"@storybook/addon-essentials": "^8.4.2",
|
|
58
|
+
"@storybook/addon-interactions": "^8.4.2",
|
|
59
|
+
"@storybook/addon-links": "^8.4.2",
|
|
60
|
+
"@storybook/addon-onboarding": "^8.4.2",
|
|
61
|
+
"@storybook/addon-webpack5-compiler-swc": "^1.0.5",
|
|
62
|
+
"@storybook/blocks": "^8.4.2",
|
|
63
|
+
"@storybook/nextjs": "^8.4.2",
|
|
64
|
+
"@storybook/preview-api": "^8.4.2",
|
|
65
|
+
"@storybook/react": "^8.4.2",
|
|
66
|
+
"@storybook/react-webpack5": "^8.4.2",
|
|
67
|
+
"@storybook/test": "^8.4.2",
|
|
68
|
+
"@storybook/types": "^8.4.2",
|
|
69
|
+
"@swc/jest": "^0.2.37",
|
|
70
|
+
"@testing-library/react": "^16.0.1",
|
|
71
|
+
"@testing-library/user-event": "14.5.2",
|
|
72
|
+
"@types/lodash": "^4.17.13",
|
|
73
|
+
"@types/react-dom": "^18.3.0",
|
|
74
|
+
"@typescript-eslint/eslint-plugin": "^8.13.0",
|
|
75
|
+
"@typescript-eslint/typescript-estree": "^8.13.0",
|
|
76
|
+
"conventional-changelog-conventionalcommits": "^8.0.0",
|
|
77
|
+
"eslint": "8.57.1",
|
|
78
|
+
"eslint-config-mitodl": "^2.1.0",
|
|
79
|
+
"eslint-config-prettier": "^9.0.0",
|
|
80
|
+
"eslint-import-resolver-typescript": "^3.6.1",
|
|
81
|
+
"eslint-plugin-import": "^2.29.1",
|
|
82
|
+
"eslint-plugin-jest": "^28.6.0",
|
|
83
|
+
"eslint-plugin-mdx": "^3.0.0",
|
|
84
|
+
"eslint-plugin-react": "^7.34.3",
|
|
85
|
+
"eslint-plugin-react-hooks": "^4.6.2",
|
|
86
|
+
"eslint-plugin-styled-components-a11y": "^2.1.35",
|
|
87
|
+
"eslint-plugin-testing-library": "^6.2.0",
|
|
88
|
+
"jest": "^29.7.0",
|
|
89
|
+
"jest-environment-jsdom": "^29.5.0",
|
|
90
|
+
"jest-extended": "^4.0.2",
|
|
91
|
+
"jest-fail-on-console": "^3.2.0",
|
|
92
|
+
"jest-watch-typeahead": "^2.2.2",
|
|
93
|
+
"next": "^15.0.2",
|
|
94
|
+
"prettier": "^3.3.3",
|
|
95
|
+
"react": "18.3.1",
|
|
96
|
+
"react-dom": "^18.3.1",
|
|
97
|
+
"react-router": "^6.22.2",
|
|
98
|
+
"react-router-dom": "^6.22.2",
|
|
99
|
+
"semantic-release": "^24.2.0",
|
|
100
|
+
"storybook": "^8.4.2",
|
|
101
|
+
"ts-node": "^10.9.2",
|
|
102
|
+
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
|
103
|
+
"type-fest": "^4.26.1",
|
|
104
|
+
"typescript": "^5.6.3"
|
|
105
|
+
},
|
|
106
|
+
"peerDependencies": {
|
|
107
|
+
"react": "*",
|
|
108
|
+
"react-dom": "*"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { ActionButton, ActionButtonLink, DEFAULT_PROPS } from "./Button"
|
|
4
|
+
import type { ActionButtonProps } from "./Button"
|
|
5
|
+
import Grid from "@mui/material/Grid2"
|
|
6
|
+
import Stack from "@mui/material/Stack"
|
|
7
|
+
import {
|
|
8
|
+
RiArrowLeftLine,
|
|
9
|
+
RiDeleteBinLine,
|
|
10
|
+
RiTestTubeLine,
|
|
11
|
+
} from "@remixicon/react"
|
|
12
|
+
|
|
13
|
+
import { fn } from "@storybook/test"
|
|
14
|
+
import { enumValues, docsEnum } from "@/story-utils"
|
|
15
|
+
|
|
16
|
+
const ICONS = {
|
|
17
|
+
None: undefined,
|
|
18
|
+
ArrowBackIcon: <RiArrowLeftLine />,
|
|
19
|
+
DeleteIcon: <RiDeleteBinLine />,
|
|
20
|
+
TestTubeIcon: <RiTestTubeLine />,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const VARIANTS = enumValues<NonNullable<ActionButtonProps["variant"]>>({
|
|
24
|
+
primary: true,
|
|
25
|
+
secondary: true,
|
|
26
|
+
tertiary: true,
|
|
27
|
+
text: true,
|
|
28
|
+
unstable_noBorder: true,
|
|
29
|
+
unstable_inverted: true,
|
|
30
|
+
unstable_success: true,
|
|
31
|
+
})
|
|
32
|
+
const STABLE_VARIANTS = VARIANTS.filter((v) => !v.startsWith("unstable"))
|
|
33
|
+
const SIZES = enumValues<NonNullable<ActionButtonProps["size"]>>({
|
|
34
|
+
small: true,
|
|
35
|
+
medium: true,
|
|
36
|
+
large: true,
|
|
37
|
+
})
|
|
38
|
+
const EDGES = enumValues<NonNullable<ActionButtonProps["edge"]>>({
|
|
39
|
+
circular: true,
|
|
40
|
+
rounded: true,
|
|
41
|
+
none: true,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const meta: Meta<typeof ActionButton> = {
|
|
45
|
+
title: "smoot-design/ActionButton",
|
|
46
|
+
component: ActionButton,
|
|
47
|
+
argTypes: {
|
|
48
|
+
variant: {
|
|
49
|
+
options: VARIANTS,
|
|
50
|
+
control: { type: "select" },
|
|
51
|
+
table: {
|
|
52
|
+
type: { summary: docsEnum(VARIANTS) },
|
|
53
|
+
defaultValue: { summary: DEFAULT_PROPS.variant },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
size: {
|
|
57
|
+
options: SIZES,
|
|
58
|
+
control: { type: "select" },
|
|
59
|
+
table: {
|
|
60
|
+
type: { summary: docsEnum(SIZES) },
|
|
61
|
+
defaultValue: { summary: DEFAULT_PROPS.size },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
edge: {
|
|
65
|
+
options: EDGES,
|
|
66
|
+
control: { type: "select" },
|
|
67
|
+
table: {
|
|
68
|
+
type: { summary: docsEnum(EDGES) },
|
|
69
|
+
defaultValue: { summary: DEFAULT_PROPS.edge },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
args: {
|
|
74
|
+
onClick: fn(),
|
|
75
|
+
},
|
|
76
|
+
tags: ["autodocs"],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default meta
|
|
80
|
+
|
|
81
|
+
type Story = StoryObj<typeof ActionButton>
|
|
82
|
+
|
|
83
|
+
export const VariantsAndEdge: Story = {
|
|
84
|
+
render: (args) => (
|
|
85
|
+
<>
|
|
86
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
87
|
+
<ActionButton {...args} edge="none" variant="primary">
|
|
88
|
+
{ICONS.DeleteIcon}
|
|
89
|
+
</ActionButton>
|
|
90
|
+
<ActionButton {...args} edge="none" variant="secondary">
|
|
91
|
+
{ICONS.DeleteIcon}
|
|
92
|
+
</ActionButton>
|
|
93
|
+
<ActionButton {...args} edge="none" variant="tertiary">
|
|
94
|
+
{ICONS.DeleteIcon}
|
|
95
|
+
</ActionButton>
|
|
96
|
+
<ActionButton {...args} edge="none" variant="text">
|
|
97
|
+
{ICONS.DeleteIcon}
|
|
98
|
+
</ActionButton>
|
|
99
|
+
</Stack>
|
|
100
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
101
|
+
<ActionButton {...args} edge="rounded" variant="primary">
|
|
102
|
+
{ICONS.DeleteIcon}
|
|
103
|
+
</ActionButton>
|
|
104
|
+
<ActionButton {...args} edge="rounded" variant="secondary">
|
|
105
|
+
{ICONS.DeleteIcon}
|
|
106
|
+
</ActionButton>
|
|
107
|
+
<ActionButton {...args} edge="rounded" variant="tertiary">
|
|
108
|
+
{ICONS.DeleteIcon}
|
|
109
|
+
</ActionButton>
|
|
110
|
+
<ActionButton {...args} edge="rounded" variant="text">
|
|
111
|
+
{ICONS.DeleteIcon}
|
|
112
|
+
</ActionButton>
|
|
113
|
+
</Stack>
|
|
114
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
115
|
+
<ActionButton {...args} edge="circular" variant="primary">
|
|
116
|
+
{ICONS.DeleteIcon}
|
|
117
|
+
</ActionButton>
|
|
118
|
+
<ActionButton {...args} edge="circular" variant="secondary">
|
|
119
|
+
{ICONS.DeleteIcon}
|
|
120
|
+
</ActionButton>
|
|
121
|
+
<ActionButton {...args} edge="circular" variant="tertiary">
|
|
122
|
+
{ICONS.DeleteIcon}
|
|
123
|
+
</ActionButton>
|
|
124
|
+
<ActionButton {...args} edge="circular" variant="text">
|
|
125
|
+
{ICONS.DeleteIcon}
|
|
126
|
+
</ActionButton>
|
|
127
|
+
</Stack>
|
|
128
|
+
</>
|
|
129
|
+
),
|
|
130
|
+
tags: ["main"],
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const Showcase: Story = {
|
|
134
|
+
render: (args) => (
|
|
135
|
+
<Grid container sx={{ maxWidth: "750px" }} rowGap={2}>
|
|
136
|
+
{STABLE_VARIANTS.flatMap((variant) =>
|
|
137
|
+
EDGES.flatMap((edge) => (
|
|
138
|
+
<React.Fragment key={`${variant}-${edge}`}>
|
|
139
|
+
<Grid size={{ xs: 12, sm: 3 }} alignItems="center">
|
|
140
|
+
<code>
|
|
141
|
+
variant={variant}
|
|
142
|
+
<br />
|
|
143
|
+
edge={edge}
|
|
144
|
+
</code>
|
|
145
|
+
</Grid>
|
|
146
|
+
{SIZES.flatMap((size) =>
|
|
147
|
+
Object.entries(ICONS)
|
|
148
|
+
.filter(([_key, icon]) => icon)
|
|
149
|
+
.map(([iconKey, icon]) => (
|
|
150
|
+
<Grid size={{ xs: 4, sm: 1 }} key={`${size}-${iconKey}`}>
|
|
151
|
+
<ActionButton
|
|
152
|
+
variant={variant}
|
|
153
|
+
edge={edge}
|
|
154
|
+
size={size}
|
|
155
|
+
{...args}
|
|
156
|
+
>
|
|
157
|
+
{icon}
|
|
158
|
+
</ActionButton>
|
|
159
|
+
</Grid>
|
|
160
|
+
)),
|
|
161
|
+
)}
|
|
162
|
+
</React.Fragment>
|
|
163
|
+
)),
|
|
164
|
+
)}
|
|
165
|
+
</Grid>
|
|
166
|
+
),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const Links: Story = {
|
|
170
|
+
render: () => (
|
|
171
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
172
|
+
<ActionButtonLink href="#fake" variant="primary">
|
|
173
|
+
{ICONS.DeleteIcon}
|
|
174
|
+
</ActionButtonLink>
|
|
175
|
+
<ActionButtonLink href="#fake" variant="secondary">
|
|
176
|
+
{ICONS.DeleteIcon}
|
|
177
|
+
</ActionButtonLink>
|
|
178
|
+
<ActionButtonLink href="#fake" variant="tertiary">
|
|
179
|
+
{ICONS.DeleteIcon}
|
|
180
|
+
</ActionButtonLink>
|
|
181
|
+
<ActionButtonLink href="#fake" variant="text">
|
|
182
|
+
{ICONS.DeleteIcon}
|
|
183
|
+
</ActionButtonLink>
|
|
184
|
+
</Stack>
|
|
185
|
+
),
|
|
186
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Button, ButtonLink, DEFAULT_PROPS } from "./Button"
|
|
4
|
+
import type { ButtonProps } from "./Button"
|
|
5
|
+
import Grid from "@mui/material/Grid2"
|
|
6
|
+
import Stack from "@mui/material/Stack"
|
|
7
|
+
import {
|
|
8
|
+
RiArrowLeftLine,
|
|
9
|
+
RiDeleteBinLine,
|
|
10
|
+
RiTestTubeLine,
|
|
11
|
+
RiMailLine,
|
|
12
|
+
} from "@remixicon/react"
|
|
13
|
+
|
|
14
|
+
import { fn } from "@storybook/test"
|
|
15
|
+
import { docsEnum, enumValues } from "@/story-utils"
|
|
16
|
+
|
|
17
|
+
const ICONS = {
|
|
18
|
+
None: undefined,
|
|
19
|
+
ArrowBackIcon: <RiArrowLeftLine />,
|
|
20
|
+
DeleteIcon: <RiDeleteBinLine />,
|
|
21
|
+
TestTubeIcon: <RiTestTubeLine />,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const VARIANTS = enumValues<NonNullable<ButtonProps["variant"]>>({
|
|
25
|
+
primary: true,
|
|
26
|
+
secondary: true,
|
|
27
|
+
tertiary: true,
|
|
28
|
+
text: true,
|
|
29
|
+
unstable_noBorder: true,
|
|
30
|
+
unstable_inverted: true,
|
|
31
|
+
unstable_success: true,
|
|
32
|
+
})
|
|
33
|
+
const STABLE_VARIANTS = VARIANTS.filter((v) => !v.startsWith("unstable"))
|
|
34
|
+
const SIZES = enumValues<NonNullable<ButtonProps["size"]>>({
|
|
35
|
+
small: true,
|
|
36
|
+
medium: true,
|
|
37
|
+
large: true,
|
|
38
|
+
})
|
|
39
|
+
const EDGES = enumValues<NonNullable<ButtonProps["edge"]>>({
|
|
40
|
+
circular: true,
|
|
41
|
+
rounded: true,
|
|
42
|
+
none: true,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const meta: Meta<typeof Button> = {
|
|
46
|
+
title: "smoot-design/Button",
|
|
47
|
+
component: Button,
|
|
48
|
+
argTypes: {
|
|
49
|
+
variant: {
|
|
50
|
+
options: VARIANTS,
|
|
51
|
+
control: { type: "select" },
|
|
52
|
+
table: {
|
|
53
|
+
type: { summary: docsEnum(VARIANTS) },
|
|
54
|
+
defaultValue: { summary: DEFAULT_PROPS.variant },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
size: {
|
|
58
|
+
options: SIZES,
|
|
59
|
+
control: { type: "select" },
|
|
60
|
+
table: {
|
|
61
|
+
type: { summary: docsEnum(SIZES) },
|
|
62
|
+
defaultValue: { summary: DEFAULT_PROPS.size },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
edge: {
|
|
66
|
+
options: ["circular", "rounded"],
|
|
67
|
+
control: { type: "select" },
|
|
68
|
+
table: {
|
|
69
|
+
type: { summary: docsEnum(EDGES) },
|
|
70
|
+
defaultValue: { summary: DEFAULT_PROPS.edge },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
startIcon: {
|
|
74
|
+
options: Object.keys(ICONS),
|
|
75
|
+
mapping: ICONS,
|
|
76
|
+
},
|
|
77
|
+
endIcon: {
|
|
78
|
+
options: Object.keys(ICONS),
|
|
79
|
+
mapping: ICONS,
|
|
80
|
+
},
|
|
81
|
+
responsive: {
|
|
82
|
+
table: {
|
|
83
|
+
defaultValue: { summary: DEFAULT_PROPS.responsive.toString() },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
args: {
|
|
88
|
+
onClick: fn(),
|
|
89
|
+
},
|
|
90
|
+
tags: ["autodocs"],
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default meta
|
|
94
|
+
|
|
95
|
+
type Story = StoryObj<typeof Button>
|
|
96
|
+
|
|
97
|
+
export const VariantsAndEdge: Story = {
|
|
98
|
+
render: (args) => (
|
|
99
|
+
<>
|
|
100
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
101
|
+
<Button edge="none" variant="primary" {...args}>
|
|
102
|
+
Primary
|
|
103
|
+
</Button>
|
|
104
|
+
<Button edge="none" variant="secondary" {...args}>
|
|
105
|
+
Secondary
|
|
106
|
+
</Button>
|
|
107
|
+
<Button edge="none" variant="tertiary" {...args}>
|
|
108
|
+
Tertiary
|
|
109
|
+
</Button>
|
|
110
|
+
<Button edge="none" variant="text" {...args}>
|
|
111
|
+
Text
|
|
112
|
+
</Button>
|
|
113
|
+
</Stack>
|
|
114
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
115
|
+
<Button edge="rounded" variant="primary" {...args}>
|
|
116
|
+
Primary
|
|
117
|
+
</Button>
|
|
118
|
+
<Button edge="rounded" variant="secondary" {...args}>
|
|
119
|
+
Secondary
|
|
120
|
+
</Button>
|
|
121
|
+
<Button edge="rounded" variant="tertiary" {...args}>
|
|
122
|
+
Tertiary
|
|
123
|
+
</Button>
|
|
124
|
+
<Button edge="rounded" variant="text" {...args}>
|
|
125
|
+
Text
|
|
126
|
+
</Button>
|
|
127
|
+
</Stack>
|
|
128
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
129
|
+
<Button edge="circular" variant="primary" {...args}>
|
|
130
|
+
Primary
|
|
131
|
+
</Button>
|
|
132
|
+
<Button edge="circular" variant="secondary" {...args}>
|
|
133
|
+
Secondary
|
|
134
|
+
</Button>
|
|
135
|
+
<Button edge="circular" variant="tertiary" {...args}>
|
|
136
|
+
Tertiary
|
|
137
|
+
</Button>
|
|
138
|
+
<Button edge="circular" variant="text" {...args}>
|
|
139
|
+
Text
|
|
140
|
+
</Button>
|
|
141
|
+
</Stack>
|
|
142
|
+
</>
|
|
143
|
+
),
|
|
144
|
+
tags: ["main"],
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const RESPONSIVE = [true, false]
|
|
148
|
+
|
|
149
|
+
export const Sizes: Story = {
|
|
150
|
+
argTypes: {
|
|
151
|
+
size: { table: { disable: true } },
|
|
152
|
+
},
|
|
153
|
+
render: (args) => (
|
|
154
|
+
<Grid container sx={{ my: 2, maxWidth: "600px" }} alignItems="center">
|
|
155
|
+
{RESPONSIVE.flatMap((responsive) => {
|
|
156
|
+
return (
|
|
157
|
+
<React.Fragment key={String(responsive)}>
|
|
158
|
+
<Grid size={{ xs: 12 }}>
|
|
159
|
+
<code>{`responsive={${responsive.toString()}}`}</code>
|
|
160
|
+
</Grid>
|
|
161
|
+
{SIZES.map((size) => (
|
|
162
|
+
<Grid
|
|
163
|
+
size={{ xs: 4 }}
|
|
164
|
+
gap={2}
|
|
165
|
+
display="flex"
|
|
166
|
+
alignItems="center"
|
|
167
|
+
key={size}
|
|
168
|
+
>
|
|
169
|
+
<Button {...args} size={size} responsive={responsive}>
|
|
170
|
+
{size}
|
|
171
|
+
</Button>
|
|
172
|
+
</Grid>
|
|
173
|
+
))}
|
|
174
|
+
</React.Fragment>
|
|
175
|
+
)
|
|
176
|
+
})}
|
|
177
|
+
</Grid>
|
|
178
|
+
),
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const WithIcons: Story = {
|
|
182
|
+
render: (args) => (
|
|
183
|
+
<Stack direction="column" alignItems="start" gap={2} sx={{ my: 2 }}>
|
|
184
|
+
{Object.entries(ICONS).map(([key, icon]) => (
|
|
185
|
+
<Button {...args} startIcon={icon} key={key}>
|
|
186
|
+
{key}
|
|
187
|
+
</Button>
|
|
188
|
+
))}
|
|
189
|
+
</Stack>
|
|
190
|
+
),
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const EXTRA_PROPS = [
|
|
194
|
+
{},
|
|
195
|
+
/**
|
|
196
|
+
* Show RiTestTubeLine because it is a fairly thin icon
|
|
197
|
+
*/
|
|
198
|
+
{ startIcon: <RiTestTubeLine /> },
|
|
199
|
+
/**
|
|
200
|
+
* Show RiTestTubeLine because it is a fairly thick icon
|
|
201
|
+
*/
|
|
202
|
+
{ startIcon: <RiMailLine /> },
|
|
203
|
+
{ endIcon: <RiTestTubeLine /> },
|
|
204
|
+
{ endIcon: <RiMailLine /> },
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* `ButtonLink` is a styled `Button` that renders an anchor tag.
|
|
209
|
+
*
|
|
210
|
+
* To use a custom link component (E.g. `Link` from `react-router` or `next/link`),
|
|
211
|
+
* pass it as the `Component` prop. Alternatively, customize the project-wide
|
|
212
|
+
* default link adapter via [Theme's LinkAdapter](../?path=/docs/smoot-design-themeprovider--docs)
|
|
213
|
+
*/
|
|
214
|
+
export const Links: Story = {
|
|
215
|
+
render: () => (
|
|
216
|
+
<Stack direction="row" gap={2} sx={{ my: 2 }}>
|
|
217
|
+
<ButtonLink href="#fake" variant="primary">
|
|
218
|
+
Link
|
|
219
|
+
</ButtonLink>
|
|
220
|
+
<ButtonLink href="#fake" variant="secondary">
|
|
221
|
+
Link
|
|
222
|
+
</ButtonLink>
|
|
223
|
+
<ButtonLink href="#fake" variant="tertiary">
|
|
224
|
+
Link
|
|
225
|
+
</ButtonLink>
|
|
226
|
+
<ButtonLink href="#fake" variant="text">
|
|
227
|
+
Link
|
|
228
|
+
</ButtonLink>
|
|
229
|
+
</Stack>
|
|
230
|
+
),
|
|
231
|
+
}
|
|
232
|
+
export const Showcase: Story = {
|
|
233
|
+
render: (args) => (
|
|
234
|
+
<Grid container rowGap={2} sx={{ maxWidth: "600px" }}>
|
|
235
|
+
{STABLE_VARIANTS.flatMap((variant) =>
|
|
236
|
+
EDGES.flatMap((edge) =>
|
|
237
|
+
EXTRA_PROPS.map((extraProps, i) => {
|
|
238
|
+
return (
|
|
239
|
+
<React.Fragment key={`${variant}-${edge}-${i}`}>
|
|
240
|
+
<Grid size={{ xs: 3 }}>
|
|
241
|
+
<pre>
|
|
242
|
+
variant={variant}
|
|
243
|
+
<br />
|
|
244
|
+
edge={edge}
|
|
245
|
+
</pre>
|
|
246
|
+
</Grid>
|
|
247
|
+
{SIZES.map((size) => (
|
|
248
|
+
<Grid
|
|
249
|
+
size={{ xs: 3 }}
|
|
250
|
+
display="flex"
|
|
251
|
+
alignItems="center"
|
|
252
|
+
key={`${size}`}
|
|
253
|
+
>
|
|
254
|
+
<Button
|
|
255
|
+
{...args}
|
|
256
|
+
variant={variant}
|
|
257
|
+
edge={edge}
|
|
258
|
+
size={size}
|
|
259
|
+
{...extraProps}
|
|
260
|
+
>
|
|
261
|
+
{args.children}
|
|
262
|
+
</Button>
|
|
263
|
+
</Grid>
|
|
264
|
+
))}
|
|
265
|
+
</React.Fragment>
|
|
266
|
+
)
|
|
267
|
+
}),
|
|
268
|
+
),
|
|
269
|
+
)}
|
|
270
|
+
</Grid>
|
|
271
|
+
),
|
|
272
|
+
args: {
|
|
273
|
+
children: "Click me",
|
|
274
|
+
},
|
|
275
|
+
}
|