@khanacademy/wonder-blocks-button 6.3.9 → 6.3.11
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/package.json +8 -8
- package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +0 -3325
- package/src/__tests__/custom-snapshot.test.tsx +0 -110
- package/src/components/__tests__/button-with-icon.test.tsx +0 -277
- package/src/components/__tests__/button.test.tsx +0 -850
- package/src/components/__tests__/button.typestest.tsx +0 -48
- package/src/components/button-core.tsx +0 -519
- package/src/components/button-icon.tsx +0 -47
- package/src/components/button.tsx +0 -309
- package/src/index.ts +0 -5
- package/src/themes/default.ts +0 -163
- package/src/themes/khanmigo.ts +0 -58
- package/src/themes/themed-button.tsx +0 -43
- package/tsconfig-build.json +0 -17
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import Button from "../button";
|
|
4
|
-
|
|
5
|
-
<Button beforeNav={() => Promise.resolve()}>Hello, world!</Button>;
|
|
6
|
-
|
|
7
|
-
<Button safeWithNav={() => Promise.resolve()}>Hello, world!</Button>;
|
|
8
|
-
|
|
9
|
-
// It's okay to use onClick with href
|
|
10
|
-
<Button href="/foo" onClick={() => {}}>
|
|
11
|
-
Hello, world!
|
|
12
|
-
</Button>;
|
|
13
|
-
|
|
14
|
-
<Button href="/foo" beforeNav={() => Promise.resolve()}>
|
|
15
|
-
Hello, world!
|
|
16
|
-
</Button>;
|
|
17
|
-
|
|
18
|
-
<Button href="/foo" safeWithNav={() => Promise.resolve()}>
|
|
19
|
-
Hello, world!
|
|
20
|
-
</Button>;
|
|
21
|
-
|
|
22
|
-
// All three of these props can be used together
|
|
23
|
-
<Button
|
|
24
|
-
href="/foo"
|
|
25
|
-
beforeNav={() => Promise.resolve()}
|
|
26
|
-
safeWithNav={() => Promise.resolve()}
|
|
27
|
-
onClick={() => {}}
|
|
28
|
-
>
|
|
29
|
-
Hello, world!
|
|
30
|
-
</Button>;
|
|
31
|
-
|
|
32
|
-
// It's also fine to use href by itself
|
|
33
|
-
<Button href="/foo">Hello, world!</Button>;
|
|
34
|
-
|
|
35
|
-
const getUrl = () => "/foo";
|
|
36
|
-
|
|
37
|
-
// This test purposefully uses a function to get a string to pass with href.
|
|
38
|
-
// This can trigger errors if there are ambiguous cases in the disjoint union
|
|
39
|
-
// type being used to describe the props. It's unclear why this error isn't
|
|
40
|
-
// trigger by passing a string directly as the href.
|
|
41
|
-
<Button href={getUrl()}>Hello, world!</Button>;
|
|
42
|
-
|
|
43
|
-
<Button href="/foo" type="submit">
|
|
44
|
-
Hello, world!
|
|
45
|
-
</Button>;
|
|
46
|
-
|
|
47
|
-
// type="submit" on its own is fine.
|
|
48
|
-
<Button type="submit">Hello, world!</Button>;
|
|
@@ -1,519 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {CSSProperties, StyleSheet} from "aphrodite";
|
|
3
|
-
import {Link} from "react-router-dom";
|
|
4
|
-
import {__RouterContext} from "react-router";
|
|
5
|
-
|
|
6
|
-
import {LabelLarge, LabelSmall} from "@khanacademy/wonder-blocks-typography";
|
|
7
|
-
import {addStyle, View} from "@khanacademy/wonder-blocks-core";
|
|
8
|
-
import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
|
|
9
|
-
import {isClientSideUrl} from "@khanacademy/wonder-blocks-clickable";
|
|
10
|
-
import {
|
|
11
|
-
ThemedStylesFn,
|
|
12
|
-
useScopedTheme,
|
|
13
|
-
useStyles,
|
|
14
|
-
} from "@khanacademy/wonder-blocks-theming";
|
|
15
|
-
|
|
16
|
-
import type {
|
|
17
|
-
ChildrenProps,
|
|
18
|
-
ClickableState,
|
|
19
|
-
} from "@khanacademy/wonder-blocks-clickable";
|
|
20
|
-
import type {SharedProps} from "./button";
|
|
21
|
-
import {ButtonThemeContext, ButtonThemeContract} from "../themes/themed-button";
|
|
22
|
-
import {ButtonIcon} from "./button-icon";
|
|
23
|
-
|
|
24
|
-
type Props = SharedProps & ChildrenProps & ClickableState;
|
|
25
|
-
|
|
26
|
-
const StyledAnchor = addStyle("a");
|
|
27
|
-
const StyledButton = addStyle("button");
|
|
28
|
-
const StyledLink = addStyle(Link);
|
|
29
|
-
|
|
30
|
-
const ButtonCore: React.ForwardRefExoticComponent<
|
|
31
|
-
Props &
|
|
32
|
-
React.RefAttributes<typeof Link | HTMLButtonElement | HTMLAnchorElement>
|
|
33
|
-
> = React.forwardRef<
|
|
34
|
-
typeof Link | HTMLButtonElement | HTMLAnchorElement,
|
|
35
|
-
Props
|
|
36
|
-
>(function ButtonCore(props: Props, ref) {
|
|
37
|
-
const {theme, themeName} = useScopedTheme(ButtonThemeContext);
|
|
38
|
-
const sharedStyles = useStyles(themedSharedStyles, theme);
|
|
39
|
-
|
|
40
|
-
const renderInner = (router: any): React.ReactNode => {
|
|
41
|
-
const {
|
|
42
|
-
children,
|
|
43
|
-
skipClientNav,
|
|
44
|
-
color,
|
|
45
|
-
disabled: disabledProp,
|
|
46
|
-
focused,
|
|
47
|
-
hovered,
|
|
48
|
-
href = undefined,
|
|
49
|
-
kind = "primary",
|
|
50
|
-
labelStyle,
|
|
51
|
-
light = false,
|
|
52
|
-
pressed,
|
|
53
|
-
size = "medium",
|
|
54
|
-
style,
|
|
55
|
-
testId,
|
|
56
|
-
type = undefined,
|
|
57
|
-
spinner,
|
|
58
|
-
startIcon,
|
|
59
|
-
endIcon,
|
|
60
|
-
id,
|
|
61
|
-
waiting: _,
|
|
62
|
-
...restProps
|
|
63
|
-
} = props;
|
|
64
|
-
|
|
65
|
-
const buttonStyles = _generateStyles(
|
|
66
|
-
color,
|
|
67
|
-
kind,
|
|
68
|
-
light,
|
|
69
|
-
size,
|
|
70
|
-
theme,
|
|
71
|
-
themeName,
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const disabled = spinner || disabledProp;
|
|
75
|
-
|
|
76
|
-
const defaultStyle = [
|
|
77
|
-
sharedStyles.shared,
|
|
78
|
-
disabled && sharedStyles.disabled,
|
|
79
|
-
startIcon && sharedStyles.withStartIcon,
|
|
80
|
-
endIcon && sharedStyles.withEndIcon,
|
|
81
|
-
buttonStyles.default,
|
|
82
|
-
disabled && buttonStyles.disabled,
|
|
83
|
-
// apply focus effect only to default and secondary buttons
|
|
84
|
-
kind !== "tertiary" &&
|
|
85
|
-
!disabled &&
|
|
86
|
-
(pressed
|
|
87
|
-
? buttonStyles.active
|
|
88
|
-
: (hovered || focused) && buttonStyles.focus),
|
|
89
|
-
kind === "tertiary" &&
|
|
90
|
-
!pressed &&
|
|
91
|
-
focused && [
|
|
92
|
-
buttonStyles.focus,
|
|
93
|
-
disabled && buttonStyles.disabledFocus,
|
|
94
|
-
],
|
|
95
|
-
size === "small" && sharedStyles.small,
|
|
96
|
-
size === "large" && sharedStyles.large,
|
|
97
|
-
];
|
|
98
|
-
|
|
99
|
-
const commonProps = {
|
|
100
|
-
"data-testid": testId,
|
|
101
|
-
id: id,
|
|
102
|
-
role: "button",
|
|
103
|
-
style: [defaultStyle, style],
|
|
104
|
-
...restProps,
|
|
105
|
-
} as const;
|
|
106
|
-
|
|
107
|
-
const Label = size === "small" ? LabelSmall : LabelLarge;
|
|
108
|
-
|
|
109
|
-
const label = (
|
|
110
|
-
<Label
|
|
111
|
-
style={[
|
|
112
|
-
sharedStyles.text,
|
|
113
|
-
size === "large" && sharedStyles.largeText,
|
|
114
|
-
labelStyle,
|
|
115
|
-
spinner && sharedStyles.hiddenText,
|
|
116
|
-
kind === "tertiary" && sharedStyles.textWithFocus,
|
|
117
|
-
// apply press/hover effects on the label
|
|
118
|
-
kind === "tertiary" &&
|
|
119
|
-
!disabled &&
|
|
120
|
-
(pressed
|
|
121
|
-
? [buttonStyles.hover, buttonStyles.active]
|
|
122
|
-
: hovered && buttonStyles.hover),
|
|
123
|
-
]}
|
|
124
|
-
testId={testId ? `${testId}-inner-label` : undefined}
|
|
125
|
-
>
|
|
126
|
-
{children}
|
|
127
|
-
</Label>
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const sizeMapping = {
|
|
131
|
-
medium: "small",
|
|
132
|
-
small: "xsmall",
|
|
133
|
-
large: "medium",
|
|
134
|
-
} as const;
|
|
135
|
-
|
|
136
|
-
// We have to use `medium` for both md and lg buttons so we can fit the
|
|
137
|
-
// icons in large buttons.
|
|
138
|
-
const iconSize = size === "small" ? "small" : "medium";
|
|
139
|
-
|
|
140
|
-
const contents = (
|
|
141
|
-
<React.Fragment>
|
|
142
|
-
{startIcon && (
|
|
143
|
-
<View
|
|
144
|
-
// The start icon doesn't have the circle around it
|
|
145
|
-
// in the Khanmigo theme, but we wrap it with
|
|
146
|
-
// iconWrapper anyway to give it the same spacing
|
|
147
|
-
// as the end icon so the button is symmetrical.
|
|
148
|
-
style={sharedStyles.iconWrapper}
|
|
149
|
-
>
|
|
150
|
-
<ButtonIcon
|
|
151
|
-
size={iconSize}
|
|
152
|
-
icon={startIcon}
|
|
153
|
-
style={[
|
|
154
|
-
sharedStyles.startIcon,
|
|
155
|
-
kind === "tertiary" &&
|
|
156
|
-
sharedStyles.tertiaryStartIcon,
|
|
157
|
-
]}
|
|
158
|
-
testId={testId ? `${testId}-start-icon` : undefined}
|
|
159
|
-
/>
|
|
160
|
-
</View>
|
|
161
|
-
)}
|
|
162
|
-
{label}
|
|
163
|
-
{spinner && (
|
|
164
|
-
<CircularSpinner
|
|
165
|
-
style={sharedStyles.spinner}
|
|
166
|
-
size={sizeMapping[size]}
|
|
167
|
-
light={kind === "primary"}
|
|
168
|
-
testId={`${testId || "button"}-spinner`}
|
|
169
|
-
/>
|
|
170
|
-
)}
|
|
171
|
-
{endIcon && (
|
|
172
|
-
<View
|
|
173
|
-
testId={
|
|
174
|
-
testId ? `${testId}-end-icon-wrapper` : undefined
|
|
175
|
-
}
|
|
176
|
-
style={[
|
|
177
|
-
styles.endIcon,
|
|
178
|
-
sharedStyles.iconWrapper,
|
|
179
|
-
sharedStyles.endIconWrapper,
|
|
180
|
-
kind === "tertiary" &&
|
|
181
|
-
sharedStyles.endIconWrapperTertiary,
|
|
182
|
-
(focused || hovered) &&
|
|
183
|
-
kind !== "primary" &&
|
|
184
|
-
sharedStyles.iconWrapperSecondaryHovered,
|
|
185
|
-
]}
|
|
186
|
-
>
|
|
187
|
-
<ButtonIcon
|
|
188
|
-
size={iconSize}
|
|
189
|
-
icon={endIcon}
|
|
190
|
-
testId={testId ? `${testId}-end-icon` : undefined}
|
|
191
|
-
/>
|
|
192
|
-
</View>
|
|
193
|
-
)}
|
|
194
|
-
</React.Fragment>
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
if (href && !disabled) {
|
|
198
|
-
return router && !skipClientNav && isClientSideUrl(href) ? (
|
|
199
|
-
<StyledLink
|
|
200
|
-
{...commonProps}
|
|
201
|
-
to={href}
|
|
202
|
-
ref={ref as React.Ref<typeof Link>}
|
|
203
|
-
>
|
|
204
|
-
{contents}
|
|
205
|
-
</StyledLink>
|
|
206
|
-
) : (
|
|
207
|
-
<StyledAnchor
|
|
208
|
-
{...commonProps}
|
|
209
|
-
href={href}
|
|
210
|
-
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
211
|
-
>
|
|
212
|
-
{contents}
|
|
213
|
-
</StyledAnchor>
|
|
214
|
-
);
|
|
215
|
-
} else {
|
|
216
|
-
return (
|
|
217
|
-
<StyledButton
|
|
218
|
-
type={type || "button"}
|
|
219
|
-
{...commonProps}
|
|
220
|
-
aria-disabled={disabled}
|
|
221
|
-
ref={ref as React.Ref<HTMLButtonElement>}
|
|
222
|
-
>
|
|
223
|
-
{contents}
|
|
224
|
-
</StyledButton>
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
return (
|
|
230
|
-
<__RouterContext.Consumer>
|
|
231
|
-
{(router) => renderInner(router)}
|
|
232
|
-
</__RouterContext.Consumer>
|
|
233
|
-
);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
export default ButtonCore;
|
|
237
|
-
|
|
238
|
-
const themedSharedStyles: ThemedStylesFn<ButtonThemeContract> = (theme) => ({
|
|
239
|
-
shared: {
|
|
240
|
-
position: "relative",
|
|
241
|
-
display: "inline-flex",
|
|
242
|
-
alignItems: "center",
|
|
243
|
-
justifyContent: "center",
|
|
244
|
-
height: theme.size.height.medium,
|
|
245
|
-
paddingTop: 0,
|
|
246
|
-
paddingBottom: 0,
|
|
247
|
-
paddingLeft: theme.padding.large,
|
|
248
|
-
paddingRight: theme.padding.large,
|
|
249
|
-
border: "none",
|
|
250
|
-
borderRadius: theme.border.radius.default,
|
|
251
|
-
cursor: "pointer",
|
|
252
|
-
outline: "none",
|
|
253
|
-
textDecoration: "none",
|
|
254
|
-
boxSizing: "border-box",
|
|
255
|
-
// This removes the 300ms click delay on mobile browsers by indicating that
|
|
256
|
-
// "double-tap to zoom" shouldn't be used on this element.
|
|
257
|
-
touchAction: "manipulation",
|
|
258
|
-
userSelect: "none",
|
|
259
|
-
":focus": {
|
|
260
|
-
// Mobile: Removes a blue highlight style shown when the user clicks a button
|
|
261
|
-
WebkitTapHighlightColor: "rgba(0,0,0,0)",
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
disabled: {
|
|
265
|
-
cursor: "auto",
|
|
266
|
-
},
|
|
267
|
-
small: {
|
|
268
|
-
borderRadius: theme.border.radius.small,
|
|
269
|
-
height: theme.size.height.small,
|
|
270
|
-
},
|
|
271
|
-
large: {
|
|
272
|
-
borderRadius: theme.border.radius.large,
|
|
273
|
-
height: theme.size.height.large,
|
|
274
|
-
},
|
|
275
|
-
text: {
|
|
276
|
-
alignItems: "center",
|
|
277
|
-
fontWeight: theme.font.weight.default,
|
|
278
|
-
whiteSpace: "nowrap",
|
|
279
|
-
overflow: "hidden",
|
|
280
|
-
textOverflow: "ellipsis",
|
|
281
|
-
display: "inline-block", // allows the button text to truncate
|
|
282
|
-
pointerEvents: "none", // fix Safari bug where the browser was eating mouse events
|
|
283
|
-
},
|
|
284
|
-
largeText: {
|
|
285
|
-
fontSize: theme.font.size.large,
|
|
286
|
-
lineHeight: `${theme.font.lineHeight.large}px`,
|
|
287
|
-
},
|
|
288
|
-
textWithFocus: {
|
|
289
|
-
position: "relative", // allows the tertiary button border to use the label width
|
|
290
|
-
},
|
|
291
|
-
hiddenText: {
|
|
292
|
-
visibility: "hidden",
|
|
293
|
-
},
|
|
294
|
-
spinner: {
|
|
295
|
-
position: "absolute",
|
|
296
|
-
},
|
|
297
|
-
startIcon: {
|
|
298
|
-
marginRight: theme.padding.small,
|
|
299
|
-
marginLeft: theme.margin.icon.offset,
|
|
300
|
-
},
|
|
301
|
-
tertiaryStartIcon: {
|
|
302
|
-
// Undo the negative padding from startIcon since tertiary
|
|
303
|
-
// buttons don't have extra padding.
|
|
304
|
-
marginLeft: 0,
|
|
305
|
-
},
|
|
306
|
-
endIcon: {
|
|
307
|
-
marginLeft: theme.padding.small,
|
|
308
|
-
},
|
|
309
|
-
iconWrapper: {
|
|
310
|
-
borderRadius: theme.border.radius.icon,
|
|
311
|
-
padding: theme.padding.xsmall,
|
|
312
|
-
// View has a default minWidth of 0, which causes the label text
|
|
313
|
-
// to encroach on the icon when it needs to truncate. We can fix
|
|
314
|
-
// this by setting the minWidth to auto.
|
|
315
|
-
minWidth: "auto",
|
|
316
|
-
},
|
|
317
|
-
iconWrapperSecondaryHovered: {
|
|
318
|
-
backgroundColor: theme.color.bg.icon.secondaryHover,
|
|
319
|
-
color: theme.color.text.icon.secondaryHover,
|
|
320
|
-
},
|
|
321
|
-
endIconWrapper: {
|
|
322
|
-
marginLeft: theme.padding.small,
|
|
323
|
-
marginRight: theme.margin.icon.offset,
|
|
324
|
-
},
|
|
325
|
-
endIconWrapperTertiary: {
|
|
326
|
-
marginRight: 0,
|
|
327
|
-
},
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
const styles: Record<string, any> = {};
|
|
331
|
-
|
|
332
|
-
// export for testing only
|
|
333
|
-
export const _generateStyles = (
|
|
334
|
-
buttonColor = "default",
|
|
335
|
-
kind: "primary" | "secondary" | "tertiary",
|
|
336
|
-
light: boolean,
|
|
337
|
-
size: "large" | "medium" | "small",
|
|
338
|
-
theme: ButtonThemeContract,
|
|
339
|
-
themeName: string,
|
|
340
|
-
) => {
|
|
341
|
-
const color: string =
|
|
342
|
-
buttonColor === "destructive"
|
|
343
|
-
? theme.color.bg.critical.default
|
|
344
|
-
: theme.color.bg.action.default;
|
|
345
|
-
|
|
346
|
-
const buttonType = `${color}-${kind}-${light}-${size}-${themeName}`;
|
|
347
|
-
|
|
348
|
-
if (styles[buttonType]) {
|
|
349
|
-
return styles[buttonType];
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const fadedColor =
|
|
353
|
-
buttonColor === "destructive"
|
|
354
|
-
? theme.color.bg.critical.inverse
|
|
355
|
-
: theme.color.bg.action.inverse;
|
|
356
|
-
const activeColor =
|
|
357
|
-
buttonColor === "destructive"
|
|
358
|
-
? theme.color.bg.critical.active
|
|
359
|
-
: theme.color.bg.action.active;
|
|
360
|
-
const padding =
|
|
361
|
-
size === "large" ? theme.padding.xLarge : theme.padding.large;
|
|
362
|
-
|
|
363
|
-
let newStyles: Record<string, CSSProperties> = {};
|
|
364
|
-
if (kind === "primary") {
|
|
365
|
-
const boxShadowInnerColor: string = light
|
|
366
|
-
? theme.color.bg.primary.inverse
|
|
367
|
-
: theme.color.bg.primary.default;
|
|
368
|
-
|
|
369
|
-
newStyles = {
|
|
370
|
-
default: {
|
|
371
|
-
background: light ? theme.color.bg.primary.default : color,
|
|
372
|
-
color: light ? color : theme.color.text.inverse,
|
|
373
|
-
paddingLeft: padding,
|
|
374
|
-
paddingRight: padding,
|
|
375
|
-
},
|
|
376
|
-
focus: {
|
|
377
|
-
// This assumes a background of white for the regular button and
|
|
378
|
-
// a background of darkBlue for the light version. The inner
|
|
379
|
-
// box shadow/ring is also small enough for a slight variation
|
|
380
|
-
// in the background color not to matter too much.
|
|
381
|
-
boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
|
|
382
|
-
light ? theme.color.bg.primary.default : color
|
|
383
|
-
}`,
|
|
384
|
-
},
|
|
385
|
-
active: {
|
|
386
|
-
boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
|
|
387
|
-
light ? fadedColor : activeColor
|
|
388
|
-
}`,
|
|
389
|
-
background: light ? fadedColor : activeColor,
|
|
390
|
-
color: light ? activeColor : fadedColor,
|
|
391
|
-
},
|
|
392
|
-
disabled: {
|
|
393
|
-
background: light
|
|
394
|
-
? fadedColor
|
|
395
|
-
: theme.color.bg.primary.disabled,
|
|
396
|
-
color: light ? color : theme.color.text.primary.disabled,
|
|
397
|
-
cursor: "default",
|
|
398
|
-
":focus": {
|
|
399
|
-
boxShadow: `0 0 0 1px ${
|
|
400
|
-
light
|
|
401
|
-
? theme.color.bg.primary.disabled
|
|
402
|
-
: theme.color.bg.primary.default
|
|
403
|
-
}, 0 0 0 3px ${
|
|
404
|
-
light ? fadedColor : theme.color.bg.primary.disabled
|
|
405
|
-
}`,
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
|
-
};
|
|
409
|
-
} else if (kind === "secondary") {
|
|
410
|
-
const secondaryBorderColor =
|
|
411
|
-
buttonColor === "destructive"
|
|
412
|
-
? theme.color.border.secondary.critical
|
|
413
|
-
: theme.color.border.secondary.action;
|
|
414
|
-
const secondaryActiveColor =
|
|
415
|
-
buttonColor === "destructive"
|
|
416
|
-
? theme.color.bg.secondary.active.critical
|
|
417
|
-
: theme.color.bg.secondary.active.action;
|
|
418
|
-
|
|
419
|
-
newStyles = {
|
|
420
|
-
default: {
|
|
421
|
-
background: light
|
|
422
|
-
? theme.color.bg.secondary.inverse
|
|
423
|
-
: theme.color.bg.secondary.default,
|
|
424
|
-
color: light ? theme.color.text.inverse : color,
|
|
425
|
-
borderColor: light
|
|
426
|
-
? theme.color.border.secondary.inverse
|
|
427
|
-
: secondaryBorderColor,
|
|
428
|
-
borderStyle: "solid",
|
|
429
|
-
borderWidth: theme.border.width.secondary,
|
|
430
|
-
paddingLeft: padding,
|
|
431
|
-
paddingRight: padding,
|
|
432
|
-
},
|
|
433
|
-
focus: {
|
|
434
|
-
background: light
|
|
435
|
-
? theme.color.bg.secondary.inverse
|
|
436
|
-
: theme.color.bg.secondary.focus,
|
|
437
|
-
borderColor: "transparent",
|
|
438
|
-
outlineColor: light
|
|
439
|
-
? theme.color.border.primary.inverse
|
|
440
|
-
: color,
|
|
441
|
-
outlineStyle: "solid",
|
|
442
|
-
outlineWidth: theme.border.width.focused,
|
|
443
|
-
},
|
|
444
|
-
|
|
445
|
-
active: {
|
|
446
|
-
background: light ? activeColor : secondaryActiveColor,
|
|
447
|
-
color: light ? fadedColor : activeColor,
|
|
448
|
-
borderColor: "transparent",
|
|
449
|
-
outlineColor: light ? fadedColor : activeColor,
|
|
450
|
-
outlineStyle: "solid",
|
|
451
|
-
outlineWidth: theme.border.width.focused,
|
|
452
|
-
},
|
|
453
|
-
disabled: {
|
|
454
|
-
color: light
|
|
455
|
-
? theme.color.text.secondary.inverse
|
|
456
|
-
: theme.color.text.disabled,
|
|
457
|
-
outlineColor: light ? fadedColor : theme.color.border.disabled,
|
|
458
|
-
cursor: "default",
|
|
459
|
-
":focus": {
|
|
460
|
-
outlineColor: light
|
|
461
|
-
? theme.color.border.secondary.inverse
|
|
462
|
-
: theme.color.border.disabled,
|
|
463
|
-
outlineStyle: "solid",
|
|
464
|
-
outlineWidth: theme.border.width.disabled,
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
};
|
|
468
|
-
} else if (kind === "tertiary") {
|
|
469
|
-
newStyles = {
|
|
470
|
-
default: {
|
|
471
|
-
background: "none",
|
|
472
|
-
color: light ? theme.color.text.inverse : color,
|
|
473
|
-
paddingLeft: 0,
|
|
474
|
-
paddingRight: 0,
|
|
475
|
-
},
|
|
476
|
-
hover: {
|
|
477
|
-
":after": {
|
|
478
|
-
content: "''",
|
|
479
|
-
position: "absolute",
|
|
480
|
-
height: theme.size.height.tertiaryHover,
|
|
481
|
-
width: "100%",
|
|
482
|
-
right: 0,
|
|
483
|
-
bottom: 0,
|
|
484
|
-
background: light ? theme.color.bg.tertiary.hover : color,
|
|
485
|
-
borderRadius: theme.border.radius.tertiary,
|
|
486
|
-
},
|
|
487
|
-
},
|
|
488
|
-
focus: {
|
|
489
|
-
outlineStyle: "solid",
|
|
490
|
-
outlineColor: light
|
|
491
|
-
? theme.color.border.tertiary.inverse
|
|
492
|
-
: color,
|
|
493
|
-
outlineWidth: theme.border.width.focused,
|
|
494
|
-
borderRadius: theme.border.radius.default,
|
|
495
|
-
},
|
|
496
|
-
active: {
|
|
497
|
-
color: light ? fadedColor : activeColor,
|
|
498
|
-
":after": {
|
|
499
|
-
height: theme.size.height.tertiaryHover,
|
|
500
|
-
background: light ? fadedColor : activeColor,
|
|
501
|
-
},
|
|
502
|
-
},
|
|
503
|
-
disabled: {
|
|
504
|
-
color: light ? fadedColor : theme.color.text.disabled,
|
|
505
|
-
cursor: "default",
|
|
506
|
-
},
|
|
507
|
-
disabledFocus: {
|
|
508
|
-
outlineColor: light
|
|
509
|
-
? theme.color.border.tertiary.inverse
|
|
510
|
-
: theme.color.border.disabled,
|
|
511
|
-
},
|
|
512
|
-
};
|
|
513
|
-
} else {
|
|
514
|
-
throw new Error("Button kind not recognized");
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
styles[buttonType] = StyleSheet.create(newStyles);
|
|
518
|
-
return styles[buttonType];
|
|
519
|
-
};
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
3
|
-
import {PhosphorIcon, PhosphorIconAsset} from "@khanacademy/wonder-blocks-icon";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Returns the phosphor icon component based on the size. This is necessary
|
|
7
|
-
* so we can cast the icon to the correct type.
|
|
8
|
-
*/
|
|
9
|
-
export function ButtonIcon({
|
|
10
|
-
icon,
|
|
11
|
-
size,
|
|
12
|
-
style,
|
|
13
|
-
testId,
|
|
14
|
-
}: {
|
|
15
|
-
icon: PhosphorIconAsset;
|
|
16
|
-
size: "small" | "medium";
|
|
17
|
-
style?: StyleType;
|
|
18
|
-
testId?: string;
|
|
19
|
-
}) {
|
|
20
|
-
const commonProps = {
|
|
21
|
-
"aria-hidden": true,
|
|
22
|
-
color: "currentColor",
|
|
23
|
-
style: style,
|
|
24
|
-
testId,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
switch (size) {
|
|
28
|
-
case "small":
|
|
29
|
-
return (
|
|
30
|
-
<PhosphorIcon
|
|
31
|
-
{...commonProps}
|
|
32
|
-
size="small"
|
|
33
|
-
icon={icon as PhosphorBold | PhosphorFill}
|
|
34
|
-
/>
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
case "medium":
|
|
38
|
-
default:
|
|
39
|
-
return (
|
|
40
|
-
<PhosphorIcon
|
|
41
|
-
{...commonProps}
|
|
42
|
-
size="medium"
|
|
43
|
-
icon={icon as PhosphorRegular | PhosphorFill}
|
|
44
|
-
/>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|