@storybook/react-native-ui 9.0.0-alpha.1 → 9.0.0-alpha.10
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.
- package/dist/index.d.ts +3 -5
- package/dist/index.js +3735 -22818
- package/package.json +6 -5
- package/src/Layout.tsx +1 -4
- package/src/Search.tsx +8 -15
- package/src/Sidebar.stories.tsx +4 -40
- package/src/Sidebar.tsx +1 -7
- package/src/Tree.stories.tsx +37 -0
- package/src/Tree.tsx +1 -5
- package/src/types.ts +3 -3
- package/src/util/status.tsx +34 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/react-native-ui",
|
|
3
|
-
"version": "9.0.0-alpha.
|
|
3
|
+
"version": "9.0.0-alpha.10",
|
|
4
4
|
"description": "ui components for react native storybook",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"typescript": "^5.3.3"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@storybook/react": "9.0.0-alpha.
|
|
62
|
-
"@storybook/react-native-theming": "^9.0.0-alpha.
|
|
61
|
+
"@storybook/react": "9.0.0-alpha.16",
|
|
62
|
+
"@storybook/react-native-theming": "^9.0.0-alpha.10",
|
|
63
63
|
"fuse.js": "^7.0.0",
|
|
64
64
|
"memoizerific": "^1.11.3",
|
|
65
65
|
"polished": "^4.3.1",
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"react-native-gesture-handler": ">=2",
|
|
73
73
|
"react-native-reanimated": ">=3",
|
|
74
74
|
"react-native-safe-area-context": "*",
|
|
75
|
-
"react-native-svg": ">=14"
|
|
75
|
+
"react-native-svg": ">=14",
|
|
76
|
+
"storybook": "9.0.0-alpha.16"
|
|
76
77
|
},
|
|
77
78
|
"engines": {
|
|
78
79
|
"node": ">=18.0.0"
|
|
@@ -80,5 +81,5 @@
|
|
|
80
81
|
"publishConfig": {
|
|
81
82
|
"access": "public"
|
|
82
83
|
},
|
|
83
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "19e0e4e5908e11d42e00dc508d58a0a4dc9abd3c"
|
|
84
85
|
}
|
package/src/Layout.tsx
CHANGED
|
@@ -30,14 +30,13 @@ const desktopLogoContainer = {
|
|
|
30
30
|
paddingRight: 10,
|
|
31
31
|
justifyContent: 'space-between',
|
|
32
32
|
} satisfies ViewStyle;
|
|
33
|
+
|
|
33
34
|
const contentContainerStyle = { flex: 1, overflow: 'hidden' } satisfies ViewStyle;
|
|
34
35
|
|
|
35
36
|
const mobileContentStyle = { flex: 1, overflow: 'hidden' } satisfies ViewStyle;
|
|
36
37
|
|
|
37
38
|
const placeholderObject = {};
|
|
38
39
|
|
|
39
|
-
const placeholderArray = [];
|
|
40
|
-
|
|
41
40
|
const iconFloatRightStyle = { marginLeft: 'auto' } satisfies ViewStyle;
|
|
42
41
|
|
|
43
42
|
const navButtonStyle = { flexShrink: 1 } satisfies ViewStyle;
|
|
@@ -177,7 +176,6 @@ export const Layout = ({
|
|
|
177
176
|
</View>
|
|
178
177
|
|
|
179
178
|
<Sidebar
|
|
180
|
-
extra={placeholderArray}
|
|
181
179
|
previewInitialized
|
|
182
180
|
indexError={undefined}
|
|
183
181
|
refs={placeholderObject}
|
|
@@ -257,7 +255,6 @@ export const Layout = ({
|
|
|
257
255
|
</View>
|
|
258
256
|
|
|
259
257
|
<Sidebar
|
|
260
|
-
extra={placeholderArray}
|
|
261
258
|
previewInitialized
|
|
262
259
|
indexError={undefined}
|
|
263
260
|
refs={placeholderObject}
|
package/src/Search.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { BottomSheetTextInput } from '@gorhom/bottom-sheet';
|
|
1
|
+
import { BottomSheetTextInput, useBottomSheetInternal } from '@gorhom/bottom-sheet';
|
|
2
2
|
import { styled } from '@storybook/react-native-theming';
|
|
3
3
|
import type { IFuseOptions } from 'fuse.js';
|
|
4
4
|
import Fuse from 'fuse.js';
|
|
5
|
-
import React, { useCallback, useDeferredValue, useRef, useState } from 'react';
|
|
5
|
+
import React, { useCallback, useContext, useDeferredValue, useRef, useState } from 'react';
|
|
6
6
|
import { Platform, TextInput, View } from 'react-native';
|
|
7
7
|
import { CloseIcon } from './icon/CloseIcon';
|
|
8
8
|
import { SearchIcon } from './icon/SearchIcon';
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
type SearchResult,
|
|
17
17
|
type Selection,
|
|
18
18
|
} from './types';
|
|
19
|
-
import { getGroupStatus, getHighestStatus } from './util/status';
|
|
20
19
|
import { searchItem } from './util/tree';
|
|
21
20
|
import { useSelectedNode } from './SelectedNodeProvider';
|
|
22
21
|
|
|
@@ -106,6 +105,9 @@ export const Search = React.memo<{
|
|
|
106
105
|
getLastViewed: () => Selection[];
|
|
107
106
|
initialQuery?: string;
|
|
108
107
|
}>(function Search({ children, dataset, setSelection, getLastViewed, initialQuery = '' }) {
|
|
108
|
+
const context = useBottomSheetInternal(true);
|
|
109
|
+
const isBottomSheet = context !== null;
|
|
110
|
+
|
|
109
111
|
const inputRef = useRef<TextInput>(null);
|
|
110
112
|
const [inputValue, setInputValue] = useState(initialQuery);
|
|
111
113
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -153,20 +155,11 @@ export const Search = React.memo<{
|
|
|
153
155
|
);
|
|
154
156
|
|
|
155
157
|
const makeFuse = useCallback(() => {
|
|
156
|
-
const list = dataset.entries.reduce<SearchItem[]>((acc, [refId, { index
|
|
157
|
-
const groupStatus = getGroupStatus(index || {}, status);
|
|
158
|
-
|
|
158
|
+
const list = dataset.entries.reduce<SearchItem[]>((acc, [refId, { index }]) => {
|
|
159
159
|
if (index) {
|
|
160
160
|
acc.push(
|
|
161
161
|
...Object.values(index).map((item) => {
|
|
162
|
-
|
|
163
|
-
status && status[item.id]
|
|
164
|
-
? getHighestStatus(Object.values(status[item.id] || {}).map((s) => s.status))
|
|
165
|
-
: null;
|
|
166
|
-
return {
|
|
167
|
-
...searchItem(item, dataset.hash[refId]),
|
|
168
|
-
status: statusValue || groupStatus[item.id] || null,
|
|
169
|
-
};
|
|
162
|
+
return searchItem(item, dataset.hash[refId]);
|
|
170
163
|
})
|
|
171
164
|
);
|
|
172
165
|
}
|
|
@@ -235,7 +228,7 @@ export const Search = React.memo<{
|
|
|
235
228
|
<SearchIcon />
|
|
236
229
|
</SearchIconWrapper>
|
|
237
230
|
|
|
238
|
-
{
|
|
231
|
+
{isBottomSheet ? (
|
|
239
232
|
<BottomSheetInput
|
|
240
233
|
ref={inputRef as any} // TODO find solution for this
|
|
241
234
|
onChangeText={setInputValue}
|
package/src/Sidebar.stories.tsx
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import type { IndexHash, State } from 'storybook/internal/manager-api';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import type { Addon_SidebarTopType } from 'storybook/internal/types';
|
|
4
|
+
import { Button } from './Button';
|
|
5
|
+
import { LayoutProvider } from './LayoutProvider';
|
|
6
6
|
import { Sidebar } from './Sidebar';
|
|
7
|
+
import { DEFAULT_REF_ID } from './constants';
|
|
7
8
|
import { mockDataset } from './mockdata';
|
|
8
9
|
import type { RefType } from './types';
|
|
9
|
-
import { LayoutProvider } from './LayoutProvider';
|
|
10
|
-
import { Button } from './Button';
|
|
11
|
-
import { IconButton } from './IconButton';
|
|
12
|
-
import { FaceHappyIcon } from './icon/FaceHappyIcon';
|
|
13
|
-
import { DEFAULT_REF_ID } from './constants';
|
|
14
10
|
|
|
15
11
|
const index = mockDataset.withRoot as IndexHash;
|
|
16
12
|
const storyId = 'root-1-child-a2--grandchild-a1-1';
|
|
@@ -22,7 +18,6 @@ const meta = {
|
|
|
22
18
|
parameters: { layout: 'fullscreen' },
|
|
23
19
|
args: {
|
|
24
20
|
previewInitialized: true,
|
|
25
|
-
extra: [] as Addon_SidebarTopType[],
|
|
26
21
|
index: index,
|
|
27
22
|
storyId,
|
|
28
23
|
refId: DEFAULT_REF_ID,
|
|
@@ -159,37 +154,6 @@ export const Searching: Story = {
|
|
|
159
154
|
parameters: { chromatic: { delay: 2200 } },
|
|
160
155
|
};
|
|
161
156
|
|
|
162
|
-
export const Bottom: Story = {
|
|
163
|
-
args: {
|
|
164
|
-
bottom: [
|
|
165
|
-
{
|
|
166
|
-
id: '1',
|
|
167
|
-
type: types.experimental_SIDEBAR_BOTTOM,
|
|
168
|
-
render: () => (
|
|
169
|
-
<Button>
|
|
170
|
-
<FaceHappyIcon />
|
|
171
|
-
Custom addon A
|
|
172
|
-
</Button>
|
|
173
|
-
),
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
id: '2',
|
|
177
|
-
type: types.experimental_SIDEBAR_BOTTOM,
|
|
178
|
-
render: () => <Button text="Custom addon B" Icon={FaceHappyIcon} />,
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
id: '3',
|
|
182
|
-
type: types.experimental_SIDEBAR_BOTTOM,
|
|
183
|
-
render: () => (
|
|
184
|
-
<IconButton>
|
|
185
|
-
<FaceHappyIcon />
|
|
186
|
-
</IconButton>
|
|
187
|
-
),
|
|
188
|
-
},
|
|
189
|
-
],
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
|
|
193
157
|
/**
|
|
194
158
|
* Given the following sequence of events:
|
|
195
159
|
* 1. Story is selected at the top of the sidebar
|
package/src/Sidebar.tsx
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { styled } from '@storybook/react-native-theming';
|
|
3
3
|
import type { State } from 'storybook/internal/manager-api';
|
|
4
|
-
import type {
|
|
5
|
-
Addon_SidebarBottomType,
|
|
6
|
-
Addon_SidebarTopType,
|
|
7
|
-
API_LoadedRefData,
|
|
8
|
-
} from 'storybook/internal/types';
|
|
4
|
+
import type { API_LoadedRefData } from 'storybook/internal/types';
|
|
9
5
|
import { Explorer } from './Explorer';
|
|
10
6
|
import { Search } from './Search';
|
|
11
7
|
import { SearchResults } from './SearchResults';
|
|
@@ -74,8 +70,6 @@ export const useCombination = (
|
|
|
74
70
|
export interface SidebarProps extends API_LoadedRefData {
|
|
75
71
|
refs: State['refs'];
|
|
76
72
|
status: State['status'];
|
|
77
|
-
extra: Addon_SidebarTopType[];
|
|
78
|
-
bottom?: Addon_SidebarBottomType[];
|
|
79
73
|
storyId?: string;
|
|
80
74
|
refId?: string;
|
|
81
75
|
menuHighlighted?: boolean;
|
package/src/Tree.stories.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
|
|
2
2
|
import type { ComponentEntry, IndexHash } from 'storybook/internal/manager-api';
|
|
3
3
|
import type { StoryObj, Meta } from '@storybook/react';
|
|
4
4
|
import { Tree } from './Tree';
|
|
5
|
+
import type { Dataset } from './types';
|
|
5
6
|
import { index } from './mockdata.large';
|
|
6
7
|
import { DEFAULT_REF_ID } from './constants';
|
|
7
8
|
import { ScrollView, Text } from 'react-native';
|
|
@@ -138,3 +139,39 @@ export const SingleStoryComponents: Story = {
|
|
|
138
139
|
);
|
|
139
140
|
},
|
|
140
141
|
};
|
|
142
|
+
|
|
143
|
+
const dataWithStoryName: Dataset = {
|
|
144
|
+
images: {
|
|
145
|
+
name: 'Testing storyNames support',
|
|
146
|
+
id: 'images',
|
|
147
|
+
depth: 0,
|
|
148
|
+
children: [],
|
|
149
|
+
type: 'component',
|
|
150
|
+
tags: [],
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const WithStoryNames: Story = {
|
|
155
|
+
name: 'Story with a storyName',
|
|
156
|
+
args: {
|
|
157
|
+
docsMode: false,
|
|
158
|
+
isBrowsing: true,
|
|
159
|
+
isMain: true,
|
|
160
|
+
refId: DEFAULT_REF_ID,
|
|
161
|
+
data: undefined,
|
|
162
|
+
onSelectStoryId: () => {},
|
|
163
|
+
selectedStoryId: 'some-component',
|
|
164
|
+
status: undefined,
|
|
165
|
+
},
|
|
166
|
+
render: function Render(args) {
|
|
167
|
+
const [selectedId, setSelectedId] = useState(storyId);
|
|
168
|
+
return (
|
|
169
|
+
<Tree
|
|
170
|
+
{...args}
|
|
171
|
+
data={dataWithStoryName}
|
|
172
|
+
selectedStoryId={selectedId}
|
|
173
|
+
onSelectStoryId={setSelectedId}
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
};
|
package/src/Tree.tsx
CHANGED
|
@@ -299,8 +299,6 @@ export const Tree = React.memo<{
|
|
|
299
299
|
onSelectStoryId,
|
|
300
300
|
});
|
|
301
301
|
|
|
302
|
-
const groupStatus = useMemo(() => getGroupStatus(collapsedData, status), [collapsedData, status]);
|
|
303
|
-
|
|
304
302
|
const treeItems = useMemo(() => {
|
|
305
303
|
return collapsedItems.map((itemId) => {
|
|
306
304
|
const item = collapsedData[itemId];
|
|
@@ -330,7 +328,6 @@ export const Tree = React.memo<{
|
|
|
330
328
|
}
|
|
331
329
|
|
|
332
330
|
const isDisplayed = !item.parent || ancestry[itemId].every((a: string) => expanded[a]);
|
|
333
|
-
const color = groupStatus[itemId] ? statusMapping[groupStatus[itemId]][1] : null;
|
|
334
331
|
|
|
335
332
|
return (
|
|
336
333
|
<Node
|
|
@@ -338,7 +335,7 @@ export const Tree = React.memo<{
|
|
|
338
335
|
item={item}
|
|
339
336
|
status={status?.[itemId]}
|
|
340
337
|
refId={refId}
|
|
341
|
-
color={
|
|
338
|
+
color={null}
|
|
342
339
|
docsMode={docsMode}
|
|
343
340
|
isOrphan={orphanIds.some((oid) => itemId === oid || itemId.startsWith(`${oid}-`))}
|
|
344
341
|
isDisplayed={isDisplayed}
|
|
@@ -356,7 +353,6 @@ export const Tree = React.memo<{
|
|
|
356
353
|
docsMode,
|
|
357
354
|
expandableDescendants,
|
|
358
355
|
expanded,
|
|
359
|
-
groupStatus,
|
|
360
356
|
onSelectStoryId,
|
|
361
357
|
orphanIds,
|
|
362
358
|
refId,
|
package/src/types.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { StoriesHash, State } from 'storybook/internal/manager-api';
|
|
2
|
-
import type {
|
|
2
|
+
import type { StatusValue, StatusesByStoryIdAndTypeId } from 'storybook/internal/types';
|
|
3
3
|
import * as Fuse from 'fuse.js';
|
|
4
4
|
import { PressableProps } from 'react-native';
|
|
5
5
|
|
|
6
6
|
export type Refs = State['refs'];
|
|
7
|
-
export type RefType = Refs[keyof Refs] & {
|
|
7
|
+
export type RefType = Refs[keyof Refs] & { allStatuses?: StatusesByStoryIdAndTypeId };
|
|
8
8
|
export type Item = StoriesHash[keyof StoriesHash];
|
|
9
9
|
export type Dataset = Record<string, Item>;
|
|
10
10
|
|
|
@@ -38,7 +38,7 @@ export interface ExpandType {
|
|
|
38
38
|
export type SearchItem = Item & {
|
|
39
39
|
refId: string;
|
|
40
40
|
path: string[];
|
|
41
|
-
status?:
|
|
41
|
+
status?: StatusValue;
|
|
42
42
|
showAll?: () => void;
|
|
43
43
|
};
|
|
44
44
|
|
package/src/util/status.tsx
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { useMemo, type ReactElement } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
API_HashEntry,
|
|
4
|
+
StatusValue,
|
|
5
|
+
StatusesByStoryIdAndTypeId,
|
|
6
|
+
} from 'storybook/internal/types';
|
|
3
7
|
|
|
4
8
|
import { useTheme } from '@storybook/react-native-theming';
|
|
5
9
|
|
|
@@ -29,19 +33,33 @@ function LoadingIcons(props: SvgProps) {
|
|
|
29
33
|
return <SmallIcons color={color} {...props} />;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
export const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
error: [<SmallIcons key="icon" color="red" />, 'brown'],
|
|
36
|
+
export const statusMapping: Record<StatusValue, [ReactElement | null, string | null]> = {
|
|
37
|
+
['status-value:unknown']: [null, null],
|
|
38
|
+
['status-value:pending']: [<LoadingIcons key="icon" />, 'currentColor'],
|
|
39
|
+
['status-value:success']: [<SmallIcons key="icon" color="green" />, 'currentColor'],
|
|
40
|
+
['status-value:warning']: [<SmallIcons key="icon" color="orange" />, '#A15C20'],
|
|
41
|
+
['status-value:error']: [<SmallIcons key="icon" color="red" />, 'brown'],
|
|
39
42
|
};
|
|
40
43
|
|
|
41
|
-
export const getHighestStatus = (statuses: API_StatusValue[]): API_StatusValue => {
|
|
44
|
+
// export const getHighestStatus = (statuses: API_StatusValue[]): API_StatusValue => {
|
|
45
|
+
// return statusPriority.reduce(
|
|
46
|
+
// (acc, status) => (statuses.includes(status) ? status : acc),
|
|
47
|
+
// 'unknown'
|
|
48
|
+
// );
|
|
49
|
+
// };
|
|
50
|
+
|
|
51
|
+
export const statusPriority: StatusValue[] = [
|
|
52
|
+
'status-value:unknown',
|
|
53
|
+
'status-value:pending',
|
|
54
|
+
'status-value:success',
|
|
55
|
+
'status-value:warning',
|
|
56
|
+
'status-value:error',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
export const getMostCriticalStatusValue = (statusValues: StatusValue[]): StatusValue => {
|
|
42
60
|
return statusPriority.reduce(
|
|
43
|
-
(acc,
|
|
44
|
-
'unknown'
|
|
61
|
+
(acc, value) => (statusValues.includes(value) ? value : acc),
|
|
62
|
+
'status-value:unknown'
|
|
45
63
|
);
|
|
46
64
|
};
|
|
47
65
|
|
|
@@ -49,16 +67,16 @@ export function getGroupStatus(
|
|
|
49
67
|
collapsedData: {
|
|
50
68
|
[x: string]: Partial<API_HashEntry>;
|
|
51
69
|
},
|
|
52
|
-
|
|
53
|
-
): Record<string,
|
|
54
|
-
return Object.values(collapsedData).reduce<Record<string,
|
|
70
|
+
allStatuses: StatusesByStoryIdAndTypeId
|
|
71
|
+
): Record<string, StatusValue> {
|
|
72
|
+
return Object.values(collapsedData).reduce<Record<string, StatusValue>>((acc, item) => {
|
|
55
73
|
if (item.type === 'group' || item.type === 'component') {
|
|
56
74
|
const leafs = getDescendantIds(collapsedData as any, item.id, false)
|
|
57
75
|
.map((id) => collapsedData[id])
|
|
58
76
|
.filter((i) => i.type === 'story');
|
|
59
77
|
|
|
60
|
-
const combinedStatus =
|
|
61
|
-
leafs.flatMap((story) => Object.values(
|
|
78
|
+
const combinedStatus = getMostCriticalStatusValue(
|
|
79
|
+
leafs.flatMap((story) => Object.values(allStatuses[story.id] || {})).map((s) => s.value)
|
|
62
80
|
);
|
|
63
81
|
|
|
64
82
|
if (combinedStatus) {
|