@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.
Files changed (40) hide show
  1. package/.eslintrc.js +142 -0
  2. package/.github/workflows/ci.yml +48 -0
  3. package/.github/workflows/publish-pages.yml +50 -0
  4. package/.github/workflows/release.yml +34 -0
  5. package/.github/workflows/validate-pr.yml +49 -0
  6. package/.pre-commit-config.yaml +90 -0
  7. package/.prettierignore +1 -0
  8. package/.prettierrc.json +4 -0
  9. package/.releaserc.json +40 -0
  10. package/.secrets.baseline +113 -0
  11. package/.storybook/main.ts +46 -0
  12. package/.storybook/manager-head.html +1 -0
  13. package/.storybook/preview-head.html +5 -0
  14. package/.storybook/preview.tsx +15 -0
  15. package/.storybook/public/pexels-photo-1851188.webp +0 -0
  16. package/.yarn/releases/yarn-4.5.1.cjs +934 -0
  17. package/.yarnrc.yml +23 -0
  18. package/LICENSE +28 -0
  19. package/README.md +13 -0
  20. package/jest.config.ts +22 -0
  21. package/package.json +110 -0
  22. package/src/components/Button/ActionButton.stories.tsx +186 -0
  23. package/src/components/Button/Button.stories.tsx +275 -0
  24. package/src/components/Button/Button.test.tsx +56 -0
  25. package/src/components/Button/Button.tsx +418 -0
  26. package/src/components/LinkAdapter/LinkAdapter.tsx +38 -0
  27. package/src/components/ThemeProvider/ThemeProvider.stories.tsx +94 -0
  28. package/src/components/ThemeProvider/ThemeProvider.tsx +127 -0
  29. package/src/components/ThemeProvider/Typography.stories.tsx +74 -0
  30. package/src/components/ThemeProvider/breakpoints.ts +20 -0
  31. package/src/components/ThemeProvider/buttons.ts +22 -0
  32. package/src/components/ThemeProvider/chips.tsx +167 -0
  33. package/src/components/ThemeProvider/colors.ts +33 -0
  34. package/src/components/ThemeProvider/typography.ts +174 -0
  35. package/src/index.ts +24 -0
  36. package/src/jest-setup.ts +0 -0
  37. package/src/story-utils/index.ts +28 -0
  38. package/src/types/theme.d.ts +106 -0
  39. package/src/types/typography.d.ts +54 -0
  40. 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
+ }