@kaizen/components 0.0.0-canary-package-bundler-v2-20241113071536 → 0.0.0-canary-link-button-wip-canary-20241121043208

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 (102) hide show
  1. package/codemods/README.md +16 -1
  2. package/codemods/upgradeIconV1/getNewIconPropsFromOldIconName.ts +9 -9
  3. package/dist/cjs/EmptyState/EmptyState.cjs +15 -17
  4. package/dist/cjs/EmptyState/EmptyState.module.css.cjs +20 -0
  5. package/dist/cjs/GuidanceBlock/GuidanceBlock.cjs +1 -1
  6. package/dist/cjs/GuidanceBlock/GuidanceBlock.module.css.cjs +28 -0
  7. package/dist/cjs/__actions__/Button/v3/Button.cjs +43 -8
  8. package/dist/cjs/__actions__/Button/v3/Button.module.css.cjs +21 -0
  9. package/dist/cjs/__actions__/Button/v3/subcomponents/ButtonContent/ButtonContent.cjs +37 -0
  10. package/dist/cjs/__actions__/Button/v3/subcomponents/ButtonContent/ButtonContent.module.css.cjs +9 -0
  11. package/dist/cjs/__actions__/Button/v3/subcomponents/PendingContent/PendingContent.cjs +35 -0
  12. package/dist/cjs/__actions__/Button/v3/subcomponents/PendingContent/PendingContent.module.css.cjs +8 -0
  13. package/dist/cjs/__actions__/LinkButton/v3/LinkButton.cjs +56 -0
  14. package/dist/cjs/__actions__/LinkButton/v3/LinkButton.module.css.cjs +6 -0
  15. package/dist/cjs/__overlays__/Tooltip/v3/Tooltip.cjs +2 -2
  16. package/dist/cjs/actionsV3.cjs +2 -0
  17. package/dist/esm/Calendar/CalendarPopover/CalendarPopover.mjs +1 -1
  18. package/dist/esm/EmptyState/EmptyState.mjs +16 -18
  19. package/dist/esm/EmptyState/EmptyState.module.css.mjs +18 -0
  20. package/dist/esm/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.mjs +1 -1
  21. package/dist/esm/GuidanceBlock/GuidanceBlock.mjs +1 -1
  22. package/dist/esm/GuidanceBlock/GuidanceBlock.module.css.mjs +26 -0
  23. package/dist/esm/MultiSelect/subcomponents/Popover/Popover.mjs +1 -1
  24. package/dist/esm/RichTextEditor/RichTextEditor/RichTextEditor.mjs +1 -1
  25. package/dist/esm/RichTextEditor/RichTextEditor/schema.mjs +1 -1
  26. package/dist/esm/RichTextEditor/utils/schema/nodes.mjs +1 -1
  27. package/dist/esm/TimeField/TimeField.mjs +1 -1
  28. package/dist/esm/__actions__/Button/v3/Button.mjs +44 -9
  29. package/dist/esm/__actions__/Button/v3/Button.module.css.mjs +19 -0
  30. package/dist/esm/__actions__/Button/v3/subcomponents/ButtonContent/ButtonContent.mjs +28 -0
  31. package/dist/esm/__actions__/Button/v3/subcomponents/ButtonContent/ButtonContent.module.css.mjs +7 -0
  32. package/dist/esm/__actions__/Button/v3/subcomponents/PendingContent/PendingContent.mjs +26 -0
  33. package/dist/esm/__actions__/Button/v3/subcomponents/PendingContent/PendingContent.module.css.mjs +6 -0
  34. package/dist/esm/__actions__/LinkButton/v3/LinkButton.mjs +47 -0
  35. package/dist/esm/__actions__/LinkButton/v3/LinkButton.module.css.mjs +4 -0
  36. package/dist/esm/__overlays__/Tooltip/v1/Tooltip.mjs +1 -1
  37. package/dist/esm/__overlays__/Tooltip/v3/Tooltip.mjs +1 -1
  38. package/dist/esm/actionsV3.mjs +1 -0
  39. package/dist/styles.css +552 -463
  40. package/dist/types/EmptyState/EmptyState.d.ts +2 -1
  41. package/dist/types/__actions__/Button/v3/Button.d.ts +17 -4
  42. package/dist/types/__actions__/Button/v3/index.d.ts +1 -0
  43. package/dist/types/__actions__/Button/v3/subcomponents/ButtonContent/ButtonContent.d.ts +11 -0
  44. package/dist/types/__actions__/Button/v3/subcomponents/ButtonContent/index.d.ts +1 -0
  45. package/dist/types/__actions__/Button/v3/subcomponents/PendingContent/PendingContent.d.ts +5 -0
  46. package/dist/types/__actions__/Button/v3/subcomponents/PendingContent/index.d.ts +1 -0
  47. package/dist/types/__actions__/Button/v3/subcomponents/index.d.ts +2 -0
  48. package/dist/types/__actions__/Button/v3/types.d.ts +21 -0
  49. package/dist/types/__actions__/LinkButton/index.d.ts +1 -0
  50. package/dist/types/__actions__/LinkButton/v3/LinkButton.d.ts +11 -0
  51. package/dist/types/__actions__/LinkButton/v3/index.d.ts +1 -0
  52. package/dist/types/__actions__/v3.d.ts +1 -0
  53. package/package.json +8 -8
  54. package/src/EmptyState/EmptyState.module.css +114 -0
  55. package/src/EmptyState/EmptyState.tsx +18 -20
  56. package/src/EmptyState/_docs/EmptyState.stickersheet.stories.tsx +55 -39
  57. package/src/GuidanceBlock/{GuidanceBlock.module.scss → GuidanceBlock.module.css} +60 -114
  58. package/src/GuidanceBlock/GuidanceBlock.tsx +1 -1
  59. package/src/__actions__/Button/v3/Button.module.css +235 -0
  60. package/src/__actions__/Button/v3/Button.tsx +95 -29
  61. package/src/__actions__/Button/v3/_docs/Button--api-specification.mdx +151 -0
  62. package/src/__actions__/Button/v3/_docs/Button--usage-guidelines.mdx +30 -0
  63. package/src/__actions__/Button/v3/_docs/Button.docs.stories.tsx +112 -50
  64. package/src/__actions__/Button/v3/_docs/Button.spec.stories.tsx +80 -120
  65. package/src/__actions__/Button/v3/_docs/Button.stickersheet.stories.tsx +183 -81
  66. package/src/__actions__/Button/v3/index.ts +1 -0
  67. package/src/__actions__/Button/v3/subcomponents/ButtonContent/ButtonContent.module.css +19 -0
  68. package/src/__actions__/Button/v3/subcomponents/ButtonContent/ButtonContent.tsx +40 -0
  69. package/src/__actions__/Button/v3/subcomponents/ButtonContent/index.ts +1 -0
  70. package/src/__actions__/Button/v3/subcomponents/PendingContent/PendingContent.module.css +16 -0
  71. package/src/__actions__/Button/v3/subcomponents/PendingContent/PendingContent.tsx +28 -0
  72. package/src/__actions__/Button/v3/subcomponents/PendingContent/index.ts +1 -0
  73. package/src/__actions__/Button/v3/subcomponents/index.ts +2 -0
  74. package/src/__actions__/Button/v3/types.ts +25 -0
  75. package/src/__actions__/LinkButton/index.ts +1 -0
  76. package/src/__actions__/LinkButton/v3/LinkButton.module.css +4 -0
  77. package/src/__actions__/LinkButton/v3/LinkButton.tsx +72 -0
  78. package/src/__actions__/LinkButton/v3/_docs/LinkButton--api-specification.mdx +210 -0
  79. package/src/__actions__/LinkButton/v3/_docs/LinkButton.doc.stories.tsx +132 -0
  80. package/src/__actions__/LinkButton/v3/_docs/LinkButton.spec.stories.tsx +100 -0
  81. package/src/__actions__/LinkButton/v3/_docs/LinkButton.stickersheet.stories.tsx +176 -0
  82. package/src/__actions__/LinkButton/v3/index.ts +1 -0
  83. package/src/__actions__/Menu/v3/_docs/Menu.docs.stories.tsx +54 -18
  84. package/src/__actions__/Menu/v3/_docs/Menu.spec.stories.tsx +30 -10
  85. package/src/__actions__/Menu/v3/_docs/Menu.stories.tsx +12 -4
  86. package/src/__actions__/v3.ts +1 -0
  87. package/src/__future__/Icon/_docs/Icon.docs.stories.tsx +7 -7
  88. package/src/__overlays__/Tooltip/v3/Tooltip.tsx +1 -1
  89. package/src/__overlays__/Tooltip/v3/_docs/Tooltip.spec.stories.tsx +2 -0
  90. package/dist/cjs/EmptyState/EmptyState.module.scss.cjs +0 -23
  91. package/dist/cjs/GuidanceBlock/GuidanceBlock.module.scss.cjs +0 -33
  92. package/dist/cjs/__actions__/Button/v3/Button.module.scss.cjs +0 -9
  93. package/dist/esm/EmptyState/EmptyState.module.scss.mjs +0 -21
  94. package/dist/esm/GuidanceBlock/GuidanceBlock.module.scss.mjs +0 -31
  95. package/dist/esm/__actions__/Button/v3/Button.module.scss.mjs +0 -7
  96. package/src/EmptyState/EmptyState.module.scss +0 -177
  97. package/src/EmptyState/EmptyState.spec.tsx +0 -48
  98. package/src/EmptyState/_mixins.scss +0 -44
  99. package/src/__actions__/Button/v3/Button.module.scss +0 -104
  100. package/src/__actions__/Button/v3/_docs/ApiSpecification.mdx +0 -173
  101. package/src/__actions__/Button/v3/_docs/Button.mdx +0 -41
  102. package/src/__actions__/Button/v3/_docs/Button.stories.tsx +0 -98
@@ -0,0 +1,25 @@
1
+ export type ButtonVariants = "primary" | "secondary" | "tertiary"
2
+
3
+ export type ButtonSizes = "small" | "medium" | "large"
4
+
5
+ export type PendingPropsUndefined = {
6
+ isPending?: undefined
7
+ /** Rendered as the child while `isPending` is `true`. This determines the accessible label for the Button while pending. */
8
+ pendingLabel?: never
9
+ /** Visually hides the `pendingLabel` and renders the loading spinner. This will maintain the width of the Button's `children` to avoid layout shifts.
10
+ * @default false
11
+ */
12
+ hasHiddenPendingLabel?: never
13
+ }
14
+
15
+ export type PendingProps = {
16
+ isPending: boolean
17
+ /** Rendered as the child while `pendingLabel` is `true`. This determines the accessible label for the Button while pending. */
18
+ pendingLabel: string
19
+ /** Visually Hides the `pendingLabel` and renders the loading spinner. This will maintain the width of the Button's `children` to avoid layout shifts.
20
+ * @default false
21
+ */
22
+ hasHiddenPendingLabel?: boolean
23
+ }
24
+
25
+ export type PendingButtonProps = PendingProps | PendingPropsUndefined
@@ -0,0 +1 @@
1
+ export * from "./v3"
@@ -0,0 +1,4 @@
1
+ .linkButton {
2
+ /* Reset */
3
+ text-decoration: inherit;
4
+ }
@@ -0,0 +1,72 @@
1
+ import React, { forwardRef } from "react"
2
+ import {
3
+ Link as RACLink,
4
+ LinkProps as RACLinkProps,
5
+ } from "react-aria-components"
6
+ import { ButtonBaseProps } from "~components/__actions__/Button/v3"
7
+ import buttonStyles from "~components/__actions__/Button/v3/Button.module.css"
8
+ import { ButtonContent } from "~components/__actions__/Button/v3/subcomponents"
9
+ import { useReversedColors } from "~components/__utilities__/v3"
10
+ import { mergeClassNames } from "~components/utils/mergeClassNames"
11
+ import styles from "./LinkButton.module.css"
12
+
13
+ export type LinkButtonProps = ButtonBaseProps &
14
+ Omit<RACLinkProps, "children"> & {
15
+ /** Used as the label for the LinkButton. */
16
+ children: RACLinkProps["children"]
17
+ }
18
+
19
+ export const LinkButton = forwardRef(
20
+ (
21
+ {
22
+ children,
23
+ variant = "primary",
24
+ size = "medium",
25
+ icon,
26
+ iconPosition = "start",
27
+ hasHiddenLabel = false,
28
+ isFullWidth = false,
29
+ isDisabled,
30
+ className,
31
+ ...otherProps
32
+ }: LinkButtonProps,
33
+ ref: React.ForwardedRef<HTMLAnchorElement>
34
+ ) => {
35
+ const isReversed = useReversedColors()
36
+
37
+ return (
38
+ <RACLink
39
+ ref={ref}
40
+ className={mergeClassNames(
41
+ styles.linkButton,
42
+ buttonStyles.button,
43
+ buttonStyles[size],
44
+ hasHiddenLabel && buttonStyles[`${size}IconButton`],
45
+ isDisabled && buttonStyles.isDisabled,
46
+ isReversed
47
+ ? buttonStyles[`${variant}Reversed`]
48
+ : buttonStyles[variant],
49
+ isFullWidth && buttonStyles.fullWidth,
50
+ className
51
+ )}
52
+ isDisabled={isDisabled}
53
+ {...otherProps}
54
+ >
55
+ {racStateProps => {
56
+ const childIsFunction = typeof children === "function"
57
+
58
+ return (
59
+ <ButtonContent
60
+ size={size}
61
+ icon={icon}
62
+ iconPosition={iconPosition}
63
+ hasHiddenLabel={hasHiddenLabel}
64
+ >
65
+ {childIsFunction ? children(racStateProps) : children}
66
+ </ButtonContent>
67
+ )
68
+ }}
69
+ </RACLink>
70
+ )
71
+ }
72
+ )
@@ -0,0 +1,210 @@
1
+ import { Canvas, Meta, Controls, ArgTypes, DocsStory } from "@storybook/blocks"
2
+ import { ResourceLinks, KAIOInstallation } from "~storybook/components"
3
+ import { LinkTo } from "../../../../../../../docs/components/LinkTo"
4
+ import * as exampleStories from "./LinkButton.doc.stories"
5
+
6
+ <Meta title="Actions/LinkButton/LinkButton (v3)/API Specification" />
7
+
8
+ # Button API Specification (v3)
9
+
10
+ Updated Nov 12, 2024
11
+
12
+ <ResourceLinks
13
+ sourceCode="https://github.com/cultureamp/kaizen-design-system/tree/main/packages/components/src/__actions__/LinkButton/v3"
14
+ figma="https://www.figma.com/design/eZKEE5kXbEMY3lx84oz8iN/branch/sPhYSlgPScLOAOYfAbkaI5/%F0%9F%92%9C-Heart-UI-Kit?node-id=1929-17364&node-type=canvas&m=dev"
15
+ designGuidelines="/?path=/docs/actions-linkbutton-linkbutton-v3-usage-guidelines--docs"
16
+ />
17
+
18
+ <KAIOInstallation exportNames={["LinkButton" ]} family="actions" version="3" />
19
+
20
+ ## Overview
21
+
22
+ `LinkButton` allows users to navigate to another page or resource within a web page or application.
23
+
24
+ The following example and table showcases the essential props that enable the core functionality of `LinkButton`. For the remaining suite of API options refer to [this section](#additional-api-options).
25
+
26
+ <Canvas of={exampleStories.Playground} />
27
+
28
+ <Controls of={exampleStories.Playground} include={["className", "children", "href", "target", "download", "onPress", "routerOptions", "hasHiddenLabel", "size", "variant", "icon", "iconPosition", "isFullWidth", "isDisabled" ]} />
29
+
30
+ ## API
31
+
32
+ This is built on top of [React Aria's Link component](https://react-spectrum.adobe.com/react-aria/Link.html) and is the counterpart to the <LinkTo pageId="actions-button-button-v3-api-specification--docs">Kaizen Button</LinkTo>, handling icons, variants and sizes in the same way. It provides a semantic wrapper for navigational buttons and allows for native `href` navigation and client side routing with [additional configuration](#client-side-routing) .
33
+
34
+ ### Navigation and routing
35
+
36
+ {/* // TODO: basic docs on href usage */}
37
+
38
+
39
+ ### Client side routing
40
+
41
+ While the `LinkButton` supports native `href` navigation out of the box, it is agnostic to the routing technology chosen. As such, additional set up is required to support client side routing from `React Router` or `Next.js`.
42
+
43
+ In order to achieve this, you can use the [RouterProvider](https://react-spectrum.adobe.com/react-aria/routing.html#routerprovider) from the `react-aria-components` library, along with the additional configuration for each routing solution.
44
+
45
+ #### Next.js config example
46
+
47
+ The following example demonstrates how to use the `RouterProvider` with `Next.js`'s Pages router. This will allow the `LinkButton` to navigate using the `router.push` method.
48
+
49
+ ```tsx
50
+ import type {AppProps} from "next/app"
51
+ import {type NextRouter, useRouter} from "next/router"
52
+ import { RouterProvider } from "react-aria-components"
53
+
54
+ // This provides the necessary typings for the routerOptions to uses of LinkButton across the app
55
+ declare module "react-aria-components" {
56
+ interface RouterConfig {
57
+ routerOptions: NonNullable<Parameters<NextRouter["push"]>[2]>;
58
+ }
59
+ }
60
+
61
+ export default function NextPageRouterApp({ Component, pageProps }: AppProps) {
62
+ const router = useRouter()
63
+
64
+ return (
65
+ <RouterProvider
66
+ navigate={(href, opts) => router.push(href, undefined, opts)}
67
+ >
68
+ {/* ...application code */}
69
+ </RouterProvider>
70
+ )
71
+ }
72
+ ```
73
+
74
+ Additional notes on Next.js config can be found in the React Aria's documentation on the [RouterProvider](https://react-spectrum.adobe.com/react-aria/routing.html#routerprovider), including the alternative setup for the [App router](https://react-spectrum.adobe.com/react-aria/routing.html##app-router).
75
+
76
+ #### React Router config example
77
+ The following example demonstrates how to use the `RouterProvider` with `React Router`'s. This will allow the `LinkButton` to navigate using the `useNavigate` hook.
78
+
79
+ ```tsx
80
+ import { RouterProvider } from "react-aria-components"
81
+ import { BrowserRouter, NavigateOptions, useHref, useNavigate } from "react-router-dom"
82
+
83
+ declare module "react-aria-components" {
84
+ interface RouterConfig {
85
+ routerOptions: NavigateOptions
86
+ }
87
+ }
88
+
89
+ function ReactRouterApp() {
90
+ const navigate = useNavigate()
91
+
92
+ return (
93
+ <RouterProvider navigate={navigate} useHref={useHref}>
94
+ {/* ...application code */}
95
+ <Routes>
96
+ <Route path="/" element={<HomePage />} />
97
+ {/* ...routes */}
98
+ </Routes>
99
+ </RouterProvider>
100
+ )
101
+ }
102
+ ```
103
+
104
+ Additional notes can be found in the React Aria's documentation on the using the `RouterProvider` with [React Router](https://react-spectrum.adobe.com/react-aria/routing.html#react-router).
105
+
106
+ ### Target `_blank` and opening in a new tab
107
+
108
+ The general recommendation is to limit the number of links that open in a new tab or window on a page. While there are valid scenarios that help avoid loss of data and or progress on a page, opening a new tab or window can be disorienting for users, especially those who have difficulty perceiving visual content.
109
+
110
+ In order to provide advance warning to all users, it is recommended that `LinkButton`'s that use `target="_blank"` be accompanied by a visual indicator and audible warning, as in the example below.
111
+
112
+ <Canvas of={exampleStories.LinkButtonOpensInNewTab} />
113
+
114
+ You can get more context on this recommendation via the [W3C page on the G200 success criteria](https://www.w3.org/TR/WCAG20-TECHS/G200.html).
115
+
116
+ ### Variants
117
+
118
+ `LinkButton` supports the following variants: `primary`, `secondary` and `tertiary`. If the `variant` prop is not specified, the default will be `primary`.
119
+
120
+ <Canvas of={exampleStories.LinkButtonVariants} />
121
+
122
+ Reversed variants are handled via the `ReversedColors` Provider.
123
+
124
+ <DocsStory of={exampleStories.LinkButtonVariantsReversed} expanded={false} />
125
+
126
+ To enable the reversed theme, you will need to wrap the component or application in the `ReversedColors` provider, ie:
127
+
128
+ ```tsx
129
+ import { RouterProvider } from "react-aria-components"
130
+ import { Button } from "@kaizen/components/v3/actions"
131
+ import { ReversedColors } from "@kaizen/components/v3/utilities"
132
+ // application code
133
+
134
+ return (
135
+ <ReversedColors isReversed={true}>
136
+ <LinkButton {...LinkbuttonProps} />
137
+ </ReversedColors>
138
+ )
139
+ ```
140
+
141
+ ### Sizes
142
+
143
+ LinkButton supports the following sizes: `small`, `medium` and `large`. If the `size` prop is not specified, the default will be `medium`.
144
+
145
+ <Canvas of={exampleStories.LinkButtonSizes} />
146
+
147
+ ### `onPress`
148
+
149
+ TODO: revamp this section: One key change to the `LinkButton`&apos;s API is the shift from `onClick` to React Aria's implementation of `onPress`. This approach has been adopted as it provides better support for consistent touch events across device types, such mobile, desktop and tablet. You can read more about the development and reason behind this pattern [here](https://react-spectrum.adobe.com/blog/building-a-button-part-1.html#touch-interactions).
150
+
151
+ Functionally this does not change the way we pass actions into `LinkButton`. Consumers can safely replace `onClick` with `onPress` without any additional changes, ie:
152
+
153
+ ```tsx
154
+ <LinkButton label="Submit" onClick={e => sumbit(e)}/>
155
+ ```
156
+
157
+ Can safely be replaced with the following:
158
+
159
+ ```tsx
160
+ <LinkButton onPress={e => submit(e)}>
161
+ Submit
162
+ </LinkButton>
163
+ ```
164
+
165
+
166
+ ### Button content and children
167
+
168
+ Labels and any button content can be passed into the `LinkButton` as `children`. Content wrapped by the `LinkButton` will be spaced evenly relative to the `size` prop.
169
+
170
+ ```tsx
171
+ <LinkButton variant="secondary" href="#link">
172
+ Label
173
+ </LinkButton>
174
+ ```
175
+
176
+ While in most cases, `children` will be a `ReactNode`, `LinkButton` also accepts a render function with React Aria's `LinkRenderProps`. This allows for more advanced styling and rendering options by hooking into React Aria's internal button state. You can read more about this [here](https://react-spectrum.adobe.com/react-aria/Link.html#styling).
177
+
178
+ ### Icons and positioning
179
+
180
+ The `icon` property abstracts the need to handle positioning and sizing logic for icons within the `LinkButton`. When paired with the [Icon component](/docs/illustrations-icon-icon-future-api-specification--docs), this will scale the icon to the `LinkButton`'s `size` prop.
181
+
182
+ <Canvas of={exampleStories.LinkButtonWithIconStart} />
183
+
184
+ Set the position of the icon using the `iconPosition` prop. This will ensure content is flipped in `RTL` layouts. Note that icons will need the [shouldMirrorInRTL](/docs/illustrations-icon-icon-future-api-specification--docs#mirror-in-rtl) prop set to `true` when mirroring is required.
185
+
186
+ <Canvas of={exampleStories.LinkButtonWithIconEnd} />
187
+
188
+
189
+ ### Icon-only `LinkButton` and `hasHiddenLabel`
190
+
191
+ To achieve an icon-only `LinkButton` (previously: `IconButton`) use the `icon` prop and set `hasHiddenLabel` to `true`. This will visually hide the button's `children`, while still announcing the content to screen readers.
192
+
193
+ <Canvas of={exampleStories.IconLinkButton} />
194
+
195
+ This pattern ensures that the `LinkButton`'s accessible name is determined by its children, which helps to announce relevant content to the screen readers. You can learn more about this [accessible pattern here](https://cultureamp.atlassian.net/wiki/spaces/PA/pages/3833331831/Accessible+button+and+link+labels).
196
+
197
+
198
+ ### Full width LinkButtons
199
+
200
+ If a `LinkButton` is statically the full width of a container you can use the `isFullWidth` property.
201
+
202
+ <Canvas of={exampleStories.LinkButtonFullWidth} />
203
+
204
+ For resizing on smaller screens, consider using the `className` prop to leverage CSS media or container queries, ie: `<LinkButton className="w-full md:w-[initial]">Label</LinkButton>`.
205
+
206
+ ## Additional API options
207
+
208
+ The following table is a collection of additional React Aria and native HTML props that are exposed from the [React Aria Link API](https://react-spectrum.adobe.com/react-aria/Link.html). These are not required for the implementation of `LinkButton` but can be used to extend its functionality. Refer back to the [overview section](#overview) for the core props that enable most use cases.
209
+
210
+ <ArgTypes of={exampleStories.Playground} exclude={["className", "children", "href", "target", "download", "routerOptions", "hasHiddenLabel", "size", "variant", "onPress", "icon", "iconPosition", "isFullWidth", "isDisabled" ]}/>
@@ -0,0 +1,132 @@
1
+ import React from "react"
2
+ import { Meta, StoryObj } from "@storybook/react"
3
+ import { VisuallyHidden } from "~components/VisuallyHidden"
4
+ import { Icon } from "~components/__future__"
5
+ import { ReversedColors } from "~components/__utilities__/v3"
6
+ import { LinkButton } from "../index"
7
+
8
+ const meta = {
9
+ title: "Actions/LinkButton/LinkButton (v3)",
10
+ component: LinkButton,
11
+ args: {
12
+ children: "Label",
13
+ href: "#link-button-clicked",
14
+ target: "_blank",
15
+ },
16
+ } satisfies Meta<typeof LinkButton>
17
+
18
+ export default meta
19
+
20
+ type Story = StoryObj<typeof meta>
21
+
22
+ export const Playground: Story = {}
23
+
24
+ export const LinkButtonOpensInNewTab: Story = {
25
+ args: {
26
+ children: (
27
+ <>
28
+ Label
29
+ <VisuallyHidden> opens a new tab</VisuallyHidden>
30
+ </>
31
+ ),
32
+ href: "https://www.google.com",
33
+ target: "_blank",
34
+ icon: <Icon isPresentational name="open_in_new" shouldMirrorInRTL />,
35
+ iconPosition: "end",
36
+ },
37
+ }
38
+
39
+ export const LinkButtonVariants: Story = {
40
+ render: args => (
41
+ <>
42
+ <LinkButton {...args} variant="primary" />
43
+ <LinkButton {...args} variant="secondary" />
44
+ <LinkButton {...args} variant="tertiary" />
45
+ </>
46
+ ),
47
+ decorators: [
48
+ Story => (
49
+ <div className="flex gap-8">
50
+ <Story />
51
+ </div>
52
+ ),
53
+ ],
54
+ }
55
+
56
+ export const LinkButtonVariantsReversed: Story = {
57
+ render: args => (
58
+ <ReversedColors isReversed={true}>
59
+ <LinkButton {...args} variant="primary" />
60
+ <LinkButton {...args} variant="secondary" />
61
+ <LinkButton {...args} variant="tertiary" />
62
+ </ReversedColors>
63
+ ),
64
+ parameters: {
65
+ reverseColors: true,
66
+ },
67
+ decorators: [
68
+ Story => (
69
+ <div className="flex gap-8 bg-purple-700 p-16">
70
+ <Story />
71
+ </div>
72
+ ),
73
+ ],
74
+ }
75
+
76
+ export const LinkButtonSizes: Story = {
77
+ render: args => (
78
+ <>
79
+ <LinkButton {...args} size="small" />
80
+ <LinkButton {...args} size="medium" />
81
+ <LinkButton {...args} size="large" />
82
+ </>
83
+ ),
84
+ decorators: [
85
+ Story => (
86
+ <div className="[&>*]:ms-8">
87
+ <Story />
88
+ </div>
89
+ ),
90
+ ],
91
+ }
92
+
93
+ export const LinkButtonWithIconStart: Story = {
94
+ args: {
95
+ icon: <Icon isPresentational name="delete" />,
96
+ },
97
+ }
98
+
99
+ export const LinkButtonWithIconEnd: Story = {
100
+ args: {
101
+ icon: <Icon isPresentational name="arrow_forward" shouldMirrorInRTL />,
102
+ iconPosition: "end",
103
+ },
104
+ }
105
+
106
+ export const IconLinkButton: Story = {
107
+ args: {
108
+ children: "Remove highlights from: May 8, 2024",
109
+ icon: <Icon isPresentational name="delete" />,
110
+ hasHiddenLabel: true,
111
+ },
112
+ }
113
+
114
+ export const ReversedLinkButton: Story = {
115
+ parameters: {
116
+ reverseColors: true,
117
+ docs: {
118
+ source: {
119
+ code: `<ReversedColors isReversed={true}>
120
+ <LinkButton>Label</LinkButton>
121
+ </ReversedColors>
122
+ `,
123
+ },
124
+ },
125
+ },
126
+ }
127
+
128
+ export const LinkButtonFullWidth: Story = {
129
+ args: {
130
+ isFullWidth: true,
131
+ },
132
+ }
@@ -0,0 +1,100 @@
1
+ import React from "react"
2
+ import { Meta, StoryObj } from "@storybook/react"
3
+ import { userEvent, waitFor, within, expect } from "@storybook/test"
4
+ import { VisuallyHidden } from "~components/VisuallyHidden"
5
+ import { Icon } from "~components/__future__/Icon"
6
+ import { LinkButton } from "../index"
7
+
8
+ const meta = {
9
+ title: "Actions/LinkButton/LinkButton (v3)/LinkButton (v3) tests",
10
+ component: LinkButton,
11
+ args: {
12
+ children: "Label",
13
+ },
14
+ } satisfies Meta<typeof LinkButton>
15
+
16
+ export default meta
17
+
18
+ type Story = StoryObj<typeof meta>
19
+
20
+ export const IconLinkButtonWithHiddenLabel: Story = {
21
+ args: {
22
+ hasHiddenLabel: true,
23
+ icon: <Icon name="add" isPresentational />,
24
+ children: (
25
+ <span>
26
+ Hidden label <span>is</span> <span>accessible</span>
27
+ </span>
28
+ ),
29
+ },
30
+ play: async ({ canvasElement }) => {
31
+ const canvas = within(canvasElement.parentElement!)
32
+ const linkButton = canvas.getByRole("link")
33
+
34
+ expect(linkButton).toHaveAccessibleName("Hidden label is accessible")
35
+ },
36
+ }
37
+
38
+ export const RACRenderPropsWithChildren: Story = {
39
+ render: ({ children: _, ...otherArgs }) => (
40
+ <LinkButton {...otherArgs}>
41
+ {({ isFocusVisible }) => (
42
+ <>
43
+ Label
44
+ <VisuallyHidden>
45
+ {isFocusVisible ? " is focused" : " is unfocused"}
46
+ </VisuallyHidden>
47
+ <Icon
48
+ name={isFocusVisible ? "thumb_up" : "thumb_down"}
49
+ isPresentational
50
+ isFilled={true}
51
+ className="ms-8 [--icon-size:16]"
52
+ />
53
+ </>
54
+ )}
55
+ </LinkButton>
56
+ ),
57
+ play: async ({ canvasElement, step }) => {
58
+ const canvas = within(canvasElement.parentElement!)
59
+ const linkButton = canvas.getByRole("link")
60
+
61
+ await step("link icon reflects unfocused state", async () => {
62
+ await waitFor(() =>
63
+ expect(linkButton).toHaveAccessibleName("Label is unfocused")
64
+ )
65
+ })
66
+
67
+ await step("focus on link and update icon", async () => {
68
+ await userEvent.tab()
69
+ await waitFor(() =>
70
+ expect(linkButton).toHaveAccessibleName("Label is focused")
71
+ )
72
+ })
73
+ },
74
+ }
75
+
76
+ export const RACRenderPropsWithClassName: Story = {
77
+ args: {
78
+ className: ({ isFocusVisible }) => (isFocusVisible ? "!bg-gray-300" : ""),
79
+ },
80
+ play: async ({ canvasElement }) => {
81
+ const canvas = within(canvasElement.parentElement!)
82
+ const linkButton = canvas.getByRole("link")
83
+ await linkButton.focus()
84
+ },
85
+ }
86
+
87
+ // Test cases:
88
+
89
+ /**
90
+ * Can route to a new page when clicked
91
+ *
92
+ * has accepts routing options from a client side router (will have to mock this)
93
+ *
94
+ * can have href and onPress at the same item
95
+ *
96
+ * can have href, onPress and download
97
+ *
98
+ * ping will send a post request with body ping to URL
99
+ *
100
+ */