@khanacademy/wonder-blocks-form 4.8.1 → 4.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.
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import {StyleSheet} from "aphrodite";
3
3
 
4
4
  import {IDProvider, addStyle} from "@khanacademy/wonder-blocks-core";
5
- import {color, spacing} from "@khanacademy/wonder-blocks-tokens";
5
+ import {border, color, mix, spacing} from "@khanacademy/wonder-blocks-tokens";
6
6
  import {styles as typographyStyles} from "@khanacademy/wonder-blocks-typography";
7
7
 
8
8
  import type {StyleType, AriaProps} from "@khanacademy/wonder-blocks-core";
@@ -152,10 +152,6 @@ type State = {
152
152
  * Displayed when the validation fails.
153
153
  */
154
154
  error: string | null | undefined;
155
- /**
156
- * The user focuses on this field.
157
- */
158
- focused: boolean;
159
155
  };
160
156
 
161
157
  /**
@@ -178,7 +174,6 @@ class TextField extends React.Component<PropsWithForwardRef, State> {
178
174
 
179
175
  state: State = {
180
176
  error: null,
181
- focused: false,
182
177
  };
183
178
 
184
179
  componentDidMount() {
@@ -222,22 +217,38 @@ class TextField extends React.Component<PropsWithForwardRef, State> {
222
217
  event,
223
218
  ) => {
224
219
  const {onFocus} = this.props;
225
- this.setState({focused: true}, () => {
226
- if (onFocus) {
227
- onFocus(event);
228
- }
229
- });
220
+ if (onFocus) {
221
+ onFocus(event);
222
+ }
230
223
  };
231
224
 
232
225
  handleBlur: (event: React.FocusEvent<HTMLInputElement>) => unknown = (
233
226
  event,
234
227
  ) => {
235
228
  const {onBlur} = this.props;
236
- this.setState({focused: false}, () => {
237
- if (onBlur) {
238
- onBlur(event);
239
- }
240
- });
229
+ if (onBlur) {
230
+ onBlur(event);
231
+ }
232
+ };
233
+
234
+ getStyles = (): StyleType => {
235
+ const {disabled, light} = this.props;
236
+ const {error} = this.state;
237
+ // Base styles are the styles that apply regardless of light mode
238
+ const baseStyles = [styles.input, typographyStyles.LabelMedium];
239
+ const defaultStyles = [
240
+ styles.default,
241
+ !disabled && styles.defaultFocus,
242
+ disabled && styles.disabled,
243
+ !!error && styles.error,
244
+ ];
245
+ const lightStyles = [
246
+ styles.light,
247
+ !disabled && styles.lightFocus,
248
+ disabled && styles.lightDisabled,
249
+ !!error && styles.lightError,
250
+ ];
251
+ return [...baseStyles, ...(light ? lightStyles : defaultStyles)];
241
252
  };
242
253
 
243
254
  render(): React.ReactNode {
@@ -249,7 +260,6 @@ class TextField extends React.Component<PropsWithForwardRef, State> {
249
260
  disabled,
250
261
  onKeyDown,
251
262
  placeholder,
252
- light,
253
263
  style,
254
264
  testId,
255
265
  readOnly,
@@ -259,6 +269,7 @@ class TextField extends React.Component<PropsWithForwardRef, State> {
259
269
  // The following props are being included here to avoid
260
270
  // passing them down to the otherProps spread
261
271
  /* eslint-disable @typescript-eslint/no-unused-vars */
272
+ light,
262
273
  onFocus,
263
274
  onBlur,
264
275
  onValidate,
@@ -274,24 +285,7 @@ class TextField extends React.Component<PropsWithForwardRef, State> {
274
285
  <IDProvider id={id} scope="text-field">
275
286
  {(uniqueId) => (
276
287
  <StyledInput
277
- style={[
278
- styles.input,
279
- typographyStyles.LabelMedium,
280
- styles.default,
281
- // Prioritizes disabled, then focused, then error (if any)
282
- disabled
283
- ? styles.disabled
284
- : this.state.focused
285
- ? [styles.focused, light && styles.defaultLight]
286
- : !!this.state.error && [
287
- styles.error,
288
- light && styles.errorLight,
289
- ],
290
- // Cast `this.state.error` into boolean since it's being
291
- // used as a conditional
292
- !!this.state.error && styles.error,
293
- style && style,
294
- ]}
288
+ style={[this.getStyles(), style]}
295
289
  id={uniqueId}
296
290
  type={type}
297
291
  placeholder={placeholder}
@@ -320,12 +314,10 @@ const styles = StyleSheet.create({
320
314
  input: {
321
315
  width: "100%",
322
316
  height: 40,
323
- borderRadius: 4,
317
+ borderRadius: border.radius.medium_4,
324
318
  boxSizing: "border-box",
325
319
  paddingLeft: spacing.medium_16,
326
320
  margin: 0,
327
- outline: "none",
328
- boxShadow: "none",
329
321
  },
330
322
  default: {
331
323
  background: color.white,
@@ -335,6 +327,13 @@ const styles = StyleSheet.create({
335
327
  color: color.offBlack64,
336
328
  },
337
329
  },
330
+ defaultFocus: {
331
+ ":focus-visible": {
332
+ borderColor: color.blue,
333
+ outline: `1px solid ${color.blue}`,
334
+ outlineOffset: 0, // Explicitly set outline offset to 0 because Safari sets a default offset
335
+ },
336
+ },
338
337
  error: {
339
338
  background: color.fadedRed8,
340
339
  border: `1px solid ${color.red}`,
@@ -342,28 +341,67 @@ const styles = StyleSheet.create({
342
341
  "::placeholder": {
343
342
  color: color.offBlack64,
344
343
  },
344
+ ":focus-visible": {
345
+ outlineColor: color.red,
346
+ borderColor: color.red,
347
+ },
345
348
  },
346
349
  disabled: {
347
350
  background: color.offWhite,
348
351
  border: `1px solid ${color.offBlack16}`,
349
352
  color: color.offBlack64,
350
353
  "::placeholder": {
351
- color: color.offBlack32,
354
+ color: color.offBlack64,
355
+ },
356
+ cursor: "not-allowed",
357
+ ":focus-visible": {
358
+ outline: "none",
359
+ boxShadow: `0 0 0 1px ${color.white}, 0 0 0 3px ${color.offBlack32}`,
352
360
  },
353
361
  },
354
- focused: {
362
+ light: {
355
363
  background: color.white,
356
- border: `1px solid ${color.blue}`,
364
+ border: `1px solid ${color.offBlack16}`,
357
365
  color: color.offBlack,
358
366
  "::placeholder": {
359
367
  color: color.offBlack64,
360
368
  },
361
369
  },
362
- defaultLight: {
363
- boxShadow: `0px 0px 0px 1px ${color.blue}, 0px 0px 0px 2px ${color.white}`,
370
+ lightFocus: {
371
+ ":focus-visible": {
372
+ outline: `1px solid ${color.blue}`,
373
+ outlineOffset: 0, // Explicitly set outline offset to 0 because Safari sets a default offset
374
+ borderColor: color.blue,
375
+ boxShadow: `0px 0px 0px 2px ${color.blue}, 0px 0px 0px 3px ${color.white}`,
376
+ },
364
377
  },
365
- errorLight: {
378
+ lightDisabled: {
379
+ backgroundColor: "transparent",
380
+ border: `1px solid ${color.white32}`,
381
+ color: color.white64,
382
+ "::placeholder": {
383
+ color: color.white64,
384
+ },
385
+ cursor: "not-allowed",
386
+ ":focus-visible": {
387
+ borderColor: mix(color.white32, color.blue),
388
+ outline: "none",
389
+ boxShadow: `0 0 0 1px ${color.offBlack32}, 0 0 0 3px ${color.fadedBlue}`,
390
+ },
391
+ },
392
+ lightError: {
393
+ background: color.fadedRed8,
394
+ border: `1px solid ${color.red}`,
366
395
  boxShadow: `0px 0px 0px 1px ${color.red}, 0px 0px 0px 2px ${color.white}`,
396
+ color: color.offBlack,
397
+ "::placeholder": {
398
+ color: color.offBlack64,
399
+ },
400
+ ":focus-visible": {
401
+ outlineColor: color.red,
402
+ borderColor: color.red,
403
+ boxShadow: `0px 0px 0px 2px ${color.red}, 0px 0px 0px 3px ${color.white}`,
404
+ },
367
405
  },
368
406
  });
369
407