@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,39 @@
1
+ "use client";
2
+
3
+ import styles from "./Label.module.css";
4
+ import { RequiredLabel } from "../../sharedComponents/RequiredLabel/RequiredLabel";
5
+ import { CustomDataAttributeProps } from "../../types/attributes"; // 追加したインポート
6
+ import type { ElementType, FC, ReactNode } from "react";
7
+
8
+ type Props = {
9
+ children: ReactNode;
10
+ /**
11
+ * レンダリングされるHTML要素
12
+ * @default label
13
+ */
14
+ as?: ElementType<{ className?: string; children: ReactNode }> | "label" | "p";
15
+ /**
16
+ * ラベルが紐づくフォーム要素のid属性。asにlabelを指定した場合に必用
17
+ */
18
+ htmlFor?: string;
19
+ /**
20
+ * 必須マークを表示するか
21
+ * 注意: trueとしてもinput要素のrequired属性は付与されません
22
+ */
23
+ showRequiredLabel?: boolean;
24
+ } & CustomDataAttributeProps;
25
+
26
+ export const Label: FC<Props> = ({
27
+ children,
28
+ as: LabelComponent = "label",
29
+ htmlFor,
30
+ showRequiredLabel,
31
+ ...otherProps
32
+ }) => {
33
+ return (
34
+ <LabelComponent htmlFor={htmlFor} className={styles.label} {...otherProps}>
35
+ {children}
36
+ {showRequiredLabel && <RequiredLabel />}
37
+ </LabelComponent>
38
+ );
39
+ };
@@ -0,0 +1,72 @@
1
+ .card {
2
+ --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
3
+
4
+ display: flex;
5
+ gap: var(--size-spacing-sm);
6
+ align-items: center;
7
+ padding: var(--size-spacing-md);
8
+ hyphens: auto;
9
+ text-decoration: none;
10
+ overflow-wrap: anywhere;
11
+ background-color: var(--color-ubie-white);
12
+ border: 1px solid var(--color-outline);
13
+ border-radius: var(--radius-md);
14
+ transition: background-color 0.3s var(--ease-out-quint);
15
+ }
16
+
17
+ .card:hover {
18
+ background-color: var(--color-ubie-blue-100);
19
+ }
20
+
21
+ .icon {
22
+ flex: none;
23
+ color: var(--color-ubie-blue-600);
24
+ }
25
+
26
+ .icon > * {
27
+ width: var(--icon-size);
28
+ height: var(--icon-size);
29
+ vertical-align: bottom;
30
+ }
31
+
32
+ .text {
33
+ display: flex;
34
+ flex: 1;
35
+ flex-direction: column;
36
+ gap: var(--size-spacing-xxs);
37
+ }
38
+
39
+ .title {
40
+ margin: 0;
41
+ font-size: var(--title-font-size);
42
+ font-weight: bold;
43
+ color: var(--color-on-surface);
44
+ }
45
+
46
+ .description {
47
+ margin: 0;
48
+ font-size: var(--description-font-size);
49
+ line-height: var(--description-line-height);
50
+ color: var(--color-on-surface-variant);
51
+ }
52
+
53
+ .caret {
54
+ width: 1.25rem;
55
+ height: 1.25rem;
56
+ color: var(--color-ubie-blue-600);
57
+ }
58
+
59
+ /* Size */
60
+ .medium {
61
+ --icon-size: 2rem;
62
+ --title-font-size: var(--text-heading-xs-line);
63
+ --description-font-size: var(--text-body-sm-size);
64
+ --description-line-height: var(--text-body-sm-line);
65
+ }
66
+
67
+ .small {
68
+ --icon-size: 1.25rem;
69
+ --title-font-size: var(--text-button-md-size);
70
+ --description-font-size: var(--text-body-xs-size);
71
+ --description-line-height: var(--text-body-xs-line);
72
+ }
@@ -0,0 +1,96 @@
1
+ "use client";
2
+
3
+ import { ArrowBRightIcon } from "@ubie/vitals-icon";
4
+ import clsx from "clsx";
5
+ import { cloneElement, forwardRef, isValidElement } from "react";
6
+ import styles from "./LinkCard.module.css";
7
+ import { CustomDataAttributeProps } from "../../types/attributes";
8
+ import { IconName } from "../../types/icon";
9
+ import { Icon } from "../Icon/Icon";
10
+ import type { ComponentType, ReactElement, ReactNode } from "react";
11
+
12
+ type IconProp = ComponentType | ReactElement | IconName;
13
+
14
+ type Props = {
15
+ /**
16
+ * 遷移先URL
17
+ */
18
+ href?: string;
19
+ /**
20
+ * サイズ
21
+ * @default medium
22
+ */
23
+ size?: "medium" | "small";
24
+ /**
25
+ * 見出しのテキスト
26
+ */
27
+ title: string;
28
+ /**
29
+ * 説明のテキスト
30
+ */
31
+ description?: string;
32
+ /**
33
+ * CSSのクラス
34
+ * * @deprecated マージンなどをつけたい場合は<Box>や<Stack>を使ってください
35
+ */
36
+ className?: string;
37
+ /**
38
+ * レンダリングされる要素を変更。フレームワークのリンクコンポーネントなどを指定
39
+ */
40
+ render?: ReactElement;
41
+ /**
42
+ * アイコン
43
+ */
44
+ icon?: IconProp;
45
+ } & CustomDataAttributeProps;
46
+
47
+ // ref https://github.com/microsoft/TypeScript/issues/53178
48
+ const _isValidElement = (el: ComponentType | ReactElement): el is ReactElement => {
49
+ return isValidElement(el);
50
+ };
51
+
52
+ const renderPropIcon = (icon: IconProp) => {
53
+ if (icon == null) {
54
+ return null;
55
+ }
56
+
57
+ if (typeof icon === "string") {
58
+ return <Icon icon={icon} />;
59
+ }
60
+
61
+ if (_isValidElement(icon)) {
62
+ return icon;
63
+ }
64
+
65
+ const IconComponent = icon;
66
+ return <IconComponent />;
67
+ };
68
+
69
+ export const LinkCard = forwardRef<HTMLAnchorElement, Props>(
70
+ ({ title, size = "medium", className, icon, description, render, ...props }, forwardedRef) => {
71
+ const cls = clsx(styles[size], styles.card, className);
72
+
73
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
74
+ const createElement = (props: any, children: ReactNode) => {
75
+ return render ? cloneElement(render, props, children) : <a {...props}>{children}</a>;
76
+ };
77
+
78
+ return createElement(
79
+ {
80
+ className: cls,
81
+ ...props,
82
+ ref: forwardedRef,
83
+ },
84
+ <>
85
+ {icon != null && <span className={styles.icon}>{renderPropIcon(icon)}</span>}
86
+ <div className={styles.text}>
87
+ <p className={styles.title}>{title}</p>
88
+ {description && <p className={styles.description}>{description}</p>}
89
+ </div>
90
+ <ArrowBRightIcon className={styles.caret} />
91
+ </>,
92
+ );
93
+ },
94
+ );
95
+
96
+ LinkCard.displayName = "LinkCard";
@@ -0,0 +1,181 @@
1
+ .modal {
2
+ position: fixed;
3
+ inset: 0;
4
+ z-index: var(--z-index-modal);
5
+ overflow: hidden;
6
+ }
7
+
8
+ .overlay {
9
+ position: fixed;
10
+ inset: 0;
11
+ }
12
+
13
+ .normalOverlay {
14
+ background: rgb(0 0 0 / 50%);
15
+ }
16
+
17
+ .darkerOverlay {
18
+ background: rgb(0 0 0 / 80%);
19
+ }
20
+
21
+ .contents {
22
+ padding: 0 var(--size-spacing-md);
23
+ }
24
+
25
+ .dialog {
26
+ position: fixed;
27
+ bottom: 0;
28
+ left: 50%;
29
+ display: flex;
30
+ flex-direction: column;
31
+ width: 100%;
32
+ max-width: 600px;
33
+ max-height: calc(100% - 24px);
34
+ margin: 0 auto;
35
+ overflow-y: hidden;
36
+ background: #fff;
37
+ border-radius: 12px;
38
+ border-end-start-radius: 0;
39
+ border-end-end-radius: 0;
40
+ transform: translate3d(-50%, 0, 0);
41
+ }
42
+
43
+ .dialog.fullscreen {
44
+ height: calc(100% - 24px);
45
+ }
46
+
47
+ .scrollContainer {
48
+ height: 100%;
49
+ overflow-y: auto;
50
+ }
51
+
52
+ .mainContent {
53
+ display: flex;
54
+ flex-direction: column;
55
+ }
56
+
57
+ .mainContent.fullscreen {
58
+ min-height: 100%;
59
+ }
60
+
61
+ .mainContent.headerLess {
62
+ padding-top: var(--size-spacing-xl);
63
+ }
64
+
65
+ .header {
66
+ padding: var(--size-spacing-lg) var(--size-spacing-md);
67
+ font-size: var(--text-heading-xs-size);
68
+ font-weight: bold;
69
+ line-height: var(--text-heading-xs-line);
70
+ text-align: center;
71
+ white-space: pre-wrap;
72
+ background-color: var(--color-ubie-white);
73
+
74
+ /* May receive focus in the initial display. */
75
+ outline: none;
76
+ }
77
+
78
+ .header.sticky {
79
+ position: sticky;
80
+ top: 0;
81
+
82
+ /* Ensure sticky header appears above content that might overlap it (e.g., with position or transform) */
83
+ z-index: 1;
84
+ }
85
+
86
+ .header.sticky.canScroll {
87
+ border-bottom: 1px solid var(--color-outline);
88
+ }
89
+
90
+ .body {
91
+ /*
92
+ * Ensure the header always appears in front of body content.
93
+ * - Place header and body in the same stacking context
94
+ * - Set body's z-index lower than header's z-index
95
+ * This prevents the header from being hidden by body content,
96
+ * regardless of how high z-index values are specified within the body.
97
+ */
98
+ z-index: 0;
99
+ padding-right: var(--size-spacing-md);
100
+ padding-left: var(--size-spacing-md);
101
+ }
102
+
103
+ .body.fullscreen {
104
+ flex: 1;
105
+ min-height: 400px;
106
+ overflow: hidden;
107
+ }
108
+
109
+ .buttonContainer {
110
+ display: grid;
111
+ gap: var(--size-spacing-md);
112
+ padding: var(--size-spacing-lg) var(--size-spacing-md) var(--size-spacing-md);
113
+ background-color: var(--color-ubie-white);
114
+ }
115
+
116
+ .buttonContainer.sticky {
117
+ position: sticky;
118
+ bottom: 0;
119
+ }
120
+
121
+ .buttonContainer.sticky.canScroll {
122
+ border-top: 1px solid var(--color-outline);
123
+ }
124
+
125
+ .overlayEnter {
126
+ transition-timing-function: ease-out;
127
+ transition-duration: 300ms;
128
+ transition-property: opacity;
129
+ }
130
+
131
+ .overlayEnterFrom {
132
+ opacity: 0;
133
+ }
134
+
135
+ .overlayEnterTo {
136
+ opacity: 1;
137
+ }
138
+
139
+ .overlayLeave {
140
+ transition-timing-function: ease-in;
141
+ transition-duration: 200ms;
142
+ }
143
+
144
+ .overlayLeaveFrom {
145
+ opacity: 1;
146
+ }
147
+
148
+ .overlayLeaveTo {
149
+ opacity: 0;
150
+ }
151
+
152
+ .panelEnter {
153
+ transition-timing-function: ease-out;
154
+ transition-duration: 300ms;
155
+ transition-property: transform, opacity;
156
+ }
157
+
158
+ .panelEnterFrom {
159
+ opacity: 0;
160
+ transform: translate3d(-50%, 100%, 0);
161
+ }
162
+
163
+ .panelEnterTo {
164
+ opacity: 1;
165
+ transform: translate3d(-50%, 0, 0);
166
+ }
167
+
168
+ .panelLeave {
169
+ transition-timing-function: ease-in;
170
+ transition-duration: 200ms;
171
+ }
172
+
173
+ .panelLeaveFrom {
174
+ opacity: 1;
175
+ transform: translate3d(-50%, 0, 0);
176
+ }
177
+
178
+ .panelLeaveTo {
179
+ opacity: 0;
180
+ transform: translate3d(-50%, 100%, 0);
181
+ }
@@ -0,0 +1,73 @@
1
+ import { composeStory } from "@storybook/react-vite";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { userEvent } from "@testing-library/user-event";
4
+ import Meta, {
5
+ WithCustomDataAttribute,
6
+ WithId,
7
+ CustomHeader,
8
+ Default,
9
+ } from "./MessageHalfModal.stories";
10
+
11
+ const WithCustomDataAttributeStory = composeStory(WithCustomDataAttribute, Meta);
12
+ const WithIdStory = composeStory(WithId, Meta);
13
+ const CustomHeaderStory = composeStory(CustomHeader, Meta);
14
+ const DefaultStory = composeStory(Default, Meta);
15
+
16
+ const user = userEvent.setup();
17
+
18
+ describe("MessageHalfModal", () => {
19
+ test("Add custom data attributes", async () => {
20
+ render(<WithCustomDataAttributeStory />);
21
+
22
+ await user.click(await screen.findByRole("button"));
23
+
24
+ const dialogElement = await screen.findByRole("dialog");
25
+
26
+ expect(dialogElement).toHaveAttribute("data-test-id", "message-half-modal-custom");
27
+ });
28
+
29
+ test("Add id", async () => {
30
+ render(<WithIdStory />);
31
+
32
+ await user.click(await screen.findByRole("button"));
33
+
34
+ const dialogElement = await screen.findByRole("dialog");
35
+
36
+ expect(dialogElement).toHaveAttribute("id", "dialog-id");
37
+ });
38
+
39
+ test("Custom heading and dialogue can be tied together", async () => {
40
+ render(<CustomHeaderStory />);
41
+
42
+ await user.click(await screen.findByRole("button"));
43
+
44
+ const dialogElement = await screen.findByRole("dialog");
45
+ const dialogHeadingElement = await screen.findByRole("heading");
46
+
47
+ expect(dialogElement).toHaveAttribute("aria-labelledby", "header-id");
48
+ expect(dialogHeadingElement).toHaveAttribute("id", "header-id");
49
+ });
50
+
51
+ test("If no header prop is present, text is inserted that serves as the default heading", async () => {
52
+ render(<CustomHeaderStory />);
53
+
54
+ await user.click(await screen.findByRole("button"));
55
+
56
+ const dialogHeadingElement = await screen.findByText("ダイアログ");
57
+
58
+ expect(document.activeElement).toEqual(dialogHeadingElement);
59
+ });
60
+
61
+ test("header prop can be used to automatically link to a dialog", async () => {
62
+ render(<DefaultStory />);
63
+
64
+ const dialogElement = await screen.findByRole("dialog");
65
+ const dialogHeadingElement = await screen.findByRole("heading");
66
+
67
+ expect(dialogElement).toHaveAttribute("aria-labelledby");
68
+ expect(dialogHeadingElement).toHaveAttribute(
69
+ "id",
70
+ dialogElement.getAttribute("aria-labelledby"),
71
+ );
72
+ });
73
+ });
@@ -0,0 +1,242 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { ComponentProps, useCallback, useState } from "react";
3
+ import { MessageHalfModal } from "./MessageHalfModal";
4
+
5
+ export default {
6
+ title: "Modal/MessageHalfModal",
7
+ component: MessageHalfModal,
8
+ } satisfies Meta<typeof MessageHalfModal>;
9
+
10
+ type Story = StoryObj<typeof MessageHalfModal>;
11
+
12
+ const LongBody = () => (
13
+ <>
14
+ <p>
15
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has
16
+ been the industry&apos;s standard dummy text ever since the 1500s, when an unknown printer
17
+ took a galley of type and scrambled it to make a type specimen book. It has survived not only
18
+ five centuries, but also the leap into electronic typesetting, remaining essentially
19
+ unchanged. It was popularised in the 1960s with the release of Letraset sheets containing
20
+ Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker
21
+ including versions of Lorem Ipsum.
22
+ </p>
23
+ <p>
24
+ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of
25
+ classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a
26
+ Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin
27
+ words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in
28
+ classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections
29
+ 1.10.32 and 1.10.33 of &quot;de Finibus Bonorum et Malorum&quot; (The Extremes of Good and
30
+ Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very
31
+ popular during the Renaissance. The first line of Lorem Ipsum, &quot;Lorem ipsum dolor sit
32
+ amet..&quot;, comes from a line in section 1.10.32.
33
+ </p>
34
+ </>
35
+ );
36
+
37
+ const defaultArgs = {
38
+ header: "モーダル",
39
+ children: <LongBody />,
40
+ stickyHeader: false,
41
+ stickyFooter: false,
42
+ } as const satisfies Partial<ComponentProps<typeof MessageHalfModal>>;
43
+
44
+ export const Default: Story = {
45
+ render: (args) => {
46
+ const [open, setOpen] = useState(true);
47
+
48
+ const onClose = useCallback(() => {
49
+ setOpen(false);
50
+ }, []);
51
+
52
+ return (
53
+ <>
54
+ <button type="button" onClick={() => setOpen(true)}>
55
+ Open Modal
56
+ </button>
57
+ <MessageHalfModal {...args} open={open} onClose={onClose} />
58
+ </>
59
+ );
60
+ },
61
+ args: defaultArgs,
62
+ };
63
+
64
+ export const Fullscreen: Story = {
65
+ render: (args) => {
66
+ const [open, setOpen] = useState(false);
67
+
68
+ const onClose = useCallback(() => {
69
+ setOpen(false);
70
+ }, []);
71
+
72
+ return (
73
+ <>
74
+ <button type="button" onClick={() => setOpen(true)}>
75
+ Open Modal
76
+ </button>
77
+ <MessageHalfModal {...args} open={open} onClose={onClose} fullscreen />
78
+ </>
79
+ );
80
+ },
81
+ args: defaultArgs,
82
+ };
83
+
84
+ export const NoCloseButton: Story = {
85
+ render: (args) => {
86
+ const [open, setOpen] = useState(false);
87
+
88
+ const onClose = useCallback(() => {
89
+ setOpen(false);
90
+ }, []);
91
+
92
+ return (
93
+ <>
94
+ <button type="button" onClick={() => setOpen(true)}>
95
+ Open Modal
96
+ </button>
97
+ <MessageHalfModal showClose={false} {...args} open={open} onClose={onClose} />
98
+ </>
99
+ );
100
+ },
101
+ args: defaultArgs,
102
+ };
103
+
104
+ export const WithCustomDataAttribute: Story = {
105
+ render: (args) => {
106
+ const [open, setOpen] = useState(false);
107
+
108
+ const onClose = useCallback(() => {
109
+ setOpen(false);
110
+ }, []);
111
+
112
+ return (
113
+ <>
114
+ <button type="button" onClick={() => setOpen(true)}>
115
+ Open Modal
116
+ </button>
117
+ <MessageHalfModal {...args} open={open} onClose={onClose} />
118
+ </>
119
+ );
120
+ },
121
+ args: {
122
+ ...defaultArgs,
123
+ "data-test-id": "message-half-modal-custom",
124
+ },
125
+ };
126
+
127
+ export const WithId: Story = {
128
+ render: (args) => {
129
+ const [open, setOpen] = useState(false);
130
+
131
+ const onClose = useCallback(() => {
132
+ setOpen(false);
133
+ }, []);
134
+
135
+ return (
136
+ <>
137
+ <button type="button" onClick={() => setOpen(true)}>
138
+ Open Modal
139
+ </button>
140
+ <MessageHalfModal {...args} open={open} onClose={onClose} />
141
+ </>
142
+ );
143
+ },
144
+ args: {
145
+ ...defaultArgs,
146
+ id: "dialog-id",
147
+ },
148
+ };
149
+
150
+ export const CustomHeader: Story = {
151
+ render: (args) => {
152
+ const [open, setOpen] = useState(false);
153
+
154
+ const onClose = useCallback(() => {
155
+ setOpen(false);
156
+ }, []);
157
+
158
+ const headerId = "header-id";
159
+
160
+ return (
161
+ <>
162
+ <button type="button" onClick={() => setOpen(true)}>
163
+ Open Modal
164
+ </button>
165
+ <MessageHalfModal ariaLabelledby={headerId} {...args} open={open} onClose={onClose}>
166
+ <h2 id={headerId}>Heading</h2>
167
+ <LongBody />
168
+ </MessageHalfModal>
169
+ </>
170
+ );
171
+ },
172
+ args: {
173
+ ...defaultArgs,
174
+ header: undefined,
175
+ },
176
+ };
177
+
178
+ export const WithHero: Story = {
179
+ render: (args) => {
180
+ const [open, setOpen] = useState(false);
181
+
182
+ const onClose = useCallback(() => {
183
+ setOpen(false);
184
+ }, []);
185
+
186
+ const headerId = "header-id";
187
+
188
+ return (
189
+ <>
190
+ <button type="button" onClick={() => setOpen(true)}>
191
+ Open Modal
192
+ </button>
193
+ <MessageHalfModal
194
+ hero={
195
+ <img
196
+ src="/images/placeholder.svg"
197
+ alt="Illustration: Modal"
198
+ style={{ width: "100%", height: "auto", verticalAlign: "bottom" }}
199
+ width={560}
200
+ height={315}
201
+ />
202
+ }
203
+ ariaLabelledby={headerId}
204
+ {...args}
205
+ open={open}
206
+ onClose={onClose}
207
+ >
208
+ <h2 id={headerId}>Heading</h2>
209
+ <LongBody />
210
+ </MessageHalfModal>
211
+ </>
212
+ );
213
+ },
214
+ args: {
215
+ ...defaultArgs,
216
+ header: undefined,
217
+ },
218
+ };
219
+
220
+ export const StickyHeaderAndFooter: Story = {
221
+ render: (args) => {
222
+ const [open, setOpen] = useState(true);
223
+
224
+ const onClose = useCallback(() => {
225
+ setOpen(false);
226
+ }, []);
227
+
228
+ return (
229
+ <>
230
+ <button type="button" onClick={() => setOpen(true)}>
231
+ Open Modal
232
+ </button>
233
+ <MessageHalfModal {...args} open={open} onClose={onClose} />
234
+ </>
235
+ );
236
+ },
237
+ args: {
238
+ ...defaultArgs,
239
+ stickyHeader: true,
240
+ stickyFooter: true,
241
+ },
242
+ };