@storybook/react-native-ui 8.0.0-alpha.3

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +492 -0
  3. package/dist/index.js +2860 -0
  4. package/package.json +85 -0
  5. package/src/Button.stories.tsx +134 -0
  6. package/src/Button.tsx +243 -0
  7. package/src/Explorer.stories.tsx +46 -0
  8. package/src/Explorer.tsx +54 -0
  9. package/src/IconButton.tsx +11 -0
  10. package/src/Layout.stories.tsx +38 -0
  11. package/src/Layout.tsx +103 -0
  12. package/src/LayoutProvider.tsx +90 -0
  13. package/src/MobileAddonsPanel.tsx +166 -0
  14. package/src/MobileMenuDrawer.tsx +75 -0
  15. package/src/Refs.tsx +142 -0
  16. package/src/Search.tsx +336 -0
  17. package/src/SearchResults.tsx +315 -0
  18. package/src/Sidebar.stories.tsx +262 -0
  19. package/src/Sidebar.tsx +200 -0
  20. package/src/Tree.stories.tsx +139 -0
  21. package/src/Tree.tsx +441 -0
  22. package/src/TreeNode.stories.tsx +122 -0
  23. package/src/TreeNode.tsx +146 -0
  24. package/src/constants.ts +4 -0
  25. package/src/icon/BottomBarToggleIcon.tsx +23 -0
  26. package/src/icon/CloseIcon.tsx +22 -0
  27. package/src/icon/CollapseAllIcon.tsx +17 -0
  28. package/src/icon/CollapseIcon.tsx +39 -0
  29. package/src/icon/ComponentIcon.tsx +14 -0
  30. package/src/icon/ExpandAllIcon.tsx +17 -0
  31. package/src/icon/FaceHappyIcon.tsx +18 -0
  32. package/src/icon/GroupIcon.tsx +14 -0
  33. package/src/icon/MenuIcon.tsx +18 -0
  34. package/src/icon/SearchIcon.tsx +17 -0
  35. package/src/icon/StoryIcon.tsx +14 -0
  36. package/src/index.tsx +9 -0
  37. package/src/mockdata.large.ts +25217 -0
  38. package/src/mockdata.ts +287 -0
  39. package/src/types.ts +78 -0
  40. package/src/useExpanded.ts +130 -0
  41. package/src/useLastViewed.ts +48 -0
  42. package/src/util/status.tsx +70 -0
  43. package/src/util/tree.ts +91 -0
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@storybook/react-native-ui",
3
+ "version": "8.0.0-alpha.3",
4
+ "description": "ui components for react native storybook",
5
+ "keywords": [
6
+ "react",
7
+ "react-native",
8
+ "storybook"
9
+ ],
10
+ "homepage": "https://storybook.js.org/",
11
+ "bugs": {
12
+ "url": "https://github.com/storybookjs/react-native/issues"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/storybookjs/react-native.git",
17
+ "directory": "packages/react-native-ui"
18
+ },
19
+ "react-native": "src/index.tsx",
20
+ "main": "dist/index.js",
21
+ "types": "src/index.tsx",
22
+ "license": "MIT",
23
+ "files": [
24
+ "dist/**/*",
25
+ "README.md",
26
+ "*.js",
27
+ "*.d.ts",
28
+ "src/**/*"
29
+ ],
30
+ "scripts": {
31
+ "dev": "tsup --watch",
32
+ "prepare": "tsup",
33
+ "test": "jest --passWithNoTests",
34
+ "test:ci": "jest"
35
+ },
36
+ "jest": {
37
+ "modulePathIgnorePatterns": [
38
+ "dist/"
39
+ ],
40
+ "moduleFileExtensions": [
41
+ "ts",
42
+ "tsx",
43
+ "js",
44
+ "jsx",
45
+ "json",
46
+ "node"
47
+ ],
48
+ "preset": "react-native"
49
+ },
50
+ "devDependencies": {
51
+ "@storybook/react": "^8",
52
+ "@storybook/types": "^8",
53
+ "@types/jest": "^29.4.3",
54
+ "@types/react": "~18.2.14",
55
+ "babel-jest": "^29.4.3",
56
+ "jest": "^29.7.0",
57
+ "react-test-renderer": "18.2.0",
58
+ "tsup": "^7.2.0",
59
+ "typescript": "^5.3.3"
60
+ },
61
+ "dependencies": {
62
+ "@storybook/core-events": "^8",
63
+ "@storybook/manager-api": "^8",
64
+ "@storybook/react-native-theming": "^8.0.0-alpha.3",
65
+ "fuse.js": "^7.0.0",
66
+ "memoizerific": "^1.11.3",
67
+ "polished": "^4.3.1"
68
+ },
69
+ "peerDependencies": {
70
+ "@gorhom/bottom-sheet": ">=4",
71
+ "react": "*",
72
+ "react-native": ">=0.57.0",
73
+ "react-native-gesture-handler": ">=2",
74
+ "react-native-reanimated": ">=3",
75
+ "react-native-safe-area-context": "*",
76
+ "react-native-svg": ">=14"
77
+ },
78
+ "engines": {
79
+ "node": ">=18.0.0"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public"
83
+ },
84
+ "gitHead": "b7c2a24d21c0a38bd5e31349b3ba370af7c3d863"
85
+ }
@@ -0,0 +1,134 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import type { ReactNode } from 'react';
3
+
4
+ import { View } from 'react-native';
5
+
6
+ import { Button } from './Button';
7
+ import { FaceHappyIcon } from './icon/FaceHappyIcon';
8
+
9
+ const meta = {
10
+ title: 'UI/Button',
11
+ component: Button,
12
+ args: {},
13
+ } satisfies Meta<typeof Button>;
14
+
15
+ export default meta;
16
+ type Story = StoryObj<typeof meta>;
17
+
18
+ const Stack = ({ children }: { children: ReactNode }) => (
19
+ <View style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>{children}</View>
20
+ );
21
+
22
+ const Row = ({ children }: { children: ReactNode }) => (
23
+ <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 16 }}>
24
+ {children}
25
+ </View>
26
+ );
27
+
28
+ export const Base: Story = {};
29
+
30
+ export const Variants: Story = {
31
+ render: (args) => (
32
+ <Stack>
33
+ <Row>
34
+ <Button variant="solid" text="Solid" {...args} />
35
+ <Button variant="outline" text="Outline" {...args} />
36
+ <Button variant="ghost" text="Ghost" {...args} />
37
+ </Row>
38
+ <Row>
39
+ <Button variant="solid" {...args} Icon={FaceHappyIcon} text="Solid" />
40
+ <Button variant="outline" Icon={FaceHappyIcon} text="Outline" {...args} />
41
+ <Button variant="ghost" Icon={FaceHappyIcon} text="Ghost" {...args} />
42
+ </Row>
43
+ <Row>
44
+ <Button variant="solid" padding="small" Icon={FaceHappyIcon} {...args} />
45
+ <Button variant="outline" padding="small" Icon={FaceHappyIcon} {...args} />
46
+ <Button variant="ghost" padding="small" Icon={FaceHappyIcon} {...args} />
47
+ </Row>
48
+ </Stack>
49
+ ),
50
+ };
51
+
52
+ export const Active: Story = {
53
+ args: {
54
+ active: true,
55
+ text: 'Button',
56
+ Icon: FaceHappyIcon,
57
+ },
58
+ render: (args) => (
59
+ <Row>
60
+ <Button variant="solid" {...args} />
61
+ <Button variant="outline" {...args} />
62
+ <Button variant="ghost" {...args} />
63
+ </Row>
64
+ ),
65
+ };
66
+
67
+ export const WithIcon: Story = {
68
+ args: {
69
+ Icon: FaceHappyIcon,
70
+ text: 'Button',
71
+ },
72
+ render: (args) => (
73
+ <Row>
74
+ <Button variant="solid" {...args} />
75
+ <Button variant="outline" {...args} />
76
+ <Button variant="ghost" {...args} />
77
+ </Row>
78
+ ),
79
+ };
80
+
81
+ export const IconOnly: Story = {
82
+ args: {
83
+ padding: 'small',
84
+ Icon: FaceHappyIcon,
85
+ },
86
+ render: (args) => (
87
+ <Row>
88
+ <Button variant="solid" {...args} />
89
+ <Button variant="outline" {...args} />
90
+ <Button variant="ghost" {...args} />
91
+ </Row>
92
+ ),
93
+ };
94
+
95
+ export const Sizes: Story = {
96
+ render: () => (
97
+ <Row>
98
+ <Button size="small" text="Small Button" />
99
+ <Button size="medium" text="Medium Button" />
100
+ </Row>
101
+ ),
102
+ };
103
+
104
+ export const Disabled: Story = {
105
+ args: {
106
+ disabled: true,
107
+ text: 'Disabled Button',
108
+ },
109
+ };
110
+
111
+ export const Animated: Story = {
112
+ args: {
113
+ variant: 'outline',
114
+ },
115
+ render: (args) => (
116
+ <Stack>
117
+ <Row>
118
+ <Button animation="glow" text="Button" {...args} />
119
+ <Button animation="jiggle" text="Button" {...args} />
120
+ <Button animation="rotate360" text="Button" {...args} />
121
+ </Row>
122
+ <Row>
123
+ <Button animation="glow" text="Button" Icon={FaceHappyIcon} {...args} />
124
+ <Button animation="jiggle" text="Button" Icon={FaceHappyIcon} {...args} />
125
+ <Button animation="rotate360" Icon={FaceHappyIcon} text="Button" {...args} />
126
+ </Row>
127
+ <Row>
128
+ <Button animation="glow" padding="small" Icon={FaceHappyIcon} {...args} />
129
+ <Button animation="jiggle" padding="small" Icon={FaceHappyIcon} {...args} />
130
+ <Button animation="rotate360" padding="small" Icon={FaceHappyIcon} {...args} />
131
+ </Row>
132
+ </Stack>
133
+ ),
134
+ };
package/src/Button.tsx ADDED
@@ -0,0 +1,243 @@
1
+ import { styled, useTheme } from '@storybook/react-native-theming';
2
+ import { ReactElement, forwardRef, useEffect, useMemo, useState } from 'react';
3
+ import { TouchableOpacity, TouchableOpacityProps } from 'react-native';
4
+ import { SvgProps } from 'react-native-svg';
5
+
6
+ export interface ButtonProps extends TouchableOpacityProps {
7
+ asChild?: boolean;
8
+ size?: 'small' | 'medium';
9
+ padding?: 'small' | 'medium';
10
+ variant?: 'outline' | 'solid' | 'ghost';
11
+ disabled?: boolean;
12
+ active?: boolean;
13
+ animation?: 'none' | 'rotate360' | 'glow' | 'jiggle';
14
+ text?: string;
15
+ Icon?: (props: SvgProps) => ReactElement;
16
+ }
17
+
18
+ export const Button = forwardRef<TouchableOpacity, ButtonProps>(
19
+ (
20
+ {
21
+ // asChild = false,
22
+ Icon,
23
+ animation = 'none',
24
+ size = 'small',
25
+ variant = 'outline',
26
+ padding = 'medium',
27
+ disabled = false,
28
+ active = false,
29
+ onPress,
30
+ children,
31
+ text,
32
+ ...props
33
+ },
34
+ ref
35
+ ) => {
36
+ // let Comp: 'button' | 'a' | typeof Slot = 'button';
37
+ // if (props.isLink) Comp = 'a';
38
+ // if (asChild) Comp = Slot;
39
+ // let localVariant = variant;
40
+ // let localSize = size;
41
+
42
+ const [isAnimating, setIsAnimating] = useState(false);
43
+
44
+ const handleClick = (event) => {
45
+ if (onPress) onPress(event);
46
+ if (animation === 'none') return;
47
+ setIsAnimating(true);
48
+ };
49
+
50
+ useEffect(() => {
51
+ const timer = setTimeout(() => {
52
+ if (isAnimating) setIsAnimating(false);
53
+ }, 1000);
54
+ return () => clearTimeout(timer);
55
+ }, [isAnimating]);
56
+
57
+ // Match the old API with the new API.
58
+ // TODO: Remove this after 9.0.
59
+ // if (props.primary) {
60
+ // localVariant = 'solid';
61
+ // localSize = 'medium';
62
+ // }
63
+
64
+ // Match the old API with the new API.
65
+ // TODO: Remove this after 9.0.
66
+ // if (props.secondary || props.tertiary || props.gray || props.outline || props.inForm) {
67
+ // localVariant = 'outline';
68
+ // localSize = 'medium';
69
+ // }
70
+
71
+ return (
72
+ <StyledButton
73
+ // as={Comp}
74
+ ref={ref}
75
+ variant={variant}
76
+ size={size}
77
+ padding={padding}
78
+ disabled={disabled}
79
+ active={active}
80
+ animating={isAnimating}
81
+ animation={animation}
82
+ onPress={handleClick}
83
+ {...props}
84
+ >
85
+ {Icon && <ButtonIcon Icon={Icon} variant={variant} active={active} />}
86
+ {text && (
87
+ <ButtonText variant={variant} active={active}>
88
+ {text}
89
+ </ButtonText>
90
+ )}
91
+ {children}
92
+ </StyledButton>
93
+ );
94
+ }
95
+ );
96
+
97
+ Button.displayName = 'Button';
98
+
99
+ // const StyledButton = styled('TouchableOpacity', {
100
+ // shouldForwardProp: (prop) => isPropValid(prop),
101
+ // })<
102
+ // ButtonProps & {
103
+ // animating: boolean;
104
+ // animation: ButtonProps['animation'];
105
+ // }
106
+ // >(({ theme, variant, size, disabled, active, animating, animation, padding }) => ({
107
+ const StyledButton = styled.TouchableOpacity<
108
+ ButtonProps & { animating: boolean; animation: ButtonProps['animation'] }
109
+ >(({ theme, variant, size, disabled, active, /* animating, animation, */ padding }) => ({
110
+ border: 0,
111
+ cursor: disabled ? 'not-allowed' : 'pointer',
112
+ display: 'flex',
113
+ flexDirection: 'row',
114
+ gap: 6,
115
+ alignItems: 'center',
116
+ justifyContent: 'center',
117
+ overflow: 'hidden',
118
+ paddingHorizontal: (() => {
119
+ if (padding === 'small' && size === 'small') return 7;
120
+ if (padding === 'small' && size === 'medium') return 9;
121
+ if (size === 'small') return 10;
122
+ if (size === 'medium') return 12;
123
+ return 0;
124
+ })(),
125
+ paddingVertical: 0,
126
+ height: size === 'small' ? 28 : 32,
127
+ position: 'relative',
128
+
129
+ // textDecoration: 'none',
130
+ transitionProperty: 'background, box-shadow',
131
+ transitionDuration: '150ms',
132
+ transitionTimingFunction: 'ease-out',
133
+ // verticalAlign: 'top',
134
+ whiteSpace: 'nowrap',
135
+ userSelect: 'none',
136
+ opacity: disabled ? 0.5 : 1,
137
+ margin: 0,
138
+
139
+ backgroundColor: (() => {
140
+ if (variant === 'solid') return theme.color.secondary;
141
+ if (variant === 'outline') return theme.button$.background;
142
+ if (variant === 'ghost' && active) return theme.background.hoverable;
143
+
144
+ return 'transparent';
145
+ })(),
146
+
147
+ boxShadow: variant === 'outline' ? `${theme.button$.border} 0 0 0 1px inset` : 'none',
148
+ borderRadius: theme.input.borderRadius,
149
+ // Making sure that the button never shrinks below its minimum size
150
+ flexShrink: 0,
151
+
152
+ // '&:hover': {
153
+ // color: variant === 'ghost' ? theme.color.secondary : null,
154
+ // background: (() => {
155
+ // let bgColor = theme.color.secondary;
156
+ // if (variant === 'solid') bgColor = theme.color.secondary;
157
+ // if (variant === 'outline') bgColor = theme.button.background;
158
+
159
+ // if (variant === 'ghost') return transparentize(0.86, theme.color.secondary);
160
+ // return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
161
+ // })(),
162
+ // },
163
+
164
+ // '&:active': {
165
+ // color: variant === 'ghost' ? theme.color.secondary : null,
166
+ // background: (() => {
167
+ // let bgColor = theme.color.secondary;
168
+ // if (variant === 'solid') bgColor = theme.color.secondary;
169
+ // if (variant === 'outline') bgColor = theme.button.background;
170
+
171
+ // if (variant === 'ghost') return theme.background.hoverable;
172
+ // return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
173
+ // })(),
174
+ // },
175
+
176
+ // '&:focus': {
177
+ // boxShadow: `${rgba(theme.color.secondary, 1)} 0 0 0 1px inset`,
178
+ // outline: 'none',
179
+ // },
180
+
181
+ // '> svg': {
182
+ // animation:
183
+ // animating && animation !== 'none' ? `${theme.animation[animation]} 1000ms ease-out` : '',
184
+ // },
185
+ }));
186
+
187
+ export const ButtonText = styled.Text<{
188
+ variant: ButtonProps['variant'];
189
+ active: ButtonProps['active'];
190
+ }>(({ theme, variant, active }) => ({
191
+ color: (() => {
192
+ if (variant === 'solid') return theme.color.lightest;
193
+ if (variant === 'outline') return theme.input.color;
194
+ if (variant === 'ghost' && active) return theme.color.secondary;
195
+ if (variant === 'ghost') return theme.color.mediumdark;
196
+ return theme.input.color;
197
+ })(),
198
+ flexDirection: 'row',
199
+ gap: 6,
200
+ textAlign: 'center',
201
+ // lineHeight: theme.typography.size.s1,
202
+ fontSize: theme.typography.size.s1,
203
+ fontWeight: theme.typography.weight.bold,
204
+ }));
205
+
206
+ export const ButtonIcon = ({
207
+ Icon,
208
+ active,
209
+ variant,
210
+ }: {
211
+ Icon: (props: SvgProps) => ReactElement;
212
+ variant: ButtonProps['variant'];
213
+ active: ButtonProps['active'];
214
+ }) => {
215
+ const theme = useTheme();
216
+
217
+ const color = useMemo(() => {
218
+ if (variant === 'solid') return theme.color.lightest;
219
+ if (variant === 'outline') return theme.input.color;
220
+ if (variant === 'ghost' && active) return theme.color.secondary;
221
+ if (variant === 'ghost') return theme.color.mediumdark;
222
+ return theme.input.color;
223
+ }, [
224
+ active,
225
+ theme.color.lightest,
226
+ theme.color.mediumdark,
227
+ theme.color.secondary,
228
+ theme.input.color,
229
+ variant,
230
+ ]);
231
+
232
+ return <Icon color={color} />;
233
+ };
234
+
235
+ // color: variant === 'ghost' ? theme.color.secondary : null,
236
+ // background: (() => {
237
+ // let bgColor = theme.color.secondary;
238
+ // if (variant === 'solid') bgColor = theme.color.secondary;
239
+ // if (variant === 'outline') bgColor = theme.button.background;
240
+
241
+ // if (variant === 'ghost') return theme.background.hoverable;
242
+ // return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
243
+ // })(),
@@ -0,0 +1,46 @@
1
+ import { ManagerContext } from '@storybook/manager-api';
2
+ import { Explorer } from './Explorer';
3
+ import { mockDataset } from './mockdata';
4
+ import type { RefType } from './types';
5
+ import { View } from 'react-native';
6
+
7
+ export default {
8
+ component: Explorer,
9
+ title: 'UI/Sidebar/Explorer',
10
+ parameters: { layout: 'fullscreen', theme: 'side-by-side' },
11
+ decorators: [
12
+ (storyFn: any) => (
13
+ <ManagerContext.Provider value={{ state: { docsOptions: {} } } as any}>
14
+ {storyFn()}
15
+ </ManagerContext.Provider>
16
+ ),
17
+ (storyFn: any) => <View style={{ paddingHorizontal: 20 }}>{storyFn()}</View>,
18
+ (storyFn: any) => <View style={{ paddingHorizontal: 20 }}>{storyFn()}</View>,
19
+ ],
20
+ };
21
+
22
+ const selected = {
23
+ refId: 'storybook_internal',
24
+ storyId: 'root-1-child-a2--grandchild-a1-1',
25
+ };
26
+
27
+ const simple: Record<string, RefType> = {
28
+ storybook_internal: {
29
+ title: undefined,
30
+ id: 'storybook_internal',
31
+ url: 'iframe.html',
32
+ previewInitialized: true,
33
+ // @ts-expect-error (invalid input)
34
+ index: mockDataset.withRoot,
35
+ },
36
+ };
37
+
38
+ export const Simple = () => (
39
+ <Explorer
40
+ dataset={{ hash: simple, entries: Object.entries(simple) }}
41
+ selected={selected}
42
+ isLoading={false}
43
+ isBrowsing
44
+ setSelection={() => {}}
45
+ />
46
+ );
@@ -0,0 +1,54 @@
1
+ import type { FC } from 'react';
2
+ import React, { useRef } from 'react';
3
+
4
+ import { Ref } from './Refs';
5
+ import type { CombinedDataset, Selection } from './types';
6
+
7
+ // import { useHighlighted } from './useHighlighted';
8
+ // import { HighlightStyles } from './HighlightStyles';
9
+ import { View } from 'react-native';
10
+
11
+ export interface ExplorerProps {
12
+ isLoading: boolean;
13
+ isBrowsing: boolean;
14
+ dataset: CombinedDataset;
15
+ selected: Selection;
16
+ setSelection: (selection: Selection) => void;
17
+ }
18
+
19
+ export const Explorer: FC<ExplorerProps> = React.memo(function Explorer({
20
+ isLoading,
21
+ isBrowsing,
22
+ dataset,
23
+ selected,
24
+ setSelection,
25
+ }) {
26
+ const containerRef = useRef<View>(null);
27
+
28
+ // Track highlighted nodes, keep it in sync with props and enable keyboard navigation
29
+ // const [highlighted, setHighlighted, highlightedRef] = useHighlighted({
30
+ // containerRef,
31
+ // isLoading,
32
+ // isBrowsing,
33
+ // dataset,
34
+ // selected,
35
+ // });
36
+
37
+ return (
38
+ <View ref={containerRef} id="storybook-explorer-tree">
39
+ {/* {highlighted && <HighlightStyles {...highlighted} />} */}
40
+ {dataset.entries.map(([refId, ref]) => (
41
+ <Ref
42
+ {...ref}
43
+ key={refId}
44
+ isLoading={isLoading}
45
+ isBrowsing={isBrowsing}
46
+ selectedStoryId={selected?.refId === ref.id ? selected.storyId : null}
47
+ setSelection={setSelection}
48
+ // highlightedRef={highlightedRef}
49
+ // setHighlighted={setHighlighted}
50
+ />
51
+ ))}
52
+ </View>
53
+ );
54
+ });
@@ -0,0 +1,11 @@
1
+ import { TouchableOpacity } from 'react-native';
2
+ import { Button, ButtonProps } from './Button';
3
+ import { forwardRef } from 'react';
4
+
5
+ export const IconButton = forwardRef<TouchableOpacity, ButtonProps>(
6
+ ({ padding = 'small', variant = 'ghost', ...props }, ref) => {
7
+ return <Button padding={padding} variant={variant} ref={ref} {...props} />;
8
+ }
9
+ );
10
+
11
+ IconButton.displayName = 'IconButton';
@@ -0,0 +1,38 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Layout } from './Layout';
4
+ // import { mockDataset } from './mockdata';
5
+ // import { index } from './mockdata.large';
6
+ import { mockDataset } from './mockdata';
7
+ // import { GestureHandlerRootView } from 'react-native-gesture-handler';
8
+ // import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
9
+ import { Text } from 'react-native';
10
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
11
+ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
12
+
13
+ const meta = {
14
+ title: 'components/Layout',
15
+ component: Layout,
16
+ decorators: [
17
+ (Story) => (
18
+ <GestureHandlerRootView style={{ flex: 1 }}>
19
+ <BottomSheetModalProvider>
20
+ <Story />
21
+ </BottomSheetModalProvider>
22
+ </GestureHandlerRootView>
23
+ ),
24
+ ],
25
+ } satisfies Meta<typeof Layout>;
26
+
27
+ export default meta;
28
+
29
+ type Story = StoryObj<typeof meta>;
30
+
31
+ export const Basic: Story = {
32
+ args: {
33
+ children: <Text>Testing</Text>,
34
+ //@ts-ignore
35
+ storyHash: mockDataset.withRoot,
36
+ storyId: 'emails-buildnotification--with-changes',
37
+ },
38
+ };