@sakura-ui/sakura-ui 0.0.16 → 0.1.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 (115) hide show
  1. package/README.md +8 -7
  2. package/package.json +3 -3
  3. package/packages/{react → core}/package.json +3 -3
  4. package/packages/{react → core}/src/components/Button.tsx +2 -2
  5. package/packages/{react → core}/src/components/Card.tsx +2 -2
  6. package/packages/{react → core}/src/components/Code.tsx +2 -2
  7. package/packages/{react → core}/src/components/Heading.tsx +18 -18
  8. package/packages/{react → core}/src/components/Icon.tsx +2 -2
  9. package/packages/{react → core}/src/components/IconButton.tsx +2 -2
  10. package/packages/{react → core}/src/components/InfoCard.tsx +4 -4
  11. package/packages/{react → core}/src/components/Link.tsx +6 -6
  12. package/packages/{react → core}/src/components/List.tsx +4 -4
  13. package/packages/{react → core}/src/components/Pre.tsx +2 -2
  14. package/packages/{react → core}/src/components/Table.tsx +14 -14
  15. package/packages/core/tsconfig.json +11 -0
  16. package/packages/forms/coverage/clover.xml +209 -0
  17. package/packages/forms/coverage/coverage-final.json +18 -0
  18. package/packages/forms/coverage/lcov-report/base.css +224 -0
  19. package/packages/forms/coverage/lcov-report/block-navigation.js +87 -0
  20. package/packages/forms/coverage/lcov-report/favicon.png +0 -0
  21. package/packages/forms/coverage/lcov-report/index.html +161 -0
  22. package/packages/forms/coverage/lcov-report/prettify.css +1 -0
  23. package/packages/forms/coverage/lcov-report/prettify.js +2 -0
  24. package/packages/forms/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  25. package/packages/forms/coverage/lcov-report/sorter.js +196 -0
  26. package/packages/forms/coverage/lcov-report/src/components/Checkbox.tsx.html +295 -0
  27. package/packages/forms/coverage/lcov-report/src/components/FieldsetControl.tsx.html +295 -0
  28. package/packages/forms/coverage/lcov-report/src/components/LabelControl.tsx.html +289 -0
  29. package/packages/forms/coverage/lcov-report/src/components/Radio.tsx.html +262 -0
  30. package/packages/forms/coverage/lcov-report/src/components/Select.tsx.html +253 -0
  31. package/packages/forms/coverage/lcov-report/src/components/Text.tsx.html +217 -0
  32. package/packages/forms/coverage/lcov-report/src/components/Textarea.tsx.html +226 -0
  33. package/packages/forms/coverage/lcov-report/src/components/context.ts.html +133 -0
  34. package/packages/forms/coverage/lcov-report/src/components/controlled/CheckboxGroup.tsx.html +244 -0
  35. package/packages/forms/coverage/lcov-report/src/components/controlled/RadioGroup.tsx.html +265 -0
  36. package/packages/forms/coverage/lcov-report/src/components/controlled/SelectControl.tsx.html +205 -0
  37. package/packages/forms/coverage/lcov-report/src/components/controlled/TextControl.tsx.html +202 -0
  38. package/packages/forms/coverage/lcov-report/src/components/controlled/TextareaControl.tsx.html +205 -0
  39. package/packages/forms/coverage/lcov-report/src/components/controlled/index.html +191 -0
  40. package/packages/forms/coverage/lcov-report/src/components/controlled/index.ts.html +100 -0
  41. package/packages/forms/coverage/lcov-report/src/components/index.html +236 -0
  42. package/packages/forms/coverage/lcov-report/src/components/index.ts.html +106 -0
  43. package/packages/forms/coverage/lcov-report/src/index.html +116 -0
  44. package/packages/forms/coverage/lcov-report/src/index.ts.html +136 -0
  45. package/packages/forms/coverage/lcov-report/src/utils/index.html +116 -0
  46. package/packages/forms/coverage/lcov-report/src/utils/index.ts.html +88 -0
  47. package/packages/forms/coverage/lcov.info +426 -0
  48. package/packages/forms/dist/index.cjs.js +24 -27
  49. package/packages/forms/dist/index.cjs.js.map +1 -1
  50. package/packages/forms/dist/index.es.js +593 -382
  51. package/packages/forms/dist/index.es.js.map +1 -1
  52. package/packages/forms/jest.config.ts +14 -0
  53. package/packages/forms/node_modules/.bin/ts-jest +2 -2
  54. package/packages/forms/package.json +6 -1
  55. package/packages/forms/src/components/Checkbox.tsx +19 -8
  56. package/packages/forms/src/components/FieldsetControl.tsx +70 -0
  57. package/packages/forms/src/components/LabelControl.tsx +65 -0
  58. package/packages/forms/src/components/Radio.tsx +24 -9
  59. package/packages/forms/src/components/Select.tsx +20 -6
  60. package/packages/forms/src/components/Text.tsx +13 -4
  61. package/packages/forms/src/components/Textarea.tsx +17 -3
  62. package/packages/forms/src/components/context.ts +16 -0
  63. package/packages/forms/src/components/controlled/CheckboxGroup.tsx +37 -15
  64. package/packages/forms/src/components/controlled/RadioGroup.tsx +44 -20
  65. package/packages/forms/src/components/controlled/SelectControl.tsx +40 -0
  66. package/packages/forms/src/components/controlled/TextControl.tsx +39 -0
  67. package/packages/forms/src/components/controlled/TextareaControl.tsx +40 -0
  68. package/packages/forms/src/components/controlled/index.ts +3 -3
  69. package/packages/forms/src/components/index.ts +2 -0
  70. package/packages/forms/src/index.ts +12 -4
  71. package/packages/forms/tests/FieldsetControl.test.tsx +54 -0
  72. package/packages/forms/tests/LabelControl.test.tsx +50 -0
  73. package/packages/forms/tests/Radio.test.tsx +99 -0
  74. package/packages/forms/tests/Text.test.tsx +96 -0
  75. package/packages/forms/tsconfig.json +1 -17
  76. package/packages/forms/dist/components/Checkbox.d.ts +0 -5
  77. package/packages/forms/dist/components/Checkbox.d.ts.map +0 -1
  78. package/packages/forms/dist/components/Radio.d.ts +0 -5
  79. package/packages/forms/dist/components/Radio.d.ts.map +0 -1
  80. package/packages/forms/dist/components/Select.d.ts +0 -5
  81. package/packages/forms/dist/components/Select.d.ts.map +0 -1
  82. package/packages/forms/dist/components/Text.d.ts +0 -5
  83. package/packages/forms/dist/components/Text.d.ts.map +0 -1
  84. package/packages/forms/dist/components/Textarea.d.ts +0 -5
  85. package/packages/forms/dist/components/Textarea.d.ts.map +0 -1
  86. package/packages/forms/dist/components/controlled/CheckboxGroup.d.ts +0 -9
  87. package/packages/forms/dist/components/controlled/CheckboxGroup.d.ts.map +0 -1
  88. package/packages/forms/dist/components/controlled/InputItem.d.ts +0 -5
  89. package/packages/forms/dist/components/controlled/InputItem.d.ts.map +0 -1
  90. package/packages/forms/dist/components/controlled/RadioGroup.d.ts +0 -9
  91. package/packages/forms/dist/components/controlled/RadioGroup.d.ts.map +0 -1
  92. package/packages/forms/dist/components/controlled/Select.d.ts +0 -6
  93. package/packages/forms/dist/components/controlled/Select.d.ts.map +0 -1
  94. package/packages/forms/dist/components/controlled/Text.d.ts +0 -6
  95. package/packages/forms/dist/components/controlled/Text.d.ts.map +0 -1
  96. package/packages/forms/dist/components/controlled/Textarea.d.ts +0 -6
  97. package/packages/forms/dist/components/controlled/Textarea.d.ts.map +0 -1
  98. package/packages/forms/dist/components/controlled/index.d.ts +0 -6
  99. package/packages/forms/dist/components/controlled/index.d.ts.map +0 -1
  100. package/packages/forms/dist/components/index.d.ts +0 -6
  101. package/packages/forms/dist/components/index.d.ts.map +0 -1
  102. package/packages/forms/dist/index.d.ts +0 -3
  103. package/packages/forms/dist/index.d.ts.map +0 -1
  104. package/packages/forms/dist/utils/index.d.ts +0 -2
  105. package/packages/forms/dist/utils/index.d.ts.map +0 -1
  106. package/packages/forms/src/components/controlled/Select.tsx +0 -22
  107. package/packages/forms/src/components/controlled/Text.tsx +0 -22
  108. package/packages/forms/src/components/controlled/Textarea.tsx +0 -23
  109. package/packages/forms/src/vite-env.d.ts +0 -1
  110. package/packages/react/tsconfig.json +0 -27
  111. /package/packages/{react → core}/src/components/index.ts +0 -0
  112. /package/packages/{react → core}/src/index.ts +0 -0
  113. /package/packages/{react → core}/src/utils/index.ts +0 -0
  114. /package/packages/{react → core}/tsconfig.node.json +0 -0
  115. /package/packages/{react → core}/vite.config.ts +0 -0
@@ -0,0 +1,65 @@
1
+ import React from 'react'
2
+ import { cx } from '../utils'
3
+ import { ControllerContext } from './context'
4
+
5
+ export interface LabelControlProps
6
+ extends React.ComponentPropsWithoutRef<'div'> {
7
+ labelText: string
8
+ helperText?: string
9
+ errorMessage?: string
10
+ isInvalid?: boolean
11
+ isRequired?: boolean
12
+ }
13
+
14
+ export const LabelControl = React.forwardRef<HTMLDivElement, LabelControlProps>(
15
+ (props, ref) => {
16
+ const id = React.useId()
17
+ const {
18
+ labelText,
19
+ className,
20
+ helperText,
21
+ errorMessage,
22
+ isInvalid,
23
+ isRequired,
24
+ children,
25
+ ...restProps
26
+ } = props
27
+
28
+ const context = {
29
+ helperTextId: `helper-text-${id}`,
30
+ errorMessageId: `error-message-${id}`,
31
+ isInvalid: isInvalid ?? false,
32
+ isRequired: isRequired ?? false
33
+ }
34
+
35
+ return (
36
+ <ControllerContext.Provider value={context}>
37
+ <div className={cx('mb-4', className)} ref={ref} {...restProps}>
38
+ <label>
39
+ <span className={cx('block mb-2', isInvalid && 'text-sun-800')}>
40
+ {labelText}
41
+ {isRequired && <span className="text-sun-800">&nbsp;*</span>}
42
+ </span>
43
+ {children}
44
+ </label>
45
+ {helperText && (
46
+ <p id={context.helperTextId} className="text-sm text-sumi-700 mt-2">
47
+ {helperText}
48
+ </p>
49
+ )}
50
+ {isInvalid && (
51
+ <p
52
+ id={context.errorMessageId}
53
+ className={cx(
54
+ 'text-sm text-sun-800',
55
+ helperText ? 'mt-1' : 'mt-2'
56
+ )}
57
+ >
58
+ {errorMessage}
59
+ </p>
60
+ )}
61
+ </div>
62
+ </ControllerContext.Provider>
63
+ )
64
+ }
65
+ )
@@ -1,17 +1,25 @@
1
1
  import React from 'react'
2
2
  import { cx } from '../utils'
3
+ import { ControllerContext } from './context'
3
4
 
4
- export interface Props extends React.ComponentProps<'input'> {}
5
+ export interface Props extends React.ComponentPropsWithoutRef<'input'> {}
5
6
 
6
7
  export const Radio = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
7
8
  const { className, children, ...newProps } = props
9
+ const ctx = React.useContext(ControllerContext)
10
+ if (ctx.isRequired) {
11
+ newProps.required = true
12
+ }
13
+ if (ctx.groupName) {
14
+ newProps.name = ctx.groupName
15
+ }
8
16
 
9
17
  const style = `
10
- flex
11
- items-center
18
+ inline-block
12
19
  text-sm
13
20
  cursor-pointer
14
21
  py-2
22
+ mr-4
15
23
  `
16
24
  const styleInput = `
17
25
  hidden
@@ -19,7 +27,6 @@ export const Radio = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
19
27
  `
20
28
 
21
29
  const styleRadio = `
22
- inline-block
23
30
  bg-clip-content
24
31
  w-6 h-6
25
32
  ml-1
@@ -33,12 +40,20 @@ export const Radio = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
33
40
  peer-checked:border-sea-600
34
41
  `
35
42
  return (
36
- <div className="inline-block">
37
- <label htmlFor={newProps.id} className={cx(style, className)}>
38
- <input className={styleInput} type="radio" {...newProps} ref={ref} />
43
+ <label htmlFor={newProps.id} className={cx(style, className)}>
44
+ <span className="flex items-center">
45
+ <input
46
+ className={styleInput}
47
+ type="radio"
48
+ aria-describedby={ctx.helperTextId}
49
+ aria-errormessage={ctx.errorMessageId}
50
+ aria-invalid={ctx.isInvalid ?? false}
51
+ {...newProps}
52
+ ref={ref}
53
+ />
39
54
  <span className={styleRadio}></span>
40
55
  {children}
41
- </label>
42
- </div>
56
+ </span>
57
+ </label>
43
58
  )
44
59
  })
@@ -1,20 +1,27 @@
1
1
  import React from 'react'
2
2
  import { cx } from '../utils'
3
+ import { ControllerContext } from './context'
3
4
 
4
5
  export interface SelectProps extends React.ComponentPropsWithoutRef<'select'> {}
5
6
 
6
7
  export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
7
8
  (props, ref) => {
8
9
  const { className, children, ...newProps } = props
10
+ const ctx = React.useContext(ControllerContext)
11
+ if (ctx.isRequired) {
12
+ newProps.required = true
13
+ }
14
+
15
+ const invalidStyle = ctx.isInvalid ? 'border-sun-800' : 'border-sumi-900'
9
16
 
10
17
  const style = `
18
+ cursor-pointer
11
19
  appearance-none
12
20
  text-base
13
21
  w-full
14
22
  bg-white-100
15
23
  border
16
24
  border-solid
17
- border-sumi-900
18
25
  py-4 px-4 pr-8
19
26
  rounded-[8px]
20
27
  focus:outline
@@ -23,11 +30,18 @@ export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
23
30
  `
24
31
 
25
32
  return (
26
- <div className="inline-block relative">
27
- <select className={cx(style, props.className)} {...newProps} ref={ref}>
33
+ <span className="inline-block relative">
34
+ <select
35
+ className={cx(style, invalidStyle, props.className)}
36
+ aria-describedby={ctx.helperTextId}
37
+ aria-errormessage={ctx.errorMessageId}
38
+ aria-invalid={ctx.isInvalid ?? false}
39
+ {...newProps}
40
+ ref={ref}
41
+ >
28
42
  {children}
29
43
  </select>
30
- <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2">
44
+ <span className="flex items-center absolute inset-y-0 right-0 px-2 pointer-events-none">
31
45
  <svg
32
46
  xmlns="http://www.w3.org/2000/svg"
33
47
  className="fill-sumi-900"
@@ -36,8 +50,8 @@ export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
36
50
  >
37
51
  <path d="M12 15.05 6.35 9.4 7.4 8.35l4.6 4.6 4.6-4.6 1.05 1.05Z" />
38
52
  </svg>
39
- </div>
40
- </div>
53
+ </span>
54
+ </span>
41
55
  )
42
56
  }
43
57
  )
@@ -1,18 +1,24 @@
1
1
  import React from 'react'
2
2
  import { cx } from '../utils'
3
+ import { ControllerContext } from './context'
3
4
 
4
5
  export interface TextProps extends React.ComponentPropsWithoutRef<'input'> {}
5
6
 
6
7
  export const Text = React.forwardRef<HTMLInputElement, TextProps>(
7
8
  (props, ref) => {
8
- const { className, ...newProps } = props
9
+ const { className, ...restProps } = props
10
+ const ctx = React.useContext(ControllerContext)
11
+ if (ctx.isRequired) {
12
+ restProps.required = true
13
+ }
14
+
15
+ const invalidStyle = ctx.isInvalid ? 'border-sun-800' : 'border-sumi-900'
9
16
 
10
17
  const style = `
11
18
  p-4
12
19
  rounded-lg
13
20
  border
14
21
  border-solid
15
- border-sumi-900
16
22
  focus:outline
17
23
  focus:outline-2
18
24
  focus:outline-wood-500
@@ -26,8 +32,11 @@ export const Text = React.forwardRef<HTMLInputElement, TextProps>(
26
32
  return (
27
33
  <input
28
34
  type="text"
29
- className={cx(style, className)}
30
- {...newProps}
35
+ className={cx(style, invalidStyle, className)}
36
+ aria-describedby={ctx.helperTextId}
37
+ aria-errormessage={ctx.errorMessageId}
38
+ aria-invalid={ctx.isInvalid ?? false}
39
+ {...restProps}
31
40
  ref={ref}
32
41
  />
33
42
  )
@@ -1,19 +1,26 @@
1
1
  import React from 'react'
2
2
  import { cx } from '../utils'
3
+ import { ControllerContext } from './context'
3
4
 
4
5
  export interface TextareaProps
5
6
  extends React.ComponentPropsWithoutRef<'textarea'> {}
6
7
 
7
8
  export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
8
9
  (props, ref) => {
9
- const { className, children, ...newProps } = props
10
+ const { className, children, ...restProps } = props
11
+
12
+ const ctx = React.useContext(ControllerContext)
13
+ if (ctx.isRequired) {
14
+ restProps.required = true
15
+ }
16
+
17
+ const invalidStyle = ctx.isInvalid ? 'border-sun-800' : 'border-sumi-900'
10
18
 
11
19
  const style = `
12
20
  p-4
13
21
  rounded-lg
14
22
  border
15
23
  border-solid
16
- border-sumi-900
17
24
  focus:outline
18
25
  focus:outline-2
19
26
  focus:outline-wood-500
@@ -25,7 +32,14 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
25
32
  `
26
33
 
27
34
  return (
28
- <textarea className={cx(style, className)} {...newProps} ref={ref}>
35
+ <textarea
36
+ className={cx(style, invalidStyle, className)}
37
+ aria-describedby={ctx.helperTextId}
38
+ aria-errormessage={ctx.errorMessageId}
39
+ aria-invalid={ctx.isInvalid ?? false}
40
+ {...restProps}
41
+ ref={ref}
42
+ >
29
43
  {children}
30
44
  </textarea>
31
45
  )
@@ -0,0 +1,16 @@
1
+ import React from 'react'
2
+
3
+ export interface ControllerContextType {
4
+ groupName?: string
5
+ helperTextId: string
6
+ errorMessageId: string
7
+ isInvalid: boolean
8
+ isRequired?: boolean
9
+ }
10
+
11
+ export const ControllerContext = React.createContext<ControllerContextType>({
12
+ helperTextId: '',
13
+ errorMessageId: '',
14
+ isInvalid: false,
15
+ isRequired: false
16
+ })
@@ -1,31 +1,53 @@
1
1
  import React from 'react'
2
2
  import { Checkbox } from '../Checkbox'
3
+ import { FieldsetControl } from '../FieldsetControl'
3
4
  import { InputItem } from './InputItem'
4
- import { cx } from '../../utils'
5
5
 
6
6
  export interface CheckboxGroupProps
7
- extends Omit<React.ComponentProps<'fieldset'>, 'onChange'> {
7
+ extends Omit<React.ComponentPropsWithoutRef<'fieldset'>, 'onChange'> {
8
8
  items: InputItem[]
9
9
  onChange: React.ChangeEventHandler<HTMLInputElement>
10
- label: string
10
+ labelText: string
11
+ helperText?: string
12
+ errorMessage?: string
13
+ isInvalid?: boolean
14
+ isRequired?: boolean
11
15
  }
12
16
 
13
- export const CheckboxGroup = (props: CheckboxGroupProps) => {
14
- const { items, onChange, label, className, ...newProps } = props
15
- const groupName = `checkbox_group_${Math.random()}`
17
+ export const CheckboxGroup = React.forwardRef<
18
+ HTMLFieldSetElement,
19
+ CheckboxGroupProps
20
+ >((props, ref) => {
21
+ const {
22
+ items,
23
+ onChange,
24
+ className,
25
+ labelText,
26
+ helperText,
27
+ errorMessage,
28
+ isInvalid,
29
+ isRequired,
30
+ ...restProps
31
+ } = props
16
32
 
17
33
  return (
18
- <fieldset className={cx('mb-4', className)} {...newProps}>
19
- <legend>{label}</legend>
34
+ <FieldsetControl
35
+ className={className}
36
+ labelText={labelText}
37
+ helperText={helperText}
38
+ errorMessage={errorMessage}
39
+ isInvalid={isInvalid}
40
+ isRequired={isRequired}
41
+ ref={ref}
42
+ {...restProps}
43
+ >
20
44
  {items.map(({ label, value }, index) => {
21
45
  return (
22
- <div key={index}>
23
- <Checkbox name={groupName} value={value} onChange={onChange}>
24
- {label}
25
- </Checkbox>
26
- </div>
46
+ <Checkbox key={index} value={value} onChange={onChange}>
47
+ {label}
48
+ </Checkbox>
27
49
  )
28
50
  })}
29
- </fieldset>
51
+ </FieldsetControl>
30
52
  )
31
- }
53
+ })
@@ -1,36 +1,60 @@
1
1
  import React from 'react'
2
2
  import { Radio } from '../Radio'
3
+ import { FieldsetControl } from '../FieldsetControl'
3
4
  import { InputItem } from './InputItem'
4
- import { cx } from '../../utils'
5
5
 
6
6
  export interface RadioGroupProps
7
- extends Omit<React.ComponentProps<'fieldset'>, 'onChange'> {
7
+ extends Omit<React.ComponentPropsWithoutRef<'fieldset'>, 'onChange'> {
8
8
  items: InputItem[]
9
9
  onChange: React.ChangeEventHandler<HTMLInputElement>
10
- label: string
10
+ labelText: string
11
+ helperText?: string
12
+ errorMessage?: string
13
+ isInvalid?: boolean
14
+ isRequired?: boolean
15
+ defaultValue?: string
11
16
  }
12
17
 
13
- export const RadioGroup = (props: RadioGroupProps) => {
14
- const { items, onChange, label, className, ...newProps } = props
15
- const groupName = `radio_group_${Math.random()}`
18
+ export const RadioGroup = React.forwardRef<
19
+ HTMLFieldSetElement,
20
+ RadioGroupProps
21
+ >((props, ref) => {
22
+ const {
23
+ items,
24
+ onChange,
25
+ className,
26
+ labelText,
27
+ helperText,
28
+ errorMessage,
29
+ isInvalid,
30
+ isRequired,
31
+ defaultValue,
32
+ ...restProps
33
+ } = props
16
34
 
17
35
  return (
18
- <fieldset className={cx('mb-4', className)} {...newProps}>
19
- <legend>{label}</legend>
36
+ <FieldsetControl
37
+ className={className}
38
+ labelText={labelText}
39
+ helperText={helperText}
40
+ errorMessage={errorMessage}
41
+ isInvalid={isInvalid}
42
+ isRequired={isRequired}
43
+ ref={ref}
44
+ {...restProps}
45
+ >
20
46
  {items.map(({ label, value }, index) => {
21
47
  return (
22
- <div key={index}>
23
- <Radio
24
- name={groupName}
25
- value={value}
26
- onChange={onChange}
27
- defaultChecked={index === 0}
28
- >
29
- {label}
30
- </Radio>
31
- </div>
48
+ <Radio
49
+ key={index}
50
+ value={value}
51
+ onChange={onChange}
52
+ defaultChecked={defaultValue === value}
53
+ >
54
+ {label}
55
+ </Radio>
32
56
  )
33
57
  })}
34
- </fieldset>
58
+ </FieldsetControl>
35
59
  )
36
- }
60
+ })
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import { LabelControl } from '../LabelControl'
3
+ import { Select } from '../Select'
4
+
5
+ export interface SelectControlProps
6
+ extends React.ComponentPropsWithoutRef<'select'> {
7
+ labelText: string
8
+ helperText?: string
9
+ errorMessage?: string
10
+ isInvalid?: boolean
11
+ isRequired?: boolean
12
+ }
13
+
14
+ export const SelectControl = React.forwardRef<
15
+ HTMLSelectElement,
16
+ SelectControlProps
17
+ >((props, ref) => {
18
+ const {
19
+ className,
20
+ labelText,
21
+ helperText,
22
+ errorMessage,
23
+ isInvalid,
24
+ isRequired,
25
+ ...restProps
26
+ } = props
27
+
28
+ return (
29
+ <LabelControl
30
+ className={className}
31
+ labelText={labelText}
32
+ helperText={helperText}
33
+ errorMessage={errorMessage}
34
+ isInvalid={isInvalid}
35
+ isRequired={isRequired}
36
+ >
37
+ <Select ref={ref} {...restProps} />
38
+ </LabelControl>
39
+ )
40
+ })
@@ -0,0 +1,39 @@
1
+ import React from 'react'
2
+ import { LabelControl } from '../LabelControl'
3
+ import { Text } from '../Text'
4
+
5
+ export interface TextControlProps
6
+ extends React.ComponentPropsWithoutRef<'input'> {
7
+ labelText: string
8
+ helperText?: string
9
+ errorMessage?: string
10
+ isInvalid?: boolean
11
+ isRequired?: boolean
12
+ }
13
+
14
+ export const TextControl = React.forwardRef<HTMLInputElement, TextControlProps>(
15
+ (props, ref) => {
16
+ const {
17
+ className,
18
+ labelText,
19
+ helperText,
20
+ errorMessage,
21
+ isInvalid,
22
+ isRequired,
23
+ ...restProps
24
+ } = props
25
+
26
+ return (
27
+ <LabelControl
28
+ className={className}
29
+ labelText={labelText}
30
+ helperText={helperText}
31
+ errorMessage={errorMessage}
32
+ isInvalid={isInvalid}
33
+ isRequired={isRequired}
34
+ >
35
+ <Text ref={ref} {...restProps} />
36
+ </LabelControl>
37
+ )
38
+ }
39
+ )
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import { LabelControl } from '../LabelControl'
3
+ import { Textarea } from '../Textarea'
4
+
5
+ export interface TextareaControlProps
6
+ extends React.ComponentPropsWithoutRef<'textarea'> {
7
+ labelText: string
8
+ helperText?: string
9
+ errorMessage?: string
10
+ isInvalid?: boolean
11
+ isRequired?: boolean
12
+ }
13
+
14
+ export const TextareaControl = React.forwardRef<
15
+ HTMLTextAreaElement,
16
+ TextareaControlProps
17
+ >((props, ref) => {
18
+ const {
19
+ className,
20
+ labelText,
21
+ helperText,
22
+ errorMessage,
23
+ isInvalid,
24
+ isRequired,
25
+ ...restProps
26
+ } = props
27
+
28
+ return (
29
+ <LabelControl
30
+ className={className}
31
+ labelText={labelText}
32
+ helperText={helperText}
33
+ errorMessage={errorMessage}
34
+ isInvalid={isInvalid}
35
+ isRequired={isRequired}
36
+ >
37
+ <Textarea ref={ref} {...restProps} />
38
+ </LabelControl>
39
+ )
40
+ })
@@ -1,5 +1,5 @@
1
1
  export { CheckboxGroup } from './CheckboxGroup'
2
2
  export { RadioGroup } from './RadioGroup'
3
- export { Select } from './Select'
4
- export { Text } from './Text'
5
- export { Textarea } from './Textarea'
3
+ export { SelectControl } from './SelectControl'
4
+ export { TextControl } from './TextControl'
5
+ export { TextareaControl } from './TextareaControl'
@@ -3,3 +3,5 @@ export { Radio } from './Radio'
3
3
  export { Select } from './Select'
4
4
  export { Text } from './Text'
5
5
  export { Textarea } from './Textarea'
6
+ export { LabelControl } from './LabelControl'
7
+ export { FieldsetControl } from './FieldsetControl'
@@ -1,9 +1,17 @@
1
- export { Checkbox, Radio, Select, Text, Textarea } from './components'
1
+ export {
2
+ Checkbox,
3
+ Radio,
4
+ Select,
5
+ Text,
6
+ Textarea,
7
+ LabelControl,
8
+ FieldsetControl
9
+ } from './components'
2
10
 
3
11
  export {
4
12
  CheckboxGroup,
5
13
  RadioGroup,
6
- Select as SelectControl,
7
- Text as TextControl,
8
- Textarea as TextareaControl
14
+ SelectControl,
15
+ TextControl,
16
+ TextareaControl
9
17
  } from './components/controlled'
@@ -0,0 +1,54 @@
1
+ import * as React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import '@testing-library/jest-dom'
4
+
5
+ import { FieldsetControl } from '../src'
6
+
7
+ describe('FieldsetControl', () => {
8
+ test('renders FieldsetControl component vol1', async () => {
9
+ render(<FieldsetControl labelText="test" />)
10
+
11
+ const fieldset = screen.getByRole('group')
12
+
13
+ expect(fieldset).toBeInTheDocument()
14
+
15
+ expect(screen.getByText('test')).toBeInTheDocument()
16
+ })
17
+
18
+ test('renders FieldsetControl component vol2', async () => {
19
+ render(
20
+ <FieldsetControl
21
+ labelText="test"
22
+ helperText="helper"
23
+ errorMessage="error"
24
+ />
25
+ )
26
+
27
+ expect(screen.getByText('helper')).toBeInTheDocument()
28
+ expect(screen.queryByText('error')).toBeNull()
29
+ })
30
+
31
+ test('renders FieldsetControl component vol3', async () => {
32
+ render(
33
+ <FieldsetControl
34
+ labelText="test"
35
+ helperText="helper"
36
+ errorMessage="error"
37
+ isInvalid
38
+ />
39
+ )
40
+
41
+ expect(screen.getByText('helper')).toBeInTheDocument()
42
+ expect(screen.getByText('error')).toBeInTheDocument()
43
+ })
44
+
45
+ test('renders FieldsetControl component vol4', async () => {
46
+ render(<FieldsetControl labelText="test" />)
47
+
48
+ expect(screen.queryByText('*')).toBeNull()
49
+
50
+ render(<FieldsetControl labelText="test" isRequired />)
51
+
52
+ expect(screen.getByText('*')).toBeInTheDocument()
53
+ })
54
+ })