@khanacademy/wonder-blocks-link 3.8.17 → 3.9.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/CHANGELOG.md +23 -0
- package/dist/es/index.js +44 -18
- package/dist/index.js +44 -18
- package/dist/index.js.flow +1 -1
- package/package.json +5 -5
- package/src/__tests__/__snapshots__/custom-snapshot.test.js.snap +1445 -62
- package/src/__tests__/custom-snapshot.test.js +32 -29
- package/src/components/__docs__/link.argtypes.js +8 -0
- package/src/components/__docs__/link.stories.js +328 -38
- package/src/components/__tests__/link.flowtest.js +5 -3
- package/src/components/__tests__/link.test.js +103 -1
- package/src/components/link-core.js +63 -23
- package/src/components/link.js +34 -32
- package/src/index.js +1 -1
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
ClickableState,
|
|
14
14
|
} from "@khanacademy/wonder-blocks-clickable";
|
|
15
15
|
import type {StyleDeclaration} from "aphrodite";
|
|
16
|
-
import type {SharedProps} from "./link
|
|
16
|
+
import type {SharedProps} from "./link";
|
|
17
17
|
|
|
18
18
|
type Props = {|
|
|
19
19
|
...SharedProps,
|
|
@@ -33,6 +33,7 @@ export default class LinkCore extends React.Component<Props> {
|
|
|
33
33
|
focused,
|
|
34
34
|
hovered,
|
|
35
35
|
href,
|
|
36
|
+
inline,
|
|
36
37
|
kind,
|
|
37
38
|
light,
|
|
38
39
|
visitable,
|
|
@@ -43,14 +44,22 @@ export default class LinkCore extends React.Component<Props> {
|
|
|
43
44
|
...restProps
|
|
44
45
|
} = this.props;
|
|
45
46
|
|
|
46
|
-
const linkStyles = _generateStyles(kind, light, visitable);
|
|
47
|
+
const linkStyles = _generateStyles(inline, kind, light, visitable);
|
|
48
|
+
const restingStyles = inline
|
|
49
|
+
? linkStyles.restingInline
|
|
50
|
+
: linkStyles.resting;
|
|
47
51
|
|
|
48
52
|
const defaultStyles = [
|
|
49
53
|
sharedStyles.shared,
|
|
50
|
-
!(hovered || focused || pressed) &&
|
|
51
|
-
pressed
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
!(hovered || focused || pressed) && restingStyles,
|
|
55
|
+
pressed && linkStyles.active,
|
|
56
|
+
// A11y: The focus ring should always be present when the
|
|
57
|
+
// the link has focus, even the link is being hovered over.
|
|
58
|
+
// TODO(WB-1498): Udpate ClickableBehavior so that focus doesn't
|
|
59
|
+
// stop on mouseleave. We want the focus ring to remain on a
|
|
60
|
+
// focused link even after hovering and un-hovering on it.
|
|
61
|
+
!pressed && hovered && linkStyles.hover,
|
|
62
|
+
!pressed && focused && linkStyles.focus,
|
|
54
63
|
];
|
|
55
64
|
|
|
56
65
|
const commonProps = {
|
|
@@ -86,11 +95,14 @@ const sharedStyles = StyleSheet.create({
|
|
|
86
95
|
cursor: "pointer",
|
|
87
96
|
textDecoration: "none",
|
|
88
97
|
outline: "none",
|
|
98
|
+
display: "inline-flex",
|
|
99
|
+
fontSize: 16,
|
|
100
|
+
lineHeight: "22px",
|
|
89
101
|
},
|
|
90
102
|
});
|
|
91
103
|
|
|
92
|
-
const _generateStyles = (kind, light, visitable) => {
|
|
93
|
-
const buttonType = kind
|
|
104
|
+
const _generateStyles = (inline, kind, light, visitable) => {
|
|
105
|
+
const buttonType = `${kind}-${inline.toString()}-${light.toString()}-${visitable.toString()}`;
|
|
94
106
|
if (styles[buttonType]) {
|
|
95
107
|
return styles[buttonType];
|
|
96
108
|
}
|
|
@@ -99,49 +111,77 @@ const _generateStyles = (kind, light, visitable) => {
|
|
|
99
111
|
throw new Error("Secondary Light links are not supported");
|
|
100
112
|
}
|
|
101
113
|
|
|
102
|
-
if (visitable &&
|
|
103
|
-
throw new Error("Only primary
|
|
114
|
+
if (visitable && kind !== "primary") {
|
|
115
|
+
throw new Error("Only primary link is visitable");
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
const {blue, purple, white, offBlack, offBlack32} = Color;
|
|
107
|
-
const linkPurple = mix(fade(offBlack, 0.08), purple);
|
|
118
|
+
const {blue, pink, purple, white, offBlack, offBlack32, offBlack64} = Color;
|
|
108
119
|
|
|
120
|
+
// Standard purple
|
|
121
|
+
const linkPurple = mix(fade(offBlack, 0.08), purple);
|
|
122
|
+
// Light blue
|
|
123
|
+
const fadedBlue = mix(fade(blue, 0.32), white);
|
|
124
|
+
// Light pink
|
|
125
|
+
const activeLightVisited = mix(fade(white, 0.32), pink);
|
|
126
|
+
// Dark blue
|
|
127
|
+
const activeDefaultPrimary = mix(offBlack32, blue);
|
|
128
|
+
|
|
129
|
+
const primaryDefaultTextColor = light ? white : blue;
|
|
130
|
+
const secondaryDefaultTextColor = inline ? offBlack : offBlack64;
|
|
109
131
|
const defaultTextColor =
|
|
110
|
-
kind === "primary"
|
|
132
|
+
kind === "primary"
|
|
133
|
+
? primaryDefaultTextColor
|
|
134
|
+
: secondaryDefaultTextColor;
|
|
111
135
|
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
:
|
|
136
|
+
const primaryActiveColor = light ? fadedBlue : activeDefaultPrimary;
|
|
137
|
+
const secondaryActiveColor = inline ? activeDefaultPrimary : offBlack;
|
|
138
|
+
const activeColor =
|
|
139
|
+
kind === "primary" ? primaryActiveColor : secondaryActiveColor;
|
|
116
140
|
|
|
117
141
|
const defaultVisited = visitable
|
|
118
142
|
? {
|
|
119
143
|
":visited": {
|
|
120
|
-
color: linkPurple,
|
|
144
|
+
color: light ? pink : linkPurple,
|
|
121
145
|
},
|
|
122
146
|
}
|
|
123
147
|
: Object.freeze({});
|
|
124
148
|
const activeVisited = visitable
|
|
125
149
|
? {
|
|
126
150
|
":visited": {
|
|
127
|
-
color:
|
|
151
|
+
color: light
|
|
152
|
+
? activeLightVisited
|
|
153
|
+
: mix(offBlack32, linkPurple),
|
|
128
154
|
},
|
|
129
155
|
}
|
|
130
156
|
: Object.freeze({});
|
|
131
157
|
|
|
132
158
|
const newStyles: StyleDeclaration = {
|
|
133
|
-
|
|
159
|
+
resting: {
|
|
160
|
+
color: defaultTextColor,
|
|
161
|
+
...defaultVisited,
|
|
162
|
+
},
|
|
163
|
+
restingInline: {
|
|
134
164
|
color: defaultTextColor,
|
|
165
|
+
textDecoration: "underline currentcolor solid 1px",
|
|
166
|
+
textUnderlineOffset: 4,
|
|
167
|
+
...defaultVisited,
|
|
168
|
+
},
|
|
169
|
+
hover: {
|
|
170
|
+
textDecoration: "underline currentcolor dashed 2px",
|
|
171
|
+
color: defaultTextColor,
|
|
172
|
+
textUnderlineOffset: 4,
|
|
135
173
|
...defaultVisited,
|
|
136
174
|
},
|
|
137
175
|
focus: {
|
|
138
|
-
|
|
139
|
-
|
|
176
|
+
color: defaultTextColor,
|
|
177
|
+
outline: `1px solid ${light ? white : blue}`,
|
|
178
|
+
borderRadius: 3,
|
|
140
179
|
...defaultVisited,
|
|
141
180
|
},
|
|
142
181
|
active: {
|
|
143
182
|
color: activeColor,
|
|
144
|
-
textDecoration: "underline currentcolor solid",
|
|
183
|
+
textDecoration: "underline currentcolor solid 1px",
|
|
184
|
+
textUnderlineOffset: 4,
|
|
145
185
|
...activeVisited,
|
|
146
186
|
},
|
|
147
187
|
};
|
package/src/components/link.js
CHANGED
|
@@ -5,9 +5,10 @@ import {getClickableBehavior} from "@khanacademy/wonder-blocks-clickable";
|
|
|
5
5
|
|
|
6
6
|
import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core";
|
|
7
7
|
import type {Typography} from "@khanacademy/wonder-blocks-typography";
|
|
8
|
-
import LinkCore from "./link-core
|
|
8
|
+
import LinkCore from "./link-core";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// TODO(FEI-5000): Convert back to conditional props after TS migration is complete.
|
|
11
|
+
export type SharedProps = {|
|
|
11
12
|
...AriaProps,
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -25,6 +26,13 @@ type CommonProps = {|
|
|
|
25
26
|
*/
|
|
26
27
|
id?: string,
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Indicates that this link is used within a body of text.
|
|
31
|
+
* This styles the link with an underline to distinguish it
|
|
32
|
+
* from surrounding text.
|
|
33
|
+
*/
|
|
34
|
+
inline: boolean,
|
|
35
|
+
|
|
28
36
|
/**
|
|
29
37
|
* Kind of Link. Note: Secondary light Links are not supported.
|
|
30
38
|
*/
|
|
@@ -125,40 +133,33 @@ type CommonProps = {|
|
|
|
125
133
|
* Respond to raw "keyup" event.
|
|
126
134
|
*/
|
|
127
135
|
onKeyUp?: (e: SyntheticKeyboardEvent<>) => mixed,
|
|
128
|
-
|};
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*
|
|
138
|
-
* TODO(WB-1262): only allow this prop when `href` is also set.t
|
|
139
|
-
*/
|
|
140
|
-
target?: "_blank",
|
|
141
|
-
|}
|
|
142
|
-
| {|
|
|
143
|
-
...CommonProps,
|
|
137
|
+
/**
|
|
138
|
+
* A target destination window for a link to open in. We only support
|
|
139
|
+
* "_blank" which opens the URL in a new tab.
|
|
140
|
+
*
|
|
141
|
+
* TODO(WB-1262): only allow this prop when `href` is also set.t
|
|
142
|
+
*/
|
|
143
|
+
target?: "_blank",
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Run async code before navigating to the URL passed to `href`. If the
|
|
147
|
+
* promise returned rejects then navigation will not occur.
|
|
148
|
+
*
|
|
149
|
+
* If both safeWithNav and beforeNav are provided, beforeNav will be run
|
|
150
|
+
* first and safeWithNav will only be run if beforeNav does not reject.
|
|
151
|
+
*
|
|
152
|
+
* WARNING: Using this with `target="_blank"` will trigger built-in popup
|
|
153
|
+
* blockers in Firefox and Safari. This is because we do navigation
|
|
154
|
+
* programmatically and `beforeNav` causes a delay which means that the
|
|
155
|
+
* browser can't make a directly link between a user action and the
|
|
156
|
+
* navigation.
|
|
157
|
+
*/
|
|
158
|
+
beforeNav?: () => Promise<mixed>,
|
|
159
|
+
|};
|
|
160
160
|
|
|
161
161
|
type DefaultProps = {|
|
|
162
|
+
inline: $PropertyType<SharedProps, "inline">,
|
|
162
163
|
kind: $PropertyType<SharedProps, "kind">,
|
|
163
164
|
light: $PropertyType<SharedProps, "light">,
|
|
164
165
|
visitable: $PropertyType<SharedProps, "visitable">,
|
|
@@ -184,6 +185,7 @@ type DefaultProps = {|
|
|
|
184
185
|
*/
|
|
185
186
|
export default class Link extends React.Component<SharedProps> {
|
|
186
187
|
static defaultProps: DefaultProps = {
|
|
188
|
+
inline: false,
|
|
187
189
|
kind: "primary",
|
|
188
190
|
light: false,
|
|
189
191
|
visitable: false,
|
package/src/index.js
CHANGED