@charcoal-ui/react 2.8.0 → 2.10.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 (48) hide show
  1. package/dist/components/Button/index.test.d.ts +4 -0
  2. package/dist/components/Button/index.test.d.ts.map +1 -0
  3. package/dist/components/Checkbox/index.d.ts +1 -0
  4. package/dist/components/Checkbox/index.d.ts.map +1 -1
  5. package/dist/components/Checkbox/index.story.d.ts +1 -0
  6. package/dist/components/Checkbox/index.story.d.ts.map +1 -1
  7. package/dist/components/LoadingSpinner/index.d.ts +8 -6
  8. package/dist/components/LoadingSpinner/index.d.ts.map +1 -1
  9. package/dist/components/LoadingSpinner/index.story.d.ts +2 -2
  10. package/dist/components/LoadingSpinner/index.story.d.ts.map +1 -1
  11. package/dist/components/Modal/index.d.ts +17 -26
  12. package/dist/components/Modal/index.d.ts.map +1 -1
  13. package/dist/components/Modal/index.story.d.ts +13 -2
  14. package/dist/components/Modal/index.story.d.ts.map +1 -1
  15. package/dist/components/MultiSelect/index.d.ts +15 -1
  16. package/dist/components/MultiSelect/index.d.ts.map +1 -1
  17. package/dist/components/MultiSelect/index.story.d.ts +15 -2
  18. package/dist/components/MultiSelect/index.story.d.ts.map +1 -1
  19. package/dist/components/Radio/index.d.ts +10 -1
  20. package/dist/components/Radio/index.d.ts.map +1 -1
  21. package/dist/components/Radio/index.story.d.ts +9 -2
  22. package/dist/components/Radio/index.story.d.ts.map +1 -1
  23. package/dist/components/SegmentedControl/index.d.ts +1 -0
  24. package/dist/components/SegmentedControl/index.d.ts.map +1 -1
  25. package/dist/components/Switch/index.d.ts +2 -1
  26. package/dist/components/Switch/index.d.ts.map +1 -1
  27. package/dist/components/Switch/index.story.d.ts +2 -2
  28. package/dist/components/Switch/index.story.d.ts.map +1 -1
  29. package/dist/index.cjs.js +170 -141
  30. package/dist/index.cjs.js.map +1 -1
  31. package/dist/index.esm.js +188 -154
  32. package/dist/index.esm.js.map +1 -1
  33. package/package.json +6 -6
  34. package/src/components/Button/__snapshots__/index.test.tsx.snap +385 -0
  35. package/src/components/Button/index.test.tsx +25 -0
  36. package/src/components/Checkbox/index.story.tsx +1 -0
  37. package/src/components/Checkbox/index.tsx +2 -1
  38. package/src/components/LoadingSpinner/index.story.tsx +7 -1
  39. package/src/components/LoadingSpinner/index.tsx +27 -11
  40. package/src/components/Modal/index.tsx +18 -12
  41. package/src/components/MultiSelect/index.story.tsx +11 -3
  42. package/src/components/MultiSelect/index.tsx +77 -61
  43. package/src/components/Radio/index.story.tsx +3 -0
  44. package/src/components/Radio/index.tsx +13 -9
  45. package/src/components/SegmentedControl/index.tsx +15 -5
  46. package/src/components/Switch/index.tsx +37 -32
  47. package/src/components/TextField/index.story.tsx +1 -1
  48. package/src/components/TextField/index.tsx +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@charcoal-ui/react",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.esm.js",
@@ -49,10 +49,10 @@
49
49
  "typescript": "^4.9.5"
50
50
  },
51
51
  "dependencies": {
52
- "@charcoal-ui/icons": "^2.8.0",
53
- "@charcoal-ui/styled": "^2.8.0",
54
- "@charcoal-ui/theme": "^2.8.0",
55
- "@charcoal-ui/utils": "^2.8.0",
52
+ "@charcoal-ui/icons": "^2.10.0",
53
+ "@charcoal-ui/styled": "^2.10.0",
54
+ "@charcoal-ui/theme": "^2.10.0",
55
+ "@charcoal-ui/utils": "^2.10.0",
56
56
  "@react-aria/button": "^3.7.0",
57
57
  "@react-aria/checkbox": "^3.8.0",
58
58
  "@react-aria/dialog": "^3.5.0",
@@ -88,5 +88,5 @@
88
88
  "url": "https://github.com/pixiv/charcoal.git",
89
89
  "directory": "packages/react"
90
90
  },
91
- "gitHead": "f45afd2e7fd92a3bed12babb8e23e4524057a96d"
91
+ "gitHead": "de4d7ee93313b0438dedc3dae21c2fc3419e4a8b"
92
92
  }
@@ -0,0 +1,385 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Basic <Button>Hello</Button> 1`] = `
4
+ .c0 {
5
+ -webkit-appearance: none;
6
+ -moz-appearance: none;
7
+ appearance: none;
8
+ background: transparent;
9
+ padding: 0;
10
+ border-style: none;
11
+ outline: none;
12
+ color: inherit;
13
+ text-rendering: inherit;
14
+ -webkit-letter-spacing: inherit;
15
+ -moz-letter-spacing: inherit;
16
+ -ms-letter-spacing: inherit;
17
+ letter-spacing: inherit;
18
+ word-spacing: inherit;
19
+ font: inherit;
20
+ margin: 0;
21
+ overflow: visible;
22
+ text-transform: none;
23
+ cursor: pointer;
24
+ }
25
+
26
+ .c0:focus {
27
+ outline: none;
28
+ }
29
+
30
+ .c0::-moz-focus-inner {
31
+ border-style: none;
32
+ padding: 0;
33
+ }
34
+
35
+ .c0:disabled,
36
+ .c0[aria-disabled]:not([aria-disabled=false]) {
37
+ cursor: default;
38
+ }
39
+
40
+ .c1 {
41
+ width: -webkit-min-content;
42
+ width: -moz-min-content;
43
+ width: min-content;
44
+ display: inline-grid;
45
+ -webkit-align-items: center;
46
+ -webkit-box-align: center;
47
+ -ms-flex-align: center;
48
+ align-items: center;
49
+ -webkit-box-pack: center;
50
+ -webkit-justify-content: center;
51
+ -ms-flex-pack: center;
52
+ justify-content: center;
53
+ cursor: pointer;
54
+ -webkit-user-select: none;
55
+ -moz-user-select: none;
56
+ -ms-user-select: none;
57
+ user-select: none;
58
+ white-space: nowrap;
59
+ color: var(--charcoal-text2);
60
+ background-color: var(--charcoal-surface3);
61
+ font-size: 14px;
62
+ line-height: 22px;
63
+ font-weight: bold;
64
+ padding-right: 24px;
65
+ padding-left: 24px;
66
+ border-radius: 999999px;
67
+ -webkit-transition: 0.2s color,0.2s background-color,0.2s box-shadow;
68
+ transition: 0.2s color,0.2s background-color,0.2s box-shadow;
69
+ height: 40px;
70
+ }
71
+
72
+ .c1:hover:not(:disabled):not([aria-disabled]),
73
+ .c1:hover[aria-disabled=false] {
74
+ color: var(--charcoal-text2-hover);
75
+ }
76
+
77
+ .c1:active:not(:disabled):not([aria-disabled]),
78
+ .c1:active[aria-disabled=false] {
79
+ color: var(--charcoal-text2-press);
80
+ }
81
+
82
+ .c1:hover:not(:disabled):not([aria-disabled]),
83
+ .c1:hover[aria-disabled=false] {
84
+ background-color: var(--charcoal-surface3-hover);
85
+ }
86
+
87
+ .c1:active:not(:disabled):not([aria-disabled]),
88
+ .c1:active[aria-disabled=false] {
89
+ background-color: var(--charcoal-surface3-press);
90
+ }
91
+
92
+ .c1:disabled,
93
+ .c1[aria-disabled]:not([aria-disabled=false]) {
94
+ opacity: 0.32;
95
+ }
96
+
97
+ .c1:not(:disabled):not([aria-disabled]):focus,
98
+ .c1[aria-disabled=false]:focus,
99
+ .c1:not(:disabled):not([aria-disabled]):active,
100
+ .c1[aria-disabled=false]:active {
101
+ outline: none;
102
+ box-shadow: 0 0 0 4px rgba(0,150,250,0.32);
103
+ }
104
+
105
+ .c1:not(:disabled):not([aria-disabled]):focus:not(:focus-visible),
106
+ .c1[aria-disabled=false]:focus:not(:focus-visible),
107
+ .c1:not(:disabled):not([aria-disabled]):active:not(:focus-visible),
108
+ .c1[aria-disabled=false]:active:not(:focus-visible) {
109
+ outline: none;
110
+ }
111
+
112
+ .c1:not(:disabled):not([aria-disabled]):focus-visible,
113
+ .c1[aria-disabled=false]:focus-visible {
114
+ outline: none;
115
+ box-shadow: 0 0 0 4px rgba(0,150,250,0.32);
116
+ }
117
+
118
+ <button
119
+ className="c0 c1"
120
+ disabled={false}
121
+ height={40}
122
+ size="M"
123
+ >
124
+ Hello
125
+ </button>
126
+ `;
127
+
128
+ exports[`Link <Button to="#">Hello</Button> 1`] = `
129
+ .c0 {
130
+ color: inherit;
131
+ cursor: pointer;
132
+ }
133
+
134
+ .c0:focus {
135
+ outline: none;
136
+ }
137
+
138
+ .c0 .text {
139
+ top: calc(1em + 2em);
140
+ }
141
+
142
+ .c0:disabled,
143
+ .c0[aria-disabled]:not([aria-disabled=false]) {
144
+ cursor: default;
145
+ }
146
+
147
+ .c1 {
148
+ width: -webkit-min-content;
149
+ width: -moz-min-content;
150
+ width: min-content;
151
+ display: inline-grid;
152
+ -webkit-align-items: center;
153
+ -webkit-box-align: center;
154
+ -ms-flex-align: center;
155
+ align-items: center;
156
+ -webkit-box-pack: center;
157
+ -webkit-justify-content: center;
158
+ -ms-flex-pack: center;
159
+ justify-content: center;
160
+ cursor: pointer;
161
+ -webkit-user-select: none;
162
+ -moz-user-select: none;
163
+ -ms-user-select: none;
164
+ user-select: none;
165
+ white-space: nowrap;
166
+ color: var(--charcoal-text2);
167
+ background-color: var(--charcoal-surface3);
168
+ font-size: 14px;
169
+ line-height: 22px;
170
+ font-weight: bold;
171
+ padding-right: 24px;
172
+ padding-left: 24px;
173
+ border-radius: 999999px;
174
+ -webkit-transition: 0.2s color,0.2s background-color,0.2s box-shadow;
175
+ transition: 0.2s color,0.2s background-color,0.2s box-shadow;
176
+ height: 40px;
177
+ }
178
+
179
+ .c1:hover:not(:disabled):not([aria-disabled]),
180
+ .c1:hover[aria-disabled=false] {
181
+ color: var(--charcoal-text2-hover);
182
+ }
183
+
184
+ .c1:active:not(:disabled):not([aria-disabled]),
185
+ .c1:active[aria-disabled=false] {
186
+ color: var(--charcoal-text2-press);
187
+ }
188
+
189
+ .c1:hover:not(:disabled):not([aria-disabled]),
190
+ .c1:hover[aria-disabled=false] {
191
+ background-color: var(--charcoal-surface3-hover);
192
+ }
193
+
194
+ .c1:active:not(:disabled):not([aria-disabled]),
195
+ .c1:active[aria-disabled=false] {
196
+ background-color: var(--charcoal-surface3-press);
197
+ }
198
+
199
+ .c1:disabled,
200
+ .c1[aria-disabled]:not([aria-disabled=false]) {
201
+ opacity: 0.32;
202
+ }
203
+
204
+ .c1:not(:disabled):not([aria-disabled]):focus,
205
+ .c1[aria-disabled=false]:focus,
206
+ .c1:not(:disabled):not([aria-disabled]):active,
207
+ .c1[aria-disabled=false]:active {
208
+ outline: none;
209
+ box-shadow: 0 0 0 4px rgba(0,150,250,0.32);
210
+ }
211
+
212
+ .c1:not(:disabled):not([aria-disabled]):focus:not(:focus-visible),
213
+ .c1[aria-disabled=false]:focus:not(:focus-visible),
214
+ .c1:not(:disabled):not([aria-disabled]):active:not(:focus-visible),
215
+ .c1[aria-disabled=false]:active:not(:focus-visible) {
216
+ outline: none;
217
+ }
218
+
219
+ .c1:not(:disabled):not([aria-disabled]):focus-visible,
220
+ .c1[aria-disabled=false]:focus-visible {
221
+ outline: none;
222
+ box-shadow: 0 0 0 4px rgba(0,150,250,0.32);
223
+ }
224
+
225
+ <a
226
+ aria-disabled={false}
227
+ background="surface3"
228
+ className="c0 c1"
229
+ font="text2"
230
+ height={40}
231
+ href="#"
232
+ padding={24}
233
+ size="M"
234
+ theme={
235
+ Object {
236
+ "border": Object {
237
+ "default": Object {
238
+ "color": "rgba(0,0,0,0.08)",
239
+ },
240
+ },
241
+ "borderRadius": Object {
242
+ "16": 16,
243
+ "24": 24,
244
+ "4": 4,
245
+ "8": 8,
246
+ "none": 0,
247
+ "oval": 999999,
248
+ },
249
+ "breakpoint": Object {
250
+ "screen1": 744,
251
+ "screen2": 952,
252
+ "screen3": 1160,
253
+ "screen4": 1368,
254
+ },
255
+ "color": Object {
256
+ "assertive": "#ff2b00",
257
+ "background1": "#ffffff",
258
+ "background2": "#f5f5f5",
259
+ "border": "rgba(0,0,0,0.08)",
260
+ "brand": "#0096fa",
261
+ "icon6": "rgba(255,255,255,0.28)",
262
+ "link1": "#3d7699",
263
+ "link2": "rgba(255,255,255,0.36)",
264
+ "success": "#b1cc29",
265
+ "surface1": "#ffffff",
266
+ "surface10": "rgba(0,0,0,0.16)",
267
+ "surface2": "rgba(0,0,0,0.02)",
268
+ "surface3": "rgba(0,0,0,0.04)",
269
+ "surface4": "rgba(0,0,0,0.32)",
270
+ "surface6": "rgba(0,0,0,0.88)",
271
+ "surface7": "rgba(0,0,0,0.02)",
272
+ "surface8": "rgba(0,0,0,0.88)",
273
+ "surface9": "#ffffff",
274
+ "text1": "#1f1f1f",
275
+ "text2": "#474747",
276
+ "text3": "#858585",
277
+ "text4": "#adadad",
278
+ "text5": "#ffffff",
279
+ "transparent": "rgba(0,0,0,0)",
280
+ "updatedItem": "rgba(0,150,250,0.04)",
281
+ "warning": "#ffaf0f",
282
+ },
283
+ "effect": Object {
284
+ "hover": Object {
285
+ "color": "rgba(0,0,0,0.04)",
286
+ "type": "alpha",
287
+ },
288
+ "press": Object {
289
+ "color": "rgba(0,0,0,0.16)",
290
+ "type": "alpha",
291
+ },
292
+ },
293
+ "elementEffect": Object {
294
+ "disabled": Object {
295
+ "opacity": 0.32,
296
+ "type": "opacity",
297
+ },
298
+ },
299
+ "gradientColor": Object {
300
+ "callToAction": Array [
301
+ Object {
302
+ "color": "#d1ff1a",
303
+ "ratio": 0,
304
+ },
305
+ Object {
306
+ "color": "#1ad1ff",
307
+ "ratio": 100,
308
+ },
309
+ ],
310
+ "surface5": Array [
311
+ Object {
312
+ "color": "rgba(0,0,0,0.32)",
313
+ "ratio": 0,
314
+ },
315
+ Object {
316
+ "color": "rgba(0,0,0,0)",
317
+ "ratio": 100,
318
+ },
319
+ ],
320
+ },
321
+ "grid": Object {
322
+ "unit": Object {
323
+ "column": 80,
324
+ "gutter": 24,
325
+ },
326
+ },
327
+ "outline": Object {
328
+ "assertive": Object {
329
+ "color": "rgba(255,43,0,0.32)",
330
+ "weight": 4,
331
+ },
332
+ "default": Object {
333
+ "color": "rgba(0,150,250,0.32)",
334
+ "weight": 4,
335
+ },
336
+ },
337
+ "spacing": Object {
338
+ "0": 0,
339
+ "104": 104,
340
+ "16": 16,
341
+ "168": 168,
342
+ "24": 24,
343
+ "272": 272,
344
+ "4": 4,
345
+ "40": 40,
346
+ "440": 440,
347
+ "64": 64,
348
+ "8": 8,
349
+ },
350
+ "transition": Object {
351
+ "default": Object {
352
+ "duration": 0.2,
353
+ },
354
+ },
355
+ "typography": Object {
356
+ "size": Object {
357
+ "12": Object {
358
+ "fontSize": 12,
359
+ "lineHeight": 20,
360
+ },
361
+ "14": Object {
362
+ "fontSize": 14,
363
+ "lineHeight": 22,
364
+ },
365
+ "16": Object {
366
+ "fontSize": 16,
367
+ "lineHeight": 24,
368
+ },
369
+ "20": Object {
370
+ "fontSize": 20,
371
+ "lineHeight": 28,
372
+ },
373
+ "32": Object {
374
+ "fontSize": 32,
375
+ "lineHeight": 40,
376
+ },
377
+ },
378
+ },
379
+ }
380
+ }
381
+ variant="Default"
382
+ >
383
+ Hello
384
+ </a>
385
+ `;
@@ -0,0 +1,25 @@
1
+ import 'jest-styled-components'
2
+
3
+ import React from 'react'
4
+ import Button from '.'
5
+ import renderder from 'react-test-renderer'
6
+ import { ThemeProvider } from 'styled-components'
7
+ import { light } from '@charcoal-ui/theme'
8
+
9
+ export function render(children: JSX.Element) {
10
+ return renderder
11
+ .create(<ThemeProvider theme={light}>{children}</ThemeProvider>)
12
+ .toJSON()
13
+ }
14
+
15
+ describe('Basic', () => {
16
+ test('<Button>Hello</Button>', () => {
17
+ expect(render(<Button>Hello</Button>)).toMatchSnapshot()
18
+ })
19
+ })
20
+
21
+ describe('Link', () => {
22
+ test('<Button to="#">Hello</Button>', () => {
23
+ expect(render(<Button to="#">Hello</Button>)).toMatchSnapshot()
24
+ })
25
+ })
@@ -13,6 +13,7 @@ type Props = {
13
13
  defaultChecked: boolean
14
14
  disabled: boolean
15
15
  readonly: boolean
16
+ className?: string
16
17
  }
17
18
 
18
19
  export const Labelled: Story<Props> = (props) => {
@@ -20,6 +20,7 @@ type CheckboxLabelProps =
20
20
  export type CheckboxProps = CheckboxLabelProps & {
21
21
  readonly id?: string
22
22
  readonly name?: string
23
+ readonly className?: string
23
24
 
24
25
  readonly checked?: boolean
25
26
  readonly defaultChecked?: boolean
@@ -52,7 +53,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
52
53
  const isDisabled = (props.disabled ?? false) || (props.readonly ?? false)
53
54
 
54
55
  return (
55
- <InputRoot aria-disabled={isDisabled}>
56
+ <InputRoot aria-disabled={isDisabled} className={props.className}>
56
57
  <CheckboxRoot>
57
58
  <CheckboxInput type="checkbox" {...inputProps} />
58
59
  <CheckboxInputOverlay aria-hidden={true} checked={inputProps.checked}>
@@ -21,9 +21,15 @@ export function Basic() {
21
21
  const size = number('size', 48)
22
22
  const padding = number('padding', 16)
23
23
  const transparent = boolean('transparent', false)
24
+ const className = text('className', 'basic')
24
25
 
25
26
  return (
26
- <LoadingSpinner size={size} padding={padding} transparent={transparent} />
27
+ <LoadingSpinner
28
+ size={size}
29
+ padding={padding}
30
+ transparent={transparent}
31
+ className={className}
32
+ />
27
33
  )
28
34
  }
29
35
 
@@ -1,19 +1,35 @@
1
- import React, { useImperativeHandle, useRef } from 'react'
1
+ import React, { forwardRef, memo, useImperativeHandle, useRef } from 'react'
2
2
  import styled, { keyframes } from 'styled-components'
3
3
  import { theme } from '../../styled'
4
4
 
5
- export default function LoadingSpinner({
6
- size = 48,
7
- padding = 16,
8
- transparent = false,
9
- }) {
10
- return (
11
- <LoadingSpinnerRoot size={size} padding={padding} transparent={transparent}>
12
- <LoadingSpinnerIcon />
13
- </LoadingSpinnerRoot>
14
- )
5
+ export type LoadingSpinnerProps = {
6
+ readonly size?: number
7
+ readonly padding?: number
8
+ readonly transparent?: boolean
9
+ readonly className?: string
15
10
  }
16
11
 
12
+ const LoadingSpinner = forwardRef<HTMLDivElement, LoadingSpinnerProps>(
13
+ function LoadingSpinnerInner(
14
+ { size = 48, padding = 16, transparent = false, className },
15
+ ref
16
+ ) {
17
+ return (
18
+ <LoadingSpinnerRoot
19
+ size={size}
20
+ padding={padding}
21
+ transparent={transparent}
22
+ className={className}
23
+ ref={ref}
24
+ >
25
+ <LoadingSpinnerIcon />
26
+ </LoadingSpinnerRoot>
27
+ )
28
+ }
29
+ )
30
+
31
+ export default memo(LoadingSpinner)
32
+
17
33
  const LoadingSpinnerRoot = styled.div.attrs({ role: 'progressbar' })<{
18
34
  size: number
19
35
  padding: number
@@ -1,4 +1,4 @@
1
- import React, { useContext, useRef } from 'react'
1
+ import React, { forwardRef, memo, useContext } from 'react'
2
2
  import {
3
3
  AriaModalOverlayProps,
4
4
  Overlay,
@@ -17,6 +17,7 @@ import { useMedia } from '@charcoal-ui/styled'
17
17
  import { animated, useTransition, easings } from 'react-spring'
18
18
  import Button, { ButtonProps } from '../Button'
19
19
  import IconButton from '../IconButton'
20
+ import { useObjectRef } from '@react-aria/utils'
20
21
 
21
22
  type BottomSheet = boolean | 'full'
22
23
  type Size = 'S' | 'M' | 'L'
@@ -30,6 +31,7 @@ export type ModalProps = AriaModalOverlayProps &
30
31
  bottomSheet?: BottomSheet
31
32
  isOpen: boolean
32
33
  onClose: () => void
34
+ className?: string
33
35
 
34
36
  /**
35
37
  * https://github.com/adobe/react-spectrum/issues/3787
@@ -55,31 +57,32 @@ const DEFAULT_Z_INDEX = 10
55
57
  *
56
58
  * <OverlayProvider>
57
59
  * <App>
58
- * <Modal isOpen={state.isOpen} onClose={() => state.close()} isDismissable>
60
+ * <Modal title="Title" isOpen={state.isOpen} onClose={() => state.close()} isDismissable>
59
61
  * <ModalHeader />
60
- * <ModalBody>...</ModalBody>
61
- * <ModalButtons>...</ModalButtons>
62
+ * <ModalBody>
63
+ * ...
64
+ * <ModalButtons>...</ModalButtons>
65
+ * </ModalBody>
62
66
  * </Modal>
63
67
  * </App>
64
68
  * </OverlayProvider>
65
69
  * ```
66
70
  */
67
- export default function Modal({
68
- children,
69
- zIndex = DEFAULT_Z_INDEX,
70
- portalContainer,
71
- ...props
72
- }: ModalProps) {
71
+ const Modal = forwardRef<HTMLDivElement, ModalProps>(function ModalInner(
72
+ { children, zIndex = DEFAULT_Z_INDEX, portalContainer, ...props },
73
+ external
74
+ ) {
73
75
  const {
74
76
  title,
75
77
  size = 'M',
76
78
  bottomSheet = false,
77
79
  isDismissable,
78
80
  onClose,
81
+ className,
79
82
  isOpen = false,
80
83
  } = props
81
84
 
82
- const ref = useRef<HTMLDivElement>(null)
85
+ const ref = useObjectRef<HTMLDivElement>(external)
83
86
  const { overlayProps, underlayProps } = useOverlay(props, ref)
84
87
 
85
88
  const { modalProps } = useModalOverlay(
@@ -145,6 +148,7 @@ export default function Modal({
145
148
  style={transitionEnabled ? { transform } : {}}
146
149
  size={size}
147
150
  bottomSheet={bottomSheet}
151
+ className={className}
148
152
  >
149
153
  <ModalContext.Provider
150
154
  value={{ titleProps, title, close: onClose, showDismiss }}
@@ -165,7 +169,9 @@ export default function Modal({
165
169
  </Overlay>
166
170
  )
167
171
  )
168
- }
172
+ })
173
+
174
+ export default memo(Modal)
169
175
 
170
176
  const ModalContext = React.createContext<{
171
177
  titleProps: React.HTMLAttributes<HTMLElement>
@@ -61,6 +61,7 @@ type Props = {
61
61
  readonly?: boolean
62
62
  hasError?: boolean
63
63
  variant?: 'default' | 'overlay'
64
+ className?: string
64
65
  }
65
66
 
66
67
  const StyledMultiSelectGroup = styled(MultiSelectGroup)`
@@ -79,6 +80,7 @@ const Template: Story<Props> = ({
79
80
  readonly,
80
81
  hasError,
81
82
  variant,
83
+ className,
82
84
  }) => {
83
85
  return (
84
86
  <StyledMultiSelectGroup
@@ -90,7 +92,6 @@ const Template: Story<Props> = ({
90
92
  readonly,
91
93
  hasError,
92
94
  }}
93
- className={''}
94
95
  selected={selected ? ['選択肢1', '選択肢3'] : []}
95
96
  >
96
97
  {[1, 2, 3, 4].map((idx) => (
@@ -99,6 +100,7 @@ const Template: Story<Props> = ({
99
100
  forceChecked={firstOptionForceChecked && idx === 1}
100
101
  variant={variant}
101
102
  key={idx}
103
+ className={className}
102
104
  >
103
105
  選択肢{idx}
104
106
  </MultiSelect>
@@ -127,10 +129,11 @@ type PlaygroundProps = {
127
129
  disabled?: boolean
128
130
  readonly?: boolean
129
131
  hasError?: boolean
132
+ className?: string
130
133
  variant?: 'default' | 'overlay'
131
134
  }
132
135
 
133
- export const Playground: Story<PlaygroundProps> = (props) => {
136
+ export const Playground: Story<PlaygroundProps> = ({ className, ...props }) => {
134
137
  const [selected, setSelected] = useState<string[]>([])
135
138
 
136
139
  return (
@@ -140,7 +143,12 @@ export const Playground: Story<PlaygroundProps> = (props) => {
140
143
  onChange={setSelected}
141
144
  >
142
145
  {[1, 2, 3, 4].map((idx) => (
143
- <MultiSelect value={`選択肢${idx}`} variant={props.variant} key={idx}>
146
+ <MultiSelect
147
+ value={`選択肢${idx}`}
148
+ variant={props.variant}
149
+ key={idx}
150
+ className={className}
151
+ >
144
152
  選択肢{idx}
145
153
  </MultiSelect>
146
154
  ))}