@sanity/ui 2.0.0-beta.2 → 2.0.0-beta.21

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.
Files changed (91) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -3
  3. package/dist/index.cjs.mjs +11 -36
  4. package/dist/index.d.ts +247 -267
  5. package/dist/index.esm.js +477 -326
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +486 -539
  8. package/dist/index.js.map +1 -1
  9. package/dist/theme.cjs.mjs +1 -1
  10. package/dist/theme.d.ts +87 -115
  11. package/dist/theme.esm.js +348 -290
  12. package/dist/theme.esm.js.map +1 -1
  13. package/dist/theme.js +348 -290
  14. package/dist/theme.js.map +1 -1
  15. package/package.json +6 -3
  16. package/src/core/__workshop__/fontsPlugin.tsx +48 -8
  17. package/src/core/_compat.ts +236 -87
  18. package/src/core/components/dialog/dialog.tsx +1 -1
  19. package/src/core/components/dialog/styles.ts +2 -2
  20. package/src/core/components/menu/__workshop__/selectedItem.tsx +7 -2
  21. package/src/core/components/menu/menuButton.tsx +12 -1
  22. package/src/core/components/toast/toast.tsx +17 -2
  23. package/src/core/components/toast/toastProvider.tsx +20 -3
  24. package/src/core/constants.ts +37 -0
  25. package/src/core/hooks/useMediaIndex/useMediaIndex.test.tsx +6 -3
  26. package/src/core/middleware/origin.ts +47 -0
  27. package/src/core/primitives/badge/badge.tsx +1 -3
  28. package/src/core/primitives/button/button.tsx +9 -11
  29. package/src/core/primitives/heading/styles.ts +4 -0
  30. package/src/core/primitives/kbd/kbd.tsx +2 -0
  31. package/src/core/primitives/popover/popover.tsx +67 -13
  32. package/src/core/primitives/popover/popoverCard.tsx +31 -15
  33. package/src/core/primitives/select/select.tsx +1 -1
  34. package/src/core/primitives/switch/styles.ts +6 -1
  35. package/src/core/primitives/textInput/__workshop__/plain.tsx +1 -1
  36. package/src/core/primitives/textInput/__workshop__/states.tsx +10 -2
  37. package/src/core/primitives/tooltip/tooltip.tsx +46 -56
  38. package/src/core/primitives/tooltip/tooltipCard.tsx +110 -0
  39. package/src/core/styles/card/_cardColorStyle.ts +2 -0
  40. package/src/core/styles/input/textInputStyle.ts +33 -2
  41. package/src/core/styles/margin/marginsStyle.test.ts +2 -2
  42. package/src/core/styles/padding/paddingStyle.test.ts +2 -2
  43. package/src/core/theme/__workshop__/build/Root.tsx +20 -3
  44. package/src/core/theme/__workshop__/build/story.tsx +16 -10
  45. package/src/core/theme/__workshop__/canvas.tsx +7 -7
  46. package/src/core/theme/__workshop__/debug/story.tsx +196 -0
  47. package/src/core/theme/__workshop__/index.ts +5 -0
  48. package/src/core/theme/theme.test.tsx +2 -2
  49. package/src/core/types/button.ts +1 -2
  50. package/src/core/{primitives/tooltip → utils/conditionalWrapper}/conditionalWrapper.tsx +3 -0
  51. package/src/core/utils/conditionalWrapper/index.ts +1 -0
  52. package/src/core/utils/index.ts +1 -0
  53. package/src/theme/build/_deprecated/color/factory.ts +3 -3
  54. package/src/theme/build/buildColorTheme.test.ts +2 -2
  55. package/src/theme/build/buildColorTheme.ts +16 -16
  56. package/src/theme/build/colorToken/colorToken.ts +19 -0
  57. package/src/theme/build/colorToken/compileColorToken.ts +21 -0
  58. package/src/theme/build/colorToken/index.ts +2 -0
  59. package/src/theme/build/colorToken/types.ts +6 -0
  60. package/src/theme/build/renderColorTheme.ts +14 -15
  61. package/src/theme/build/renderColorValue.ts +9 -2
  62. package/src/theme/config/system.ts +12 -10
  63. package/src/theme/config/tokens/color/types.ts +12 -1
  64. package/src/theme/config/tokens/parseTokenKey.ts +1 -0
  65. package/src/theme/config/tokens/parseTokenValue.test.ts +13 -16
  66. package/src/theme/config/tokens/parseTokenValue.ts +45 -14
  67. package/src/theme/config/tokens/types.ts +3 -2
  68. package/src/theme/defaults/colorTokens.ts +279 -253
  69. package/src/theme/defaults/config.ts +2 -2
  70. package/src/theme/getScopedTheme.ts +1 -0
  71. package/src/theme/index.ts +0 -7
  72. package/src/theme/system/color/_constants.ts +5 -2
  73. package/src/theme/system/color/_system.ts +2 -1
  74. package/src/theme/system/color/avatar.ts +2 -12
  75. package/src/theme/system/color/button.ts +4 -20
  76. package/src/theme/system/color/input.ts +3 -11
  77. package/src/theme/system/color/selectable.ts +3 -14
  78. package/src/theme/system/shadow.ts +0 -6
  79. package/src/theme/system/styles.ts +0 -6
  80. package/src/theme/system/theme.ts +11 -1
  81. package/src/theme/system/v0/avatar.ts +1 -1
  82. package/src/theme/system/v0/color/button.ts +4 -4
  83. package/src/theme/system/v0/color/card.ts +2 -2
  84. package/src/theme/system/v0/color/color.ts +0 -1
  85. package/src/theme/system/v0/color/input.ts +3 -3
  86. package/src/theme/system/v0/color/muted.ts +2 -2
  87. package/src/theme/system/v0/color/solid.ts +2 -2
  88. package/src/theme/versioning/getTheme_v2.ts +2 -1
  89. package/src/theme/versioning/themeColor_v0_v2.ts +2 -2
  90. package/src/theme/versioning/v2_v0.ts +3 -2
  91. package/src/theme/build/colorToken.ts +0 -39
@@ -1,9 +1,12 @@
1
1
  /** @jest-environment node */
2
- import {defaultTheme} from '@sanity/ui/theme'
2
+
3
+ import {buildTheme} from '@sanity/ui/theme'
3
4
  import {renderToString, renderToStaticMarkup} from 'react-dom/server'
4
5
  import {ThemeProvider} from '../../theme'
5
6
  import {useMediaIndex} from './useMediaIndex'
6
7
 
8
+ const theme = buildTheme()
9
+
7
10
  function Log() {
8
11
  const mediaIndex = useMediaIndex()
9
12
 
@@ -14,7 +17,7 @@ describe('useMediaIndex', () => {
14
17
  it(`SSR to static markup returns 0`, () => {
15
18
  expect(
16
19
  renderToStaticMarkup(
17
- <ThemeProvider theme={defaultTheme}>
20
+ <ThemeProvider theme={theme}>
18
21
  <Log />
19
22
  </ThemeProvider>,
20
23
  ),
@@ -23,7 +26,7 @@ describe('useMediaIndex', () => {
23
26
  it(`SSR to markup for hydration doesn't throw`, () => {
24
27
  expect(
25
28
  renderToString(
26
- <ThemeProvider theme={defaultTheme}>
29
+ <ThemeProvider theme={theme}>
27
30
  <Log />
28
31
  </ThemeProvider>,
29
32
  ),
@@ -0,0 +1,47 @@
1
+ import {Middleware} from '@floating-ui/react-dom'
2
+
3
+ /**
4
+ * Custom floating-ui middleware which calculates transform-origin X + Y offsets
5
+ * based on the current floating rect's dimensions and shift offset.
6
+ *
7
+ * Scaling popovers with these transform-origin offsets will give the effect of
8
+ * popvers slightly 'growing' from the origin/reference element.
9
+ *
10
+ * This middleware must be applied after both `@sanity/ui/size` and `shift` middlewares.
11
+ */
12
+ export const origin: Middleware = {
13
+ name: '@sanity/ui/origin',
14
+ fn({middlewareData, placement, rects}) {
15
+ const [side] = placement.split('-')
16
+
17
+ const floatingWidth = rects.floating.width
18
+ const floatingHeight = rects.floating.height
19
+
20
+ const shiftX = middlewareData.shift?.x || 0
21
+ const shiftY = middlewareData.shift?.y || 0
22
+
23
+ if (floatingWidth <= 0 || floatingHeight <= 0) {
24
+ return {}
25
+ }
26
+
27
+ const isVerticalPlacement = ['bottom', 'top'].includes(side)
28
+
29
+ const {originX, originY}: {originX: number; originY: number} = isVerticalPlacement
30
+ ? {
31
+ originX: clamp(0.5 - shiftX / floatingWidth, 0, 1),
32
+ originY: side === 'bottom' ? 0 : 1,
33
+ }
34
+ : {
35
+ originX: side === 'left' ? 1 : 0,
36
+ originY: clamp(0.5 - shiftY / floatingHeight, 0, 1),
37
+ }
38
+
39
+ return {
40
+ data: {originX, originY},
41
+ }
42
+ },
43
+ }
44
+
45
+ function clamp(num: number, min: number, max: number) {
46
+ return Math.min(Math.max(num, min), max)
47
+ }
@@ -54,9 +54,7 @@ export const Badge = forwardRef(function Badge(
54
54
  padding={useArrayProp(padding)}
55
55
  ref={ref}
56
56
  >
57
- <Text size={fontSize} weight="medium">
58
- {children}
59
- </Text>
57
+ <Text size={fontSize}>{children}</Text>
60
58
  </Root>
61
59
  )
62
60
  })
@@ -149,17 +149,15 @@ export const Button = forwardRef(function Button(
149
149
  )}
150
150
 
151
151
  {text && (
152
- <Box flex={iconRight ? 1 : undefined}>
153
- <Text
154
- muted={muted}
155
- align={textAlign}
156
- size={fontSize}
157
- textOverflow="ellipsis"
158
- weight={button.textWeight}
159
- >
160
- {text}
161
- </Text>
162
- </Box>
152
+ <Text
153
+ muted={muted}
154
+ align={textAlign}
155
+ size={fontSize}
156
+ textOverflow="ellipsis"
157
+ weight={button.textWeight}
158
+ >
159
+ {text}
160
+ </Text>
163
161
  )}
164
162
 
165
163
  {iconRight && (
@@ -46,6 +46,10 @@ export function headingBaseStyle(props: HeadingStyleProps & ThemeProps): ReturnT
46
46
  }
47
47
  }
48
48
 
49
+ & strong {
50
+ font-weight: ${font.heading.weights.bold};
51
+ }
52
+
49
53
  & svg {
50
54
  /* Certain popular CSS libraries changes the defaults for SVG display */
51
55
  /* Make sure SVGs are rendered as inline elements */
@@ -26,6 +26,8 @@ function kbdStyle() {
26
26
  background: var(--card-bg-color);
27
27
  font: inherit;
28
28
 
29
+ vertical-align: top;
30
+
29
31
  &:not([hidden]) {
30
32
  display: inline-block;
31
33
  }
@@ -10,6 +10,7 @@ import {
10
10
  useFloating,
11
11
  } from '@floating-ui/react-dom'
12
12
  import {ThemeColorSchemeKey} from '@sanity/ui/theme'
13
+ import {AnimatePresence} from 'framer-motion'
13
14
  import {
14
15
  MutableRefObject,
15
16
  RefCallback,
@@ -22,9 +23,16 @@ import {
22
23
  useRef,
23
24
  } from 'react'
24
25
  import {useForwardedRef, useArrayProp, useElementSize, useMediaIndex} from '../../hooks'
26
+ import {origin} from '../../middleware/origin'
25
27
  import {useTheme_v2} from '../../theme'
26
28
  import {BoxOverflow, CardTone, Placement, PopoverMargins} from '../../types'
27
- import {LayerProps, LayerProvider, Portal, useBoundaryElement} from '../../utils'
29
+ import {
30
+ ConditionalWrapper,
31
+ LayerProps,
32
+ LayerProvider,
33
+ Portal,
34
+ useBoundaryElement,
35
+ } from '../../utils'
28
36
  import {ResponsiveRadiusProps, ResponsiveShadowProps} from '../types'
29
37
  import {
30
38
  DEFAULT_POPOVER_DISTANCE,
@@ -46,15 +54,41 @@ export interface PopoverProps
46
54
  ResponsiveShadowProps {
47
55
  /** @beta */
48
56
  __unstable_margins?: PopoverMargins
57
+ /**
58
+ * Whether the popover should animate in and out.
59
+ *
60
+ * @beta
61
+ * @defaultValue false
62
+ */
63
+ animate?: boolean
49
64
  arrow?: boolean
50
65
  /** @deprecated Use `floatingBoundary` and/or `referenceBoundary` instead */
51
66
  boundaryElement?: HTMLElement | null
52
67
  children?: React.ReactElement
68
+ /**
69
+ * When `true`, prevent overflow within the current boundary:
70
+ * - by flipping on its side axis
71
+ * - by resizing
72
+ /*
73
+ * Note that:
74
+ * - setting `preventOverflow` to `true` also prevents overflow on its side axis
75
+ * - setting `matchReferenceWidth` to `true` also causes the popover to resize
76
+ *
77
+ * @defaultValue false
78
+ */
53
79
  constrainSize?: boolean
54
80
  content?: React.ReactNode
55
81
  disabled?: boolean
56
82
  fallbackPlacements?: Placement[]
57
83
  floatingBoundary?: HTMLElement | null
84
+ /**
85
+ * When `true`, set the maximum width to match the reference element, and also prevent overflow within
86
+ * the current boundary by resizing.
87
+ *
88
+ * Note that setting `constrainSize` to `true` also causes the popover to resize
89
+ *
90
+ * @defaultValue false
91
+ */
58
92
  matchReferenceWidth?: boolean
59
93
  open?: boolean
60
94
  overflow?: BoxOverflow
@@ -90,6 +124,7 @@ export const Popover = memo(
90
124
 
91
125
  const {
92
126
  __unstable_margins: margins = DEFAULT_POPOVER_MARGINS,
127
+ animate = false,
93
128
  arrow: arrowProp = false,
94
129
  boundaryElement = boundaryElementContext.element,
95
130
  children: childProp,
@@ -234,7 +269,7 @@ export const Popover = memo(
234
269
  )
235
270
  }
236
271
 
237
- // Shift the popover so its sits within the boundary eleement
272
+ // Shift the popover so its sits within the boundary element
238
273
  if (preventOverflow) {
239
274
  ret.push(
240
275
  shift({
@@ -255,6 +290,12 @@ export const Popover = memo(
255
290
  )
256
291
  }
257
292
 
293
+ // Determine the origin to scale from.
294
+ // Must be placed after `@sanity/ui/size` and `shift` middleware.
295
+ if (animate) {
296
+ ret.push(origin)
297
+ }
298
+
258
299
  ret.push(
259
300
  hide({
260
301
  boundary: referenceBoundary || undefined,
@@ -265,6 +306,7 @@ export const Popover = memo(
265
306
 
266
307
  return ret
267
308
  }, [
309
+ animate,
268
310
  arrowProp,
269
311
  constrainSize,
270
312
  fallbackPlacements,
@@ -286,6 +328,9 @@ export const Popover = memo(
286
328
  const arrowX = middlewareData.arrow?.x
287
329
  const arrowY = middlewareData.arrow?.y
288
330
 
331
+ const originX = middlewareData['@sanity/ui/origin']?.originX
332
+ const originY = middlewareData['@sanity/ui/origin']?.originY
333
+
289
334
  const setArrow = useCallback((arrowEl: HTMLDivElement | null) => {
290
335
  arrowRef.current = arrowEl
291
336
  }, [])
@@ -343,6 +388,7 @@ export const Popover = memo(
343
388
  <PopoverCard
344
389
  {...restProps}
345
390
  __unstable_margins={margins}
391
+ animate={animate}
346
392
  arrow={arrowProp}
347
393
  arrowRef={setArrow}
348
394
  arrowX={arrowX}
@@ -355,6 +401,8 @@ export const Popover = memo(
355
401
  ref={setFloating}
356
402
  scheme={scheme}
357
403
  shadow={shadow}
404
+ originX={originX}
405
+ originY={originY}
358
406
  strategy={strategy}
359
407
  tone={tone}
360
408
  width={matchReferenceWidth ? referenceWidthRef.current : width}
@@ -369,17 +417,23 @@ export const Popover = memo(
369
417
  return (
370
418
  <>
371
419
  {/* the popover */}
372
- {open && (
373
- <>
374
- {portal ? (
375
- <Portal __unstable_name={typeof portal === 'string' ? portal : undefined}>
376
- {popover}
377
- </Portal>
378
- ) : (
379
- popover
380
- )}
381
- </>
382
- )}
420
+ <ConditionalWrapper
421
+ condition={animate}
422
+ wrapper={(children) => <AnimatePresence>{children}</AnimatePresence>}
423
+ >
424
+ {open && (
425
+ <ConditionalWrapper
426
+ condition={!!portal}
427
+ wrapper={(children) => (
428
+ <Portal __unstable_name={typeof portal === 'string' ? portal : undefined}>
429
+ {children}
430
+ </Portal>
431
+ )}
432
+ >
433
+ {popover}
434
+ </ConditionalWrapper>
435
+ )}
436
+ </ConditionalWrapper>
383
437
 
384
438
  {/* the referred element */}
385
439
  {child}
@@ -1,10 +1,12 @@
1
1
  import {Strategy} from '@floating-ui/react-dom'
2
2
  import {ThemeColorSchemeKey} from '@sanity/ui/theme'
3
+ import {MotionProps, motion} from 'framer-motion'
3
4
  import React, {CSSProperties, forwardRef, memo, useMemo} from 'react'
4
5
  import styled from 'styled-components'
6
+ import {POPOVER_MOTION_CONTENT_OPACITY_PROPERTY, POPOVER_MOTION_PROPS} from '../../constants'
5
7
  import {BoxOverflow, CardTone, Placement, PopoverMargins, Radius} from '../../types'
6
8
  import {Arrow, useLayer} from '../../utils'
7
- import {Card} from '../card'
9
+ import {Card, CardProps} from '../card'
8
10
  import {Flex} from '../flex'
9
11
  import {
10
12
  DEFAULT_POPOVER_ARROW_HEIGHT,
@@ -13,14 +15,18 @@ import {
13
15
  DEFAULT_POPOVER_MARGINS,
14
16
  } from './constants'
15
17
 
16
- const Root = styled(Card)({
17
- '&:not([hidden])': {
18
- display: 'flex',
19
- },
20
- flexDirection: 'column',
21
- width: 'max-content',
22
- minWidth: 'min-content',
23
- })
18
+ const MotionCard = styled(motion(Card))`
19
+ &:not([hidden]) {
20
+ display: flex;
21
+ }
22
+ flex-direction: column;
23
+ width: max-content;
24
+ min-width: min-content;
25
+ & > * {
26
+ opacity: var(${POPOVER_MOTION_CONTENT_OPACITY_PROPERTY}, 1);
27
+ will-change: opacity;
28
+ }
29
+ `
24
30
 
25
31
  /**
26
32
  * @internal
@@ -30,13 +36,16 @@ export const PopoverCard = memo(
30
36
  props: {
31
37
  /** @beta*/
32
38
  __unstable_margins?: PopoverMargins
39
+ animate?: boolean
33
40
  arrow: boolean
34
41
  arrowRef: React.Ref<HTMLDivElement>
35
42
  arrowX?: number
36
43
  arrowY?: number
44
+ originX?: number
45
+ originY?: number
37
46
  overflow?: BoxOverflow
38
47
  padding?: number | number[]
39
- placement?: Placement
48
+ placement: Placement
40
49
  radius?: Radius | Radius[]
41
50
  scheme?: ThemeColorSchemeKey
42
51
  shadow?: number | number[]
@@ -50,6 +59,7 @@ export const PopoverCard = memo(
50
59
  ) {
51
60
  const {
52
61
  __unstable_margins: marginsProp,
62
+ animate,
53
63
  arrow,
54
64
  arrowRef,
55
65
  arrowX,
@@ -57,6 +67,8 @@ export const PopoverCard = memo(
57
67
  children,
58
68
  padding,
59
69
  placement,
70
+ originX,
71
+ originY,
60
72
  overflow,
61
73
  radius,
62
74
  scheme,
@@ -84,14 +96,17 @@ export const PopoverCard = memo(
84
96
 
85
97
  const rootStyle: CSSProperties = useMemo(
86
98
  () => ({
99
+ left: x,
100
+ originX,
101
+ originY,
87
102
  position: strategy,
88
103
  top: y,
89
- left: x,
90
104
  width,
91
105
  zIndex,
106
+ willChange: animate ? 'transform' : undefined,
92
107
  ...style,
93
108
  }),
94
- [strategy, style, width, x, y, zIndex],
109
+ [animate, originX, originY, strategy, style, width, x, y, zIndex],
95
110
  )
96
111
 
97
112
  const arrowStyle: CSSProperties = useMemo(
@@ -105,9 +120,9 @@ export const PopoverCard = memo(
105
120
  )
106
121
 
107
122
  return (
108
- <Root
123
+ <MotionCard
109
124
  data-ui="Popover"
110
- {...restProps}
125
+ {...(restProps as CardProps & MotionProps)}
111
126
  data-placement={placement}
112
127
  radius={radius}
113
128
  ref={ref}
@@ -116,6 +131,7 @@ export const PopoverCard = memo(
116
131
  sizing="border"
117
132
  style={rootStyle}
118
133
  tone={tone}
134
+ {...(animate ? POPOVER_MOTION_PROPS : {})}
119
135
  >
120
136
  <Flex data-ui="Popover__wrapper" direction="column" flex={1} overflow={overflow}>
121
137
  <Flex direction="column" flex={1} padding={padding}>
@@ -132,7 +148,7 @@ export const PopoverCard = memo(
132
148
  radius={DEFAULT_POPOVER_ARROW_RADIUS}
133
149
  />
134
150
  )}
135
- </Root>
151
+ </MotionCard>
136
152
  )
137
153
  }),
138
154
  )
@@ -44,7 +44,7 @@ export const Select = forwardRef(function Select(
44
44
  disabled,
45
45
  fontSize = 2,
46
46
  padding = 3,
47
- radius = 1,
47
+ radius = 2,
48
48
  readOnly,
49
49
  space = 3,
50
50
  ...restProps
@@ -102,6 +102,11 @@ export function switchRepresentationStyles(props: ThemeProps): ReturnType<typeof
102
102
  --switch-bg-color: ${color.input.default.readOnly.border};
103
103
  --switch-fg-color: ${color.input.default.readOnly.bg};
104
104
  }
105
+
106
+ input:checked[data-read-only]:disabled + && {
107
+ --switch-bg-color: ${color.input.default.readOnly.fg};
108
+ --switch-fg-color: ${color.input.default.readOnly.bg};
109
+ }
105
110
  `
106
111
  }
107
112
 
@@ -152,7 +157,7 @@ export function switchThumbStyles(
152
157
  transition-timing-function: ${input.switch.transitionTimingFunction};
153
158
  background: var(--switch-fg-color);
154
159
  transform: translate3d(0, 0, 0);
155
- box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.1);
160
+ box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.05);
156
161
 
157
162
  ${checked &&
158
163
  css`
@@ -62,7 +62,7 @@ function TextInputTest(props: {inputRef: React.Ref<HTMLInputElement>}) {
62
62
  ) as IconSymbol
63
63
  const padding = useSelect('Padding', WORKSHOP_SPACE_OPTIONS, 3, 'Props')
64
64
  const placeholder = useText('Placeholder', '', 'Props') || undefined
65
- const radius = useSelect('Radius', WORKSHOP_RADIUS_OPTIONS, 0, 'Props')
65
+ const radius = useSelect('Radius', WORKSHOP_RADIUS_OPTIONS, 2, 'Props')
66
66
  const readOnly = useBoolean('Read only', false, 'Props')
67
67
  const space = useSelect('Space', WORKSHOP_SPACE_OPTIONS, 3, 'Props')
68
68
  const weight = useSelect('Weight', WORKSHOP_FONT_WEIGHT_OPTIONS, '', 'Props') || undefined
@@ -1,7 +1,8 @@
1
1
  import {Container, Flex, Stack, Text, TextInput} from '@sanity/ui'
2
- import {useAction} from '@sanity/ui-workshop'
2
+ import {useAction, useBoolean} from '@sanity/ui-workshop'
3
3
 
4
4
  export default function StatesStory() {
5
+ const invalid = useBoolean('Invalid', false, 'Props')
5
6
  const onChange = useAction('onChange')
6
7
 
7
8
  return (
@@ -12,7 +13,12 @@ export default function StatesStory() {
12
13
  <Text size={1} weight="medium">
13
14
  <label htmlFor="enabled-example">Enabled (default)</label>
14
15
  </Text>
15
- <TextInput id="enabled-example" onChange={onChange} value="This is some text" />
16
+ <TextInput
17
+ customValidity={invalid ? 'invalid' : undefined}
18
+ id="enabled-example"
19
+ onChange={onChange}
20
+ value="This is some text"
21
+ />
16
22
  </Stack>
17
23
 
18
24
  <Stack space={3}>
@@ -20,6 +26,7 @@ export default function StatesStory() {
20
26
  <label htmlFor="disabled-example">Disabled</label>
21
27
  </Text>
22
28
  <TextInput
29
+ customValidity={invalid ? 'invalid' : undefined}
23
30
  disabled
24
31
  id="disabled-example"
25
32
  onChange={onChange}
@@ -32,6 +39,7 @@ export default function StatesStory() {
32
39
  <label htmlFor="read-only-example">Read-only</label>
33
40
  </Text>
34
41
  <TextInput
42
+ customValidity={invalid ? 'invalid' : undefined}
35
43
  id="read-only-example"
36
44
  readOnly
37
45
  onChange={onChange}