@ubie/vitals-ui-consumer 0.0.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.
Files changed (170) hide show
  1. package/.storybook/main.ts +14 -0
  2. package/.storybook/preview.tsx +25 -0
  3. package/.storybook/vitest.setup.ts +7 -0
  4. package/dist/chunk-DKo7XVKm.mjs +33 -0
  5. package/dist/index.d.mts +1720 -0
  6. package/dist/index.d.mts.map +1 -0
  7. package/dist/index.mjs +10594 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/style.css +2299 -0
  10. package/package.json +47 -0
  11. package/src/components/Accordion/Accordion.module.css +75 -0
  12. package/src/components/Accordion/Accordion.spec.tsx +18 -0
  13. package/src/components/Accordion/Accordion.stories.tsx +61 -0
  14. package/src/components/Accordion/Accordion.tsx +89 -0
  15. package/src/components/ActionHalfModal/ActionHalfModal.module.css +180 -0
  16. package/src/components/ActionHalfModal/ActionHalfModal.spec.tsx +57 -0
  17. package/src/components/ActionHalfModal/ActionHalfModal.stories.tsx +469 -0
  18. package/src/components/ActionHalfModal/ActionHalfModal.tsx +269 -0
  19. package/src/components/ActionModal/ActionModal.module.css +145 -0
  20. package/src/components/ActionModal/ActionModal.spec.tsx +57 -0
  21. package/src/components/ActionModal/ActionModal.stories.tsx +302 -0
  22. package/src/components/ActionModal/ActionModal.tsx +232 -0
  23. package/src/components/Bold/Bold.module.css +4 -0
  24. package/src/components/Bold/Bold.spec.tsx +24 -0
  25. package/src/components/Bold/Bold.stories.tsx +54 -0
  26. package/src/components/Bold/Bold.tsx +31 -0
  27. package/src/components/Box/Box.module.css +46 -0
  28. package/src/components/Box/Box.spec.tsx +188 -0
  29. package/src/components/Box/Box.tsx +242 -0
  30. package/src/components/Button/Button.module.css +261 -0
  31. package/src/components/Button/Button.spec.tsx +82 -0
  32. package/src/components/Button/Button.tsx +99 -0
  33. package/src/components/Button/ButtonTypes.ts +107 -0
  34. package/src/components/Button/LinkButton.spec.tsx +86 -0
  35. package/src/components/Button/LinkButton.tsx +80 -0
  36. package/src/components/Button/VariantIcon.tsx +20 -0
  37. package/src/components/Button/useIcon.tsx +16 -0
  38. package/src/components/ButtonCard/ButtonCard.module.css +35 -0
  39. package/src/components/ButtonCard/ButtonCard.spec.tsx +18 -0
  40. package/src/components/ButtonCard/ButtonCard.stories.tsx +54 -0
  41. package/src/components/ButtonCard/ButtonCard.tsx +18 -0
  42. package/src/components/Center/Center.module.css +19 -0
  43. package/src/components/Center/Center.spec.tsx +143 -0
  44. package/src/components/Center/Center.tsx +108 -0
  45. package/src/components/Checkbox/Checkbox.module.css +124 -0
  46. package/src/components/Checkbox/Checkbox.spec.tsx +17 -0
  47. package/src/components/Checkbox/Checkbox.stories.tsx +213 -0
  48. package/src/components/Checkbox/Checkbox.tsx +50 -0
  49. package/src/components/CheckboxCard/CheckboxCard.module.css +102 -0
  50. package/src/components/CheckboxCard/CheckboxCard.spec.tsx +16 -0
  51. package/src/components/CheckboxCard/CheckboxCard.stories.tsx +205 -0
  52. package/src/components/CheckboxCard/CheckboxCard.tsx +53 -0
  53. package/src/components/CheckboxGroup/CheckboxGroup.module.css +16 -0
  54. package/src/components/CheckboxGroup/CheckboxGroup.spec.tsx +17 -0
  55. package/src/components/CheckboxGroup/CheckboxGroup.tsx +64 -0
  56. package/src/components/Color/Color.module.css +3 -0
  57. package/src/components/Color/Color.spec.tsx +24 -0
  58. package/src/components/Color/Color.stories.tsx +71 -0
  59. package/src/components/Color/Color.tsx +28 -0
  60. package/src/components/Divider/Divider.module.css +9 -0
  61. package/src/components/Divider/Divider.spec.tsx +42 -0
  62. package/src/components/Divider/Divider.stories.tsx +77 -0
  63. package/src/components/Divider/Divider.tsx +49 -0
  64. package/src/components/ErrorMessage/ErrorMessage.module.css +8 -0
  65. package/src/components/ErrorMessage/ErrorMessage.spec.tsx +12 -0
  66. package/src/components/ErrorMessage/ErrorMessage.tsx +20 -0
  67. package/src/components/Flex/Flex.module.css +24 -0
  68. package/src/components/Flex/Flex.spec.tsx +188 -0
  69. package/src/components/Flex/Flex.tsx +173 -0
  70. package/src/components/FlexItem/FlexItem.module.css +14 -0
  71. package/src/components/FlexItem/FlexItem.spec.tsx +84 -0
  72. package/src/components/FlexItem/FlexItem.tsx +106 -0
  73. package/src/components/Heading/Heading.module.css +131 -0
  74. package/src/components/Heading/Heading.tsx +86 -0
  75. package/src/components/HelperMessage/HelperMessage.module.css +8 -0
  76. package/src/components/HelperMessage/HelperMessage.tsx +15 -0
  77. package/src/components/Icon/Icon.module.css +6 -0
  78. package/src/components/Icon/Icon.spec.tsx +24 -0
  79. package/src/components/Icon/Icon.stories.tsx +100 -0
  80. package/src/components/Icon/Icon.tsx +101 -0
  81. package/src/components/Input/Input.module.css +51 -0
  82. package/src/components/Input/Input.spec.tsx +14 -0
  83. package/src/components/Input/Input.tsx +27 -0
  84. package/src/components/Label/Label.module.css +14 -0
  85. package/src/components/Label/Label.tsx +39 -0
  86. package/src/components/LinkCard/LinkCard.module.css +72 -0
  87. package/src/components/LinkCard/LinkCard.tsx +96 -0
  88. package/src/components/MessageHalfModal/MessageHalfModal.module.css +181 -0
  89. package/src/components/MessageHalfModal/MessageHalfModal.spec.tsx +73 -0
  90. package/src/components/MessageHalfModal/MessageHalfModal.stories.tsx +242 -0
  91. package/src/components/MessageHalfModal/MessageHalfModal.tsx +194 -0
  92. package/src/components/MessageModal/MessageModal.module.css +149 -0
  93. package/src/components/MessageModal/MessageModal.spec.tsx +57 -0
  94. package/src/components/MessageModal/MessageModal.stories.tsx +223 -0
  95. package/src/components/MessageModal/MessageModal.tsx +178 -0
  96. package/src/components/Pre/Pre.module.css +8 -0
  97. package/src/components/Pre/Pre.spec.tsx +11 -0
  98. package/src/components/Pre/Pre.stories.tsx +76 -0
  99. package/src/components/Pre/Pre.tsx +40 -0
  100. package/src/components/RadioButton/RadioButton.module.css +92 -0
  101. package/src/components/RadioButton/RadioButton.spec.tsx +25 -0
  102. package/src/components/RadioButton/RadioButton.tsx +55 -0
  103. package/src/components/RadioCard/RadioCard.module.css +109 -0
  104. package/src/components/RadioCard/RadioCard.tsx +61 -0
  105. package/src/components/RadioGroup/RadioGroup.module.css +16 -0
  106. package/src/components/RadioGroup/RadioGroup.spec.tsx +17 -0
  107. package/src/components/RadioGroup/RadioGroup.tsx +60 -0
  108. package/src/components/Select/Select.module.css +70 -0
  109. package/src/components/Select/Select.spec.tsx +12 -0
  110. package/src/components/Select/Select.tsx +56 -0
  111. package/src/components/Stack/Stack.module.css +10 -0
  112. package/src/components/Stack/Stack.spec.tsx +177 -0
  113. package/src/components/Stack/Stack.tsx +151 -0
  114. package/src/components/Stepper/Stepper.module.css +137 -0
  115. package/src/components/Stepper/Stepper.spec.tsx +198 -0
  116. package/src/components/Stepper/Stepper.stories.tsx +192 -0
  117. package/src/components/Stepper/Stepper.tsx +70 -0
  118. package/src/components/Stepper/StepperItem.tsx +113 -0
  119. package/src/components/Text/Text.module.css +168 -0
  120. package/src/components/Text/Text.tsx +192 -0
  121. package/src/components/TextArea/TextArea.module.css +46 -0
  122. package/src/components/TextArea/TextArea.spec.tsx +13 -0
  123. package/src/components/TextArea/TextArea.tsx +29 -0
  124. package/src/components/Toggle/Toggle.module.css +71 -0
  125. package/src/components/Toggle/Toggle.spec.tsx +21 -0
  126. package/src/components/Toggle/Toggle.tsx +56 -0
  127. package/src/font.ts +2 -0
  128. package/src/hooks/useScrollable.ts +58 -0
  129. package/src/icons/AppleIcon.tsx +14 -0
  130. package/src/icons/GoogleIcon.tsx +27 -0
  131. package/src/icons/LINEIcon.tsx +16 -0
  132. package/src/index.ts +35 -0
  133. package/src/sharedComponents/RequiredLabel/RequiredLabel.module.css +10 -0
  134. package/src/sharedComponents/RequiredLabel/RequiredLabel.tsx +8 -0
  135. package/src/sharedComponents/VisuallyHidden/VisuallyHidden.module.css +15 -0
  136. package/src/sharedComponents/VisuallyHidden/VisuallyHidden.tsx +22 -0
  137. package/src/stories/Accordion.stories.portable.ts +4 -0
  138. package/src/stories/Box.stories.tsx +474 -0
  139. package/src/stories/Button.stories.tsx +262 -0
  140. package/src/stories/Center.stories.tsx +126 -0
  141. package/src/stories/ErrorMessage.stories.tsx +19 -0
  142. package/src/stories/Flex.stories.tsx +345 -0
  143. package/src/stories/Form.stories.tsx +83 -0
  144. package/src/stories/Heading.stories.tsx +263 -0
  145. package/src/stories/HelperMessage.stories.tsx +22 -0
  146. package/src/stories/Input.stories.tsx +145 -0
  147. package/src/stories/Label.stories.tsx +32 -0
  148. package/src/stories/LinkButton.stories.tsx +207 -0
  149. package/src/stories/LinkCard.stories.tsx +90 -0
  150. package/src/stories/RadioButton.stories.tsx +168 -0
  151. package/src/stories/RadioCard.stories.tsx +236 -0
  152. package/src/stories/Select.stories.tsx +97 -0
  153. package/src/stories/Stack.stories.tsx +167 -0
  154. package/src/stories/Text.stories.tsx +396 -0
  155. package/src/stories/TextArea.stories.tsx +49 -0
  156. package/src/stories/Toggle.stories.tsx +30 -0
  157. package/src/test/vitest-jest-dom.d.ts +12 -0
  158. package/src/types/attributes.ts +6 -0
  159. package/src/types/global.d.ts +11 -0
  160. package/src/types/icon.ts +3 -0
  161. package/src/types/style.ts +254 -0
  162. package/src/utils/component.ts +8 -0
  163. package/src/utils/style.spec.ts +57 -0
  164. package/src/utils/style.ts +387 -0
  165. package/src/utils/types.ts +8 -0
  166. package/tsconfig.json +18 -0
  167. package/tsconfig.spec-lint.tsbuildinfo +1 -0
  168. package/tsconfig.tsbuildinfo +1 -0
  169. package/vite.config.ts +50 -0
  170. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,143 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { Center } from "./Center";
3
+
4
+ describe("<Center>", () => {
5
+ it("has a vertical margins of through my prop", () => {
6
+ render(
7
+ <Center my="xxs" data-testid="box">
8
+ Test
9
+ </Center>,
10
+ );
11
+ const div = screen.getByTestId("box");
12
+
13
+ expect(div).toHaveStyle("--margin-top: var(--size-spacing-xxs)");
14
+ expect(div).toHaveStyle("--margin-bottom: var(--size-spacing-xxs)");
15
+ });
16
+
17
+ it("has all margins through individual margin props", () => {
18
+ render(
19
+ <Center mt="xs" mb="sm" data-testid="box">
20
+ Test
21
+ </Center>,
22
+ );
23
+ const div = screen.getByTestId("box");
24
+
25
+ expect(div).toHaveStyle("--margin-top: var(--size-spacing-xs)");
26
+ expect(div).toHaveStyle("--margin-bottom: var(--size-spacing-sm)");
27
+ });
28
+
29
+ it("gives priority to individual margin props", () => {
30
+ render(
31
+ <Center my="xxs" mt="xs" mb="sm" data-testid="box">
32
+ Test
33
+ </Center>,
34
+ );
35
+ const div = screen.getByTestId("box");
36
+
37
+ expect(div).toHaveStyle("--margin-top: var(--size-spacing-xs)");
38
+ expect(div).toHaveStyle("--margin-bottom: var(--size-spacing-sm)");
39
+ });
40
+
41
+ it("has all the paddings through p prop", () => {
42
+ render(
43
+ <Center p="xxs" data-testid="box">
44
+ Test
45
+ </Center>,
46
+ );
47
+ const div = screen.getByTestId("box");
48
+
49
+ expect(div).toHaveStyle("--padding-top: var(--size-spacing-xxs)");
50
+ expect(div).toHaveStyle("--padding-right: var(--size-spacing-xxs)");
51
+ expect(div).toHaveStyle("--padding-bottom: var(--size-spacing-xxs)");
52
+ expect(div).toHaveStyle("--padding-left: var(--size-spacing-xxs)");
53
+ });
54
+
55
+ it("has a horizontal paddings through px prop", () => {
56
+ render(
57
+ <Center px="xxs" data-testid="box">
58
+ Test
59
+ </Center>,
60
+ );
61
+ const div = screen.getByTestId("box");
62
+
63
+ expect(div).toHaveStyle("--padding-top: 0");
64
+ expect(div).toHaveStyle("--padding-right: var(--size-spacing-xxs)");
65
+ expect(div).toHaveStyle("--padding-bottom: 0");
66
+ expect(div).toHaveStyle("--padding-left: var(--size-spacing-xxs)");
67
+ });
68
+
69
+ it("has a vertical paddings of through px prop", () => {
70
+ render(
71
+ <Center py="xxs" data-testid="box">
72
+ Test
73
+ </Center>,
74
+ );
75
+ const div = screen.getByTestId("box");
76
+
77
+ expect(div).toHaveStyle("--padding-top: var(--size-spacing-xxs)");
78
+ expect(div).toHaveStyle("--padding-right: 0");
79
+ expect(div).toHaveStyle("--padding-bottom: var(--size-spacing-xxs)");
80
+ expect(div).toHaveStyle("--padding-left: 0");
81
+ });
82
+
83
+ it("has all paddings through individual padding props", () => {
84
+ render(
85
+ <Center pt="xxs" pr="xs" pb="sm" pl="md" data-testid="box">
86
+ Test
87
+ </Center>,
88
+ );
89
+ const div = screen.getByTestId("box");
90
+
91
+ expect(div).toHaveStyle("--padding-top: var(--size-spacing-xxs)");
92
+ expect(div).toHaveStyle("--padding-right: var(--size-spacing-xs)");
93
+ expect(div).toHaveStyle("--padding-bottom: var(--size-spacing-sm)");
94
+ expect(div).toHaveStyle("--padding-left: var(--size-spacing-md)");
95
+ });
96
+
97
+ it("gives priority to individual padding props", () => {
98
+ render(
99
+ <Center p="xxs" px="xs" py="xs" pt="sm" pr="md" pb="lg" pl="xl" data-testid="box">
100
+ Test
101
+ </Center>,
102
+ );
103
+ const div = screen.getByTestId("box");
104
+
105
+ expect(div).toHaveStyle("--padding-top: var(--size-spacing-sm)");
106
+ expect(div).toHaveStyle("--padding-right: var(--size-spacing-md)");
107
+ expect(div).toHaveStyle("--padding-bottom: var(--size-spacing-lg)");
108
+ expect(div).toHaveStyle("--padding-left: var(--size-spacing-xl)");
109
+ });
110
+
111
+ it("receives width", () => {
112
+ render(
113
+ <Center width="100px" data-testid="flex-item">
114
+ Test
115
+ </Center>,
116
+ );
117
+ const div = screen.getByTestId("flex-item");
118
+
119
+ expect(div).toHaveStyle("--width: 100px");
120
+ });
121
+
122
+ it("receives max-width", () => {
123
+ render(
124
+ <Center maxWidth="100px" data-testid="flex-item">
125
+ Test
126
+ </Center>,
127
+ );
128
+ const div = screen.getByTestId("flex-item");
129
+
130
+ expect(div).toHaveStyle("--max-width: 100px");
131
+ });
132
+
133
+ it("receives min-width", () => {
134
+ render(
135
+ <Center minWidth="100px" data-testid="flex-item">
136
+ Test
137
+ </Center>,
138
+ );
139
+ const div = screen.getByTestId("flex-item");
140
+
141
+ expect(div).toHaveStyle("--min-width: 100px");
142
+ });
143
+ });
@@ -0,0 +1,108 @@
1
+ "use client";
2
+
3
+ import clsx from "clsx";
4
+ import {
5
+ isValidElement,
6
+ cloneElement,
7
+ type PropsWithChildren,
8
+ FC,
9
+ CSSProperties,
10
+ ComponentType,
11
+ ReactElement,
12
+ ReactNode,
13
+ } from "react";
14
+ import styles from "./Center.module.css";
15
+ import { CustomDataAttributeProps } from "../../types/attributes";
16
+ import { marginVariables, paddingVariables, widthVariables } from "../../utils/style";
17
+ import { HTMLTagname } from "../../utils/types";
18
+ import { Box } from "../Box/Box";
19
+ import type { MarginYProps, PaddingProps, WidthProps } from "../../types/style";
20
+
21
+ type Props = {
22
+ /**
23
+ * レンダリングされるHTML要素
24
+ * @default div
25
+ */
26
+ as?: HTMLTagname | ReactElement<ComponentType<typeof Box>>;
27
+ /**
28
+ * 内包するテキストを中央に配置。設定は継承され、子孫要素にも影響します
29
+ */
30
+ textCenter?: boolean;
31
+ /**
32
+ * 子要素を中央に配置。孫要素には影響しません
33
+ */
34
+ childrenCenter?: boolean;
35
+ /**
36
+ * HTMLのID属性の値
37
+ */
38
+ id?: string;
39
+ } & MarginYProps &
40
+ PaddingProps &
41
+ WidthProps &
42
+ CustomDataAttributeProps;
43
+
44
+ export const Center: FC<PropsWithChildren<Props>> = ({
45
+ as: CenterCopmonent = "div",
46
+ children,
47
+ p,
48
+ px,
49
+ py,
50
+ pt,
51
+ pr,
52
+ pb,
53
+ pl,
54
+ my,
55
+ mt,
56
+ mb,
57
+ textCenter,
58
+ childrenCenter,
59
+ id,
60
+ width,
61
+ minWidth,
62
+ maxWidth,
63
+ ...props
64
+ }) => {
65
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
66
+ const createElement = (props: any, children: ReactNode) => {
67
+ if (isValidElement(CenterCopmonent)) {
68
+ return <div {...props}>{cloneElement(CenterCopmonent, CenterCopmonent.props, children)}</div>;
69
+ } else {
70
+ return <CenterCopmonent {...props}>{children}</CenterCopmonent>;
71
+ }
72
+ };
73
+
74
+ return createElement(
75
+ {
76
+ id,
77
+ className: clsx(
78
+ styles.center,
79
+ textCenter && styles.textCenter,
80
+ childrenCenter && styles.childrenCenter,
81
+ ),
82
+ style: {
83
+ "--max-width": maxWidth,
84
+ ...paddingVariables({
85
+ p,
86
+ px,
87
+ py,
88
+ pt,
89
+ pr,
90
+ pb,
91
+ pl,
92
+ }),
93
+ ...marginVariables({
94
+ my,
95
+ mt,
96
+ mb,
97
+ }),
98
+ ...widthVariables({
99
+ width,
100
+ minWidth,
101
+ maxWidth,
102
+ }),
103
+ } as CSSProperties,
104
+ ...props,
105
+ },
106
+ children,
107
+ );
108
+ };
@@ -0,0 +1,124 @@
1
+ .checkbox {
2
+ position: absolute;
3
+ width: 1px;
4
+ height: 1px;
5
+ padding: 0;
6
+ overflow: hidden;
7
+ clip: rect(1px, 1px, 1px, 1px);
8
+ border: 0;
9
+
10
+ --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
11
+ }
12
+
13
+ .checkbox,
14
+ .label,
15
+ .symbol {
16
+ transition:
17
+ border-color 0.3s var(--ease-out-quint),
18
+ background-color 0.3s var(--ease-out-quint),
19
+ outline-color 0.3s var(--ease-out-quint);
20
+ }
21
+
22
+ /**
23
+ * label
24
+ */
25
+ .label {
26
+ display: flex;
27
+ gap: var(--size-spacing-xs);
28
+ align-items: center;
29
+ hyphens: auto;
30
+ overflow-wrap: anywhere;
31
+ cursor: pointer;
32
+ }
33
+
34
+ .label:has(.checkbox:disabled) {
35
+ cursor: initial;
36
+ }
37
+
38
+ .label.medium {
39
+ font-size: var(--text-body-md-size);
40
+ line-height: var(--text-body-md-narrow-line);
41
+ }
42
+
43
+ .label.small {
44
+ font-size: var(--text-body-sm-size);
45
+ line-height: var(--text-body-sm-narrow-line);
46
+ }
47
+
48
+ /**
49
+ * symbol
50
+ */
51
+ .symbol {
52
+ box-sizing: border-box;
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ color: var(--color-ubie-white);
57
+ background-color: var(--color-ubie-white);
58
+ border: 2px solid var(--color-outline);
59
+ border-radius: var(--radius-sm);
60
+ }
61
+
62
+ .symbol.medium {
63
+ width: 1.5rem;
64
+ height: 1.5rem;
65
+ }
66
+
67
+ .symbol.small {
68
+ width: 1.25rem;
69
+ height: 1.25rem;
70
+ }
71
+
72
+ .symbol.isIndeterminate {
73
+ background-color: var(--color-ubie-blue-600);
74
+ border-color: var(--color-ubie-blue-600);
75
+ }
76
+
77
+ .checkbox:checked + .symbol,
78
+ .symbol.isIndeterminate {
79
+ border-width: 0;
80
+ }
81
+
82
+ .checkbox:checked + .symbol {
83
+ background-color: var(--color-ubie-blue-600);
84
+ }
85
+
86
+ .checkbox:focus-visible + .symbol {
87
+ outline: 2px solid var(--color-ubie-pink-500);
88
+ }
89
+
90
+ .checkbox:disabled:checked + .symbol,
91
+ .checkbox:disabled + .symbol.isIndeterminate {
92
+ background-color: var(--color-placeholder);
93
+ }
94
+
95
+ @media (hover: hover) {
96
+ .checkbox:not(:disabled) + .symbol:not(.isIndeterminate):hover {
97
+ border-color: var(--color-ubie-blue-500);
98
+ }
99
+
100
+ .checkbox:checked:not(:disabled) + .symbol:hover {
101
+ background-color: var(--color-ubie-blue-700);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * symbol icon
107
+ */
108
+ .symbolCheckIcon {
109
+ visibility: hidden;
110
+ }
111
+
112
+ .checkbox:checked + .symbol > .symbolCheckIcon {
113
+ visibility: initial;
114
+ }
115
+
116
+ .symbolCheckIcon.medium,
117
+ .symbolIndeterminate.medium {
118
+ font-size: 1.25rem;
119
+ }
120
+
121
+ .symbolCheckIcon.small,
122
+ .symbolIndeterminate.small {
123
+ font-size: 1rem;
124
+ }
@@ -0,0 +1,17 @@
1
+ import { render } from "@testing-library/react";
2
+ import { createRef } from "react";
3
+ import { Checkbox } from "./Checkbox";
4
+
5
+ describe("Checkbox", () => {
6
+ it("access to DOM through ref prop", () => {
7
+ const ref = createRef<HTMLInputElement>();
8
+ render(
9
+ <Checkbox name="test" value="test" ref={ref}>
10
+ Test
11
+ </Checkbox>,
12
+ );
13
+ expect(ref.current).not.toBeNull();
14
+ expect(ref.current?.tagName).toBe("INPUT");
15
+ expect(ref.current?.type).toBe("checkbox");
16
+ });
17
+ });
@@ -0,0 +1,213 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { useState, useCallback } from "react";
3
+ import { Checkbox, CheckboxGroup, Stack } from "../../index";
4
+ import type { ChangeEventHandler } from "react";
5
+
6
+ export default {
7
+ title: "Form/Checkbox",
8
+ component: Checkbox,
9
+ } satisfies Meta<typeof Checkbox>;
10
+
11
+ type Story = StoryObj<typeof Checkbox>;
12
+
13
+ const options = ["option1", "option2", "option3", "option4", "option5"];
14
+
15
+ export const Default: Story = {
16
+ render: () => {
17
+ const [selectedItem, setSelectedItem] = useState<string[]>([options[0]]);
18
+
19
+ const onChange: ChangeEventHandler<HTMLInputElement> = useCallback(
20
+ (event) => {
21
+ if (event.target.checked) {
22
+ setSelectedItem([...selectedItem, event.target.value]);
23
+ } else {
24
+ setSelectedItem(selectedItem.filter((item) => item !== event.target.value));
25
+ }
26
+ },
27
+ [selectedItem],
28
+ );
29
+
30
+ return (
31
+ <Stack spacing="lg">
32
+ <CheckboxGroup label="Checkbox">
33
+ {options.map((option) => (
34
+ <Checkbox
35
+ name="default"
36
+ value={option}
37
+ onChange={onChange}
38
+ checked={selectedItem.includes(option)}
39
+ key={option}
40
+ >
41
+ {option}
42
+ </Checkbox>
43
+ ))}
44
+ </CheckboxGroup>
45
+
46
+ <dl>
47
+ <dt>Values</dt>
48
+ <dd>{selectedItem.join(",")}</dd>
49
+ </dl>
50
+ </Stack>
51
+ );
52
+ },
53
+ };
54
+
55
+ export const Horizontally: Story = {
56
+ render: () => {
57
+ const [selectedItem, setSelectedItem] = useState<string[]>([options[0]]);
58
+
59
+ const onChange: ChangeEventHandler<HTMLInputElement> = useCallback(
60
+ (event) => {
61
+ if (event.target.checked) {
62
+ setSelectedItem([...selectedItem, event.target.value]);
63
+ } else {
64
+ setSelectedItem(selectedItem.filter((item) => item !== event.target.value));
65
+ }
66
+ },
67
+ [selectedItem],
68
+ );
69
+
70
+ return (
71
+ <Stack spacing="lg">
72
+ <CheckboxGroup label="Checkbox" direction="row">
73
+ {options.map((option) => (
74
+ <Checkbox
75
+ name="horizontally"
76
+ value={option}
77
+ onChange={onChange}
78
+ checked={selectedItem.includes(option)}
79
+ key={option}
80
+ >
81
+ {option}
82
+ </Checkbox>
83
+ ))}
84
+ </CheckboxGroup>
85
+
86
+ <dl>
87
+ <dt>Values</dt>
88
+ <dd>{selectedItem.join(",")}</dd>
89
+ </dl>
90
+ </Stack>
91
+ );
92
+ },
93
+ };
94
+
95
+ export const SingleUse: Story = {
96
+ render: () => {
97
+ const [checked, setChecked] = useState<boolean>(false);
98
+
99
+ const onChange: ChangeEventHandler<HTMLInputElement> = useCallback(
100
+ (event) => setChecked(event.target.checked),
101
+ [],
102
+ );
103
+
104
+ return (
105
+ <Checkbox name="single-use" value="enable" checked={checked} onChange={onChange}>
106
+ Enable Option
107
+ </Checkbox>
108
+ );
109
+ },
110
+ };
111
+
112
+ export const Intermediate: Story = {
113
+ render: () => {
114
+ return (
115
+ <Stack spacing="xs">
116
+ <Checkbox name="indeterminate" isIndeterminate checked>
117
+ Indeterminate (checked)
118
+ </Checkbox>
119
+
120
+ <Checkbox name="indeterminate" isIndeterminate>
121
+ Indeterminate
122
+ </Checkbox>
123
+ </Stack>
124
+ );
125
+ },
126
+ };
127
+
128
+ export const Size: Story = {
129
+ render: () => (
130
+ <Stack spacing="xs">
131
+ <Checkbox name="size" value="medium">
132
+ Medium
133
+ </Checkbox>
134
+ <Checkbox name="size" value="small" size="small">
135
+ Small
136
+ </Checkbox>
137
+ <Checkbox name="size" value="medium" isIndeterminate>
138
+ Indeterminate & Medium
139
+ </Checkbox>
140
+ <Checkbox name="size" value="small" size="small" isIndeterminate>
141
+ Indeterminate & Small
142
+ </Checkbox>
143
+ </Stack>
144
+ ),
145
+ };
146
+
147
+ export const Disabled: Story = {
148
+ render: () => (
149
+ <Stack spacing="xs">
150
+ <Checkbox name="disabled" value="checked" checked disabled>
151
+ Checked
152
+ </Checkbox>
153
+
154
+ <Checkbox name="disabled" value="unchecked" disabled>
155
+ Unchecked
156
+ </Checkbox>
157
+
158
+ <Checkbox name="disabled" value="isIndeterminate" disabled isIndeterminate checked>
159
+ isIndeterminate (checked)
160
+ </Checkbox>
161
+
162
+ <Checkbox name="disabled" value="isIndeterminate" disabled isIndeterminate>
163
+ isIndeterminate
164
+ </Checkbox>
165
+ </Stack>
166
+ ),
167
+ };
168
+
169
+ export const ShowRequiredLabel: Story = {
170
+ render: () => {
171
+ const [selectedItem, setSelectedItem] = useState<string[]>([options[0]]);
172
+
173
+ const onChange: ChangeEventHandler<HTMLInputElement> = useCallback(
174
+ (event) => {
175
+ if (event.target.checked) {
176
+ setSelectedItem([...selectedItem, event.target.value]);
177
+ } else {
178
+ setSelectedItem(selectedItem.filter((item) => item !== event.target.value));
179
+ }
180
+ },
181
+ [selectedItem],
182
+ );
183
+
184
+ return (
185
+ <CheckboxGroup label="Checkbox" showRequiredLabel>
186
+ {options.map((option) => (
187
+ <Checkbox
188
+ name="default"
189
+ value={option}
190
+ onChange={onChange}
191
+ checked={selectedItem.includes(option)}
192
+ key={option}
193
+ >
194
+ {option}
195
+ </Checkbox>
196
+ ))}
197
+ </CheckboxGroup>
198
+ );
199
+ },
200
+ };
201
+
202
+ export const CustomDataAttribute: Story = {
203
+ render: () => (
204
+ <Stack spacing="xs">
205
+ <Checkbox name="custom-data" value="data1" data-test-id="checkbox-data1">
206
+ Data 1
207
+ </Checkbox>
208
+ <Checkbox name="custom-data" value="data2" data-test-id="checkbox-data2">
209
+ Data 2
210
+ </Checkbox>
211
+ </Stack>
212
+ ),
213
+ };
@@ -0,0 +1,50 @@
1
+ "use client";
2
+
3
+ import { CheckAIcon, MinusAIcon } from "@ubie/vitals-icon";
4
+ import clsx from "clsx";
5
+ import { forwardRef } from "react";
6
+ import styles from "./Checkbox.module.css";
7
+ import type { InputHTMLAttributes } from "react";
8
+
9
+ type Props = {
10
+ /**
11
+ * サイズ
12
+ * @default medium
13
+ */
14
+ size?: "medium" | "small";
15
+ /**
16
+ * 中間状態の見た目とする。input要素のindeterminateプロパティの変更は行わないため注意。
17
+ */
18
+ isIndeterminate?: boolean;
19
+ } & Omit<InputHTMLAttributes<HTMLInputElement>, "size">;
20
+
21
+ export const Checkbox = forwardRef<HTMLInputElement, Props>(
22
+ ({ size = "medium", children, disabled, isIndeterminate = false, ...otherProps }, ref) => {
23
+ return (
24
+ <label className={clsx(styles.label, styles[size])}>
25
+ <input
26
+ ref={ref}
27
+ type="checkbox"
28
+ className={styles.checkbox}
29
+ disabled={disabled}
30
+ {...otherProps}
31
+ />
32
+ <span
33
+ className={clsx(styles.symbol, styles[size], isIndeterminate && styles.isIndeterminate)}
34
+ >
35
+ {isIndeterminate ? (
36
+ <MinusAIcon
37
+ className={clsx(styles.symbolIndeterminate, styles[size])}
38
+ aria-hidden="true"
39
+ />
40
+ ) : (
41
+ <CheckAIcon className={clsx(styles.symbolCheckIcon, styles[size])} aria-hidden="true" />
42
+ )}
43
+ </span>
44
+ {children}
45
+ </label>
46
+ );
47
+ },
48
+ );
49
+
50
+ Checkbox.displayName = "Checkbox";