@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/react-native-ui",
3
- "version": "9.0.0-alpha.1",
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.3",
62
- "@storybook/react-native-theming": "^9.0.0-alpha.1",
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": "ec42eef3ebdf389ebf9cf669d425712db99f9e37"
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, status }]) => {
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
- const statusValue =
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
- {isMobile ? (
231
+ {isBottomSheet ? (
239
232
  <BottomSheetInput
240
233
  ref={inputRef as any} // TODO find solution for this
241
234
  onChangeText={setInputValue}
@@ -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 { types } from 'storybook/internal/manager-api';
4
- import type { StoryObj, Meta } from '@storybook/react';
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;
@@ -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={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 { API_StatusState, API_StatusValue } from 'storybook/internal/types';
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] & { status?: API_StatusState };
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?: API_StatusValue;
41
+ status?: StatusValue;
42
42
  showAll?: () => void;
43
43
  };
44
44
 
@@ -1,5 +1,9 @@
1
1
  import { useMemo, type ReactElement } from 'react';
2
- import type { API_HashEntry, API_StatusState, API_StatusValue } from 'storybook/internal/types';
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 statusPriority: API_StatusValue[] = ['unknown', 'pending', 'success', 'warn', 'error'];
33
- export const statusMapping: Record<API_StatusValue, [ReactElement | null, string | null]> = {
34
- unknown: [null, null],
35
- pending: [<LoadingIcons key="icon" />, 'currentColor'],
36
- success: [<SmallIcons key="icon" color="green" />, 'currentColor'],
37
- warn: [<SmallIcons key="icon" color="orange" />, '#A15C20'],
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, status) => (statuses.includes(status) ? status : 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
- status: API_StatusState
53
- ): Record<string, API_StatusValue> {
54
- return Object.values(collapsedData).reduce<Record<string, API_StatusValue>>((acc, item) => {
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 = getHighestStatus(
61
- leafs.flatMap((story) => Object.values(status?.[story.id] || {})).map((s) => s.status)
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) {