@saas-ui/forms 1.5.2 → 2.0.0-next.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.
Files changed (112) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/ajv/index.d.ts +21 -2
  3. package/dist/ajv/index.js +31 -2
  4. package/dist/ajv/index.js.map +1 -1
  5. package/dist/ajv/index.mjs +25 -0
  6. package/dist/ajv/index.mjs.map +1 -0
  7. package/dist/index.d.ts +606 -19
  8. package/dist/index.js +1251 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +1140 -0
  11. package/dist/index.mjs.map +1 -0
  12. package/dist/yup/index.d.ts +41 -2
  13. package/dist/yup/index.js +95 -2
  14. package/dist/yup/index.js.map +1 -1
  15. package/dist/yup/index.mjs +86 -0
  16. package/dist/yup/index.mjs.map +1 -0
  17. package/dist/zod/index.d.ts +38 -2
  18. package/dist/zod/index.js +95 -2
  19. package/dist/zod/index.js.map +1 -1
  20. package/dist/zod/index.mjs +85 -0
  21. package/dist/zod/index.mjs.map +1 -0
  22. package/package.json +18 -28
  23. package/src/array-field.tsx +20 -13
  24. package/src/auto-form.tsx +13 -19
  25. package/src/field-resolver.ts +1 -1
  26. package/src/field.tsx +4 -9
  27. package/src/fields.tsx +1 -3
  28. package/src/form.tsx +70 -26
  29. package/src/index.ts +3 -1
  30. package/src/input-right-button/index.ts +1 -0
  31. package/src/input-right-button/input-right-button.stories.tsx +47 -0
  32. package/src/input-right-button/input-right-button.test.tsx +12 -0
  33. package/src/input-right-button/input-right-button.tsx +26 -0
  34. package/src/layout.tsx +1 -1
  35. package/src/number-input/index.ts +1 -0
  36. package/src/number-input/number-input.stories.tsx +39 -0
  37. package/src/number-input/number-input.test.tsx +6 -0
  38. package/src/number-input/number-input.tsx +56 -0
  39. package/src/object-field.tsx +1 -1
  40. package/src/password-input/index.ts +1 -0
  41. package/src/password-input/password-input.stories.tsx +50 -0
  42. package/src/password-input/password-input.test.tsx +20 -0
  43. package/src/password-input/password-input.tsx +69 -0
  44. package/src/pin-input/index.ts +1 -0
  45. package/src/pin-input/pin-input.stories.tsx +38 -0
  46. package/src/pin-input/pin-input.test.tsx +6 -0
  47. package/src/pin-input/pin-input.tsx +50 -0
  48. package/src/radio/index.ts +1 -0
  49. package/src/radio/radio-input.stories.tsx +45 -0
  50. package/src/radio/radio-input.tsx +58 -0
  51. package/src/radio/radio.test.tsx +6 -0
  52. package/src/select/index.ts +2 -0
  53. package/src/select/native-select.tsx +42 -0
  54. package/src/select/select.stories.tsx +144 -0
  55. package/src/select/select.test.tsx +8 -0
  56. package/src/select/select.tsx +185 -0
  57. package/src/step-form.tsx +24 -13
  58. package/src/submit-button.tsx +32 -38
  59. package/src/use-step-form.tsx +1 -1
  60. package/ajv/package.json +0 -28
  61. package/dist/ajv/ajv-resolver.d.ts +0 -11
  62. package/dist/ajv/ajv-resolver.d.ts.map +0 -1
  63. package/dist/ajv/index.d.ts.map +0 -1
  64. package/dist/ajv/index.modern.mjs +0 -2
  65. package/dist/ajv/index.modern.mjs.map +0 -1
  66. package/dist/array-field.d.ts +0 -64
  67. package/dist/array-field.d.ts.map +0 -1
  68. package/dist/auto-form.d.ts +0 -32
  69. package/dist/auto-form.d.ts.map +0 -1
  70. package/dist/display-field.d.ts +0 -10
  71. package/dist/display-field.d.ts.map +0 -1
  72. package/dist/display-if.d.ts +0 -15
  73. package/dist/display-if.d.ts.map +0 -1
  74. package/dist/field-resolver.d.ts +0 -13
  75. package/dist/field-resolver.d.ts.map +0 -1
  76. package/dist/field.d.ts +0 -147
  77. package/dist/field.d.ts.map +0 -1
  78. package/dist/fields.d.ts +0 -9
  79. package/dist/fields.d.ts.map +0 -1
  80. package/dist/form.d.ts +0 -44
  81. package/dist/form.d.ts.map +0 -1
  82. package/dist/index.d.ts.map +0 -1
  83. package/dist/index.modern.mjs +0 -2
  84. package/dist/index.modern.mjs.map +0 -1
  85. package/dist/layout.d.ts +0 -14
  86. package/dist/layout.d.ts.map +0 -1
  87. package/dist/object-field.d.ts +0 -12
  88. package/dist/object-field.d.ts.map +0 -1
  89. package/dist/step-form.d.ts +0 -38
  90. package/dist/step-form.d.ts.map +0 -1
  91. package/dist/submit-button.d.ts +0 -20
  92. package/dist/submit-button.d.ts.map +0 -1
  93. package/dist/use-array-field.d.ts +0 -95
  94. package/dist/use-array-field.d.ts.map +0 -1
  95. package/dist/use-step-form.d.ts +0 -40
  96. package/dist/use-step-form.d.ts.map +0 -1
  97. package/dist/utils.d.ts +0 -3
  98. package/dist/utils.d.ts.map +0 -1
  99. package/dist/watch-field.d.ts +0 -11
  100. package/dist/watch-field.d.ts.map +0 -1
  101. package/dist/yup/index.d.ts.map +0 -1
  102. package/dist/yup/index.modern.mjs +0 -2
  103. package/dist/yup/index.modern.mjs.map +0 -1
  104. package/dist/yup/yup-resolver.d.ts +0 -29
  105. package/dist/yup/yup-resolver.d.ts.map +0 -1
  106. package/dist/zod/index.d.ts.map +0 -1
  107. package/dist/zod/index.modern.mjs +0 -2
  108. package/dist/zod/index.modern.mjs.map +0 -1
  109. package/dist/zod/zod-resolver.d.ts +0 -35
  110. package/dist/zod/zod-resolver.d.ts.map +0 -1
  111. package/yup/package.json +0 -26
  112. package/zod/package.json +0 -27
@@ -0,0 +1,26 @@
1
+ import * as React from 'react'
2
+
3
+ import {
4
+ Button,
5
+ ButtonProps,
6
+ forwardRef,
7
+ InputRightElement,
8
+ } from '@chakra-ui/react'
9
+
10
+ import { __DEV__ } from '@chakra-ui/utils'
11
+
12
+ export type InputRightButtonProps = ButtonProps
13
+
14
+ export const InputRightButton = forwardRef<InputRightButtonProps, 'div'>(
15
+ (props, ref) => {
16
+ return (
17
+ <InputRightElement w="auto" px="1" py="1" alignItems="stretch">
18
+ <Button ref={ref} height="auto" {...props} />
19
+ </InputRightElement>
20
+ )
21
+ }
22
+ )
23
+
24
+ InputRightButton.id = 'InputRightElement'
25
+
26
+ InputRightButton.displayName = 'InputRightButton'
package/src/layout.tsx CHANGED
@@ -38,7 +38,7 @@ export const FormLayout = ({ children, ...props }: FormLayoutProps) => {
38
38
  return (
39
39
  <SimpleGrid
40
40
  {...gridProps}
41
- className={cx('saas-form__layout', props.className)}
41
+ className={cx('sui-form__layout', props.className)}
42
42
  >
43
43
  {React.Children.map(children, (child) => {
44
44
  if (React.isValidElement(child)) {
@@ -0,0 +1 @@
1
+ export * from './number-input'
@@ -0,0 +1,39 @@
1
+ import { Container } from '@chakra-ui/react'
2
+ import * as React from 'react'
3
+ import { Story, Meta } from '@storybook/react'
4
+
5
+ import { NumberInput } from '../src'
6
+
7
+ export default {
8
+ title: 'Components/Forms/NumberInput',
9
+ decorators: [
10
+ (Story: any) => (
11
+ <Container mt="40px">
12
+ <Story />
13
+ </Container>
14
+ ),
15
+ ],
16
+ } as Meta
17
+
18
+ const Template: Story = (args) => <NumberInput aria-label="Number" {...args} />
19
+
20
+ export const Basic = Template.bind({})
21
+ Basic.args = {}
22
+
23
+ export const HideStepper = Template.bind({})
24
+ HideStepper.args = {
25
+ hideStepper: true,
26
+ }
27
+
28
+ export const MinMax = Template.bind({})
29
+ MinMax.args = {
30
+ defaultValue: 5,
31
+ min: 0,
32
+ max: 10,
33
+ }
34
+
35
+ export const WithFormatter = Template.bind({})
36
+ WithFormatter.args = {
37
+ format: (value) => `$${value}`, // use any currency formatter here
38
+ parse: (value) => value.replace('$', ''),
39
+ }
@@ -0,0 +1,6 @@
1
+ import * as React from 'react'
2
+
3
+ import { render, testStories } from '@saas-ui/test-utils'
4
+ import * as stories from './number-input.stories'
5
+
6
+ testStories<typeof stories>(stories)
@@ -0,0 +1,56 @@
1
+ import * as React from 'react'
2
+
3
+ import {
4
+ forwardRef,
5
+ NumberInput as ChakraNumberInput,
6
+ NumberInputField,
7
+ NumberInputStepper,
8
+ NumberIncrementStepper,
9
+ NumberDecrementStepper,
10
+ NumberInputProps as ChakraNumberInputProps,
11
+ } from '@chakra-ui/react'
12
+ import { __DEV__ } from '@chakra-ui/utils'
13
+
14
+ interface NumberInputOptions {
15
+ /**
16
+ * Hide the stepper.
17
+ */
18
+ hideStepper?: boolean
19
+ /**
20
+ * Render a custom increment icon.
21
+ */
22
+ incrementIcon?: React.ReactNode
23
+ /**
24
+ * Render a custom decrement icon.
25
+ */
26
+ decrementIcon?: React.ReactNode
27
+ }
28
+
29
+ export interface NumberInputProps
30
+ extends ChakraNumberInputProps,
31
+ NumberInputOptions {}
32
+
33
+ export const NumberInput = forwardRef<NumberInputProps, 'div'>((props, ref) => {
34
+ const { hideStepper, incrementIcon, decrementIcon, ...rest } = props
35
+
36
+ return (
37
+ <ChakraNumberInput {...rest} ref={ref}>
38
+ <NumberInputField />
39
+
40
+ {!hideStepper && (
41
+ <NumberInputStepper>
42
+ <NumberIncrementStepper children={incrementIcon} />
43
+ <NumberDecrementStepper children={decrementIcon} />
44
+ </NumberInputStepper>
45
+ )}
46
+ </ChakraNumberInput>
47
+ )
48
+ })
49
+
50
+ NumberInput.defaultProps = {
51
+ hideStepper: false,
52
+ }
53
+
54
+ if (__DEV__) {
55
+ NumberInput.displayName = 'NumberInput'
56
+ }
@@ -21,7 +21,7 @@ export interface ObjectFieldProps extends FieldProps {
21
21
  }
22
22
 
23
23
  export const FormLegend = (props: FormLabelProps) => {
24
- const styles = useStyleConfig('FormLegend')
24
+ const styles = useStyleConfig('SuiFormLegend')
25
25
  return <FormLabel as="legend" sx={styles} {...props} />
26
26
  }
27
27
 
@@ -0,0 +1 @@
1
+ export * from './password-input'
@@ -0,0 +1,50 @@
1
+ import { Container, FormControl, FormLabel, Icon } from '@chakra-ui/react'
2
+ import { Story } from '@storybook/react'
3
+ import * as React from 'react'
4
+
5
+ import { FiEye, FiEyeOff } from 'react-icons/fi'
6
+
7
+ import { PasswordInput } from './password-input'
8
+
9
+ export default {
10
+ title: 'Components/Forms/PasswordInput',
11
+ decorators: [
12
+ (Story: any) => (
13
+ <Container mt="40px">
14
+ <Story />
15
+ </Container>
16
+ ),
17
+ ],
18
+ }
19
+
20
+ export const Basic: Story = () => (
21
+ <FormControl>
22
+ <FormLabel>Password</FormLabel>
23
+ <PasswordInput name="password" placeholder="Password" />
24
+ </FormControl>
25
+ )
26
+
27
+ export const ReactIcons: Story = () => (
28
+ <>
29
+ <PasswordInput
30
+ name="password"
31
+ placeholder="Password"
32
+ viewIcon={<Icon as={FiEye} />}
33
+ viewOffIcon={<Icon as={FiEyeOff} />}
34
+ />
35
+ </>
36
+ )
37
+
38
+ export const Variant: Story = () => (
39
+ <FormControl>
40
+ <FormLabel>Password</FormLabel>
41
+ <PasswordInput name="password" variant="flushed" />
42
+ </FormControl>
43
+ )
44
+
45
+ export const CustomWidth: Story = () => (
46
+ <FormControl>
47
+ <FormLabel>Password</FormLabel>
48
+ <PasswordInput name="password" width="200px" />
49
+ </FormControl>
50
+ )
@@ -0,0 +1,20 @@
1
+ import * as React from 'react'
2
+
3
+ import { render, act, fireEvent, testStories } from '@saas-ui/test-utils'
4
+ import * as stories from './password-input.stories'
5
+
6
+ const { Basic } = testStories<typeof stories>(stories)
7
+
8
+ test('should render correct aria labels', async () => {
9
+ const { getByText, getByLabelText, getByPlaceholderText, getByRole } = render(
10
+ <Basic />
11
+ )
12
+
13
+ const toggle = getByLabelText('Show password')
14
+
15
+ await act(async () => {
16
+ fireEvent.click(toggle)
17
+ })
18
+
19
+ expect(toggle).toHaveAttribute('aria-label', 'Hide password')
20
+ })
@@ -0,0 +1,69 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import { forwardRef, InputGroup, Input, InputProps } from '@chakra-ui/react'
4
+ import { __DEV__ } from '@chakra-ui/utils'
5
+ import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'
6
+
7
+ import { InputRightButton } from '../input-right-button'
8
+
9
+ interface PasswordOptions {
10
+ viewIcon?: React.ReactNode
11
+ viewOffIcon?: React.ReactNode
12
+ }
13
+
14
+ export interface PasswordInputProps extends InputProps, PasswordOptions {}
15
+
16
+ export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
17
+ (props, ref) => {
18
+ const {
19
+ viewIcon,
20
+ viewOffIcon,
21
+ autoComplete,
22
+ w,
23
+ width,
24
+ size,
25
+ variant,
26
+ ...inputProps
27
+ } = props
28
+ const [show, setShow] = useState(false)
29
+ const handleClick = () => setShow(!show)
30
+
31
+ const label = show ? 'Hide password' : 'Show password'
32
+
33
+ let icon
34
+ if (show) {
35
+ icon = viewIcon || <ViewIcon />
36
+ } else {
37
+ icon = viewOffIcon || <ViewOffIcon />
38
+ }
39
+
40
+ const groupProps = {
41
+ width: w || width,
42
+ size,
43
+ variant,
44
+ }
45
+
46
+ return (
47
+ <InputGroup {...groupProps}>
48
+ <Input
49
+ {...inputProps}
50
+ ref={ref}
51
+ type={show ? 'text' : 'password'}
52
+ autoComplete={show ? 'off' : autoComplete}
53
+ />
54
+
55
+ <InputRightButton
56
+ onClick={handleClick}
57
+ aria-label={label}
58
+ variant="ghost"
59
+ >
60
+ {icon}
61
+ </InputRightButton>
62
+ </InputGroup>
63
+ )
64
+ }
65
+ )
66
+
67
+ if (__DEV__) {
68
+ PasswordInput.displayName = 'PasswordInput'
69
+ }
@@ -0,0 +1 @@
1
+ export * from './pin-input'
@@ -0,0 +1,38 @@
1
+ import { Container } from '@chakra-ui/react'
2
+ import * as React from 'react'
3
+
4
+ import { ComponentStory } from '@storybook/react'
5
+
6
+ import { PinInput } from './pin-input'
7
+
8
+ export default {
9
+ title: 'Components/Forms/PinInput',
10
+ decorators: [
11
+ (Story: any) => (
12
+ <Container mt="40px">
13
+ <Story />
14
+ </Container>
15
+ ),
16
+ ],
17
+ }
18
+
19
+ const Template: ComponentStory<typeof PinInput> = (args) => (
20
+ <PinInput {...args} />
21
+ )
22
+
23
+ export const Basic = Template.bind({})
24
+ Basic.args = {
25
+ /**
26
+ * Description
27
+ */
28
+ name: 'pin-input',
29
+ }
30
+
31
+ export const PinLength = Template.bind({})
32
+ PinLength.args = {
33
+ /**
34
+ * Description
35
+ */
36
+ name: 'pin-input',
37
+ pinLength: 10,
38
+ }
@@ -0,0 +1,6 @@
1
+ import * as React from 'react'
2
+
3
+ import { render, testStories } from '@saas-ui/test-utils'
4
+ import * as stories from './pin-input.stories'
5
+
6
+ testStories<typeof stories>(stories)
@@ -0,0 +1,50 @@
1
+ import * as React from 'react'
2
+ import {
3
+ forwardRef,
4
+ PinInput as ChakraPinInput,
5
+ UsePinInputProps,
6
+ PinInputField,
7
+ HStack,
8
+ SystemProps,
9
+ } from '@chakra-ui/react'
10
+
11
+ import { __DEV__ } from '@chakra-ui/utils'
12
+
13
+ interface PinInputOptions {
14
+ /**
15
+ * The pin length.
16
+ */
17
+ pinLength?: number
18
+ /**
19
+ * Spacing between the inputs.
20
+ */
21
+ spacing?: SystemProps['margin']
22
+ }
23
+
24
+ export interface PinInputProps extends UsePinInputProps, PinInputOptions {}
25
+
26
+ /**
27
+ * @deprecated
28
+ */
29
+ export const PinInput = forwardRef<PinInputProps, 'div'>((props, ref) => {
30
+ const { pinLength = 4, spacing, ...inputProps } = props
31
+
32
+ const inputs: React.ReactNode[] = []
33
+ for (let i = 0; i < pinLength; i++) {
34
+ inputs.push(<PinInputField key={i} ref={ref} />)
35
+ }
36
+
37
+ return (
38
+ <HStack spacing={spacing}>
39
+ <ChakraPinInput {...inputProps}>{inputs}</ChakraPinInput>
40
+ </HStack>
41
+ )
42
+ })
43
+
44
+ PinInput.defaultProps = {
45
+ pinLength: 4,
46
+ }
47
+
48
+ if (__DEV__) {
49
+ PinInput.displayName = 'PinInput'
50
+ }
@@ -0,0 +1 @@
1
+ export * from './radio-input'
@@ -0,0 +1,45 @@
1
+ import { Container } from '@chakra-ui/react'
2
+ import * as React from 'react'
3
+
4
+ import { ComponentStory } from '@storybook/react'
5
+
6
+ import { RadioInput } from './'
7
+
8
+ export default {
9
+ title: 'Components/Forms/Radio',
10
+ decorators: [
11
+ (Story: any) => (
12
+ <Container mt="40px">
13
+ <Story />
14
+ </Container>
15
+ ),
16
+ ],
17
+ }
18
+
19
+ const options = [
20
+ {
21
+ value: '1',
22
+ label: 'Option 1',
23
+ },
24
+ {
25
+ value: '2',
26
+ label: 'Option 2',
27
+ },
28
+ ]
29
+
30
+ const Template: ComponentStory<typeof RadioInput> = (args) => (
31
+ <RadioInput {...args} />
32
+ )
33
+
34
+ export const Basic = Template.bind({})
35
+ Basic.args = {
36
+ name: 'radio',
37
+ options,
38
+ }
39
+
40
+ export const Direction = Template.bind({})
41
+ Direction.args = {
42
+ name: 'radio',
43
+ direction: 'row',
44
+ options,
45
+ }
@@ -0,0 +1,58 @@
1
+ import * as React from 'react'
2
+
3
+ import {
4
+ forwardRef,
5
+ Stack,
6
+ RadioGroup,
7
+ RadioGroupProps,
8
+ Radio,
9
+ RadioProps,
10
+ SystemProps,
11
+ StackDirection,
12
+ } from '@chakra-ui/react'
13
+ import { __DEV__ } from '@chakra-ui/utils'
14
+
15
+ interface Option extends RadioProps {
16
+ value: string
17
+ label?: string
18
+ }
19
+
20
+ interface RadioInputOptions {
21
+ options: Option[]
22
+ spacing?: SystemProps['margin']
23
+ direction?: StackDirection
24
+ }
25
+
26
+ export interface RadioInputProps
27
+ extends Omit<RadioGroupProps, 'children'>,
28
+ RadioInputOptions {}
29
+
30
+ export const RadioInput = forwardRef<RadioInputProps, 'div'>(
31
+ ({ options, spacing, direction, ...props }, ref) => {
32
+ const { onBlur, onChange, ...groupProps } = props
33
+
34
+ return (
35
+ <RadioGroup onChange={onChange} {...groupProps}>
36
+ <Stack spacing={spacing} direction={direction}>
37
+ {options.map(({ value, label, ...radioProps }, i) => {
38
+ return (
39
+ <Radio
40
+ key={i}
41
+ onBlur={onBlur}
42
+ value={value}
43
+ ref={ref}
44
+ {...radioProps}
45
+ >
46
+ {label || value}
47
+ </Radio>
48
+ )
49
+ })}
50
+ </Stack>
51
+ </RadioGroup>
52
+ )
53
+ }
54
+ )
55
+
56
+ if (__DEV__) {
57
+ RadioInput.displayName = 'RadioInput'
58
+ }
@@ -0,0 +1,6 @@
1
+ import * as React from 'react'
2
+
3
+ import { render, testStories } from '@saas-ui/test-utils'
4
+ import * as stories from './radio-input.stories'
5
+
6
+ testStories<typeof stories>(stories)
@@ -0,0 +1,2 @@
1
+ export * from './select'
2
+ export * from './native-select'
@@ -0,0 +1,42 @@
1
+ import * as React from 'react'
2
+
3
+ import {
4
+ forwardRef,
5
+ Select as ChakraSelect,
6
+ SelectProps as ChakraSelectProps,
7
+ } from '@chakra-ui/react'
8
+ import { __DEV__ } from '@chakra-ui/utils'
9
+
10
+ interface Option {
11
+ value: string
12
+ label?: string
13
+ }
14
+
15
+ interface NativeSelectOptions {
16
+ options?: Option[]
17
+ }
18
+
19
+ export interface NativeSelectProps
20
+ extends ChakraSelectProps,
21
+ NativeSelectOptions {}
22
+
23
+ export const NativeSelect = forwardRef<NativeSelectProps, 'select'>(
24
+ ({ options, children, ...props }, ref) => {
25
+ return (
26
+ <ChakraSelect ref={ref} {...props}>
27
+ {children ||
28
+ options?.map(({ value, label }) => {
29
+ return (
30
+ <option key={value} value={value}>
31
+ {label || value}
32
+ </option>
33
+ )
34
+ })}
35
+ </ChakraSelect>
36
+ )
37
+ }
38
+ )
39
+
40
+ if (__DEV__) {
41
+ NativeSelect.displayName = 'NativeSelect'
42
+ }