@khanacademy/wonder-blocks-link 4.1.0 → 4.2.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/components/link.d.ts +11 -0
- package/dist/components/link.js.flow +13 -0
- package/dist/es/index.js +39 -13
- package/dist/index.js +39 -13
- package/package.json +3 -3
- package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +847 -176
- package/src/components/__tests__/link.test.tsx +90 -62
- package/src/components/link-core.tsx +43 -11
- package/src/components/link.tsx +11 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-link
|
|
2
2
|
|
|
3
|
+
## 4.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ef08a547: Adds start and end icon props to link
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 3686db61: Stop showing a focus outline on the first link when revisiting a tab
|
|
12
|
+
|
|
3
13
|
## 4.1.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { AriaProps, StyleType } from "@khanacademy/wonder-blocks-core";
|
|
3
3
|
import type { Typography } from "@khanacademy/wonder-blocks-typography";
|
|
4
|
+
import type { IconAsset } from "@khanacademy/wonder-blocks-icon";
|
|
4
5
|
export type SharedProps = AriaProps & {
|
|
5
6
|
/**
|
|
6
7
|
* Text to appear on the link. It can be a plain text or a Typography element.
|
|
@@ -119,6 +120,16 @@ export type SharedProps = AriaProps & {
|
|
|
119
120
|
* An optional title attribute.
|
|
120
121
|
*/
|
|
121
122
|
title?: string;
|
|
123
|
+
/**
|
|
124
|
+
* An optional icon displayed before the link label.
|
|
125
|
+
*/
|
|
126
|
+
startIcon?: IconAsset;
|
|
127
|
+
/**
|
|
128
|
+
* An optional icon displayed after the link label.
|
|
129
|
+
* If `target="_blank"` and `endIcon` is passed in, `endIcon` will override
|
|
130
|
+
* the default `externalIcon`.
|
|
131
|
+
*/
|
|
132
|
+
endIcon?: IconAsset;
|
|
122
133
|
};
|
|
123
134
|
type DefaultProps = {
|
|
124
135
|
inline: SharedProps["inline"];
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
import type { AriaProps, StyleType } from "@khanacademy/wonder-blocks-core";
|
|
9
9
|
import type { Typography } from "@khanacademy/wonder-blocks-typography";
|
|
10
|
+
import type { IconAsset } from "@khanacademy/wonder-blocks-icon";
|
|
10
11
|
export type SharedProps = {|
|
|
11
12
|
...AriaProps,
|
|
12
13
|
...{|
|
|
@@ -146,6 +147,18 @@ export type SharedProps = {|
|
|
|
146
147
|
* An optional title attribute.
|
|
147
148
|
*/
|
|
148
149
|
title?: string,
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* An optional icon displayed before the link label.
|
|
153
|
+
*/
|
|
154
|
+
startIcon?: IconAsset,
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* An optional icon displayed after the link label.
|
|
158
|
+
* If `target="_blank"` and `endIcon` is passed in, `endIcon` will override
|
|
159
|
+
* the default `externalIcon`.
|
|
160
|
+
*/
|
|
161
|
+
endIcon?: IconAsset,
|
|
149
162
|
|},
|
|
150
163
|
|};
|
|
151
164
|
declare type DefaultProps = {|
|
package/dist/es/index.js
CHANGED
|
@@ -40,7 +40,7 @@ function _objectWithoutPropertiesLoose(source, excluded) {
|
|
|
40
40
|
return target;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const _excluded$1 = ["children", "skipClientNav", "focused", "hovered", "href", "inline", "kind", "light", "visitable", "pressed", "style", "testId", "waiting", "target"];
|
|
43
|
+
const _excluded$1 = ["children", "skipClientNav", "focused", "hovered", "href", "inline", "kind", "light", "visitable", "pressed", "style", "testId", "waiting", "target", "startIcon", "endIcon"];
|
|
44
44
|
const StyledAnchor = addStyle("a");
|
|
45
45
|
const StyledLink = addStyle(Link$1);
|
|
46
46
|
class LinkCore extends React.Component {
|
|
@@ -59,12 +59,14 @@ class LinkCore extends React.Component {
|
|
|
59
59
|
pressed,
|
|
60
60
|
style,
|
|
61
61
|
testId,
|
|
62
|
-
target
|
|
62
|
+
target,
|
|
63
|
+
startIcon,
|
|
64
|
+
endIcon
|
|
63
65
|
} = _this$props,
|
|
64
66
|
restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
|
|
65
67
|
const linkStyles = _generateStyles(inline, kind, light, visitable);
|
|
66
68
|
const restingStyles = inline ? linkStyles.restingInline : linkStyles.resting;
|
|
67
|
-
const defaultStyles = [sharedStyles.shared,
|
|
69
|
+
const defaultStyles = [sharedStyles.shared, restingStyles, pressed && linkStyles.active, !pressed && hovered && linkStyles.hover, !pressed && focused && linkStyles.focus];
|
|
68
70
|
const commonProps = _extends({
|
|
69
71
|
"data-test-id": testId,
|
|
70
72
|
style: [defaultStyles, style],
|
|
@@ -76,10 +78,26 @@ class LinkCore extends React.Component {
|
|
|
76
78
|
const externalIcon = React.createElement(Icon, {
|
|
77
79
|
icon: externalIconPath,
|
|
78
80
|
size: "small",
|
|
79
|
-
style: iconStyles.
|
|
81
|
+
style: iconStyles.endIcon,
|
|
80
82
|
testId: "external-icon"
|
|
81
83
|
});
|
|
82
|
-
const linkContent = React.createElement(React.Fragment, null,
|
|
84
|
+
const linkContent = React.createElement(React.Fragment, null, startIcon && React.createElement(Icon, {
|
|
85
|
+
icon: startIcon,
|
|
86
|
+
size: "small",
|
|
87
|
+
style: iconStyles.startIcon,
|
|
88
|
+
testId: "start-icon",
|
|
89
|
+
"aria-hidden": "true"
|
|
90
|
+
}), React.createElement("span", {
|
|
91
|
+
style: {
|
|
92
|
+
verticalAlign: "middle"
|
|
93
|
+
}
|
|
94
|
+
}, children), endIcon ? React.createElement(Icon, {
|
|
95
|
+
icon: endIcon,
|
|
96
|
+
size: "small",
|
|
97
|
+
style: iconStyles.endIcon,
|
|
98
|
+
testId: "end-icon",
|
|
99
|
+
"aria-hidden": "true"
|
|
100
|
+
}) : target === "_blank" && externalIcon);
|
|
83
101
|
return router && !skipClientNav && isClientSideUrl(href) ? React.createElement(StyledLink, _extends({}, commonProps, {
|
|
84
102
|
to: href
|
|
85
103
|
}), linkContent) : React.createElement(StyledAnchor, _extends({}, commonProps, {
|
|
@@ -92,8 +110,13 @@ class LinkCore extends React.Component {
|
|
|
92
110
|
}
|
|
93
111
|
const styles = {};
|
|
94
112
|
const iconStyles = StyleSheet.create({
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
startIcon: {
|
|
114
|
+
marginInlineEnd: Spacing.xxxSmall_4,
|
|
115
|
+
verticalAlign: "middle"
|
|
116
|
+
},
|
|
117
|
+
endIcon: {
|
|
118
|
+
marginInlineStart: Spacing.xxxSmall_4,
|
|
119
|
+
verticalAlign: "middle"
|
|
97
120
|
}
|
|
98
121
|
});
|
|
99
122
|
const sharedStyles = StyleSheet.create({
|
|
@@ -101,7 +124,8 @@ const sharedStyles = StyleSheet.create({
|
|
|
101
124
|
cursor: "pointer",
|
|
102
125
|
textDecoration: "none",
|
|
103
126
|
outline: "none",
|
|
104
|
-
|
|
127
|
+
verticalAlign: "bottom",
|
|
128
|
+
textUnderlineOffset: "3px",
|
|
105
129
|
alignItems: "center"
|
|
106
130
|
}
|
|
107
131
|
});
|
|
@@ -157,11 +181,13 @@ const _generateStyles = (inline, kind, light, visitable) => {
|
|
|
157
181
|
textDecoration: "underline currentcolor solid",
|
|
158
182
|
color: defaultTextColor
|
|
159
183
|
}, defaultVisited),
|
|
160
|
-
focus:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
184
|
+
focus: {
|
|
185
|
+
":focus-visible": _extends({
|
|
186
|
+
color: defaultTextColor,
|
|
187
|
+
outline: `1px solid ${light ? white : blue}`,
|
|
188
|
+
borderRadius: 3
|
|
189
|
+
}, defaultVisited)
|
|
190
|
+
},
|
|
165
191
|
active: _extends({
|
|
166
192
|
color: activeColor,
|
|
167
193
|
textDecoration: "underline currentcolor solid"
|
package/dist/index.js
CHANGED
|
@@ -67,7 +67,7 @@ function _objectWithoutPropertiesLoose(source, excluded) {
|
|
|
67
67
|
return target;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const _excluded$1 = ["children", "skipClientNav", "focused", "hovered", "href", "inline", "kind", "light", "visitable", "pressed", "style", "testId", "waiting", "target"];
|
|
70
|
+
const _excluded$1 = ["children", "skipClientNav", "focused", "hovered", "href", "inline", "kind", "light", "visitable", "pressed", "style", "testId", "waiting", "target", "startIcon", "endIcon"];
|
|
71
71
|
const StyledAnchor = wonderBlocksCore.addStyle("a");
|
|
72
72
|
const StyledLink = wonderBlocksCore.addStyle(reactRouterDom.Link);
|
|
73
73
|
class LinkCore extends React__namespace.Component {
|
|
@@ -86,12 +86,14 @@ class LinkCore extends React__namespace.Component {
|
|
|
86
86
|
pressed,
|
|
87
87
|
style,
|
|
88
88
|
testId,
|
|
89
|
-
target
|
|
89
|
+
target,
|
|
90
|
+
startIcon,
|
|
91
|
+
endIcon
|
|
90
92
|
} = _this$props,
|
|
91
93
|
restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
|
|
92
94
|
const linkStyles = _generateStyles(inline, kind, light, visitable);
|
|
93
95
|
const restingStyles = inline ? linkStyles.restingInline : linkStyles.resting;
|
|
94
|
-
const defaultStyles = [sharedStyles.shared,
|
|
96
|
+
const defaultStyles = [sharedStyles.shared, restingStyles, pressed && linkStyles.active, !pressed && hovered && linkStyles.hover, !pressed && focused && linkStyles.focus];
|
|
95
97
|
const commonProps = _extends({
|
|
96
98
|
"data-test-id": testId,
|
|
97
99
|
style: [defaultStyles, style],
|
|
@@ -103,10 +105,26 @@ class LinkCore extends React__namespace.Component {
|
|
|
103
105
|
const externalIcon = React__namespace.createElement(Icon__default["default"], {
|
|
104
106
|
icon: externalIconPath,
|
|
105
107
|
size: "small",
|
|
106
|
-
style: iconStyles.
|
|
108
|
+
style: iconStyles.endIcon,
|
|
107
109
|
testId: "external-icon"
|
|
108
110
|
});
|
|
109
|
-
const linkContent = React__namespace.createElement(React__namespace.Fragment, null,
|
|
111
|
+
const linkContent = React__namespace.createElement(React__namespace.Fragment, null, startIcon && React__namespace.createElement(Icon__default["default"], {
|
|
112
|
+
icon: startIcon,
|
|
113
|
+
size: "small",
|
|
114
|
+
style: iconStyles.startIcon,
|
|
115
|
+
testId: "start-icon",
|
|
116
|
+
"aria-hidden": "true"
|
|
117
|
+
}), React__namespace.createElement("span", {
|
|
118
|
+
style: {
|
|
119
|
+
verticalAlign: "middle"
|
|
120
|
+
}
|
|
121
|
+
}, children), endIcon ? React__namespace.createElement(Icon__default["default"], {
|
|
122
|
+
icon: endIcon,
|
|
123
|
+
size: "small",
|
|
124
|
+
style: iconStyles.endIcon,
|
|
125
|
+
testId: "end-icon",
|
|
126
|
+
"aria-hidden": "true"
|
|
127
|
+
}) : target === "_blank" && externalIcon);
|
|
110
128
|
return router && !skipClientNav && wonderBlocksClickable.isClientSideUrl(href) ? React__namespace.createElement(StyledLink, _extends({}, commonProps, {
|
|
111
129
|
to: href
|
|
112
130
|
}), linkContent) : React__namespace.createElement(StyledAnchor, _extends({}, commonProps, {
|
|
@@ -119,8 +137,13 @@ class LinkCore extends React__namespace.Component {
|
|
|
119
137
|
}
|
|
120
138
|
const styles = {};
|
|
121
139
|
const iconStyles = aphrodite.StyleSheet.create({
|
|
122
|
-
|
|
123
|
-
|
|
140
|
+
startIcon: {
|
|
141
|
+
marginInlineEnd: Spacing__default["default"].xxxSmall_4,
|
|
142
|
+
verticalAlign: "middle"
|
|
143
|
+
},
|
|
144
|
+
endIcon: {
|
|
145
|
+
marginInlineStart: Spacing__default["default"].xxxSmall_4,
|
|
146
|
+
verticalAlign: "middle"
|
|
124
147
|
}
|
|
125
148
|
});
|
|
126
149
|
const sharedStyles = aphrodite.StyleSheet.create({
|
|
@@ -128,7 +151,8 @@ const sharedStyles = aphrodite.StyleSheet.create({
|
|
|
128
151
|
cursor: "pointer",
|
|
129
152
|
textDecoration: "none",
|
|
130
153
|
outline: "none",
|
|
131
|
-
|
|
154
|
+
verticalAlign: "bottom",
|
|
155
|
+
textUnderlineOffset: "3px",
|
|
132
156
|
alignItems: "center"
|
|
133
157
|
}
|
|
134
158
|
});
|
|
@@ -184,11 +208,13 @@ const _generateStyles = (inline, kind, light, visitable) => {
|
|
|
184
208
|
textDecoration: "underline currentcolor solid",
|
|
185
209
|
color: defaultTextColor
|
|
186
210
|
}, defaultVisited),
|
|
187
|
-
focus:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
211
|
+
focus: {
|
|
212
|
+
":focus-visible": _extends({
|
|
213
|
+
color: defaultTextColor,
|
|
214
|
+
outline: `1px solid ${light ? white : blue}`,
|
|
215
|
+
borderRadius: 3
|
|
216
|
+
}, defaultVisited)
|
|
217
|
+
},
|
|
192
218
|
active: _extends({
|
|
193
219
|
color: activeColor,
|
|
194
220
|
textDecoration: "underline currentcolor solid"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-link",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"@khanacademy/wonder-blocks-clickable": "^3.0.9",
|
|
20
20
|
"@khanacademy/wonder-blocks-color": "^2.0.1",
|
|
21
21
|
"@khanacademy/wonder-blocks-core": "^5.1.0",
|
|
22
|
-
"@khanacademy/wonder-blocks-icon": "2.0.9",
|
|
23
|
-
"@khanacademy/wonder-blocks-spacing": "4.0.1"
|
|
22
|
+
"@khanacademy/wonder-blocks-icon": "^2.0.9",
|
|
23
|
+
"@khanacademy/wonder-blocks-spacing": "^4.0.1"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"aphrodite": "^1.2.5",
|