@khanacademy/wonder-blocks-link 3.8.17 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/es/index.js +44 -18
- package/dist/index.js +44 -18
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/custom-snapshot.test.js.snap +1445 -62
- package/src/__tests__/custom-snapshot.test.js +30 -27
- package/src/components/__docs__/link.argtypes.js +8 -0
- package/src/components/__docs__/link.stories.js +326 -36
- package/src/components/__tests__/link.test.js +102 -0
- package/src/components/link-core.js +62 -22
- package/src/components/link.js +9 -0
|
@@ -43,35 +43,38 @@ describe("LinkCore", () => {
|
|
|
43
43
|
for (const kind of ["primary", "secondary"]) {
|
|
44
44
|
for (const href of ["#", "#non-existent-link"]) {
|
|
45
45
|
for (const light of kind === "primary" ? [true, false] : [false]) {
|
|
46
|
-
for (const visitable of kind === "primary"
|
|
46
|
+
for (const visitable of kind === "primary"
|
|
47
47
|
? [true, false]
|
|
48
48
|
: [false]) {
|
|
49
|
-
for (const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
light
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
49
|
+
for (const inline of [true, false]) {
|
|
50
|
+
for (const state of ["focused", "hovered", "pressed"]) {
|
|
51
|
+
const stateProps = {
|
|
52
|
+
focused: state === "focused",
|
|
53
|
+
hovered: state === "hovered",
|
|
54
|
+
pressed: state === "pressed",
|
|
55
|
+
waiting: false,
|
|
56
|
+
};
|
|
57
|
+
test(`kind:${kind} href:${href} light:${String(
|
|
58
|
+
light,
|
|
59
|
+
)} visitable:${String(visitable)} ${state}`, () => {
|
|
60
|
+
const tree = renderer
|
|
61
|
+
.create(
|
|
62
|
+
<LinkCore
|
|
63
|
+
href="#"
|
|
64
|
+
inline={inline}
|
|
65
|
+
kind={kind}
|
|
66
|
+
light={light}
|
|
67
|
+
visitable={visitable}
|
|
68
|
+
{...stateProps}
|
|
69
|
+
{...defaultHandlers}
|
|
70
|
+
>
|
|
71
|
+
Click me
|
|
72
|
+
</LinkCore>,
|
|
73
|
+
)
|
|
74
|
+
.toJSON();
|
|
75
|
+
expect(tree).toMatchSnapshot();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
}
|
|
@@ -20,6 +20,14 @@ export default {
|
|
|
20
20
|
table: {type: {summary: "string"}},
|
|
21
21
|
type: {required: false},
|
|
22
22
|
},
|
|
23
|
+
inline: {
|
|
24
|
+
control: {type: "boolean"},
|
|
25
|
+
description: `Indicates that this link is used within a body of text.
|
|
26
|
+
This styles the link with an underline to distinguish it
|
|
27
|
+
from surrounding text.`,
|
|
28
|
+
table: {type: {summary: "boolean"}},
|
|
29
|
+
type: {required: false},
|
|
30
|
+
},
|
|
23
31
|
kind: {
|
|
24
32
|
control: {type: "select"},
|
|
25
33
|
description:
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
// We need to use fireEvent for mouseDown in these tests, none of the userEvent
|
|
2
|
+
// alternatives work. Click includes mouseUp, which removes the pressed style.
|
|
3
|
+
/* eslint-disable testing-library/prefer-user-event */
|
|
1
4
|
// @flow
|
|
5
|
+
import {expect} from "@storybook/jest";
|
|
2
6
|
import * as React from "react";
|
|
7
|
+
import {within, userEvent, fireEvent} from "@storybook/testing-library";
|
|
3
8
|
import {StyleSheet} from "aphrodite";
|
|
4
9
|
import {MemoryRouter, Route, Switch} from "react-router-dom";
|
|
5
10
|
|
|
6
11
|
import Color from "@khanacademy/wonder-blocks-color";
|
|
7
12
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
13
|
+
import {Strut} from "@khanacademy/wonder-blocks-layout";
|
|
8
14
|
import Link from "@khanacademy/wonder-blocks-link";
|
|
9
15
|
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
10
16
|
import {
|
|
@@ -29,6 +35,9 @@ export default {
|
|
|
29
35
|
argTypes: LinkArgTypes,
|
|
30
36
|
};
|
|
31
37
|
|
|
38
|
+
const activeBlue = "#1b50b3";
|
|
39
|
+
const fadedBlue = "#b5cefb";
|
|
40
|
+
|
|
32
41
|
export const Default: StoryComponentType = (args) => (
|
|
33
42
|
<Link target="_blank" {...args} />
|
|
34
43
|
);
|
|
@@ -38,61 +47,324 @@ Default.args = {
|
|
|
38
47
|
children: "Hello, world!",
|
|
39
48
|
};
|
|
40
49
|
|
|
41
|
-
export const
|
|
42
|
-
<Link href="#">
|
|
50
|
+
export const Primary: StoryComponentType = () => (
|
|
51
|
+
<Link href="#">The quick brown fox jumps over the lazy dog.</Link>
|
|
43
52
|
);
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
Primary.parameters = {
|
|
46
55
|
docs: {
|
|
47
56
|
storyDescription: `Minimal link usage.
|
|
48
57
|
This links to the top of the page.`,
|
|
49
58
|
},
|
|
50
59
|
};
|
|
51
60
|
|
|
52
|
-
|
|
61
|
+
Primary.play = async ({canvasElement}) => {
|
|
62
|
+
const canvas = within(canvasElement);
|
|
63
|
+
|
|
64
|
+
const link = canvas.getByRole("link");
|
|
65
|
+
|
|
66
|
+
await expect(link).toHaveStyle(`color: ${Color.blue}`);
|
|
67
|
+
|
|
68
|
+
await userEvent.hover(link);
|
|
69
|
+
await expect(link).toHaveStyle(
|
|
70
|
+
`text-decoration: underline ${Color.blue} dashed 2px`,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
await fireEvent.mouseDown(link);
|
|
74
|
+
await expect(link).toHaveStyle(
|
|
75
|
+
`text-decoration: underline solid ${activeBlue} 1px`,
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Secondary: StoryComponentType = () => (
|
|
80
|
+
<Link href="#" kind="secondary">
|
|
81
|
+
The quick brown fox jumps over the lazy dog.
|
|
82
|
+
</Link>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
Secondary.parameters = {
|
|
86
|
+
docs: {
|
|
87
|
+
storyDescription: `Minimal secondary link usage. A secondary link
|
|
88
|
+
has lighter text. This links to the top of the page.`,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
Secondary.play = async ({canvasElement}) => {
|
|
93
|
+
const canvas = within(canvasElement);
|
|
94
|
+
|
|
95
|
+
const link = canvas.getByRole("link");
|
|
96
|
+
|
|
97
|
+
await expect(link).toHaveStyle(`color: ${Color.offBlack64}`);
|
|
98
|
+
|
|
99
|
+
await userEvent.hover(link);
|
|
100
|
+
await expect(link).toHaveStyle(
|
|
101
|
+
`text-decoration: underline ${Color.offBlack64} dashed 2px`,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
await fireEvent.mouseDown(link);
|
|
105
|
+
await expect(link).toHaveStyle(
|
|
106
|
+
`text-decoration: underline solid ${Color.offBlack} 1px`,
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const Visitable: StoryComponentType = () => (
|
|
111
|
+
<Link href="#" visitable={true}>
|
|
112
|
+
The quick brown fox jumps over the lazy dog.
|
|
113
|
+
</Link>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
Visitable.parameters = {
|
|
117
|
+
docs: {
|
|
118
|
+
storyDescription: `This is a visitable link. It changes color after
|
|
119
|
+
it has been clicked on to indicate that it's been visited before.
|
|
120
|
+
This link's \`visitable\` prop is set to true.
|
|
121
|
+
It links to the top of the page.`,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const LightPrimary: StoryComponentType = () => (
|
|
126
|
+
<Link href="#" light={true}>
|
|
127
|
+
The quick brown fox jumps over the lazy dog.
|
|
128
|
+
</Link>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
LightPrimary.parameters = {
|
|
132
|
+
docs: {
|
|
133
|
+
storyDescription: `Minimal link usage on a dark background. This
|
|
134
|
+
link has its \`light\` prop set to true. It links to the top
|
|
135
|
+
of the page.`,
|
|
136
|
+
},
|
|
137
|
+
backgrounds: {
|
|
138
|
+
default: "darkBlue",
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
LightPrimary.play = async ({canvasElement}) => {
|
|
143
|
+
const canvas = within(canvasElement);
|
|
144
|
+
|
|
145
|
+
const link = canvas.getByRole("link");
|
|
146
|
+
|
|
147
|
+
await userEvent.hover(link);
|
|
148
|
+
await expect(link).toHaveStyle(
|
|
149
|
+
`text-decoration: underline ${Color.white} dashed 2px`,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
await fireEvent.mouseDown(link);
|
|
153
|
+
await expect(link).toHaveStyle(
|
|
154
|
+
`text-decoration: underline solid ${fadedBlue} 1px`,
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const LightVisitable: StoryComponentType = () => (
|
|
159
|
+
<Link href="#" light={true} visitable={true}>
|
|
160
|
+
The quick brown fox jumps over the lazy dog.
|
|
161
|
+
</Link>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
LightVisitable.parameters = {
|
|
165
|
+
backgrounds: {
|
|
166
|
+
default: "darkBlue",
|
|
167
|
+
},
|
|
168
|
+
docs: {
|
|
169
|
+
storyDescription: `This is a visitable link on a dark background.
|
|
170
|
+
It changes color after it has been clicked on to indicate
|
|
171
|
+
that it's been visited before. This link's \`visitable\` prop
|
|
172
|
+
is set to true. It links to the top of the page.`,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const Inline: StoryComponentType = () => (
|
|
53
177
|
<Body>
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
</
|
|
62
|
-
|
|
63
|
-
<Link href="#" visitable={true}>
|
|
64
|
-
Visitable Primary
|
|
65
|
-
</Link>
|
|
66
|
-
|
|
178
|
+
This is an inline{" "}
|
|
179
|
+
<Link href="#" inline={true}>
|
|
180
|
+
Primary link
|
|
181
|
+
</Link>
|
|
182
|
+
, whereas this is an inline{" "}
|
|
183
|
+
<Link href="#" kind="secondary" inline={true}>
|
|
184
|
+
Secondary link
|
|
185
|
+
</Link>
|
|
186
|
+
, and this is an inline{" "}
|
|
187
|
+
<Link href="#" visitable={true} inline={true}>
|
|
188
|
+
Visitable link (Primary only)
|
|
189
|
+
</Link>
|
|
190
|
+
.
|
|
67
191
|
</Body>
|
|
68
192
|
);
|
|
69
193
|
|
|
70
|
-
|
|
194
|
+
Inline.parameters = {
|
|
71
195
|
docs: {
|
|
72
|
-
storyDescription: `
|
|
73
|
-
|
|
196
|
+
storyDescription: `Inline links include an underline to distinguish
|
|
197
|
+
them from the surrounding text. Make a link inline by setting the
|
|
198
|
+
\`inline\` prop to \`true\`. It is recommended to use inline
|
|
199
|
+
links within paragraphs and sentences.`,
|
|
74
200
|
},
|
|
75
201
|
};
|
|
76
202
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
203
|
+
Inline.play = async ({canvasElement}) => {
|
|
204
|
+
const canvas = within(canvasElement);
|
|
205
|
+
|
|
206
|
+
const primaryLink = canvas.getByText("Primary link");
|
|
207
|
+
const secondaryLink = canvas.getByText("Secondary link");
|
|
208
|
+
|
|
209
|
+
// Primary link styles
|
|
210
|
+
await expect(primaryLink).toHaveStyle(`color: ${Color.blue}`);
|
|
211
|
+
|
|
212
|
+
await userEvent.hover(primaryLink);
|
|
213
|
+
await expect(primaryLink).toHaveStyle(
|
|
214
|
+
`text-decoration: underline ${Color.blue} dashed 2px`,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
await fireEvent.mouseDown(primaryLink);
|
|
218
|
+
await expect(primaryLink).toHaveStyle(
|
|
219
|
+
`text-decoration: underline solid ${activeBlue} 1px`,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// Secondary link styles
|
|
223
|
+
await expect(secondaryLink).toHaveStyle(`color: ${Color.offBlack}`);
|
|
224
|
+
|
|
225
|
+
await userEvent.hover(secondaryLink);
|
|
226
|
+
await expect(secondaryLink).toHaveStyle(
|
|
227
|
+
`text-decoration: underline ${Color.offBlack} dashed 2px`,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
await fireEvent.mouseDown(secondaryLink);
|
|
231
|
+
await expect(secondaryLink).toHaveStyle(
|
|
232
|
+
`text-decoration: underline solid ${activeBlue} 1px`,
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export const InlineLight: StoryComponentType = () => (
|
|
237
|
+
<Body style={{color: Color.white}}>
|
|
238
|
+
This is an inline{" "}
|
|
239
|
+
<Link href="#" inline={true} light={true}>
|
|
240
|
+
Primary link
|
|
241
|
+
</Link>
|
|
242
|
+
, whereas this is an inline{" "}
|
|
243
|
+
<Link href="#" visitable={true} inline={true} light={true}>
|
|
244
|
+
Visitable link (Primary only)
|
|
245
|
+
</Link>
|
|
246
|
+
. Secondary light links are not supported.
|
|
85
247
|
</Body>
|
|
86
248
|
);
|
|
87
249
|
|
|
88
|
-
|
|
250
|
+
InlineLight.parameters = {
|
|
251
|
+
backgrounds: {
|
|
252
|
+
default: "darkBlue",
|
|
253
|
+
},
|
|
254
|
+
docs: {
|
|
255
|
+
storyDescription: `Inline links include an underline to distinguish
|
|
256
|
+
them from the surrounding text. If the link is on a
|
|
257
|
+
dark background, set the \`light\` prop to true for it to
|
|
258
|
+
be appropriately visible.\n\n**NOTE:** Secondary light links are
|
|
259
|
+
not supported.`,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
InlineLight.play = async ({canvasElement}) => {
|
|
264
|
+
const canvas = within(canvasElement);
|
|
265
|
+
|
|
266
|
+
const primaryLink = canvas.getByText("Primary link");
|
|
267
|
+
|
|
268
|
+
await expect(primaryLink).toHaveStyle(`color: ${Color.white}`);
|
|
269
|
+
|
|
270
|
+
await userEvent.hover(primaryLink);
|
|
271
|
+
await expect(primaryLink).toHaveStyle(
|
|
272
|
+
`text-decoration: underline ${Color.white} dashed 2px`,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
await fireEvent.mouseDown(primaryLink);
|
|
276
|
+
await expect(primaryLink).toHaveStyle(
|
|
277
|
+
`text-decoration: underline solid ${fadedBlue} 1px`,
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export const Variants: StoryComponentType = () => (
|
|
282
|
+
<View>
|
|
283
|
+
{/* Default (dark) */}
|
|
284
|
+
<View style={{padding: Spacing.large_24}}>
|
|
285
|
+
{/* Standalone */}
|
|
286
|
+
<View>
|
|
287
|
+
<View style={styles.standaloneLinkWrapper}>
|
|
288
|
+
<Link href="#nonexistent-link">
|
|
289
|
+
Standalone Primary Link
|
|
290
|
+
</Link>
|
|
291
|
+
</View>
|
|
292
|
+
<View style={styles.standaloneLinkWrapper}>
|
|
293
|
+
<Link href="#secondary-nonexistent-link" kind="secondary">
|
|
294
|
+
Standalone Secondary Link
|
|
295
|
+
</Link>
|
|
296
|
+
</View>
|
|
297
|
+
<View style={styles.standaloneLinkWrapper}>
|
|
298
|
+
<Link href="#" visitable={true}>
|
|
299
|
+
Standalone Visitable Link (Primary only)
|
|
300
|
+
</Link>
|
|
301
|
+
</View>
|
|
302
|
+
</View>
|
|
303
|
+
<Strut size={Spacing.xSmall_8} />
|
|
304
|
+
{/* Inline */}
|
|
305
|
+
<Body>
|
|
306
|
+
This is an{" "}
|
|
307
|
+
<Link href="#" inline={true}>
|
|
308
|
+
Inline Primary link
|
|
309
|
+
</Link>
|
|
310
|
+
, whereas this is an{" "}
|
|
311
|
+
<Link href="#" kind="secondary" inline={true}>
|
|
312
|
+
Inline Secondary link
|
|
313
|
+
</Link>
|
|
314
|
+
, and this is an{" "}
|
|
315
|
+
<Link href="#" visitable={true} inline={true}>
|
|
316
|
+
Inline Visitable link (Primary only)
|
|
317
|
+
</Link>
|
|
318
|
+
.
|
|
319
|
+
</Body>
|
|
320
|
+
</View>
|
|
321
|
+
{/* Light */}
|
|
322
|
+
<View
|
|
323
|
+
style={{
|
|
324
|
+
backgroundColor: Color.darkBlue,
|
|
325
|
+
padding: Spacing.large_24,
|
|
326
|
+
}}
|
|
327
|
+
>
|
|
328
|
+
{/* Standalone */}
|
|
329
|
+
<View>
|
|
330
|
+
<View style={styles.standaloneLinkWrapper}>
|
|
331
|
+
<Link href="#nonexistent-link" light={true}>
|
|
332
|
+
Standalone Light Link (Primary only)
|
|
333
|
+
</Link>
|
|
334
|
+
</View>
|
|
335
|
+
<View style={styles.standaloneLinkWrapper}>
|
|
336
|
+
<Link href="#" visitable={true} light={true}>
|
|
337
|
+
Standalone Light Visitable Link (Primary only)
|
|
338
|
+
</Link>
|
|
339
|
+
</View>
|
|
340
|
+
</View>
|
|
341
|
+
<Strut size={Spacing.xSmall_8} />
|
|
342
|
+
{/* Inline */}
|
|
343
|
+
<Body style={{color: Color.white}}>
|
|
344
|
+
This is an{" "}
|
|
345
|
+
<Link href="#" inline={true} light={true}>
|
|
346
|
+
Inline Primary link
|
|
347
|
+
</Link>
|
|
348
|
+
, whereas this is an{" "}
|
|
349
|
+
<Link href="#" visitable={true} inline={true} light={true}>
|
|
350
|
+
Inline Visitable link (Primary only)
|
|
351
|
+
</Link>
|
|
352
|
+
. Secondary light links are not supported.
|
|
353
|
+
</Body>
|
|
354
|
+
</View>
|
|
355
|
+
</View>
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
Variants.parameters = {
|
|
89
359
|
docs: {
|
|
90
|
-
storyDescription: `
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
360
|
+
storyDescription: `By default, primary links are blue, secondary
|
|
361
|
+
links are gray, and visitable links turn purple after they've
|
|
362
|
+
been clicked on. Default inline links are underlined, and the
|
|
363
|
+
secondary kind is black to match surrounding text color.
|
|
364
|
+
Light standalone and inline links have the same colors - white
|
|
365
|
+
with visited visitable links being pink. Light inline links are
|
|
366
|
+
also underlined like default inline links. Light secondary links
|
|
367
|
+
are not supported and will result in an error.`,
|
|
96
368
|
},
|
|
97
369
|
};
|
|
98
370
|
|
|
@@ -110,6 +382,17 @@ WithTypography.parameters = {
|
|
|
110
382
|
},
|
|
111
383
|
};
|
|
112
384
|
|
|
385
|
+
WithTypography.play = async ({canvasElement}) => {
|
|
386
|
+
const canvas = within(canvasElement);
|
|
387
|
+
|
|
388
|
+
const heading = canvas.getByText("Heading inside a Link element");
|
|
389
|
+
|
|
390
|
+
// Confirm that the default font size (16px) and line height (22px)
|
|
391
|
+
// are successfully overridden by typography.
|
|
392
|
+
await expect(heading).toHaveStyle("font-size: 20px");
|
|
393
|
+
await expect(heading).toHaveStyle("lineHeight: 24px");
|
|
394
|
+
};
|
|
395
|
+
|
|
113
396
|
export const WithStyle: StoryComponentType = () => (
|
|
114
397
|
<Link href="#" style={styles.pinkLink}>
|
|
115
398
|
This link has a style.
|
|
@@ -176,7 +459,7 @@ Navigation.parameters = {
|
|
|
176
459
|
const styles = StyleSheet.create({
|
|
177
460
|
darkBackground: {
|
|
178
461
|
backgroundColor: Color.darkBlue,
|
|
179
|
-
color: Color.
|
|
462
|
+
color: Color.white,
|
|
180
463
|
padding: 10,
|
|
181
464
|
},
|
|
182
465
|
heading: {
|
|
@@ -194,4 +477,11 @@ const styles = StyleSheet.create({
|
|
|
194
477
|
flexDirection: "row",
|
|
195
478
|
alignItems: "center",
|
|
196
479
|
},
|
|
480
|
+
standaloneLinkWrapper: {
|
|
481
|
+
// Use inline-block so the outline wraps only the text
|
|
482
|
+
// instead of taking the full width of the parent
|
|
483
|
+
// container.
|
|
484
|
+
display: "inline-block",
|
|
485
|
+
marginBottom: Spacing.xSmall_8,
|
|
486
|
+
},
|
|
197
487
|
});
|
|
@@ -4,6 +4,8 @@ import {MemoryRouter, Route, Switch} from "react-router-dom";
|
|
|
4
4
|
import {fireEvent, render, screen, waitFor} from "@testing-library/react";
|
|
5
5
|
import userEvent from "@testing-library/user-event";
|
|
6
6
|
|
|
7
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
8
|
+
|
|
7
9
|
import Link from "../link.js";
|
|
8
10
|
|
|
9
11
|
describe("Link", () => {
|
|
@@ -332,4 +334,104 @@ describe("Link", () => {
|
|
|
332
334
|
expect(keyCode).toEqual(32);
|
|
333
335
|
});
|
|
334
336
|
});
|
|
337
|
+
|
|
338
|
+
describe("focus style", () => {
|
|
339
|
+
test("blue outline around primary links on focus", () => {
|
|
340
|
+
// Arrange
|
|
341
|
+
render(<Link href="/">Click me!</Link>);
|
|
342
|
+
|
|
343
|
+
// Act
|
|
344
|
+
userEvent.tab();
|
|
345
|
+
const link = screen.getByText("Click me!");
|
|
346
|
+
|
|
347
|
+
// Assert
|
|
348
|
+
expect(link).toHaveFocus();
|
|
349
|
+
expect(link).toHaveStyle(`outline: 1px solid ${Color.blue}`);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test("blue outline around secondary links on focus", () => {
|
|
353
|
+
// Arrange
|
|
354
|
+
render(
|
|
355
|
+
<Link href="/" kind="secondary">
|
|
356
|
+
Click me!
|
|
357
|
+
</Link>,
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// Act
|
|
361
|
+
userEvent.tab();
|
|
362
|
+
const link = screen.getByText("Click me!");
|
|
363
|
+
|
|
364
|
+
// Assert
|
|
365
|
+
expect(link).toHaveFocus();
|
|
366
|
+
expect(link).toHaveStyle(`outline: 1px solid ${Color.blue}`);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("blue outline around primary inline links on focus", () => {
|
|
370
|
+
// Arrange
|
|
371
|
+
render(
|
|
372
|
+
<Link href="/" kind="primary" inline={true}>
|
|
373
|
+
Click me!
|
|
374
|
+
</Link>,
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// Act
|
|
378
|
+
userEvent.tab();
|
|
379
|
+
const link = screen.getByText("Click me!");
|
|
380
|
+
|
|
381
|
+
// Assert
|
|
382
|
+
expect(link).toHaveFocus();
|
|
383
|
+
expect(link).toHaveStyle(`outline: 1px solid ${Color.blue}`);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("blue outline around secondary inline links on focus", () => {
|
|
387
|
+
// Arrange
|
|
388
|
+
render(
|
|
389
|
+
<Link href="/" kind="secondary" inline={true}>
|
|
390
|
+
Click me!
|
|
391
|
+
</Link>,
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// Act
|
|
395
|
+
userEvent.tab();
|
|
396
|
+
const link = screen.getByText("Click me!");
|
|
397
|
+
|
|
398
|
+
// Assert
|
|
399
|
+
expect(link).toHaveFocus();
|
|
400
|
+
expect(link).toHaveStyle(`outline: 1px solid ${Color.blue}`);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("white outline around light links on focus", () => {
|
|
404
|
+
// Arrange
|
|
405
|
+
render(
|
|
406
|
+
<Link href="/" light={true}>
|
|
407
|
+
Click me!
|
|
408
|
+
</Link>,
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
// Act
|
|
412
|
+
userEvent.tab();
|
|
413
|
+
const link = screen.getByText("Click me!");
|
|
414
|
+
|
|
415
|
+
// Assert
|
|
416
|
+
expect(link).toHaveFocus();
|
|
417
|
+
expect(link).toHaveStyle(`outline: 1px solid ${Color.white}`);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test("white outline around inline light links on focus", () => {
|
|
421
|
+
// Arrange
|
|
422
|
+
render(
|
|
423
|
+
<Link href="/" light={true} inline={true}>
|
|
424
|
+
Click me!
|
|
425
|
+
</Link>,
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
// Act
|
|
429
|
+
userEvent.tab();
|
|
430
|
+
const link = screen.getByText("Click me!");
|
|
431
|
+
|
|
432
|
+
// Assert
|
|
433
|
+
expect(link).toHaveFocus();
|
|
434
|
+
expect(link).toHaveStyle(`outline: 1px solid ${Color.white}`);
|
|
435
|
+
});
|
|
436
|
+
});
|
|
335
437
|
});
|