@kaizen/components 1.72.0 → 1.73.1
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/bin/codemod.sh +2 -0
- package/codemods/README.md +46 -0
- package/codemods/upgradeV1Buttons/index.ts +19 -0
- package/codemods/upgradeV1Buttons/transformV1ButtonAttributes.spec.ts +202 -0
- package/codemods/upgradeV1Buttons/transformV1ButtonAttributes.ts +146 -0
- package/codemods/upgradeV1Buttons/upgradeV1Buttons.spec.ts +658 -0
- package/codemods/upgradeV1Buttons/upgradeV1Buttons.ts +93 -0
- package/codemods/utils/createJsxElementWithChildren.spec.ts +119 -0
- package/codemods/utils/createJsxElementWithChildren.ts +55 -0
- package/codemods/utils/createProp.spec.ts +75 -19
- package/codemods/utils/createProp.ts +8 -1
- package/codemods/utils/getKaioTagName.ts +13 -5
- package/codemods/utils/index.ts +1 -0
- package/dist/cjs/Link/Link.cjs +45 -0
- package/dist/cjs/Link/Link.module.css.cjs +20 -0
- package/dist/cjs/Link/subcomponents/LinkContent.cjs +34 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/utilitiesV3.cjs +2 -0
- package/dist/esm/Link/Link.mjs +40 -0
- package/dist/esm/Link/Link.module.css.mjs +18 -0
- package/dist/esm/Link/subcomponents/LinkContent.mjs +26 -0
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/utilitiesV3.mjs +1 -0
- package/dist/styles.css +120 -0
- package/dist/types/Link/Link.d.ts +39 -0
- package/dist/types/Link/index.d.ts +1 -0
- package/dist/types/Link/subcomponents/LinkContent.d.ts +8 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +3 -3
- package/src/Link/Link.module.css +119 -0
- package/src/Link/Link.tsx +90 -0
- package/src/Link/_docs/Link--api-specification.mdx +133 -0
- package/src/Link/_docs/Link--api-usage-guidelines.mdx +107 -0
- package/src/Link/_docs/Link.doc.stories.tsx +238 -0
- package/src/Link/_docs/Link.stickersheet.stories.tsx +191 -0
- package/src/Link/index.ts +1 -0
- package/src/Link/subcomponents/LinkContent.tsx +31 -0
- package/src/LinkButton/_docs/LinkButton--api-specification.mdx +1 -57
- package/src/__next__/Button/_docs/Button--migration-guide.mdx +81 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks'
|
|
2
|
+
import {
|
|
3
|
+
ResourceLinks,
|
|
4
|
+
KAIOInstallation,
|
|
5
|
+
LinkTo,
|
|
6
|
+
DosAndDonts,
|
|
7
|
+
DoOrDont,
|
|
8
|
+
} from '~storybook/components'
|
|
9
|
+
import * as Link from './Link.doc.stories'
|
|
10
|
+
|
|
11
|
+
<Meta title="Components/Link/Usage Guidelines" />
|
|
12
|
+
|
|
13
|
+
# Link
|
|
14
|
+
|
|
15
|
+
Updated Jan 30, 2025
|
|
16
|
+
|
|
17
|
+
<ResourceLinks
|
|
18
|
+
sourceCode="https://github.com/cultureamp/kaizen-design-system/tree/main/packages/components/src/Link"
|
|
19
|
+
figma="https://www.figma.com/design/FWIOtGjpv9z0by95j1SgP0/Link?node-id=273-4107"
|
|
20
|
+
apiSpecification="/?path=/docs/components-link-api-specification--docs"
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
<KAIOInstallation exportNames={'Link'} />
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
|
|
27
|
+
`Link` allow users to navigate to a different location. They can be presented inside a paragraph or as standalone text.
|
|
28
|
+
|
|
29
|
+
<Canvas of={Link.Playground} />
|
|
30
|
+
|
|
31
|
+
<Controls
|
|
32
|
+
of={Link.Playground}
|
|
33
|
+
include={['href', 'variant', 'size', 'isDisabled', 'icon', 'iconPosition']}
|
|
34
|
+
className="mb-64"
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
## When to use
|
|
38
|
+
|
|
39
|
+
- Navigating to a different page within the application
|
|
40
|
+
- Navigating to an entirely different site
|
|
41
|
+
- Jump to an element on the same page
|
|
42
|
+
- Link to emails or phone numbers
|
|
43
|
+
|
|
44
|
+
## When not to use
|
|
45
|
+
|
|
46
|
+
- Do not use links for actions that will change data or manipulate how it is displayed, change a state, or trigger an action. Instead, use buttons to guide users to specific actions.
|
|
47
|
+
|
|
48
|
+
### Use primary links to highlight
|
|
49
|
+
|
|
50
|
+
The primary link is the default and is blue. This should be used to call attention to the link and for when the blue color won’t feel too overwhelming in the experience.
|
|
51
|
+
|
|
52
|
+
### Use secondary links to optimise readability
|
|
53
|
+
|
|
54
|
+
The secondary link is the same color as the paragraph text. Its subdued appearance is optimal for when the primary variant is too overwhelming, such as in blocks of text with several references linked throughout.
|
|
55
|
+
|
|
56
|
+
### Use links in body copy
|
|
57
|
+
|
|
58
|
+
Put links in regular text, not in titles. If you need something bigger or more noticeable, try using a button instead.
|
|
59
|
+
|
|
60
|
+
### Use a relative link size
|
|
61
|
+
|
|
62
|
+
The link component has four size variants, each aligning with body text sizes.
|
|
63
|
+
|
|
64
|
+
- **Inline Links**: Use the isInline prop to inherit the font size from the parent Text component.
|
|
65
|
+
- **Standalone Links**: Choose a size that maintains consistency with the surrounding text within the same component or pattern.
|
|
66
|
+
|
|
67
|
+
### Give context to inline links
|
|
68
|
+
|
|
69
|
+
For links in a sentence, write them as part of the sentence and include enough information so people know what to expect. This approach can draw a user’s focus to the linked text. If the link leads to more content, it can be helpful to write it as a descriptive noun (e.g., “survey feedback”). If the link launches a task or action, start it with a verb (e.g., “Share your feedback”).
|
|
70
|
+
|
|
71
|
+
### Specs
|
|
72
|
+
|
|
73
|
+
#### Write standalone links like calls-to-action
|
|
74
|
+
|
|
75
|
+
Standalone links are not full sentences, and do not have punctuation at the end. Treat these like calls-to-action by writing them as short verb phrases. Where possible, do not break over multiple lines.
|
|
76
|
+
|
|
77
|
+
<DosAndDonts>
|
|
78
|
+
<DoOrDont story={Link.StandaloneLinkDo} />
|
|
79
|
+
<DoOrDont story={Link.StandaloneLinkDont} isDont />
|
|
80
|
+
</DosAndDonts>
|
|
81
|
+
|
|
82
|
+
#### Ensure clarity by prioritizing the most valuable links
|
|
83
|
+
|
|
84
|
+
Think about how many links you use and where you put them. Don't overload your interface with too many links as clustering links can confuse people.
|
|
85
|
+
|
|
86
|
+
<DosAndDonts>
|
|
87
|
+
<DoOrDont story={Link.OneLinkInSentence} />
|
|
88
|
+
<DoOrDont story={Link.FiveLinksInSentence} isDont />
|
|
89
|
+
</DosAndDonts>
|
|
90
|
+
|
|
91
|
+
#### Use icons in links sparingly and consistently
|
|
92
|
+
|
|
93
|
+
Overusing icons can create visual clutter and overwhelm users. Use them sparingly to highlight common and recognisable navigation.
|
|
94
|
+
|
|
95
|
+
<DosAndDonts>
|
|
96
|
+
<DoOrDont story={Link.ExternalIconLink} />
|
|
97
|
+
<DoOrDont story={Link.RandomIconLink} isDont />
|
|
98
|
+
</DosAndDonts>
|
|
99
|
+
|
|
100
|
+
#### Links should make sense in isolation
|
|
101
|
+
|
|
102
|
+
If links on a page have the same label repeated multiple times, they should have distinct, accessible names that add context for users. This ensures [better accessibility for screen reader](https://cultureamp.atlassian.net/wiki/spaces/PA/pages/3240099910/Buttons+and+link+labels+make+sense+in+isolation) users, who rely on descriptive labels to distinguish between links.
|
|
103
|
+
|
|
104
|
+
<DosAndDonts>
|
|
105
|
+
<DoOrDont story={Link.DistinctNamedLink} />
|
|
106
|
+
<DoOrDont story={Link.GenericNamedLink} isDont />
|
|
107
|
+
</DosAndDonts>
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { type Meta, type StoryObj } from '@storybook/react'
|
|
3
|
+
import { Text } from '~components/Text'
|
|
4
|
+
import { Icon } from '~components/__next__/Icon'
|
|
5
|
+
import { Link } from '../Link'
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: 'Components/Link',
|
|
9
|
+
component: Link,
|
|
10
|
+
args: {
|
|
11
|
+
children: 'Link',
|
|
12
|
+
href: 'https://www.google.com',
|
|
13
|
+
},
|
|
14
|
+
argTypes: {
|
|
15
|
+
variant: {
|
|
16
|
+
options: ['primary', 'secondary', 'white'],
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
options: ['intro-lede', 'body', 'small', 'extra-small'],
|
|
20
|
+
},
|
|
21
|
+
icon: {
|
|
22
|
+
options: ['arrow_forward', 'open_in_new'],
|
|
23
|
+
mapping: {
|
|
24
|
+
// eslint-disable-next-line camelcase
|
|
25
|
+
arrow_forward: <Icon isPresentational name="arrow_forward" />,
|
|
26
|
+
// eslint-disable-next-line camelcase
|
|
27
|
+
open_in_new: <Icon isPresentational name="open_in_new" />,
|
|
28
|
+
},
|
|
29
|
+
description:
|
|
30
|
+
'Renders an icon at the specified `iconPosition`. For size scaling, use the `Icon` component from `"@kaizen/components/future"`. See [all available icons](https://cultureamp.design/?path=/docs/components-icon-icon-future-api-specification--docs)',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
} satisfies Meta<typeof Link>
|
|
34
|
+
|
|
35
|
+
export default meta
|
|
36
|
+
|
|
37
|
+
type Story = StoryObj<typeof meta>
|
|
38
|
+
|
|
39
|
+
export const Playground: Story = {
|
|
40
|
+
render: (props) =>
|
|
41
|
+
props.variant !== 'white' ? (
|
|
42
|
+
<Link {...props} />
|
|
43
|
+
) : (
|
|
44
|
+
<div className="flex p-12 bg-purple-600">
|
|
45
|
+
{' '}
|
|
46
|
+
<Link {...props} />
|
|
47
|
+
</div>
|
|
48
|
+
),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const LinkVariants: Story = {
|
|
52
|
+
render: (props) => (
|
|
53
|
+
<>
|
|
54
|
+
<Link {...props} variant="primary" />
|
|
55
|
+
<br />
|
|
56
|
+
<Link {...props} variant="secondary" />
|
|
57
|
+
</>
|
|
58
|
+
),
|
|
59
|
+
decorators: [
|
|
60
|
+
(Story) => (
|
|
61
|
+
<div className="flex gap-8">
|
|
62
|
+
<Story />
|
|
63
|
+
</div>
|
|
64
|
+
),
|
|
65
|
+
],
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const LinkVariantWhite: Story = {
|
|
69
|
+
render: (props) => <Link {...props} variant="white" />,
|
|
70
|
+
parameters: {
|
|
71
|
+
reverseColors: true,
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const LinkWithIconStart: Story = {
|
|
76
|
+
render: (props) => (
|
|
77
|
+
<Link {...props} icon={<Icon name="add" isPresentational />} iconPosition="start" />
|
|
78
|
+
),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const LinkWithIconEnd: Story = {
|
|
82
|
+
render: (props) => (
|
|
83
|
+
<Link {...props} icon={<Icon name="add" isPresentational />} iconPosition="end" />
|
|
84
|
+
),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const LinkOpensInNewTab: Story = {
|
|
88
|
+
render: (props) => (
|
|
89
|
+
<Link
|
|
90
|
+
{...props}
|
|
91
|
+
target="_blank"
|
|
92
|
+
icon={<Icon name="open_in_new" isPresentational />}
|
|
93
|
+
iconPosition="end"
|
|
94
|
+
/>
|
|
95
|
+
),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const WithText: Story = {
|
|
99
|
+
render: ({ size: _, ...otherArgs }) => (
|
|
100
|
+
<>
|
|
101
|
+
<Text variant="intro-lede">
|
|
102
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae eaque amet atque. Dolores
|
|
103
|
+
repellendus eligendi <span style={{ textDecoration: 'underline' }}> totam.</span>{' '}
|
|
104
|
+
<Link {...otherArgs} icon={<Icon name="add" isPresentational />} isInline /> Mollitia vero
|
|
105
|
+
asperiores assumenda, odit ratione id perspiciatis suscipit molestias quas facere, commodi
|
|
106
|
+
saepe! Quisquam, quidem quas a quos quae quia quidem, quod, voluptates, dolorum quibusdam.
|
|
107
|
+
Quisquam, quidem quas a quos quae
|
|
108
|
+
</Text>
|
|
109
|
+
<br />
|
|
110
|
+
<Text variant="body">
|
|
111
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae eaque amet atque. Dolores
|
|
112
|
+
repellendus eligendi <span style={{ textDecoration: 'underline' }}> totam.</span>{' '}
|
|
113
|
+
<Link
|
|
114
|
+
{...otherArgs}
|
|
115
|
+
icon={<Icon name="add" isPresentational />}
|
|
116
|
+
isInline
|
|
117
|
+
size={undefined}
|
|
118
|
+
/>
|
|
119
|
+
Mollitia vero asperiores assumenda, odit ratione id perspiciatis suscipit molestias quas
|
|
120
|
+
facere, commodi saepe! Quisquam, quidem quas a quos quae quia quidem, quod, voluptates,
|
|
121
|
+
dolorum quibusdam. Quisquam, quidem quas a quos quae
|
|
122
|
+
</Text>
|
|
123
|
+
<br />
|
|
124
|
+
<Text variant="small">
|
|
125
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae eaque amet atque. Dolores
|
|
126
|
+
repellendus eligendi <span style={{ textDecoration: 'underline' }}> totam.</span>{' '}
|
|
127
|
+
<Link {...otherArgs} icon={<Icon name="add" isPresentational />} isInline /> Mollitia vero
|
|
128
|
+
asperiores assumenda, odit ratione id perspiciatis suscipit molestias quas facere, commodi
|
|
129
|
+
saepe! Quisquam, quidem quas a quos quae quia quidem, quod, voluptates, dolorum quibusdam.
|
|
130
|
+
Quisquam, quidem quas a quos quae
|
|
131
|
+
</Text>
|
|
132
|
+
<br />
|
|
133
|
+
<Text variant="extra-small">
|
|
134
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae eaque amet atque. Dolores
|
|
135
|
+
repellendus eligendi <span style={{ textDecoration: 'underline' }}> totam.</span>{' '}
|
|
136
|
+
<Link {...otherArgs} icon={<Icon name="add" isPresentational />} isInline /> Mollitia vero
|
|
137
|
+
asperiores assumenda, odit ratione id perspiciatis suscipit molestias quas facere, commodi
|
|
138
|
+
saepe! Quisquam, quidem quas a quos quae quia quidem, quod, voluptates, dolorum quibusdam.
|
|
139
|
+
Quisquam, quidem quas a quos quae
|
|
140
|
+
</Text>
|
|
141
|
+
</>
|
|
142
|
+
),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Links of every different size
|
|
146
|
+
export const LinkSizes: Story = {
|
|
147
|
+
render: ({ children: _, size: __, isInline: ___, ...otherArgs }) => (
|
|
148
|
+
<>
|
|
149
|
+
<Link size="extra-small" {...otherArgs}>
|
|
150
|
+
Extra Small
|
|
151
|
+
</Link>
|
|
152
|
+
<br />
|
|
153
|
+
<Link {...otherArgs} size="small">
|
|
154
|
+
Small
|
|
155
|
+
</Link>
|
|
156
|
+
<br />
|
|
157
|
+
<Link {...otherArgs} size="body">
|
|
158
|
+
Body
|
|
159
|
+
</Link>
|
|
160
|
+
<br />
|
|
161
|
+
<Link {...otherArgs} size="intro-lede">
|
|
162
|
+
Intro Lede
|
|
163
|
+
</Link>
|
|
164
|
+
</>
|
|
165
|
+
),
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const StandaloneLinkDo: Story = {
|
|
169
|
+
render: (props) => <Link {...props}>Learn more about demographics</Link>,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const StandaloneLinkDont: Story = {
|
|
173
|
+
render: ({ size: _, ...otherArgs }) => (
|
|
174
|
+
<Text variant="body">
|
|
175
|
+
Learn more about{' '}
|
|
176
|
+
<Link {...otherArgs} isInline>
|
|
177
|
+
demographics
|
|
178
|
+
</Link>
|
|
179
|
+
</Text>
|
|
180
|
+
),
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const OneLinkInSentence: Story = {
|
|
184
|
+
render: (props) => (
|
|
185
|
+
<Text variant="body">
|
|
186
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Illo ad nobis, ut aspernatur deserunt
|
|
187
|
+
fuga expedita amet architecto{' '}
|
|
188
|
+
<Link {...props} isInline size={undefined}>
|
|
189
|
+
pariatur cum itaque
|
|
190
|
+
</Link>{' '}
|
|
191
|
+
dicta veritatis inventore ea esse rem dolore natus! Architecto.
|
|
192
|
+
</Text>
|
|
193
|
+
),
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const FiveLinksInSentence: Story = {
|
|
197
|
+
render: ({ size: _, ...otherArgs }) => (
|
|
198
|
+
<Text variant="body">
|
|
199
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Illo{' '}
|
|
200
|
+
<Link {...otherArgs} isInline>
|
|
201
|
+
{' '}
|
|
202
|
+
ad nobis
|
|
203
|
+
</Link>
|
|
204
|
+
, ut aspernatur{' '}
|
|
205
|
+
<Link {...otherArgs} isInline>
|
|
206
|
+
deserunt fuga expedita amet architecto
|
|
207
|
+
</Link>{' '}
|
|
208
|
+
<Link {...otherArgs} isInline>
|
|
209
|
+
pariatur cum itaque
|
|
210
|
+
</Link>{' '}
|
|
211
|
+
dicta veritatis{' '}
|
|
212
|
+
<Link {...otherArgs} isInline>
|
|
213
|
+
inventore ea
|
|
214
|
+
</Link>{' '}
|
|
215
|
+
esse rem dolore{' '}
|
|
216
|
+
<Link {...otherArgs} isInline>
|
|
217
|
+
natus
|
|
218
|
+
</Link>
|
|
219
|
+
! Architecto.
|
|
220
|
+
</Text>
|
|
221
|
+
),
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export const ExternalIconLink: Story = {
|
|
225
|
+
render: (props) => <Link {...props} icon={<Icon name="open_in_new" isPresentational />} />,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const RandomIconLink: Story = {
|
|
229
|
+
render: (props) => <Link {...props} icon={<Icon name="flag" isPresentational />} />,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const DistinctNamedLink: Story = {
|
|
233
|
+
render: (props) => <Link {...props}>View Q4 2024 dataset</Link>,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export const GenericNamedLink: Story = {
|
|
237
|
+
render: (props) => <Link {...props}>Learn more</Link>,
|
|
238
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { type Meta } from '@storybook/react'
|
|
3
|
+
import { within } from '@storybook/test'
|
|
4
|
+
import { Icon } from '~components/__next__/Icon'
|
|
5
|
+
import { StickerSheet, type StickerSheetStory } from '~storybook/components/StickerSheet'
|
|
6
|
+
import { Link, type LinkProps } from '../Link'
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'Components/Link',
|
|
10
|
+
component: Link,
|
|
11
|
+
parameters: {
|
|
12
|
+
chromatic: { disable: false },
|
|
13
|
+
controls: { disable: true },
|
|
14
|
+
},
|
|
15
|
+
} satisfies Meta
|
|
16
|
+
|
|
17
|
+
const variants = ['primary', 'secondary'] satisfies LinkProps['variant'][]
|
|
18
|
+
const sizes = ['extra-small', 'small', 'body', 'intro-lede'] satisfies LinkProps['size'][]
|
|
19
|
+
const href = 'https://www.google.com' satisfies LinkProps['href']
|
|
20
|
+
|
|
21
|
+
const StickerSheetTemplate: StickerSheetStory = {
|
|
22
|
+
render: ({ isReversed }) => (
|
|
23
|
+
<>
|
|
24
|
+
<StickerSheet
|
|
25
|
+
title="Link"
|
|
26
|
+
headers={[
|
|
27
|
+
'isUnderlined',
|
|
28
|
+
'isUnderlined + Icon Start',
|
|
29
|
+
'isUnderlined + Icon End',
|
|
30
|
+
'Icon start',
|
|
31
|
+
'Icon end',
|
|
32
|
+
'isDisabled',
|
|
33
|
+
]}
|
|
34
|
+
isReversed={isReversed}
|
|
35
|
+
>
|
|
36
|
+
{variants.map((variant) =>
|
|
37
|
+
sizes.map((size) => (
|
|
38
|
+
<StickerSheet.Row key={size + variant} header={`${variant} (${size})`}>
|
|
39
|
+
<Link
|
|
40
|
+
variant={isReversed ? 'white' : variant}
|
|
41
|
+
size={size}
|
|
42
|
+
href={href}
|
|
43
|
+
isUnderlined={true}
|
|
44
|
+
isInline={false}
|
|
45
|
+
>
|
|
46
|
+
Link
|
|
47
|
+
</Link>
|
|
48
|
+
<Link
|
|
49
|
+
variant={isReversed ? 'white' : variant}
|
|
50
|
+
size={size}
|
|
51
|
+
href={href}
|
|
52
|
+
isUnderlined={true}
|
|
53
|
+
isInline={false}
|
|
54
|
+
icon={<Icon name="add" isPresentational />}
|
|
55
|
+
iconPosition="start"
|
|
56
|
+
>
|
|
57
|
+
Link
|
|
58
|
+
</Link>
|
|
59
|
+
<Link
|
|
60
|
+
variant={isReversed ? 'white' : variant}
|
|
61
|
+
size={size}
|
|
62
|
+
href={href}
|
|
63
|
+
isUnderlined={true}
|
|
64
|
+
isInline={false}
|
|
65
|
+
icon={<Icon name="add" isPresentational />}
|
|
66
|
+
iconPosition="end"
|
|
67
|
+
>
|
|
68
|
+
Link
|
|
69
|
+
</Link>
|
|
70
|
+
<Link
|
|
71
|
+
variant={isReversed ? 'white' : variant}
|
|
72
|
+
size={size}
|
|
73
|
+
href={href}
|
|
74
|
+
isUnderlined={false}
|
|
75
|
+
isInline={false}
|
|
76
|
+
icon={<Icon name="add" isPresentational />}
|
|
77
|
+
iconPosition="start"
|
|
78
|
+
>
|
|
79
|
+
Link
|
|
80
|
+
</Link>
|
|
81
|
+
<Link
|
|
82
|
+
variant={isReversed ? 'white' : variant}
|
|
83
|
+
size={size}
|
|
84
|
+
href={href}
|
|
85
|
+
isUnderlined={false}
|
|
86
|
+
isInline={false}
|
|
87
|
+
icon={<Icon name="add" isPresentational />}
|
|
88
|
+
iconPosition="end"
|
|
89
|
+
>
|
|
90
|
+
Link
|
|
91
|
+
</Link>
|
|
92
|
+
<Link
|
|
93
|
+
variant={isReversed ? 'white' : variant}
|
|
94
|
+
size={size}
|
|
95
|
+
href={href}
|
|
96
|
+
isUnderlined={true}
|
|
97
|
+
isInline={false}
|
|
98
|
+
isDisabled={true}
|
|
99
|
+
>
|
|
100
|
+
Link
|
|
101
|
+
</Link>
|
|
102
|
+
</StickerSheet.Row>
|
|
103
|
+
)),
|
|
104
|
+
)}
|
|
105
|
+
</StickerSheet>
|
|
106
|
+
<StickerSheet
|
|
107
|
+
title="Pseudo states"
|
|
108
|
+
headers={['isHovered', 'isFocusVisible', 'isPressed']}
|
|
109
|
+
isReversed={isReversed}
|
|
110
|
+
>
|
|
111
|
+
{variants.map((variant) => (
|
|
112
|
+
<StickerSheet.Row key={variant} isReversed={isReversed} header={variant}>
|
|
113
|
+
<Link
|
|
114
|
+
variant={isReversed ? 'white' : variant}
|
|
115
|
+
size="small"
|
|
116
|
+
href={href}
|
|
117
|
+
isUnderlined={true}
|
|
118
|
+
isInline={false}
|
|
119
|
+
icon={<Icon name="add" isPresentational />}
|
|
120
|
+
data-testid="testid__link-hover"
|
|
121
|
+
>
|
|
122
|
+
Label
|
|
123
|
+
</Link>
|
|
124
|
+
<Link
|
|
125
|
+
variant={isReversed ? 'white' : variant}
|
|
126
|
+
size="small"
|
|
127
|
+
href={href}
|
|
128
|
+
isUnderlined={true}
|
|
129
|
+
isInline={false}
|
|
130
|
+
icon={<Icon name="add" isPresentational />}
|
|
131
|
+
data-testid="testid__link-focus"
|
|
132
|
+
>
|
|
133
|
+
Label
|
|
134
|
+
</Link>
|
|
135
|
+
<Link
|
|
136
|
+
variant={isReversed ? 'white' : variant}
|
|
137
|
+
size="small"
|
|
138
|
+
href={href}
|
|
139
|
+
isUnderlined={true}
|
|
140
|
+
isInline={false}
|
|
141
|
+
icon={<Icon name="add" isPresentational />}
|
|
142
|
+
data-testid="testid__link-pressed"
|
|
143
|
+
>
|
|
144
|
+
Label
|
|
145
|
+
</Link>
|
|
146
|
+
</StickerSheet.Row>
|
|
147
|
+
))}
|
|
148
|
+
</StickerSheet>
|
|
149
|
+
</>
|
|
150
|
+
),
|
|
151
|
+
play: ({ canvasElement }) => {
|
|
152
|
+
const canvas = within(canvasElement)
|
|
153
|
+
const focusLinks = canvas.getAllByTestId('testid__link-focus')
|
|
154
|
+
const hoverLinks = canvas.getAllByTestId('testid__link-hover')
|
|
155
|
+
const pressedLinks = canvas.getAllByTestId('testid__link-pressed')
|
|
156
|
+
|
|
157
|
+
focusLinks.forEach((Link) => {
|
|
158
|
+
Link.setAttribute('data-focus-visible', 'true')
|
|
159
|
+
})
|
|
160
|
+
hoverLinks.forEach((Link) => {
|
|
161
|
+
Link.setAttribute('data-hovered', 'true')
|
|
162
|
+
})
|
|
163
|
+
pressedLinks.forEach((Link) => {
|
|
164
|
+
Link.setAttribute('data-pressed', 'true')
|
|
165
|
+
})
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const StickerSheetDefault: StickerSheetStory = {
|
|
170
|
+
...StickerSheetTemplate,
|
|
171
|
+
name: 'Sticker Sheet (Default)',
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const StickerSheetRTL: StickerSheetStory = {
|
|
175
|
+
...StickerSheetTemplate,
|
|
176
|
+
name: 'Sticker Sheet (RTL)',
|
|
177
|
+
parameters: {
|
|
178
|
+
textDirection: 'rtl',
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const StickerSheetWhite: StickerSheetStory = {
|
|
183
|
+
...StickerSheetTemplate,
|
|
184
|
+
name: 'Sticker Sheet (White)',
|
|
185
|
+
parameters: {
|
|
186
|
+
reverseColors: true,
|
|
187
|
+
},
|
|
188
|
+
args: {
|
|
189
|
+
isReversed: true,
|
|
190
|
+
},
|
|
191
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Link'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react'
|
|
2
|
+
import { mergeClassNames } from '~components/utils/mergeClassNames'
|
|
3
|
+
import styles from '../Link.module.css'
|
|
4
|
+
|
|
5
|
+
export type LinkContentProps = {
|
|
6
|
+
children: ReactNode
|
|
7
|
+
icon?: JSX.Element
|
|
8
|
+
iconPosition?: 'start' | 'end'
|
|
9
|
+
isUnderlined: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const LinkIcon = ({ icon }: { icon: JSX.Element }): JSX.Element => (
|
|
13
|
+
<span className={styles.icon}>{icon}</span>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
export const LinkContent = ({
|
|
17
|
+
children,
|
|
18
|
+
icon,
|
|
19
|
+
iconPosition,
|
|
20
|
+
isUnderlined,
|
|
21
|
+
}: LinkContentProps): JSX.Element => {
|
|
22
|
+
const iconPositionStyling = iconPosition === 'start' ? styles.iconStart : styles.iconEnd
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<span className={mergeClassNames(styles.linkContent, isUnderlined && styles.isUnderlined)}>
|
|
26
|
+
{icon && iconPosition === 'start' && <LinkIcon icon={icon} />}
|
|
27
|
+
<span className={mergeClassNames(icon && iconPositionStyling)}>{children}</span>
|
|
28
|
+
{icon && iconPosition === 'end' && <LinkIcon icon={icon} />}
|
|
29
|
+
</span>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -163,63 +163,7 @@ For resizing on smaller screens, consider using the `className` prop to leverage
|
|
|
163
163
|
|
|
164
164
|
## Client side routing
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
### Next.js config example
|
|
169
|
-
|
|
170
|
-
The following example demonstrates how you might use the React Aria's `RouterProvider` with `Next.js`'s Pages router. This will allow the `LinkButton` to navigate using the `router.push` method.
|
|
171
|
-
|
|
172
|
-
```tsx
|
|
173
|
-
// ...imports
|
|
174
|
-
import type { AppProps } from 'next/app'
|
|
175
|
-
import { type NextRouter } from 'next/router'
|
|
176
|
-
import { RouterProvider as RacRouterProvider } from 'react-aria-components'
|
|
177
|
-
|
|
178
|
-
// This provides the correct types for `routerOptions` based on the routing solution. As the component agnostic to routing technology this must defined here
|
|
179
|
-
declare module 'react-aria-components' {
|
|
180
|
-
interface RouterConfig {
|
|
181
|
-
// index 2 is the types for the pages routerOptions
|
|
182
|
-
routerOptions: NonNullable<Parameters<NextRouter['push']>[2]>
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function App({ Component, pageProps, router }: AppProps) {
|
|
187
|
-
return (
|
|
188
|
-
<FrontendServices {...config}>
|
|
189
|
-
{/* application code */}
|
|
190
|
-
<RacRouterProvider navigate={(href, opts) => router.push(href, undefined, opts)}>
|
|
191
|
-
<Component {...pageProps} />
|
|
192
|
-
</RacRouterProvider>
|
|
193
|
-
{/* application code */}
|
|
194
|
-
</FrontendServices>
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export default App
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
The implementation in your application would then look something like this:
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
import { useRouter } from 'next/router'
|
|
205
|
-
import { LinkButton } from '@kaizen/components'
|
|
206
|
-
|
|
207
|
-
const Component = () => {
|
|
208
|
-
const router = useRouter()
|
|
209
|
-
|
|
210
|
-
return (
|
|
211
|
-
<>
|
|
212
|
-
<LinkButton href="http://google.com">External link</LinkButton>
|
|
213
|
-
<LinkButton href={`${router.pathname}/path-1`}>Internal link</LinkButton>
|
|
214
|
-
<LinkButton href={`${router.pathname}/path-2`} routerOptions={{ scroll: false }}>
|
|
215
|
-
Link with routerOptions
|
|
216
|
-
</LinkButton>
|
|
217
|
-
</>
|
|
218
|
-
)
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
Additional config options for Next.js can be found in the React Aria's documentation on the [RouterProvider](https://react-spectrum.adobe.com/react-aria/routing.html#nextjs), including the alternative setup for the [App router](https://react-spectrum.adobe.com/react-aria/routing.html##app-router).
|
|
166
|
+
Please refer to the [client side routing](/docs/guides-client-side-routing--docs) for more information on how to set up client side routing with the `LinkButton`.
|
|
223
167
|
|
|
224
168
|
### React Router config example
|
|
225
169
|
|