@khanacademy/wonder-blocks-icon-button 4.1.9 → 4.2.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 +19 -0
- package/dist/components/icon-button.d.ts +7 -0
- package/dist/es/index.js +55 -39
- package/dist/index.js +55 -39
- package/dist/util/icon-button-util.d.ts +10 -0
- package/package.json +3 -3
- package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +2523 -251
- package/src/__tests__/custom-snapshot.test.tsx +7 -1
- package/src/components/icon-button-core.tsx +35 -30
- package/src/components/icon-button.tsx +18 -8
- package/src/util/icon-button-util.test.ts +29 -0
- package/src/util/icon-button-util.ts +22 -0
- package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-icon-button
|
|
2
2
|
|
|
3
|
+
## 4.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [48d3c7e9]
|
|
8
|
+
- @khanacademy/wonder-blocks-color@3.0.0
|
|
9
|
+
- @khanacademy/wonder-blocks-clickable@4.0.8
|
|
10
|
+
|
|
11
|
+
## 4.2.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- fb704043: Add size prop to icon button
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [80cab317]
|
|
20
|
+
- @khanacademy/wonder-blocks-clickable@4.0.7
|
|
21
|
+
|
|
3
22
|
## 4.1.9
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import type { IconAsset } from "@khanacademy/wonder-blocks-icon";
|
|
3
3
|
import type { AriaProps, StyleType } from "@khanacademy/wonder-blocks-core";
|
|
4
4
|
import { Link } from "react-router-dom";
|
|
5
|
+
export type IconButtonSize = "xsmall" | "small" | "medium";
|
|
5
6
|
export type SharedProps = Partial<Omit<AriaProps, "aria-disabled">> & {
|
|
6
7
|
/**
|
|
7
8
|
* A Wonder Blocks icon asset, an object specifing paths for one or more of
|
|
@@ -35,6 +36,12 @@ export type SharedProps = Partial<Omit<AriaProps, "aria-disabled">> & {
|
|
|
35
36
|
* Test ID used for e2e testing.
|
|
36
37
|
*/
|
|
37
38
|
testId?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Size of the icon button.
|
|
41
|
+
* One of `xsmall` (16 icon, 20 target), `small` (24, 32), or `medium (24, 40).
|
|
42
|
+
* Defaults to `medium`.
|
|
43
|
+
*/
|
|
44
|
+
size?: IconButtonSize;
|
|
38
45
|
/**
|
|
39
46
|
* Optional custom styles.
|
|
40
47
|
*/
|
package/dist/es/index.js
CHANGED
|
@@ -39,13 +39,28 @@ function _objectWithoutPropertiesLoose(source, excluded) {
|
|
|
39
39
|
return target;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const
|
|
42
|
+
const iconSizeForButtonSize = size => {
|
|
43
|
+
switch (size) {
|
|
44
|
+
case "xsmall":
|
|
45
|
+
return "small";
|
|
46
|
+
case "small":
|
|
47
|
+
return "medium";
|
|
48
|
+
case "medium":
|
|
49
|
+
return "medium";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const targetPixelsForSize = size => ({
|
|
53
|
+
xsmall: 24,
|
|
54
|
+
small: 32,
|
|
55
|
+
medium: 40
|
|
56
|
+
})[size];
|
|
57
|
+
|
|
58
|
+
const _excluded$1 = ["color", "disabled", "focused", "hovered", "href", "icon", "kind", "light", "pressed", "size", "skipClientNav", "style", "testId", "waiting"];
|
|
43
59
|
const StyledAnchor = addStyle("a");
|
|
44
60
|
const StyledButton = addStyle("button");
|
|
45
61
|
const StyledLink = addStyle(Link);
|
|
46
62
|
const IconButtonCore = React.forwardRef(function IconButtonCore(props, ref) {
|
|
47
63
|
const {
|
|
48
|
-
skipClientNav,
|
|
49
64
|
color,
|
|
50
65
|
disabled,
|
|
51
66
|
focused,
|
|
@@ -55,16 +70,18 @@ const IconButtonCore = React.forwardRef(function IconButtonCore(props, ref) {
|
|
|
55
70
|
kind = "primary",
|
|
56
71
|
light = false,
|
|
57
72
|
pressed,
|
|
73
|
+
size = "medium",
|
|
74
|
+
skipClientNav,
|
|
58
75
|
style,
|
|
59
76
|
testId
|
|
60
77
|
} = props,
|
|
61
78
|
restProps = _objectWithoutPropertiesLoose(props, _excluded$1);
|
|
62
79
|
const renderInner = router => {
|
|
63
80
|
const buttonColor = color === "destructive" ? SemanticColor.controlDestructive : SemanticColor.controlDefault;
|
|
64
|
-
const buttonStyles = _generateStyles(buttonColor, kind, light);
|
|
65
|
-
const defaultStyle = [sharedStyles.shared,
|
|
81
|
+
const buttonStyles = _generateStyles(buttonColor, kind, light, size);
|
|
82
|
+
const defaultStyle = [sharedStyles.shared, buttonStyles.default, disabled && buttonStyles.disabled, !disabled && (pressed ? buttonStyles.active : (hovered || focused) && buttonStyles.focus)];
|
|
66
83
|
const child = React.createElement(Icon, {
|
|
67
|
-
size:
|
|
84
|
+
size: iconSizeForButtonSize(size),
|
|
68
85
|
color: "currentColor",
|
|
69
86
|
icon: icon
|
|
70
87
|
});
|
|
@@ -98,8 +115,6 @@ const sharedStyles = StyleSheet.create({
|
|
|
98
115
|
alignItems: "center",
|
|
99
116
|
justifyContent: "center",
|
|
100
117
|
boxSizing: "border-box",
|
|
101
|
-
height: 40,
|
|
102
|
-
width: 40,
|
|
103
118
|
padding: 0,
|
|
104
119
|
cursor: "pointer",
|
|
105
120
|
border: "none",
|
|
@@ -111,14 +126,11 @@ const sharedStyles = StyleSheet.create({
|
|
|
111
126
|
":focus": {
|
|
112
127
|
WebkitTapHighlightColor: "rgba(0,0,0,0)"
|
|
113
128
|
}
|
|
114
|
-
},
|
|
115
|
-
disabled: {
|
|
116
|
-
cursor: "default"
|
|
117
129
|
}
|
|
118
130
|
});
|
|
119
131
|
const styles = {};
|
|
120
|
-
const _generateStyles = (color, kind, light) => {
|
|
121
|
-
const buttonType = color
|
|
132
|
+
const _generateStyles = (color, kind, light, size) => {
|
|
133
|
+
const buttonType = `${color}-${kind}-${light}-${size}`;
|
|
122
134
|
if (styles[buttonType]) {
|
|
123
135
|
return styles[buttonType];
|
|
124
136
|
}
|
|
@@ -131,8 +143,25 @@ const _generateStyles = (color, kind, light) => {
|
|
|
131
143
|
offBlack64,
|
|
132
144
|
offBlack
|
|
133
145
|
} = Color;
|
|
146
|
+
const defaultColor = (() => {
|
|
147
|
+
switch (kind) {
|
|
148
|
+
case "primary":
|
|
149
|
+
return light ? white : color;
|
|
150
|
+
case "secondary":
|
|
151
|
+
return offBlack;
|
|
152
|
+
case "tertiary":
|
|
153
|
+
return offBlack64;
|
|
154
|
+
default:
|
|
155
|
+
throw new Error("IconButton kind not recognized");
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
const pixelsForSize = targetPixelsForSize(size);
|
|
134
159
|
const newStyles = {
|
|
135
|
-
default: {
|
|
160
|
+
default: {
|
|
161
|
+
height: pixelsForSize,
|
|
162
|
+
width: pixelsForSize,
|
|
163
|
+
color: defaultColor
|
|
164
|
+
},
|
|
136
165
|
focus: {
|
|
137
166
|
color: light ? white : color,
|
|
138
167
|
borderWidth: 2,
|
|
@@ -152,37 +181,23 @@ const _generateStyles = (color, kind, light) => {
|
|
|
152
181
|
cursor: "default"
|
|
153
182
|
}
|
|
154
183
|
};
|
|
155
|
-
if (kind === "primary") {
|
|
156
|
-
newStyles["default"] = {
|
|
157
|
-
color: light ? white : color
|
|
158
|
-
};
|
|
159
|
-
} else if (kind === "secondary") {
|
|
160
|
-
newStyles["default"] = {
|
|
161
|
-
color: offBlack
|
|
162
|
-
};
|
|
163
|
-
} else if (kind === "tertiary") {
|
|
164
|
-
newStyles["default"] = {
|
|
165
|
-
color: offBlack64
|
|
166
|
-
};
|
|
167
|
-
} else {
|
|
168
|
-
throw new Error("IconButton kind not recognized");
|
|
169
|
-
}
|
|
170
184
|
styles[buttonType] = StyleSheet.create(newStyles);
|
|
171
185
|
return styles[buttonType];
|
|
172
186
|
};
|
|
173
187
|
|
|
174
|
-
const _excluded = ["
|
|
188
|
+
const _excluded = ["color", "disabled", "href", "kind", "light", "onClick", "size", "skipClientNav", "tabIndex", "target"];
|
|
175
189
|
const IconButton = React.forwardRef(function IconButton(props, ref) {
|
|
176
190
|
const {
|
|
177
|
-
onClick,
|
|
178
|
-
href,
|
|
179
|
-
skipClientNav,
|
|
180
|
-
tabIndex,
|
|
181
|
-
target,
|
|
182
191
|
color = "default",
|
|
192
|
+
disabled = false,
|
|
193
|
+
href,
|
|
183
194
|
kind = "primary",
|
|
184
195
|
light = false,
|
|
185
|
-
|
|
196
|
+
onClick,
|
|
197
|
+
size = "medium",
|
|
198
|
+
skipClientNav,
|
|
199
|
+
tabIndex,
|
|
200
|
+
target
|
|
186
201
|
} = props,
|
|
187
202
|
sharedProps = _objectWithoutPropertiesLoose(props, _excluded);
|
|
188
203
|
const renderClickableBehavior = router => {
|
|
@@ -197,14 +212,15 @@ const IconButton = React.forwardRef(function IconButton(props, ref) {
|
|
|
197
212
|
let childrenProps = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
|
|
198
213
|
return React.createElement(IconButtonCore, _extends({}, sharedProps, state, childrenProps, {
|
|
199
214
|
color: color,
|
|
215
|
+
disabled: disabled,
|
|
216
|
+
href: href,
|
|
200
217
|
kind: kind,
|
|
201
218
|
light: light,
|
|
202
|
-
|
|
219
|
+
ref: ref,
|
|
203
220
|
skipClientNav: skipClientNav,
|
|
204
|
-
|
|
221
|
+
size: size,
|
|
205
222
|
target: target,
|
|
206
|
-
tabIndex: tabIndex
|
|
207
|
-
ref: ref
|
|
223
|
+
tabIndex: tabIndex
|
|
208
224
|
}));
|
|
209
225
|
});
|
|
210
226
|
};
|
package/dist/index.js
CHANGED
|
@@ -65,13 +65,28 @@ function _objectWithoutPropertiesLoose(source, excluded) {
|
|
|
65
65
|
return target;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const
|
|
68
|
+
const iconSizeForButtonSize = size => {
|
|
69
|
+
switch (size) {
|
|
70
|
+
case "xsmall":
|
|
71
|
+
return "small";
|
|
72
|
+
case "small":
|
|
73
|
+
return "medium";
|
|
74
|
+
case "medium":
|
|
75
|
+
return "medium";
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const targetPixelsForSize = size => ({
|
|
79
|
+
xsmall: 24,
|
|
80
|
+
small: 32,
|
|
81
|
+
medium: 40
|
|
82
|
+
})[size];
|
|
83
|
+
|
|
84
|
+
const _excluded$1 = ["color", "disabled", "focused", "hovered", "href", "icon", "kind", "light", "pressed", "size", "skipClientNav", "style", "testId", "waiting"];
|
|
69
85
|
const StyledAnchor = wonderBlocksCore.addStyle("a");
|
|
70
86
|
const StyledButton = wonderBlocksCore.addStyle("button");
|
|
71
87
|
const StyledLink = wonderBlocksCore.addStyle(reactRouterDom.Link);
|
|
72
88
|
const IconButtonCore = React__namespace.forwardRef(function IconButtonCore(props, ref) {
|
|
73
89
|
const {
|
|
74
|
-
skipClientNav,
|
|
75
90
|
color,
|
|
76
91
|
disabled,
|
|
77
92
|
focused,
|
|
@@ -81,16 +96,18 @@ const IconButtonCore = React__namespace.forwardRef(function IconButtonCore(props
|
|
|
81
96
|
kind = "primary",
|
|
82
97
|
light = false,
|
|
83
98
|
pressed,
|
|
99
|
+
size = "medium",
|
|
100
|
+
skipClientNav,
|
|
84
101
|
style,
|
|
85
102
|
testId
|
|
86
103
|
} = props,
|
|
87
104
|
restProps = _objectWithoutPropertiesLoose(props, _excluded$1);
|
|
88
105
|
const renderInner = router => {
|
|
89
106
|
const buttonColor = color === "destructive" ? Color.SemanticColor.controlDestructive : Color.SemanticColor.controlDefault;
|
|
90
|
-
const buttonStyles = _generateStyles(buttonColor, kind, light);
|
|
91
|
-
const defaultStyle = [sharedStyles.shared,
|
|
107
|
+
const buttonStyles = _generateStyles(buttonColor, kind, light, size);
|
|
108
|
+
const defaultStyle = [sharedStyles.shared, buttonStyles.default, disabled && buttonStyles.disabled, !disabled && (pressed ? buttonStyles.active : (hovered || focused) && buttonStyles.focus)];
|
|
92
109
|
const child = React__namespace.createElement(Icon__default["default"], {
|
|
93
|
-
size:
|
|
110
|
+
size: iconSizeForButtonSize(size),
|
|
94
111
|
color: "currentColor",
|
|
95
112
|
icon: icon
|
|
96
113
|
});
|
|
@@ -124,8 +141,6 @@ const sharedStyles = aphrodite.StyleSheet.create({
|
|
|
124
141
|
alignItems: "center",
|
|
125
142
|
justifyContent: "center",
|
|
126
143
|
boxSizing: "border-box",
|
|
127
|
-
height: 40,
|
|
128
|
-
width: 40,
|
|
129
144
|
padding: 0,
|
|
130
145
|
cursor: "pointer",
|
|
131
146
|
border: "none",
|
|
@@ -137,14 +152,11 @@ const sharedStyles = aphrodite.StyleSheet.create({
|
|
|
137
152
|
":focus": {
|
|
138
153
|
WebkitTapHighlightColor: "rgba(0,0,0,0)"
|
|
139
154
|
}
|
|
140
|
-
},
|
|
141
|
-
disabled: {
|
|
142
|
-
cursor: "default"
|
|
143
155
|
}
|
|
144
156
|
});
|
|
145
157
|
const styles = {};
|
|
146
|
-
const _generateStyles = (color, kind, light) => {
|
|
147
|
-
const buttonType = color
|
|
158
|
+
const _generateStyles = (color, kind, light, size) => {
|
|
159
|
+
const buttonType = `${color}-${kind}-${light}-${size}`;
|
|
148
160
|
if (styles[buttonType]) {
|
|
149
161
|
return styles[buttonType];
|
|
150
162
|
}
|
|
@@ -157,8 +169,25 @@ const _generateStyles = (color, kind, light) => {
|
|
|
157
169
|
offBlack64,
|
|
158
170
|
offBlack
|
|
159
171
|
} = Color__default["default"];
|
|
172
|
+
const defaultColor = (() => {
|
|
173
|
+
switch (kind) {
|
|
174
|
+
case "primary":
|
|
175
|
+
return light ? white : color;
|
|
176
|
+
case "secondary":
|
|
177
|
+
return offBlack;
|
|
178
|
+
case "tertiary":
|
|
179
|
+
return offBlack64;
|
|
180
|
+
default:
|
|
181
|
+
throw new Error("IconButton kind not recognized");
|
|
182
|
+
}
|
|
183
|
+
})();
|
|
184
|
+
const pixelsForSize = targetPixelsForSize(size);
|
|
160
185
|
const newStyles = {
|
|
161
|
-
default: {
|
|
186
|
+
default: {
|
|
187
|
+
height: pixelsForSize,
|
|
188
|
+
width: pixelsForSize,
|
|
189
|
+
color: defaultColor
|
|
190
|
+
},
|
|
162
191
|
focus: {
|
|
163
192
|
color: light ? white : color,
|
|
164
193
|
borderWidth: 2,
|
|
@@ -178,37 +207,23 @@ const _generateStyles = (color, kind, light) => {
|
|
|
178
207
|
cursor: "default"
|
|
179
208
|
}
|
|
180
209
|
};
|
|
181
|
-
if (kind === "primary") {
|
|
182
|
-
newStyles["default"] = {
|
|
183
|
-
color: light ? white : color
|
|
184
|
-
};
|
|
185
|
-
} else if (kind === "secondary") {
|
|
186
|
-
newStyles["default"] = {
|
|
187
|
-
color: offBlack
|
|
188
|
-
};
|
|
189
|
-
} else if (kind === "tertiary") {
|
|
190
|
-
newStyles["default"] = {
|
|
191
|
-
color: offBlack64
|
|
192
|
-
};
|
|
193
|
-
} else {
|
|
194
|
-
throw new Error("IconButton kind not recognized");
|
|
195
|
-
}
|
|
196
210
|
styles[buttonType] = aphrodite.StyleSheet.create(newStyles);
|
|
197
211
|
return styles[buttonType];
|
|
198
212
|
};
|
|
199
213
|
|
|
200
|
-
const _excluded = ["
|
|
214
|
+
const _excluded = ["color", "disabled", "href", "kind", "light", "onClick", "size", "skipClientNav", "tabIndex", "target"];
|
|
201
215
|
const IconButton = React__namespace.forwardRef(function IconButton(props, ref) {
|
|
202
216
|
const {
|
|
203
|
-
onClick,
|
|
204
|
-
href,
|
|
205
|
-
skipClientNav,
|
|
206
|
-
tabIndex,
|
|
207
|
-
target,
|
|
208
217
|
color = "default",
|
|
218
|
+
disabled = false,
|
|
219
|
+
href,
|
|
209
220
|
kind = "primary",
|
|
210
221
|
light = false,
|
|
211
|
-
|
|
222
|
+
onClick,
|
|
223
|
+
size = "medium",
|
|
224
|
+
skipClientNav,
|
|
225
|
+
tabIndex,
|
|
226
|
+
target
|
|
212
227
|
} = props,
|
|
213
228
|
sharedProps = _objectWithoutPropertiesLoose(props, _excluded);
|
|
214
229
|
const renderClickableBehavior = router => {
|
|
@@ -223,14 +238,15 @@ const IconButton = React__namespace.forwardRef(function IconButton(props, ref) {
|
|
|
223
238
|
let childrenProps = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
|
|
224
239
|
return React__namespace.createElement(IconButtonCore, _extends({}, sharedProps, state, childrenProps, {
|
|
225
240
|
color: color,
|
|
241
|
+
disabled: disabled,
|
|
242
|
+
href: href,
|
|
226
243
|
kind: kind,
|
|
227
244
|
light: light,
|
|
228
|
-
|
|
245
|
+
ref: ref,
|
|
229
246
|
skipClientNav: skipClientNav,
|
|
230
|
-
|
|
247
|
+
size: size,
|
|
231
248
|
target: target,
|
|
232
|
-
tabIndex: tabIndex
|
|
233
|
-
ref: ref
|
|
249
|
+
tabIndex: tabIndex
|
|
234
250
|
}));
|
|
235
251
|
});
|
|
236
252
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IconSize } from "@khanacademy/wonder-blocks-icon";
|
|
2
|
+
import { IconButtonSize } from "../components/icon-button";
|
|
3
|
+
/**
|
|
4
|
+
* A function that returns the icon size for a given icon button size.
|
|
5
|
+
*/
|
|
6
|
+
export declare const iconSizeForButtonSize: (size: IconButtonSize) => IconSize;
|
|
7
|
+
/**
|
|
8
|
+
* A function that returns the size of the touch target in pixels for a given icon button size.
|
|
9
|
+
*/
|
|
10
|
+
export declare const targetPixelsForSize: (size: IconButtonSize) => number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-icon-button",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@babel/runtime": "^7.18.6",
|
|
19
|
-
"@khanacademy/wonder-blocks-clickable": "^4.0.
|
|
20
|
-
"@khanacademy/wonder-blocks-color": "^
|
|
19
|
+
"@khanacademy/wonder-blocks-clickable": "^4.0.8",
|
|
20
|
+
"@khanacademy/wonder-blocks-color": "^3.0.0",
|
|
21
21
|
"@khanacademy/wonder-blocks-core": "^6.2.0",
|
|
22
22
|
"@khanacademy/wonder-blocks-icon": "^2.1.6"
|
|
23
23
|
},
|