@khanacademy/wonder-blocks-icon-button 6.1.4 → 8.0.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 CHANGED
@@ -1,5 +1,40 @@
1
1
  # @khanacademy/wonder-blocks-icon-button
2
2
 
3
+ ## 8.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 4846e9c: - Updated to match `Button` styles.
8
+ - Reworked `kind` styles.
9
+ - Removed `secondary` style.
10
+ - Removed `khanmigo` theme to have a more consistent experience in the current site (OG).
11
+
12
+ ### Minor Changes
13
+
14
+ - 4846e9c: Add `neutral` value to `actionType` prop.
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [3dc5dac]
19
+ - @khanacademy/wonder-blocks-tokens@5.2.0
20
+ - @khanacademy/wonder-blocks-clickable@6.1.5
21
+ - @khanacademy/wonder-blocks-styles@0.2.1
22
+
23
+ ## 7.0.0
24
+
25
+ ### Major Changes
26
+
27
+ - 61f7837: Remove `light` variant, and replace it with `actionStyles.inverse` for one-off cases
28
+ - 3cacbe7: Rename `color` prop to `actionType`. Also rename the `default` value to `progressive`.
29
+
30
+ ### Patch Changes
31
+
32
+ - 86e1901: Update IconButton to use `box-shadow` instead of `outline`. Also removed the negative margins around the icon button.
33
+ - 4887c59: Use `focus` styles from `wonder-blocks-styles` to match the global focus outline.
34
+ - Updated dependencies [aace76a]
35
+ - Updated dependencies [61f7837]
36
+ - @khanacademy/wonder-blocks-styles@0.2.0
37
+
3
38
  ## 6.1.4
4
39
 
5
40
  ### Patch Changes
@@ -3,6 +3,7 @@ import type { PhosphorIconAsset } 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
5
  export type IconButtonSize = "xsmall" | "small" | "medium" | "large";
6
+ export type IconButtonActionType = "progressive" | "destructive" | "neutral";
6
7
  export type SharedProps = Partial<Omit<AriaProps, "aria-disabled">> & {
7
8
  /**
8
9
  * A unique identifier for the IconButton.
@@ -13,9 +14,16 @@ export type SharedProps = Partial<Omit<AriaProps, "aria-disabled">> & {
13
14
  */
14
15
  icon: PhosphorIconAsset;
15
16
  /**
16
- * The color of the icon button, either blue or red.
17
+ * The action type/category of the icon button.
18
+ *
19
+ * - `progressive` is used for actions that move the user forward in a flow.
20
+ * - `destructive` is used for actions that have a negative impact on the
21
+ * user.
22
+ * - `neutral` is used for actions that are neither positive nor negative.
23
+ *
24
+ * Defaults to `progressive`.
17
25
  */
18
- color?: "default" | "destructive";
26
+ actionType?: IconButtonActionType;
19
27
  /**
20
28
  * The kind of the icon button, either primary, secondary, or tertiary.
21
29
  *
@@ -27,10 +35,6 @@ export type SharedProps = Partial<Omit<AriaProps, "aria-disabled">> & {
27
35
  * In the hover/focus/press states, all variants have a border.
28
36
  */
29
37
  kind?: "primary" | "secondary" | "tertiary";
30
- /**
31
- * Whether the icon button is on a dark/colored background.
32
- */
33
- light?: boolean;
34
38
  /**
35
39
  * Whether the icon button is disabled.
36
40
  */
package/dist/es/index.js CHANGED
@@ -7,8 +7,9 @@ import { __RouterContext } from 'react-router';
7
7
  import { addStyle } from '@khanacademy/wonder-blocks-core';
8
8
  import { isClientSideUrl } from '@khanacademy/wonder-blocks-clickable';
9
9
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
10
- import { mergeTheme, createThemeContext, ThemeSwitcherContext, useScopedTheme } from '@khanacademy/wonder-blocks-theming';
11
- import { semanticColor, color, border } from '@khanacademy/wonder-blocks-tokens';
10
+ import { createThemeContext, ThemeSwitcherContext, useScopedTheme } from '@khanacademy/wonder-blocks-theming';
11
+ import { focusStyles } from '@khanacademy/wonder-blocks-styles';
12
+ import { semanticColor, border } from '@khanacademy/wonder-blocks-tokens';
12
13
 
13
14
  const iconSizeForButtonSize = size => {
14
15
  switch (size) {
@@ -29,137 +30,30 @@ const targetPixelsForSize = size => ({
29
30
  large: 48
30
31
  })[size];
31
32
 
32
- const disabledStates = {
33
- border: semanticColor.action.primary.disabled.border,
34
- background: "transparent",
35
- foreground: semanticColor.action.secondary.disabled.foreground
36
- };
37
- const disabledLightStates = {
38
- border: color.white50,
39
- background: "transparent",
40
- foreground: color.white50
41
- };
42
- const focusOutline = {
43
- border: semanticColor.focus.outer
44
- };
45
- const focusOutlineLight = {
46
- border: semanticColor.border.inverse
47
- };
48
- const baseColorStates = _extends({}, semanticColor.action.secondary, {
49
- progressive: _extends({}, semanticColor.action.secondary.progressive, {
50
- default: _extends({}, semanticColor.action.secondary.progressive.default, {
51
- border: "transparent",
52
- background: "transparent"
53
- }),
54
- focus: focusOutline,
55
- press: {
56
- border: semanticColor.action.secondary.progressive.press.border,
57
- background: "transparent",
58
- foreground: semanticColor.action.secondary.progressive.press.foreground
59
- }
60
- }),
61
- destructive: _extends({}, semanticColor.action.secondary.destructive, {
62
- default: _extends({}, semanticColor.action.secondary.destructive.default, {
63
- border: "transparent",
64
- background: "transparent"
65
- }),
66
- focus: focusOutline,
67
- press: {
68
- border: semanticColor.action.secondary.destructive.press.border,
69
- background: "transparent",
70
- foreground: semanticColor.action.secondary.destructive.press.foreground
71
- }
72
- }),
73
- disabled: {
74
- default: disabledStates,
75
- focus: focusOutline,
76
- hover: disabledStates,
77
- press: disabledStates
78
- },
79
- disabledLight: {
80
- default: disabledLightStates,
81
- focus: focusOutlineLight,
82
- hover: disabledLightStates,
83
- press: disabledLightStates
84
- }
85
- });
86
- const theme$1 = {
87
- color: {
88
- primary: _extends({}, baseColorStates, {
89
- progressiveLight: {
90
- default: {
91
- border: semanticColor.border.inverse,
92
- background: "transparent",
93
- foreground: semanticColor.text.inverse
94
- },
95
- hover: {
96
- border: semanticColor.border.inverse,
97
- background: "transparent",
98
- foreground: semanticColor.text.inverse
99
- },
100
- focus: focusOutlineLight,
101
- press: {
102
- border: color.fadedBlue,
103
- background: "transparent",
104
- foreground: color.fadedBlue
105
- }
106
- },
107
- destructiveLight: {
108
- default: {
109
- border: semanticColor.border.inverse,
110
- background: "transparent",
111
- foreground: semanticColor.text.inverse
112
- },
113
- hover: {
114
- border: semanticColor.border.inverse,
115
- background: "transparent",
116
- foreground: semanticColor.text.inverse
117
- },
118
- focus: focusOutlineLight,
119
- press: {
120
- border: color.fadedRed,
121
- background: "transparent",
122
- foreground: color.fadedRed
123
- }
124
- }
125
- }),
126
- secondary: _extends({}, baseColorStates, {
127
- progressive: _extends({}, baseColorStates.progressive, {
128
- default: _extends({}, baseColorStates.progressive.default, {
129
- foreground: semanticColor.text.primary
130
- })
131
- }),
132
- destructive: _extends({}, baseColorStates.destructive, {
133
- default: _extends({}, baseColorStates.destructive.default, {
134
- foreground: semanticColor.icon.secondary
135
- })
136
- })
137
- }),
138
- tertiary: _extends({}, baseColorStates, {
139
- progressive: _extends({}, baseColorStates.progressive, {
140
- default: _extends({}, baseColorStates.progressive.default, {
141
- foreground: semanticColor.icon.primary
142
- }),
143
- hover: _extends({}, baseColorStates.progressive.hover, {
144
- background: "transparent"
145
- })
146
- }),
147
- destructive: _extends({}, baseColorStates.destructive, {
148
- default: _extends({}, baseColorStates.destructive.default, {
149
- foreground: semanticColor.icon.primary
150
- }),
151
- hover: _extends({}, baseColorStates.destructive.hover, {
152
- background: "transparent"
153
- })
154
- })
155
- })
156
- },
33
+ const theme = {
34
+ color: semanticColor.action,
157
35
  border: {
36
+ offset: {
37
+ primary: border.width.thin,
38
+ secondary: -border.width.thin,
39
+ tertiary: -border.width.thin
40
+ },
158
41
  width: {
159
- default: border.width.thin,
160
- active: border.width.none,
161
- hovered: border.width.thin,
162
- hoveredInverse: border.width.thin
42
+ primary: {
43
+ default: border.width.none,
44
+ hover: border.width.thin,
45
+ press: border.width.thin
46
+ },
47
+ secondary: {
48
+ default: border.width.hairline,
49
+ hover: border.width.thin,
50
+ press: border.width.thin
51
+ },
52
+ tertiary: {
53
+ default: border.width.none,
54
+ hover: border.width.thin,
55
+ press: border.width.thin
56
+ }
163
57
  },
164
58
  radius: {
165
59
  default: border.radius.medium_4
@@ -167,75 +61,20 @@ const theme$1 = {
167
61
  }
168
62
  };
169
63
 
170
- const primaryState = {
171
- hover: {
172
- foreground: semanticColor.khanmigo.primary
173
- }
174
- };
175
- const primaryLightState = {
176
- hover: {
177
- background: semanticColor.surface.primary,
178
- foreground: semanticColor.khanmigo.primary
179
- },
180
- press: {
181
- border: "transparent",
182
- background: color.white64,
183
- foreground: semanticColor.khanmigo.primary
184
- }
185
- };
186
- const actionType = semanticColor.action.primary;
187
- const theme = mergeTheme(theme$1, {
188
- color: {
189
- primary: {
190
- progressive: primaryState,
191
- destructive: primaryState,
192
- progressiveLight: primaryLightState,
193
- destructiveLight: primaryLightState
194
- },
195
- secondary: {
196
- progressive: {
197
- hover: actionType.progressive.hover,
198
- press: actionType.progressive.press
199
- },
200
- destructive: {
201
- hover: actionType.destructive.hover,
202
- press: actionType.destructive.press
203
- }
204
- },
205
- tertiary: {
206
- progressive: {
207
- hover: actionType.progressive.hover,
208
- press: actionType.progressive.press
209
- },
210
- destructive: {
211
- hover: actionType.destructive.hover,
212
- press: actionType.destructive.press
213
- }
214
- }
215
- },
216
- border: {
217
- width: {
218
- hovered: border.width.none,
219
- hoveredInverse: border.width.none
220
- }
221
- }
222
- });
223
-
224
64
  const themes = {
225
- default: theme$1,
226
- khanmigo: theme
65
+ default: theme
227
66
  };
228
- const IconButtonThemeContext = createThemeContext(theme$1);
67
+ const IconButtonThemeContext = createThemeContext(theme);
229
68
  function ThemedIconButton(props) {
230
69
  var _themes$currentTheme;
231
70
  const currentTheme = React.useContext(ThemeSwitcherContext);
232
- const theme = (_themes$currentTheme = themes[currentTheme]) != null ? _themes$currentTheme : theme$1;
71
+ const theme$1 = (_themes$currentTheme = themes[currentTheme]) != null ? _themes$currentTheme : theme;
233
72
  return React.createElement(IconButtonThemeContext.Provider, {
234
- value: theme
73
+ value: theme$1
235
74
  }, props.children);
236
75
  }
237
76
 
238
- const _excluded$1 = ["color", "disabled", "href", "icon", "kind", "light", "size", "skipClientNav", "style", "testId", "type"];
77
+ const _excluded$1 = ["actionType", "disabled", "href", "icon", "kind", "size", "skipClientNav", "style", "testId", "type"];
239
78
  function IconChooser({
240
79
  icon,
241
80
  size
@@ -262,12 +101,11 @@ const StyledButton = addStyle("button");
262
101
  const StyledLink = addStyle(Link);
263
102
  const IconButtonCore = React.forwardRef(function IconButtonCore(props, ref) {
264
103
  const {
265
- color,
104
+ actionType,
266
105
  disabled,
267
106
  href,
268
107
  icon,
269
108
  kind = "primary",
270
- light = false,
271
109
  size = "medium",
272
110
  skipClientNav,
273
111
  style,
@@ -280,7 +118,7 @@ const IconButtonCore = React.forwardRef(function IconButtonCore(props, ref) {
280
118
  themeName
281
119
  } = useScopedTheme(IconButtonThemeContext);
282
120
  const renderInner = router => {
283
- const buttonStyles = _generateStyles(color, !!disabled, kind, light, size, theme, themeName);
121
+ const buttonStyles = _generateStyles(actionType, !!disabled, kind, size, theme, themeName);
284
122
  const defaultStyle = [sharedStyles.shared, buttonStyles.default, disabled && buttonStyles.disabled];
285
123
  const child = React.createElement(IconChooser, {
286
124
  size: size,
@@ -323,136 +161,55 @@ const sharedStyles = StyleSheet.create({
323
161
  outline: "none",
324
162
  textDecoration: "none",
325
163
  background: "none",
326
- margin: -8,
164
+ margin: 0,
327
165
  touchAction: "manipulation"
328
166
  }
329
167
  });
330
168
  const styles = {};
331
- function getActionType(buttonColor, disabled) {
332
- const actionType = buttonColor === "destructive" ? "destructive" : "progressive";
333
- if (disabled) {
334
- return "disabled";
335
- }
336
- return actionType;
337
- }
338
- function getStylesByKind(buttonColor, disabled, kind, light, theme) {
339
- let actionType = getActionType(buttonColor, disabled);
340
- const themeVariant = theme.color[kind][actionType];
341
- if (kind === "primary") {
342
- if (light) {
343
- actionType = `${actionType}Light`;
344
- }
345
- const _themeVariant = theme.color[kind][actionType];
346
- return {
347
- default: {
348
- borderColor: _themeVariant.default.border,
349
- background: _themeVariant.default.background,
350
- color: _themeVariant.default.foreground
351
- },
352
- ":hover": {
353
- background: _themeVariant.hover.background,
354
- color: _themeVariant.hover.foreground,
355
- outlineColor: _themeVariant.hover.border,
356
- outlineOffset: 1,
357
- outlineStyle: "solid",
358
- outlineWidth: light ? theme.border.width.hoveredInverse : theme.border.width.hovered
359
- },
360
- ":focus-visible": {
361
- outlineColor: _themeVariant.focus.border
362
- },
363
- ":active": {
364
- borderColor: _themeVariant.press.border,
365
- background: _themeVariant.press.background,
366
- color: _themeVariant.press.foreground,
367
- outlineColor: _themeVariant.press.border
368
- },
369
- disabled: {
370
- background: _themeVariant.default.background,
371
- color: _themeVariant.default.foreground,
372
- outlineColor: _themeVariant.focus.border
373
- }
374
- };
375
- }
376
- if (kind === "secondary" || kind === "tertiary") {
377
- return {
378
- default: {
379
- borderColor: themeVariant.default.border,
380
- background: themeVariant.default.background,
381
- color: themeVariant.default.foreground
382
- },
383
- ":hover": {
384
- borderColor: themeVariant.hover.border,
385
- background: themeVariant.hover.background,
386
- color: themeVariant.hover.foreground,
387
- outlineWidth: theme.border.width.active
388
- },
389
- ":focus-visible": {
390
- outlineColor: themeVariant.focus.border
391
- },
392
- ":active": {
393
- borderColor: themeVariant.press.border,
394
- background: themeVariant.press.background,
395
- color: themeVariant.press.foreground,
396
- outlineColor: themeVariant.press.border,
397
- outlineWidth: theme.border.width.active
398
- },
399
- disabled: {
400
- background: themeVariant.default.background,
401
- color: themeVariant.default.foreground,
402
- outlineColor: themeVariant.focus.border
403
- }
404
- };
405
- }
406
- return {
407
- default: {},
408
- ":hover": {},
409
- ":focus-visible": {},
410
- ":active": {},
411
- disabled: {}
412
- };
413
- }
414
- const _generateStyles = (buttonColor = "default", disabled, kind, light, size, theme, themeName) => {
415
- const buttonType = `${buttonColor}-d_${disabled}-${kind}-l_${light}-${size}-${themeName}`;
169
+ const _generateStyles = (actionType = "progressive", disabled, kind, size, theme, themeName) => {
170
+ const buttonType = `${actionType}-d_${disabled}-${kind}-${size}-${themeName}`;
416
171
  if (styles[buttonType]) {
417
172
  return styles[buttonType];
418
173
  }
419
- if (light && kind !== "primary") {
420
- throw new Error("Light is only supported for primary IconButtons");
421
- }
422
174
  const pixelsForSize = targetPixelsForSize(size);
423
- const kindOverrides = getStylesByKind(buttonColor, disabled, kind, light, theme);
424
- const disabledStatesStyles = kindOverrides.disabled;
175
+ const borderWidthKind = theme.border.width[kind];
176
+ const outlineOffsetKind = theme.border.offset[kind];
177
+ const themeVariant = theme.color[kind][actionType];
178
+ const disabledState = theme.color[kind].disabled;
179
+ const disabledStatesStyles = {
180
+ borderColor: disabledState.border,
181
+ background: disabledState.background,
182
+ color: disabledState.foreground
183
+ };
425
184
  const newStyles = {
426
185
  default: _extends({
427
186
  height: pixelsForSize,
428
187
  width: pixelsForSize,
429
- borderRadius: theme.border.radius.default
430
- }, kindOverrides.default, {
431
- ":hover": _extends({
432
- boxShadow: "none",
433
- borderRadius: theme.border.radius.default,
434
- outlineWidth: theme.border.width.default
435
- }, kindOverrides[":hover"]),
188
+ borderRadius: theme.border.radius.default,
189
+ borderStyle: "solid",
190
+ borderWidth: borderWidthKind.default,
191
+ borderColor: themeVariant.default.border,
192
+ background: themeVariant.default.background,
193
+ color: themeVariant.default.foreground,
194
+ ":hover:not([aria-disabled=true])": {
195
+ background: themeVariant.hover.background,
196
+ color: themeVariant.hover.foreground,
197
+ outline: kind === "primary" ? `${borderWidthKind.hover}px solid ${themeVariant.hover.border}` : undefined,
198
+ outlineOffset: kind === "primary" ? outlineOffsetKind : undefined,
199
+ border: kind !== "primary" ? `${borderWidthKind.hover}px solid ${themeVariant.hover.border}` : undefined
200
+ },
436
201
  ["@media not (hover: hover)"]: {
437
202
  ":hover": {
438
- boxShadow: "none",
439
- borderRadius: theme.border.radius.default,
440
- outline: "none",
441
203
  backgroundColor: "transparent"
442
204
  }
443
- },
444
- ":focus-visible": _extends({
445
- outlineWidth: theme.border.width.default,
446
- outlineOffset: 1,
447
- outlineStyle: "solid",
448
- borderRadius: theme.border.radius.default
449
- }, kindOverrides[":focus-visible"]),
450
- ":active": _extends({
451
- outlineWidth: theme.border.width.default,
452
- outlineOffset: 1,
453
- outlineStyle: "solid",
454
- borderRadius: theme.border.radius.default
455
- }, kindOverrides[":active"])
205
+ }
206
+ }, focusStyles.focus, {
207
+ ":active:not([aria-disabled=true])": {
208
+ outlineColor: kind === "primary" ? themeVariant.press.border : "undefined",
209
+ border: kind !== "primary" ? `${borderWidthKind.hover}px solid ${themeVariant.press.border}` : undefined,
210
+ background: themeVariant.press.background,
211
+ color: themeVariant.press.foreground
212
+ }
456
213
  }),
457
214
  disabled: _extends({
458
215
  cursor: "not-allowed"
@@ -470,14 +227,13 @@ const _generateStyles = (buttonColor = "default", disabled, kind, light, size, t
470
227
  return styles[buttonType];
471
228
  };
472
229
 
473
- const _excluded = ["color", "disabled", "href", "kind", "light", "size", "skipClientNav", "tabIndex", "target", "type"];
230
+ const _excluded = ["actionType", "disabled", "href", "kind", "size", "skipClientNav", "tabIndex", "target", "type"];
474
231
  const IconButton = React.forwardRef(function IconButton(props, ref) {
475
232
  const {
476
- color = "default",
233
+ actionType = "progressive",
477
234
  disabled = false,
478
235
  href,
479
236
  kind = "primary",
480
- light = false,
481
237
  size = "medium",
482
238
  skipClientNav,
483
239
  tabIndex,
@@ -500,11 +256,10 @@ const IconButton = React.forwardRef(function IconButton(props, ref) {
500
256
  }
501
257
  }
502
258
  return React.createElement(ThemedIconButton, null, React.createElement(IconButtonCore, _extends({}, sharedProps, {
503
- color: color,
259
+ actionType: actionType,
504
260
  disabled: disabled,
505
261
  href: href,
506
262
  kind: kind,
507
- light: light,
508
263
  ref: ref,
509
264
  skipClientNav: skipClientNav,
510
265
  size: size,