paris 0.2.2 → 0.4.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 (72) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +61 -5
  3. package/package.json +27 -15
  4. package/src/helpers/renderEnhancer.tsx +21 -0
  5. package/src/pages/_app.tsx +1 -1
  6. package/src/pages/index.tsx +1 -1
  7. package/src/stories/Pagination.mdx +73 -0
  8. package/src/stories/Tokens.mdx +0 -8
  9. package/src/stories/button/Button.module.scss +23 -7
  10. package/src/stories/button/Button.stories.ts +17 -0
  11. package/src/stories/button/Button.tsx +80 -20
  12. package/src/stories/card/Card.module.scss +14 -0
  13. package/src/stories/card/Card.stories.ts +33 -0
  14. package/src/stories/card/Card.tsx +60 -0
  15. package/src/stories/card/index.ts +1 -0
  16. package/src/stories/checkbox/Checkbox.module.scss +57 -0
  17. package/src/stories/checkbox/Checkbox.stories.ts +27 -0
  18. package/src/stories/checkbox/Checkbox.tsx +58 -0
  19. package/src/stories/checkbox/index.ts +1 -0
  20. package/src/stories/combobox/Combobox.module.scss +5 -0
  21. package/src/stories/combobox/Combobox.stories.ts +84 -0
  22. package/src/stories/combobox/Combobox.tsx +264 -0
  23. package/src/stories/combobox/index.ts +1 -0
  24. package/src/stories/dialog/Dialog.module.scss +187 -0
  25. package/src/stories/dialog/Dialog.stories.tsx +70 -0
  26. package/src/stories/dialog/Dialog.tsx +279 -0
  27. package/src/stories/dialog/index.ts +1 -0
  28. package/src/stories/drawer/Drawer.module.scss +284 -0
  29. package/src/stories/drawer/Drawer.stories.tsx +94 -0
  30. package/src/stories/drawer/Drawer.tsx +339 -0
  31. package/src/stories/drawer/index.ts +1 -0
  32. package/src/stories/dropdown/Dropdown.module.scss +23 -0
  33. package/src/stories/field/Field.module.scss +5 -0
  34. package/src/stories/field/Field.stories.ts +32 -0
  35. package/src/stories/field/Field.tsx +106 -0
  36. package/src/stories/field/index.ts +1 -0
  37. package/src/stories/icon/ChevronLeft.tsx +11 -0
  38. package/src/stories/icon/ChevronRight.tsx +11 -0
  39. package/src/stories/icon/Close.tsx +11 -0
  40. package/src/stories/icon/Icon.module.scss +5 -0
  41. package/src/stories/icon/Icon.stories.ts +28 -0
  42. package/src/stories/icon/Icon.tsx +46 -0
  43. package/src/stories/icon/index.ts +4 -0
  44. package/src/stories/input/Input.module.scss +135 -0
  45. package/src/stories/input/Input.stories.ts +89 -0
  46. package/src/stories/input/Input.tsx +137 -0
  47. package/src/stories/input/index.ts +1 -0
  48. package/src/stories/pagination/index.ts +1 -0
  49. package/src/stories/pagination/usePagination.ts +106 -0
  50. package/src/stories/select/Select.module.scss +74 -0
  51. package/src/stories/select/Select.stories.ts +73 -0
  52. package/src/stories/select/Select.tsx +176 -0
  53. package/src/stories/select/index.ts +1 -0
  54. package/src/stories/text/Text.module.scss +1 -1
  55. package/src/stories/text/Text.tsx +36 -14
  56. package/src/stories/textarea/TextArea.stories.ts +19 -0
  57. package/src/stories/textarea/TextArea.tsx +120 -0
  58. package/src/stories/textarea/index.ts +1 -0
  59. package/src/stories/theme/global.scss +2 -0
  60. package/src/stories/theme/index.ts +1 -0
  61. package/src/stories/theme/themes.ts +126 -7
  62. package/src/{styles → stories/theme}/tw-preflight.css +3 -1
  63. package/src/stories/theme/util.scss +8 -0
  64. package/src/stories/tilt/Tilt.module.scss +1 -0
  65. package/src/stories/tilt/Tilt.stories.tsx +43 -0
  66. package/src/stories/tilt/Tilt.tsx +63 -0
  67. package/src/stories/tilt/index.ts +1 -0
  68. package/src/stories/utility/RemoveFromDOM.tsx +19 -0
  69. package/src/stories/utility/TextWhenString.tsx +28 -0
  70. package/src/stories/utility/VisuallyHidden.tsx +25 -0
  71. package/src/types/Enhancer.ts +3 -0
  72. package/src/styles/util.scss +0 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # paris
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0a23074: Allow rendering `Button`s as anchors by passing `href` prop
8
+ - 6e5ea2c: `Tilt` component
9
+ - 50ab5e3: `Icon` component for included icons
10
+ - 50ab5e3: `Drawer` component
11
+ - 6e5ea2c: `Card` component
12
+ - dcd9bb6: `Combobox` component
13
+ - 05903f0: `Checkbox` component
14
+ - b758949: `Dialog` component
15
+
16
+ ### Patch Changes
17
+
18
+ - bbe60be: `Field` component for generic form fields with a label and description
19
+ - 50ab5e3: Drawer pagination
20
+ - bbe60be: Add label/description for Select
21
+ - 50ab5e3: Glassmorphic Dialog variant
22
+
23
+ ## 0.3.0
24
+
25
+ ### Minor Changes
26
+
27
+ - eef2375: `TextArea` component
28
+ - 2f6cd01: `Input` component
29
+ - eef2375: `Select` component
30
+
31
+ ### Patch Changes
32
+
33
+ - b61e950: Use `clsx` for cleaner class names
34
+ - b61e950: Add `global.scss` file and update docs to reflect global styles import
35
+ - 16c9c3e: Update docs to specify transpile requirement
36
+ - 2f6cd01: Memoized enhancers
37
+ - 646fccf: Add `status` for `Input` for success/error styling
38
+ - b61e950: Have `Text` use the theme fontFamily instead of inheriting
39
+
3
40
  ## 0.2.2
4
41
 
5
42
  ### Patch Changes
package/README.md CHANGED
@@ -10,17 +10,73 @@ Paris 1.x styling is heavily inspired by Uber's [Base Web](https://baseweb.desig
10
10
 
11
11
  ## Getting started
12
12
 
13
- First, install Paris and SASS in your project:
13
+ First, install Paris in your project:
14
14
 
15
15
  ```bash
16
- pnpm i paris sass
16
+ pnpm i paris
17
17
  # or
18
- yarn add paris sass
18
+ yarn add paris
19
19
  # or
20
- npm i paris sass
20
+ npm i paris
21
21
  ```
22
22
 
23
- Map our types in your `.tsconfig.json` file (this is a temporary workaround that we'll fix soon):
23
+ You'll need to tell your bundler to transpile files from Paris. This is easy in Next.js 13.1+ with the `transpilePackages` option in `next.config.js`:
24
+
25
+ ```js
26
+ // next.config.js
27
+ module.exports = {
28
+ // ...
29
+ transpilePackages: ['paris'],
30
+ };
31
+ ```
32
+
33
+ For older versions of Next.js, you can use a plugin like [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules). Instructions for other bundlers are coming soon.
34
+
35
+ You'll need to configure your bundler to support Sass/SCSS and SCSS modules. In Next.js, support for SCSS modules is built-in and can be enabled by simply installing `sass` as a dependency.
36
+
37
+ Paris uses `pte` (our theming engine) for powering theming and initial styles through CSS variables. You can use `generateCSS` or `generateThemeInjection` from `paris/theme` to generate the CSS variables and inject them into your app through either a `style` or `script` tag in your document head respectively. Either method supports SSR and server components, since the initial theme is static.
38
+
39
+ Additionally, you need to import the static global styles from `paris/theme/global.scss`.
40
+
41
+ For example, with the Next.js 13 app directory, you can do all of this in your root `layout.tsx` file:
42
+
43
+ ```tsx
44
+ // app/layout.tsx
45
+ import { generateCSS, generateThemeInjection, theme } from 'paris/theme';
46
+
47
+ // Import Paris's static global styling
48
+ import 'paris/theme/global.scss';
49
+
50
+ export default function RootLayout({
51
+ children,
52
+ }: {
53
+ children: React.ReactNode
54
+ }) {
55
+ return (
56
+ <html lang="en">
57
+ <head>
58
+ {/* Using a `style` tag (MUST have the id `pte-vars` and be in the document head) */}
59
+ <style
60
+ id="pte-vars"
61
+ dangerouslySetInnerHTML={{
62
+ __html: generateCSS(theme),
63
+ }}
64
+ />
65
+
66
+ {/* Or, use a `script` tag (can be located anywhere, should be loaded as early as possible to avoid an unstyled flash) */}
67
+ <script
68
+ dangerouslySetInnerHTML={{
69
+ __html: generateThemeInjection(theme),
70
+ }}
71
+ />
72
+ </head>
73
+ <body className={inter.className}>{children}</body>
74
+ </html>
75
+ );
76
+ }
77
+ ```
78
+
79
+ Finally, map our types in your `.tsconfig.json` file (this is a temporary workaround that we'll fix soon):
24
80
 
25
81
  ```json
26
82
  {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "paris",
3
3
  "author": "Sanil Chawla <sanil@slingshot.fm> (https://sanil.co)",
4
4
  "description": "Paris is Slingshot's React design system. It's a collection of reusable components, design tokens, and guidelines that help us build consistent, accessible, and performant user interfaces.",
5
- "version": "0.2.2",
5
+ "version": "0.4.0",
6
6
  "homepage": "https://paris.slingshot.fm",
7
7
  "license": "MIT",
8
8
  "repository": {
@@ -31,7 +31,20 @@
31
31
  "exports": {
32
32
  "./*": "./src/stories/*"
33
33
  },
34
+ "dependencies": {
35
+ "@ariakit/react": "^0.2.3",
36
+ "@headlessui/react": "^1.7.14",
37
+ "@radix-ui/react-checkbox": "^1.0.4",
38
+ "clsx": "^1.2.1",
39
+ "pte": "^0.4.9",
40
+ "react-parallax-tilt": "^1.7.144",
41
+ "ts-deepmerge": "^6.0.3"
42
+ },
34
43
  "peerDependencies": {
44
+ "@fortawesome/fontawesome-svg-core": "^6.4.0",
45
+ "@fortawesome/free-regular-svg-icons": "^6.4.0",
46
+ "@fortawesome/free-solid-svg-icons": "^6.4.0",
47
+ "@fortawesome/react-fontawesome": "^0.2.0",
35
48
  "react": "^18.x || ^17.x",
36
49
  "react-dom": "^18.x || ^17.x",
37
50
  "sass": "^1.x",
@@ -41,23 +54,25 @@
41
54
  "@changesets/cli": "^2.26.1",
42
55
  "@ssh/csstypes": "^1.1.0",
43
56
  "@ssh/eslint-config": "^1.0.0",
44
- "@storybook/addon-essentials": "^7.0.12",
45
- "@storybook/addon-interactions": "^7.0.12",
46
- "@storybook/addon-links": "^7.0.12",
47
- "@storybook/blocks": "^7.0.12",
48
- "@storybook/manager-api": "^7.0.12",
49
- "@storybook/nextjs": "^7.0.12",
50
- "@storybook/react": "^7.0.12",
57
+ "@storybook/addon-essentials": "^7.0.18",
58
+ "@storybook/addon-interactions": "^7.0.18",
59
+ "@storybook/addon-links": "^7.0.18",
60
+ "@storybook/addon-mdx-gfm": "^7.0.18",
61
+ "@storybook/addon-styling": "^1.0.8",
62
+ "@storybook/blocks": "^7.0.18",
63
+ "@storybook/manager-api": "^7.0.18",
64
+ "@storybook/nextjs": "^7.0.18",
65
+ "@storybook/react": "^7.0.18",
51
66
  "@storybook/testing-library": "^0.1.0",
52
- "@storybook/theming": "^7.0.12",
67
+ "@storybook/theming": "^7.0.18",
53
68
  "@types/node": "18.15.11",
54
69
  "@types/react": "18.0.31",
55
70
  "@types/react-dom": "18.0.11",
56
71
  "@typescript-eslint/eslint-plugin": "^5.13.0",
57
72
  "@typescript-eslint/parser": "^5.0.0",
73
+ "autoprefixer": "^10.4.14",
58
74
  "change-case": "^4.1.2",
59
75
  "csstype": "^3.1.2",
60
- "esbuild-scss-modules-plugin": "^1.1.1",
61
76
  "eslint": "^8.2.0",
62
77
  "eslint-config-next": "13.4.2",
63
78
  "eslint-plugin-css": "^0.8.0",
@@ -66,21 +81,18 @@
66
81
  "jss": "^10.10.0",
67
82
  "jss-preset-default": "^10.10.0",
68
83
  "next": "^13.4.2",
84
+ "postcss": "^8.4.23",
69
85
  "react": "^18.2.0",
70
86
  "react-dom": "^18.2.0",
71
87
  "sass": "^1.62.1",
72
- "storybook": "^7.0.12",
88
+ "storybook": "^7.0.18",
73
89
  "storybook-dark-mode": "^3.0.0",
74
90
  "title-case": "^3.0.3",
75
91
  "ts-node": "^10.9.1",
76
92
  "tsconfig-paths-webpack-plugin": "^4.0.1",
77
- "tsup": "^6.7.0",
78
93
  "type-fest": "^3.10.0",
79
94
  "typescript": "^5.0.2"
80
95
  },
81
- "dependencies": {
82
- "pte": "^0.4.7"
83
- },
84
96
  "scripts": {
85
97
  "dev": "next dev",
86
98
  "build": "next build",
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from 'react';
2
+ import { isValidElement, memo } from 'react';
3
+ import type { Enhancer } from '../types/Enhancer';
4
+
5
+ export const renderEnhancer = (enhancer: Enhancer, size: number): ReactNode => {
6
+ if (isValidElement(enhancer)) {
7
+ return enhancer;
8
+ }
9
+
10
+ if (typeof enhancer === 'function') {
11
+ return enhancer({ size });
12
+ }
13
+
14
+ return enhancer;
15
+ };
16
+
17
+ export const MemoizedEnhancer = memo<{ enhancer: Enhancer, size: number }>(({ enhancer, size }) => (
18
+ <>
19
+ { renderEnhancer(enhancer, size)}
20
+ </>
21
+ ), (prev, next) => prev.enhancer === next.enhancer && prev.size === next.size);
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable react/jsx-props-no-spreading */
2
- import '@/styles/globals.css';
2
+ import 'src/styles/globals.css';
3
3
  import type { AppProps } from 'next/app';
4
4
 
5
5
  export default function App({ Component, pageProps }: AppProps) {
@@ -1,7 +1,7 @@
1
1
  import Head from 'next/head';
2
2
  import Image from 'next/image';
3
3
  import { Inter } from 'next/font/google';
4
- import styles from '@/styles/Home.module.css';
4
+ import styles from 'src/styles/Home.module.css';
5
5
 
6
6
  const inter = Inter({ subsets: ['latin'] });
7
7
 
@@ -0,0 +1,73 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ <Meta title="Surfaces/Pagination" />
4
+
5
+ # Paginating Drawers
6
+
7
+ Paris contains a mechanism for creating pagination within a single surface. This is useful for complex or multi-step processes that would otherwise require a lot of scrolling or complicate the UI.
8
+
9
+ > This API currently supports `Drawer`, and will soon be expanded to support pagination of any surface.
10
+
11
+ ## Usage
12
+
13
+ Paris provides a hook for pagination called `usePagination`. This hook functions as a router that allows you to control pagination state and navigate between pages.
14
+
15
+ To enable pagination:
16
+
17
+ 1. Instantiate a pagination router using `usePagination`.
18
+ 2. Pass the pagination object to the Drawer using the `pagination` prop.
19
+ 3. Pass your pages as children to the Drawer. Each must have a key that is unique across all pages, which can then be passed to `pagination.open` to navigate to that page.
20
+
21
+ The pagination object returned by `usePagination` has the following properties:
22
+
23
+ - `open`: A function that takes a page key and navigates to that page.
24
+ - `currentPage`: The key of the current page.
25
+ - `history`: An array of page keys that are currently in the navigable history stack.
26
+ - `back`: A function that navigates to the previous page.
27
+ - `forward`: A function that navigates to the next page.
28
+ - `canGoBack`: A boolean indicating whether the user can go back.
29
+ - `canGoForward`: A boolean indicating whether the user can go forward.
30
+
31
+ The hook creates a shared state object between your parent component and the drawer. This means that you can use the `pagination` object's methods to control the drawer from your parent component, or from pages passed to the drawer.
32
+
33
+ ## Example
34
+
35
+ ```tsx
36
+ import { Drawer } from 'paris/drawer';
37
+ import { Button } from 'paris/button';
38
+ import { usePagination } from 'paris/pagination';
39
+
40
+ const MyComponent = () => {
41
+ const pages = ['page1', 'page2', 'page3'] as const;
42
+
43
+ const pagination = usePagination<typeof pages>('step1');
44
+
45
+ return (
46
+ <Drawer
47
+ title="User creation flow"
48
+ open={true}
49
+ onClose={() => {}}
50
+ // Pass the entire pagination object to the Drawer to enable pagination
51
+ pagination={pagination}
52
+ >
53
+ <div key={pages[0]}>
54
+ Page 1
55
+ <Button
56
+ onClick={() => {
57
+ // Navigate to the next page using `open`
58
+ pagination.open('page2');
59
+ }}
60
+ >
61
+ Next Page
62
+ </Button>
63
+ </div>
64
+ <div key={pages[1]}>
65
+ Page 2
66
+ </div>
67
+ <div key={pages[2]}>
68
+ Page 3
69
+ </div>
70
+ </Drawer>
71
+ );
72
+ };
73
+ ```
@@ -44,11 +44,3 @@ Tokens are the smallest pieces of design language that can be used to build comp
44
44
  ))}
45
45
  </div>
46
46
 
47
- <br />
48
-
49
- ## Typography
50
-
51
- <div>
52
- <pre style={{ color: pvar('colors.contentPrimary') }}>{JSON.stringify(LightTheme.typography, null, 4).replaceAll('"', '').replaceAll(',', '\n').replaceAll('{', '\n').replaceAll('}', '\n').replaceAll('\\', '')}</pre>
53
- </div>
54
-
@@ -1,6 +1,8 @@
1
- // Main styles
1
+ /*
2
+ * Main styles
3
+ */
2
4
  .button {
3
- user-select: none;
5
+ user-select: var(--pte-utils-defaultUserSelect);
4
6
 
5
7
  border: 1px solid;
6
8
 
@@ -12,17 +14,21 @@
12
14
 
13
15
  transition: var(--pte-animations-interaction);
14
16
 
17
+ & > span {
18
+ margin-block-end: var(--pte-typography-verticalMetricsAdjust);
19
+ }
20
+
15
21
  &:hover {
16
22
  cursor: default;
17
23
  background-color: var(--pte-colors-backgroundInverseTertiary);
18
24
  }
19
25
 
20
- &:active {
26
+ &[aria-disabled=false]:active {
21
27
  opacity: 0.9;
22
28
  transform: scale(0.98);
23
29
  }
24
30
 
25
- &[disabled] {
31
+ &[aria-disabled=true] {
26
32
  pointer-events: none;
27
33
  }
28
34
  }
@@ -41,7 +47,7 @@
41
47
  border-color: var(--pte-colors-backgroundInverseTertiary);
42
48
  }
43
49
 
44
- &[disabled] {
50
+ &[aria-disabled=true] {
45
51
  color: var(--pte-colors-contentDisabled);
46
52
  background-color: var(--pte-colors-backgroundTertiary);
47
53
  border-color: var(--pte-colors-backgroundTertiary);
@@ -53,7 +59,7 @@
53
59
  background-color: transparent;
54
60
  border-color: var(--pte-colors-contentPrimary);
55
61
 
56
- &[disabled] {
62
+ &[aria-disabled=true] {
57
63
  color: var(--pte-colors-contentDisabled);
58
64
  border-color: var(--pte-colors-contentDisabled);
59
65
  }
@@ -70,7 +76,7 @@
70
76
  background-color: var(--pte-colors-backgroundTertiary);
71
77
  }
72
78
 
73
- &[disabled] {
79
+ &[aria-disabled=true] {
74
80
  color: var(--pte-colors-contentDisabled);
75
81
  }
76
82
  }
@@ -90,6 +96,11 @@
90
96
  padding: 4px 15px;
91
97
  }
92
98
 
99
+ .xs {
100
+ height: 16px;
101
+ padding: 2px 8px;
102
+ }
103
+
93
104
 
94
105
  /*
95
106
  * SHAPE
@@ -125,4 +136,9 @@
125
136
  width: 20px;
126
137
  padding: 4px;
127
138
  }
139
+
140
+ &.xs {
141
+ width: 16px;
142
+ padding: 2px;
143
+ }
128
144
  }
@@ -1,4 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
+ import { createElement } from 'react';
3
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4
+ import { faPlus } from '@fortawesome/free-solid-svg-icons';
2
5
  import { Button } from './Button';
3
6
 
4
7
  const meta: Meta<typeof Button> = {
@@ -40,3 +43,17 @@ export const Tertiary: Story = {
40
43
  kind: 'tertiary',
41
44
  },
42
45
  };
46
+
47
+ /**
48
+ * Enhancers can be used to add icons at the start (left) or end (right) of a button.
49
+ */
50
+ export const WithEnhancer: Story = {
51
+ args: {
52
+ children: 'Button',
53
+ kind: 'primary',
54
+ startEnhancer: ({ size }) => createElement(FontAwesomeIcon, {
55
+ icon: faPlus,
56
+ width: `${size}px`,
57
+ }),
58
+ },
59
+ };
@@ -1,10 +1,21 @@
1
1
  'use client';
2
2
 
3
3
  import type {
4
- ButtonHTMLAttributes, FC, MouseEventHandler, ReactNode,
4
+ FC, HTMLAttributeAnchorTarget, MouseEventHandler, ReactNode,
5
5
  } from 'react';
6
+ import type { ButtonProps as AriaButtonProps } from '@ariakit/react';
7
+ import { Button as AriaButton } from '@ariakit/react';
8
+ import clsx from 'clsx';
6
9
  import styles from './Button.module.scss';
7
10
  import { Text } from '../text';
11
+ import type { Enhancer } from '../../types/Enhancer';
12
+ import { MemoizedEnhancer, renderEnhancer } from '../../helpers/renderEnhancer';
13
+
14
+ const EnhancerSizes = {
15
+ large: 13,
16
+ small: 11,
17
+ xs: 9,
18
+ };
8
19
 
9
20
  export type ButtonProps = {
10
21
  /**
@@ -16,7 +27,7 @@ export type ButtonProps = {
16
27
  * The size of the Button.
17
28
  * @default large
18
29
  */
19
- size?: 'large' | 'small';
30
+ size?: 'large' | 'small' | 'xs';
20
31
  /**
21
32
  * The shape of the Button.
22
33
  * @default pill
@@ -27,11 +38,11 @@ export type ButtonProps = {
27
38
  *
28
39
  * When Button shape is `circle` or `square`, this element will be the only visible content.
29
40
  */
30
- startEnhancer?: (size: number) => ReactNode;
41
+ startEnhancer?: Enhancer;
31
42
  /**
32
43
  * An icon or other element to render after the Button's text. A `size` argument is passed that should be used to determine the width & height of the content displayed.
33
44
  */
34
- endEnhancer?: (size: number) => ReactNode;
45
+ endEnhancer?: Enhancer;
35
46
  /**
36
47
  * Disables the Button, disallowing user interaction.
37
48
  * @default false
@@ -41,41 +52,90 @@ export type ButtonProps = {
41
52
  * The interaction handler for the Button.
42
53
  */
43
54
  onClick?: MouseEventHandler<HTMLButtonElement>;
55
+ /**
56
+ * Optionally, the Button can be rendered as an anchor element by passing an `href` prop. To use a Next.js Link component, use the `render` prop directly.
57
+ */
58
+ href?: string;
59
+ /**
60
+ * Optionally, the target of the anchor element can be specified (defaults to `_self`).
61
+ */
62
+ hrefTarget?: HTMLAttributeAnchorTarget;
44
63
  /**
45
64
  * The contents of the Button.
46
65
  *
47
66
  * This should be text. When Button shape is `circle` or `square`, the action description should still be passed here for screen readers.
48
67
  */
49
68
  children: string;
50
- } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'disabled' | 'onClick'>;
69
+ } & Omit<AriaButtonProps, 'children' | 'disabled' | 'onClick'>;
51
70
 
52
71
  /**
53
72
  * A `Button` is used to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation.
54
73
  *
74
+ * <hr />
75
+ *
76
+ * To use the `Button` component, import it as follows:
77
+ *
78
+ * ```js
79
+ * import { Button } from 'paris/button';
80
+ * ```
81
+ *
55
82
  * @constructor
56
83
  */
57
84
  export const Button: FC<ButtonProps> = ({
58
- kind,
59
- size,
60
- shape,
61
- type,
85
+ kind = 'primary',
86
+ size = 'large',
87
+ shape = 'pill',
88
+ type = 'button',
62
89
  startEnhancer,
63
90
  endEnhancer,
64
91
  onClick,
65
92
  children,
93
+ disabled,
94
+ href,
66
95
  ...props
67
96
  }) => (
68
- <button
69
- className={`${props.className} ${styles[kind || 'primary']} ${styles[shape || 'pill']} ${styles[size || 'large']} ${styles.button}`}
70
- type={type || 'button'}
71
- aria-details={children}
72
- onClick={onClick}
97
+ <AriaButton
73
98
  {...props}
99
+ className={clsx(
100
+ styles.button,
101
+ styles[kind],
102
+ styles[shape],
103
+ styles[size],
104
+ props?.className,
105
+ )}
106
+ aria-disabled={disabled ?? false}
107
+ type={type}
108
+ aria-details={children}
109
+ onClick={!disabled && !href ? onClick : () => {}}
110
+ disabled={false}
111
+ {...href ? {
112
+ render: (properties) => (
113
+ // eslint-disable-next-line jsx-a11y/anchor-has-content
114
+ <a
115
+ {...properties}
116
+ href={href}
117
+ target={props.hrefTarget ?? '_self'}
118
+ rel={props.hrefTarget === '_self' ? undefined : 'noreferrer'}
119
+ />
120
+ ),
121
+ } : {}}
74
122
  >
75
- {!!startEnhancer && startEnhancer(size === 'large' ? 16 : 12)}
76
- <Text kind="labelXSmall">
77
- {children || 'Button'}
78
- </Text>
79
- {!!endEnhancer && endEnhancer(size === 'large' ? 16 : 12)}
80
- </button>
123
+ {!!startEnhancer && (
124
+ <MemoizedEnhancer
125
+ enhancer={startEnhancer}
126
+ size={EnhancerSizes[size]}
127
+ />
128
+ )}
129
+ {!['circle', 'square'].includes(shape) && (
130
+ <Text kind="labelXSmall">
131
+ {children || 'Button'}
132
+ </Text>
133
+ )}
134
+ {!!endEnhancer && (
135
+ <MemoizedEnhancer
136
+ enhancer={endEnhancer}
137
+ size={EnhancerSizes[size]}
138
+ />
139
+ )}
140
+ </AriaButton>
81
141
  );
@@ -0,0 +1,14 @@
1
+ .container {
2
+ border: 1px solid var(--pte-borders-dropdown-color);
3
+ border-radius: var(--pte-borders-radius-rounded);
4
+ box-shadow: var(--pte-lighting-subtlePopup);
5
+
6
+ user-select: var(--pte-utils-defaultUserSelect);
7
+ width: max-content;
8
+ padding: 16px;
9
+
10
+ &.flat {
11
+ box-shadow: none;
12
+ border-color: var(--pte-colors-borderOpaque);
13
+ }
14
+ }
@@ -0,0 +1,33 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Card } from './Card';
3
+
4
+ const meta: Meta<typeof Card> = {
5
+ title: 'Surfaces/Card',
6
+ component: Card,
7
+ tags: ['autodocs'],
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof Card>;
12
+
13
+ /**
14
+ * By default, the card has a shadow and parallax tilt effect. You may want to adjust the max tilt angles through the `overrides.tilt.tiltMaxAngle(X|Y)` props to provide a good experience depending on the size of the content. See the [Tilt component](/docs/surfaces-tilt--docs) for all available props.
15
+ */
16
+ export const Default: Story = {
17
+ args: {
18
+ children: 'Revenue: $3000',
19
+ },
20
+ };
21
+
22
+ /**
23
+ * Setting the `kind` prop to `flat` will remove the shadow from the card.
24
+ *
25
+ * This example also disables the `tilt` effect by setting the `tilt` prop to `false`, but by default the `tilt` prop will remain set to `true`.
26
+ */
27
+ export const Flat: Story = {
28
+ args: {
29
+ kind: 'flat',
30
+ tilt: false,
31
+ children: 'Revenue: $3000',
32
+ },
33
+ };