@charcoal-ui/react 2.0.0-alpha.8 → 2.0.0-rc.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 (83) hide show
  1. package/README.md +16 -0
  2. package/dist/components/Checkbox/index.d.ts +21 -0
  3. package/dist/components/Checkbox/index.d.ts.map +1 -0
  4. package/dist/components/Checkbox/index.story.d.ts +16 -0
  5. package/dist/components/Checkbox/index.story.d.ts.map +1 -0
  6. package/dist/components/DropdownSelector/Listbox.d.ts +10 -0
  7. package/dist/components/DropdownSelector/Listbox.d.ts.map +1 -0
  8. package/dist/components/DropdownSelector/Popover.d.ts +10 -0
  9. package/dist/components/DropdownSelector/Popover.d.ts.map +1 -0
  10. package/dist/components/DropdownSelector/index.d.ts +32 -0
  11. package/dist/components/DropdownSelector/index.d.ts.map +1 -0
  12. package/dist/components/DropdownSelector/index.story.d.ts +22 -0
  13. package/dist/components/DropdownSelector/index.story.d.ts.map +1 -0
  14. package/dist/components/Icon/index.story.d.ts +1 -1
  15. package/dist/components/LoadingSpinner/index.d.ts +15 -0
  16. package/dist/components/LoadingSpinner/index.d.ts.map +1 -0
  17. package/dist/components/LoadingSpinner/index.story.d.ts +10 -0
  18. package/dist/components/LoadingSpinner/index.story.d.ts.map +1 -0
  19. package/dist/components/Modal/ModalPlumbing.d.ts +5 -0
  20. package/dist/components/Modal/ModalPlumbing.d.ts.map +1 -0
  21. package/dist/components/Modal/index.d.ts +16 -0
  22. package/dist/components/Modal/index.d.ts.map +1 -0
  23. package/dist/components/Modal/index.story.d.ts +33 -0
  24. package/dist/components/Modal/index.story.d.ts.map +1 -0
  25. package/dist/components/{Select → MultiSelect}/context.d.ts +2 -2
  26. package/dist/components/MultiSelect/context.d.ts.map +1 -0
  27. package/dist/components/MultiSelect/index.d.ts +24 -0
  28. package/dist/components/MultiSelect/index.d.ts.map +1 -0
  29. package/dist/components/{Select → MultiSelect}/index.story.d.ts +2 -2
  30. package/dist/components/MultiSelect/index.story.d.ts.map +1 -0
  31. package/dist/components/{Select → MultiSelect}/index.test.d.ts +0 -0
  32. package/dist/components/MultiSelect/index.test.d.ts.map +1 -0
  33. package/dist/components/SegmentedControl/RadioGroupContext.d.ts +9 -0
  34. package/dist/components/SegmentedControl/RadioGroupContext.d.ts.map +1 -0
  35. package/dist/components/SegmentedControl/index.d.ts +20 -0
  36. package/dist/components/SegmentedControl/index.d.ts.map +1 -0
  37. package/dist/components/SegmentedControl/index.story.d.ts +11 -0
  38. package/dist/components/SegmentedControl/index.story.d.ts.map +1 -0
  39. package/dist/components/TextField/index.d.ts +6 -3
  40. package/dist/components/TextField/index.d.ts.map +1 -1
  41. package/dist/components/TextField/index.story.d.ts +1 -0
  42. package/dist/components/TextField/index.story.d.ts.map +1 -1
  43. package/dist/core/SSRProvider.d.ts +2 -0
  44. package/dist/core/SSRProvider.d.ts.map +1 -0
  45. package/dist/index.cjs +22686 -1
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.ts +8 -2
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.modern.js +21612 -45
  50. package/dist/index.modern.js.map +1 -1
  51. package/dist/index.module.js +22641 -1
  52. package/dist/index.module.js.map +1 -1
  53. package/package.json +15 -8
  54. package/src/components/Checkbox/index.story.tsx +64 -0
  55. package/src/components/Checkbox/index.tsx +122 -0
  56. package/src/components/DropdownSelector/Listbox.tsx +127 -0
  57. package/src/components/DropdownSelector/Popover.tsx +46 -0
  58. package/src/components/DropdownSelector/index.story.tsx +134 -0
  59. package/src/components/DropdownSelector/index.tsx +214 -0
  60. package/src/components/FieldLabel/index.tsx +1 -1
  61. package/src/components/LoadingSpinner/index.story.tsx +52 -0
  62. package/src/components/LoadingSpinner/index.tsx +87 -0
  63. package/src/components/Modal/ModalPlumbing.tsx +47 -0
  64. package/src/components/Modal/index.story.tsx +195 -0
  65. package/src/components/Modal/index.tsx +226 -0
  66. package/src/components/{Select → MultiSelect}/context.ts +3 -3
  67. package/src/components/{Select → MultiSelect}/index.story.tsx +16 -12
  68. package/src/components/{Select → MultiSelect}/index.test.tsx +13 -13
  69. package/src/components/{Select → MultiSelect}/index.tsx +24 -21
  70. package/src/components/SegmentedControl/RadioGroupContext.tsx +22 -0
  71. package/src/components/SegmentedControl/index.story.tsx +36 -0
  72. package/src/components/SegmentedControl/index.tsx +157 -0
  73. package/src/components/TextField/index.story.tsx +31 -16
  74. package/src/components/TextField/index.tsx +53 -34
  75. package/src/components/a11y.test.tsx +11 -0
  76. package/src/core/SSRProvider.tsx +1 -0
  77. package/src/index.ts +22 -5
  78. package/src/styled.ts +1 -1
  79. package/dist/components/Select/context.d.ts.map +0 -1
  80. package/dist/components/Select/index.d.ts +0 -24
  81. package/dist/components/Select/index.d.ts.map +0 -1
  82. package/dist/components/Select/index.story.d.ts.map +0 -1
  83. package/dist/components/Select/index.test.d.ts.map +0 -1
@@ -0,0 +1,214 @@
1
+ import React, { Key, useMemo, useRef } from 'react'
2
+ import styled from 'styled-components'
3
+ import { Item, useSelectState } from 'react-stately'
4
+ import { disabledSelector } from '@charcoal-ui/utils'
5
+ import { useVisuallyHidden } from '@react-aria/visually-hidden'
6
+ import { useSelect, HiddenSelect } from '@react-aria/select'
7
+ import { useButton } from '@react-aria/button'
8
+ import { SelectProps } from '@react-types/select'
9
+ import Listbox, { ListboxProps } from './Listbox'
10
+ import Popover from './Popover'
11
+ import Icon from '../Icon'
12
+ import FieldLabel from '../FieldLabel'
13
+ import { theme } from '../../styled'
14
+
15
+ import type { CollectionBase } from '@react-types/shared'
16
+ import type { ReactNode } from 'react'
17
+
18
+ type LabelProps = {
19
+ readonly showLabel?: boolean
20
+ readonly label: string
21
+ readonly subLabel?: ReactNode
22
+ readonly requiredText?: string
23
+ }
24
+
25
+ type Empty = Record<string, unknown>
26
+ export type DropdownSelectorProps<T extends Empty = Empty> = LabelProps &
27
+ Readonly<CollectionBase<T>> & {
28
+ readonly id?: string
29
+ readonly name?: string
30
+ readonly autoComplete?: string
31
+ readonly placeholder?: string
32
+ readonly className?: string
33
+ readonly disabled?: boolean
34
+ readonly required?: boolean
35
+ readonly invalid?: boolean
36
+ readonly assertiveText?: string
37
+ readonly value?: Key
38
+ readonly defaultValue?: Key
39
+ readonly open?: boolean
40
+ readonly onOpenChange?: (isOpen?: boolean) => void
41
+ readonly onChange?: (key: Key) => void
42
+ readonly mode?: ListboxProps<T>['mode']
43
+ }
44
+
45
+ const DropdownSelector = <T extends Record<string, unknown>>({
46
+ open,
47
+ className,
48
+ label = '',
49
+ requiredText = '',
50
+ subLabel,
51
+ assertiveText,
52
+ autoComplete,
53
+ invalid = false,
54
+ disabled = false,
55
+ required = false,
56
+ showLabel = false,
57
+ mode = 'default',
58
+ ...props
59
+ }: DropdownSelectorProps<T>) => {
60
+ const { visuallyHiddenProps } = useVisuallyHidden()
61
+ const triggerRef = useRef<HTMLButtonElement>(null)
62
+ const selectProps = useMemo<SelectProps<T>>(
63
+ () => ({
64
+ ...props,
65
+ label,
66
+ isOpen: open,
67
+ isDisabled: disabled,
68
+ isRequired: required,
69
+ errorMessage: invalid && assertiveText,
70
+ validationState: invalid ? 'invalid' : 'valid',
71
+ onSelectionChange: props.onChange,
72
+ selectedKey: props.value,
73
+ defaultSelectedKey: props.defaultValue,
74
+ }),
75
+ [assertiveText, disabled, invalid, label, open, props, required]
76
+ )
77
+ const state = useSelectState<T>(selectProps)
78
+
79
+ const {
80
+ labelProps,
81
+ triggerProps,
82
+ valueProps,
83
+ menuProps,
84
+ errorMessageProps,
85
+ descriptionProps,
86
+ } = useSelect<T>(selectProps, state, triggerRef)
87
+
88
+ const { buttonProps } = useButton(triggerProps, triggerRef)
89
+
90
+ const hasAssertiveText =
91
+ assertiveText !== undefined && assertiveText.length > 0
92
+
93
+ return (
94
+ <DropdownSelectorRoot aria-disabled={disabled} className={className}>
95
+ <DropdownFieldLabel
96
+ label={label}
97
+ required={required}
98
+ requiredText={requiredText}
99
+ subLabel={subLabel}
100
+ {...labelProps}
101
+ {...(!showLabel ? visuallyHiddenProps : {})}
102
+ />
103
+ <HiddenSelect
104
+ state={state}
105
+ triggerRef={triggerRef}
106
+ label={label}
107
+ name={props.name}
108
+ isDisabled={disabled}
109
+ autoComplete={autoComplete}
110
+ />
111
+ <DropdownButtonWrapper>
112
+ <DropdownButton {...buttonProps} ref={triggerRef} invalid={invalid}>
113
+ <DropdownButtonText {...valueProps}>
114
+ {/*
115
+ * react-stately の useSelectState から取得される selectedItem の型が常に
116
+ * Node<T> であるが runtime では null が帰ってくることがある
117
+ */}
118
+ {/* eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unnecessary-condition*/}
119
+ {state.selectedItem
120
+ ? state.selectedItem.rendered
121
+ : props.placeholder}
122
+ </DropdownButtonText>
123
+
124
+ <Icon name="16/Menu" />
125
+ </DropdownButton>
126
+ {state.isOpen && (
127
+ <DropdownPopover open={state.isOpen} onClose={() => state.close()}>
128
+ <Listbox {...menuProps} state={state} mode={mode} />
129
+ </DropdownPopover>
130
+ )}
131
+ </DropdownButtonWrapper>
132
+
133
+ {hasAssertiveText && (
134
+ <AssertiveText
135
+ invalid={invalid}
136
+ {...(invalid ? errorMessageProps : descriptionProps)}
137
+ >
138
+ {assertiveText}
139
+ </AssertiveText>
140
+ )}
141
+ </DropdownSelectorRoot>
142
+ )
143
+ }
144
+
145
+ export default DropdownSelector
146
+ export const DropdownSelectorItem = Item
147
+
148
+ const DropdownSelectorRoot = styled.div`
149
+ position: relative;
150
+ display: inline-block;
151
+
152
+ ${disabledSelector} {
153
+ cursor: default;
154
+ ${theme((o) => o.disabled)}
155
+ }
156
+ `
157
+
158
+ const DropdownFieldLabel = styled(FieldLabel)`
159
+ width: 100%;
160
+
161
+ ${theme((o) => o.margin.bottom(8))}
162
+ `
163
+
164
+ const DropdownButtonWrapper = styled.div`
165
+ position: relative;
166
+ `
167
+
168
+ const DropdownButton = styled.button<{ invalid: boolean }>`
169
+ display: flex;
170
+ justify-content: space-between;
171
+ align-items: center;
172
+
173
+ height: 40px;
174
+ width: 288px;
175
+ box-sizing: border-box;
176
+ cursor: pointer;
177
+
178
+ ${disabledSelector} {
179
+ cursor: default;
180
+ }
181
+
182
+ ${({ invalid }) =>
183
+ theme((o) => [
184
+ o.border.default,
185
+ o.padding.horizontal(8),
186
+ o.outline.default.focus,
187
+ o.bg.surface3,
188
+ o.borderRadius(4),
189
+ invalid && o.outline.assertive,
190
+ ])}
191
+ `
192
+
193
+ const DropdownButtonText = styled.span`
194
+ text-align: left;
195
+
196
+ ${theme((o) => [o.typography(14), o.font.text2])}
197
+ `
198
+
199
+ const AssertiveText = styled.div<{ invalid: boolean }>`
200
+ ${({ invalid }) =>
201
+ theme((o) => [
202
+ o.typography(14),
203
+ o.margin.top(8),
204
+ invalid && o.font.assertive,
205
+ ])}
206
+ `
207
+
208
+ const DropdownPopover = styled(Popover)`
209
+ position: absolute;
210
+ width: 100%;
211
+
212
+ top: 100%;
213
+ margin-top: 2px;
214
+ `
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import styled from 'styled-components'
3
- import createTheme from '@charcoal-ui/styled'
3
+ import { createTheme } from '@charcoal-ui/styled'
4
4
 
5
5
  export interface FieldLabelProps
6
6
  extends React.LabelHTMLAttributes<HTMLLabelElement> {
@@ -0,0 +1,52 @@
1
+ import {
2
+ boolean,
3
+ button,
4
+ number,
5
+ text,
6
+ withKnobs,
7
+ } from '@storybook/addon-knobs'
8
+ import React, { useRef } from 'react'
9
+ import LoadingSpinner, {
10
+ LoadingSpinnerIcon,
11
+ LoadingSpinnerIconHandler,
12
+ } from '.'
13
+
14
+ export default {
15
+ title: 'LoadingSpinner',
16
+ component: LoadingSpinner,
17
+ decorators: [withKnobs],
18
+ }
19
+
20
+ export function Basic() {
21
+ const size = number('size', 48)
22
+ const padding = number('padding', 16)
23
+ const transparent = boolean('transparent', false)
24
+
25
+ return (
26
+ <LoadingSpinner size={size} padding={padding} transparent={transparent} />
27
+ )
28
+ }
29
+
30
+ export function Icon() {
31
+ return <IconComponent />
32
+ }
33
+
34
+ function IconComponent() {
35
+ const size = number('size', 12)
36
+ const color = text('color', '#B1CC29')
37
+ const once = boolean('once', false)
38
+ button('restart', () => ref.current?.restart())
39
+
40
+ const ref = useRef<LoadingSpinnerIconHandler>(null)
41
+
42
+ return (
43
+ <div
44
+ css={`
45
+ font-size: ${size}px;
46
+ color: ${color};
47
+ `}
48
+ >
49
+ <LoadingSpinnerIcon once={once} ref={ref} />
50
+ </div>
51
+ )
52
+ }
@@ -0,0 +1,87 @@
1
+ import { transparentize } from 'polished'
2
+ import React, { useImperativeHandle, useRef } from 'react'
3
+ import styled, { keyframes } from 'styled-components'
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
+ )
15
+ }
16
+
17
+ const LoadingSpinnerRoot = styled.div.attrs({ role: 'progressbar' })<{
18
+ size: number
19
+ padding: number
20
+ transparent: boolean
21
+ }>`
22
+ margin: auto;
23
+ padding: ${(props) => props.padding}px;
24
+ border-radius: 8px;
25
+ font-size: ${(props) => props.size}px;
26
+ width: ${(props) => props.size}px;
27
+ height: ${(props) => props.size}px;
28
+ background-color: ${({ theme, transparent }) =>
29
+ transparent
30
+ ? 'transparent'
31
+ : transparentize(0.32, theme.color.background1)};
32
+ color: ${({ theme }) => theme.color.text4};
33
+ `
34
+
35
+ const scaleout = keyframes`
36
+ from {
37
+ transform: scale(0);
38
+ opacity: 1;
39
+ }
40
+
41
+ to {
42
+ transform: scale(1);
43
+ opacity: 0;
44
+ }
45
+ `
46
+
47
+ const Icon = styled.div.attrs({ role: 'presentation' })<{ once: boolean }>`
48
+ width: 1em;
49
+ height: 1em;
50
+ border-radius: 1em;
51
+ background-color: currentColor;
52
+ animation: ${scaleout} 1s both ease-out;
53
+ animation-iteration-count: ${(p) => (p.once ? 1 : 'infinite')};
54
+
55
+ &[data-reset-animation] {
56
+ animation: none;
57
+ }
58
+ `
59
+
60
+ interface Props {
61
+ once?: boolean
62
+ }
63
+
64
+ export interface LoadingSpinnerIconHandler {
65
+ restart(): void
66
+ }
67
+
68
+ export const LoadingSpinnerIcon = React.forwardRef<
69
+ LoadingSpinnerIconHandler,
70
+ Props
71
+ >(function LoadingSpinnerIcon({ once = false }, ref) {
72
+ const iconRef = useRef<HTMLDivElement>(null)
73
+
74
+ useImperativeHandle(ref, () => ({
75
+ restart: () => {
76
+ if (!iconRef.current) {
77
+ return
78
+ }
79
+ iconRef.current.dataset.resetAnimation = 'true'
80
+ // Force reflow hack!
81
+ void iconRef.current.offsetWidth
82
+ delete iconRef.current.dataset.resetAnimation
83
+ },
84
+ }))
85
+
86
+ return <Icon ref={iconRef} once={once} />
87
+ })
@@ -0,0 +1,47 @@
1
+ import React from 'react'
2
+ import { ModalTitle } from '.'
3
+ import styled from 'styled-components'
4
+ import { theme } from '../../styled'
5
+ import { maxWidth } from '@charcoal-ui/utils'
6
+
7
+ export function ModalHeader() {
8
+ return (
9
+ <ModalHeaderRoot>
10
+ <StyledModalTitle />
11
+ </ModalHeaderRoot>
12
+ )
13
+ }
14
+
15
+ const ModalHeaderRoot = styled.div`
16
+ height: 64px;
17
+ display: grid;
18
+ align-content: center;
19
+ justify-content: center;
20
+ `
21
+
22
+ const StyledModalTitle = styled(ModalTitle)`
23
+ ${theme((o) => [o.font.text1, o.typography(16).bold])}
24
+ `
25
+
26
+ export const ModalAlign = styled.div`
27
+ ${theme((o) => [o.padding.horizontal(24)])}
28
+
29
+ @media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
30
+ ${theme((o) => [o.padding.horizontal(16)])}
31
+ }
32
+ `
33
+
34
+ export const ModalBody = styled.div`
35
+ ${theme((o) => [o.padding.bottom(40)])}
36
+ `
37
+
38
+ export const ModalButtons = styled.div`
39
+ display: grid;
40
+ grid-auto-flow: row;
41
+ grid-row-gap: 8px;
42
+ ${theme((o) => [o.padding.horizontal(24).top(16)])}
43
+
44
+ @media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
45
+ ${theme((o) => [o.padding.horizontal(16)])}
46
+ }
47
+ `
@@ -0,0 +1,195 @@
1
+ import React from 'react'
2
+ import { Story } from '../../_lib/compat'
3
+ import Modal, { ModalDismissButton, ModalProps } from '.'
4
+ import { OverlayProvider } from '@react-aria/overlays'
5
+ import { useOverlayTriggerState } from 'react-stately'
6
+ import Button from '../Button'
7
+ import {
8
+ ModalAlign,
9
+ ModalBody,
10
+ ModalButtons,
11
+ ModalHeader,
12
+ } from './ModalPlumbing'
13
+ import styled from 'styled-components'
14
+ import { theme } from '../../styled'
15
+ import TextField from '../TextField'
16
+
17
+ export default {
18
+ title: 'Modal',
19
+ component: Modal,
20
+ args: {
21
+ title: 'Title',
22
+ },
23
+ argTypes: {
24
+ size: {
25
+ options: ['S', 'M', 'L'],
26
+ control: {
27
+ type: 'inline-radio',
28
+ },
29
+ },
30
+ bottomSheet: {
31
+ options: ['full', 'true', 'false'],
32
+ mapping: { full: 'full', true: true, false: false },
33
+ control: {
34
+ type: 'inline-radio',
35
+ },
36
+ },
37
+ },
38
+ }
39
+
40
+ const DefaultStory = (args: ModalProps) => {
41
+ const state = useOverlayTriggerState({})
42
+ return (
43
+ // Application must be wrapped in an OverlayProvider so that it can be
44
+ // hidden from screen readers when a modal opens.
45
+ <OverlayProvider>
46
+ <Button onClick={() => state.open()}>Open Modal</Button>
47
+
48
+ <Modal
49
+ isOpen={state.isOpen}
50
+ onClose={() => state.close()}
51
+ isDismissable
52
+ {...args}
53
+ >
54
+ <ModalHeader />
55
+ <ModalBody>
56
+ <ModalVStack>
57
+ <StyledModalText>
58
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
59
+ placeat tenetur, necessitatibus laudantium cumque exercitationem
60
+ provident. Quaerat iure enim, eveniet dolores earum odio quo
61
+ possimus fugiat aspernatur, numquam, commodi repellat.
62
+ </StyledModalText>
63
+ <ModalAlign>
64
+ <TextField
65
+ showLabel
66
+ label="Name"
67
+ placeholder="Nagisa"
68
+ ></TextField>
69
+ </ModalAlign>
70
+ <ModalAlign>
71
+ <TextField
72
+ showLabel
73
+ label="Country"
74
+ placeholder="Tokyo"
75
+ ></TextField>
76
+ </ModalAlign>
77
+ </ModalVStack>
78
+ <ModalButtons>
79
+ <Button variant="Primary" onClick={() => state.close()} fixed>
80
+ Apply
81
+ </Button>
82
+ <Button onClick={() => state.close()} fixed>
83
+ Cancel
84
+ </Button>
85
+ </ModalButtons>
86
+ </ModalBody>
87
+ </Modal>
88
+ </OverlayProvider>
89
+ )
90
+ }
91
+
92
+ const ModalVStack = styled.div`
93
+ display: grid;
94
+ gap: 24px;
95
+ `
96
+
97
+ const StyledModalText = styled(ModalAlign)`
98
+ ${theme((o) => [o.font.text2, o.typography(14)])}
99
+ `
100
+
101
+ export const Default: Story<ModalProps> = DefaultStory.bind({})
102
+
103
+ const FullBottomSheetStory = (args: ModalProps) => {
104
+ const state = useOverlayTriggerState({})
105
+ return (
106
+ // Application must be wrapped in an OverlayProvider so that it can be
107
+ // hidden from screen readers when a modal opens.
108
+ <OverlayProvider>
109
+ <Button onClick={() => state.open()}>Open Modal</Button>
110
+
111
+ <Modal
112
+ isOpen={state.isOpen}
113
+ onClose={() => state.close()}
114
+ isDismissable
115
+ bottomSheet="full"
116
+ {...args}
117
+ >
118
+ <ModalHeader />
119
+ <ModalBody>
120
+ <ModalVStack>
121
+ <StyledModalText>
122
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
123
+ placeat tenetur, necessitatibus laudantium cumque exercitationem
124
+ provident. Quaerat iure enim, eveniet dolores earum odio quo
125
+ possimus fugiat aspernatur, numquam, commodi repellat.
126
+ </StyledModalText>
127
+ <ModalAlign>
128
+ <TextField
129
+ showLabel
130
+ label="Name"
131
+ placeholder="Nagisa"
132
+ ></TextField>
133
+ </ModalAlign>
134
+ <ModalAlign>
135
+ <TextField
136
+ showLabel
137
+ label="Country"
138
+ placeholder="Tokyo"
139
+ ></TextField>
140
+ </ModalAlign>
141
+ </ModalVStack>
142
+ <ModalButtons>
143
+ <Button variant="Primary" onClick={() => state.close()} fixed>
144
+ Apply
145
+ </Button>
146
+ <Button onClick={() => state.close()} fixed>
147
+ Cancel
148
+ </Button>
149
+ </ModalButtons>
150
+ </ModalBody>
151
+ </Modal>
152
+ </OverlayProvider>
153
+ )
154
+ }
155
+
156
+ export const FullBottomSheet: Story<ModalProps> = FullBottomSheetStory.bind({})
157
+
158
+ const BottomSheetStory = (args: ModalProps) => {
159
+ const state = useOverlayTriggerState({})
160
+ return (
161
+ // Application must be wrapped in an OverlayProvider so that it can be
162
+ // hidden from screen readers when a modal opens.
163
+ <OverlayProvider>
164
+ <Button onClick={() => state.open()}>Open Modal</Button>
165
+
166
+ <Modal
167
+ isOpen={state.isOpen}
168
+ onClose={() => state.close()}
169
+ bottomSheet
170
+ isDismissable
171
+ {...args}
172
+ >
173
+ <ModalHeader />
174
+ <ModalBody>
175
+ <ModalVStack>
176
+ <StyledModalText>
177
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
178
+ placeat tenetur, necessitatibus laudantium cumque exercitationem
179
+ provident. Quaerat iure enim, eveniet dolores earum odio quo
180
+ possimus fugiat aspernatur, numquam, commodi repellat.
181
+ </StyledModalText>
182
+ </ModalVStack>
183
+ <ModalButtons>
184
+ <Button variant="Danger" onClick={() => state.close()} fixed>
185
+ 削除する
186
+ </Button>
187
+ <ModalDismissButton>キャンセル</ModalDismissButton>
188
+ </ModalButtons>
189
+ </ModalBody>
190
+ </Modal>
191
+ </OverlayProvider>
192
+ )
193
+ }
194
+
195
+ export const BottomSheet: Story<ModalProps> = BottomSheetStory.bind({})