@storybook/react-native-ui 8.5.5-alpha.2 → 8.5.5-alpha.4

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": "8.5.5-alpha.2",
3
+ "version": "8.5.5-alpha.4",
4
4
  "description": "ui components for react native storybook",
5
5
  "keywords": [
6
6
  "react",
@@ -60,7 +60,7 @@
60
60
  "dependencies": {
61
61
  "@storybook/core": "^8.5.1",
62
62
  "@storybook/react": "^8.5.1",
63
- "@storybook/react-native-theming": "^8.5.5-alpha.2",
63
+ "@storybook/react-native-theming": "^8.5.5-alpha.4",
64
64
  "fuse.js": "^7.0.0",
65
65
  "memoizerific": "^1.11.3",
66
66
  "polished": "^4.3.1",
@@ -81,5 +81,5 @@
81
81
  "publishConfig": {
82
82
  "access": "public"
83
83
  },
84
- "gitHead": "08215ac1fa39a7c2105d03fc861e4f6a138030c8"
84
+ "gitHead": "1d84b44bf483612cdb27f8bf9b36dfd931776511"
85
85
  }
package/src/Layout.tsx CHANGED
@@ -19,6 +19,7 @@ import { CloseFullscreenIcon } from './icon/CloseFullscreenIcon';
19
19
  import { FullscreenIcon } from './icon/FullscreenIcon';
20
20
  import { MenuIcon } from './icon/MenuIcon';
21
21
  import { useStyle } from './util/useStyle';
22
+ import { SelectedNodeProvider } from './SelectedNodeProvider';
22
23
 
23
24
  const desktopLogoContainer = {
24
25
  flexDirection: 'row',
@@ -249,23 +250,25 @@ export const Layout = ({
249
250
  ) : null}
250
251
 
251
252
  {isDesktop ? null : (
252
- <MobileMenuDrawer ref={mobileMenuDrawerRef}>
253
- <View style={mobileMenuDrawerContentStyle}>
254
- <StorybookLogo theme={theme} />
255
- </View>
256
-
257
- <Sidebar
258
- extra={placeholderArray}
259
- previewInitialized
260
- indexError={undefined}
261
- refs={placeholderObject}
262
- setSelection={setSelection}
263
- status={placeholderObject}
264
- index={storyHash}
265
- storyId={story?.id}
266
- refId={DEFAULT_REF_ID}
267
- />
268
- </MobileMenuDrawer>
253
+ <SelectedNodeProvider>
254
+ <MobileMenuDrawer ref={mobileMenuDrawerRef}>
255
+ <View style={mobileMenuDrawerContentStyle}>
256
+ <StorybookLogo theme={theme} />
257
+ </View>
258
+
259
+ <Sidebar
260
+ extra={placeholderArray}
261
+ previewInitialized
262
+ indexError={undefined}
263
+ refs={placeholderObject}
264
+ setSelection={setSelection}
265
+ status={placeholderObject}
266
+ index={storyHash}
267
+ storyId={story?.id}
268
+ refId={DEFAULT_REF_ID}
269
+ />
270
+ </MobileMenuDrawer>
271
+ </SelectedNodeProvider>
269
272
  )}
270
273
 
271
274
  {isDesktop ? null : <MobileAddonsPanel ref={addonPanelRef} storyId={story?.id} />}
@@ -3,11 +3,12 @@ import BottomSheet, {
3
3
  BottomSheetBackdropProps,
4
4
  BottomSheetScrollView,
5
5
  } from '@gorhom/bottom-sheet';
6
- import { ReactNode, forwardRef, memo, useImperativeHandle, useMemo, useRef } from 'react';
6
+ import { useTheme } from '@storybook/react-native-theming';
7
+ import { forwardRef, memo, ReactNode, useImperativeHandle, useMemo, useRef } from 'react';
7
8
  import { Keyboard } from 'react-native';
8
9
  import { useReducedMotion } from 'react-native-reanimated';
9
10
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
10
- import { useTheme } from '@storybook/react-native-theming';
11
+ import { useSelectedNode } from './SelectedNodeProvider';
11
12
 
12
13
  interface MobileMenuDrawerProps {
13
14
  children: ReactNode | ReactNode[];
@@ -34,18 +35,18 @@ export const MobileMenuDrawer = memo(
34
35
  const reducedMotion = useReducedMotion();
35
36
  const insets = useSafeAreaInsets();
36
37
  const theme = useTheme();
37
-
38
38
  const menuBottomSheetRef = useRef<BottomSheet>(null);
39
+ const { scrollToSelectedNode, scrollRef } = useSelectedNode();
39
40
 
40
41
  useImperativeHandle(ref, () => ({
41
42
  setMobileMenuOpen: (open: boolean) => {
42
43
  if (open) {
43
- // menuBottomSheetRef.current?.present();
44
44
  menuBottomSheetRef.current?.snapToIndex(1);
45
+
46
+ scrollToSelectedNode();
45
47
  } else {
46
48
  Keyboard.dismiss();
47
49
 
48
- // menuBottomSheetRef.current?.dismiss();
49
50
  menuBottomSheetRef.current?.close();
50
51
  }
51
52
  },
@@ -82,6 +83,7 @@ export const MobileMenuDrawer = memo(
82
83
  handleIndicatorStyle={handleIndicatorStyle}
83
84
  >
84
85
  <BottomSheetScrollView
86
+ ref={scrollRef}
85
87
  keyboardShouldPersistTaps="handled"
86
88
  contentContainerStyle={contentContainerStyle}
87
89
  >
package/src/Search.tsx CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  } from './types';
19
19
  import { getGroupStatus, getHighestStatus } from './util/status';
20
20
  import { searchItem } from './util/tree';
21
+ import { useSelectedNode } from './SelectedNodeProvider';
21
22
 
22
23
  const DEFAULT_MAX_SEARCH_RESULTS = 50;
23
24
 
@@ -111,6 +112,7 @@ export const Search = React.memo<{
111
112
  const [isOpen, setIsOpen] = useState(false);
112
113
  const [allComponents, showAllComponents] = useState(false);
113
114
  const { isMobile } = useLayout();
115
+ const { scrollToSelectedNode } = useSelectedNode();
114
116
 
115
117
  const selectStory = useCallback(
116
118
  (id: string, refId: string) => {
@@ -121,8 +123,10 @@ export const Search = React.memo<{
121
123
  setIsOpen(false);
122
124
 
123
125
  showAllComponents(false);
126
+
127
+ scrollToSelectedNode();
124
128
  },
125
- [setSelection]
129
+ [scrollToSelectedNode, setSelection]
126
130
  );
127
131
 
128
132
  const getItemProps: GetSearchItemProps = useCallback(
@@ -0,0 +1,58 @@
1
+ import { BottomSheetScrollViewMethods } from '@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/types';
2
+ import type { FC, PropsWithChildren } from 'react';
3
+ import { createContext, useCallback, useContext, useRef } from 'react';
4
+ import type { View } from 'react-native';
5
+
6
+ type SelectedNodeContextType = {
7
+ nodeRef: React.RefObject<View>;
8
+ setNodeRef: (node: View | null) => void;
9
+ scrollToSelectedNode: () => void;
10
+ scrollRef: React.RefObject<BottomSheetScrollViewMethods>;
11
+ };
12
+
13
+ const SelectedNodeContext = createContext<SelectedNodeContextType>({
14
+ nodeRef: { current: null },
15
+ setNodeRef: () => {},
16
+ scrollToSelectedNode: () => {},
17
+ scrollRef: null,
18
+ });
19
+
20
+ export const SelectedNodeProvider: FC<PropsWithChildren> = ({ children }) => {
21
+ const nodeRef = useRef<View | null>(null);
22
+
23
+ const setNodeRef = useCallback((node: View | null) => {
24
+ nodeRef.current = node;
25
+ }, []);
26
+
27
+ const scrollRef = useRef<BottomSheetScrollViewMethods>(null);
28
+
29
+ const scrollToSelectedNode = useCallback(() => {
30
+ // maybe later we can improve on this to not use setTimeout but right now it seems like the simplest solution
31
+ setTimeout(() => {
32
+ if (nodeRef?.current && scrollRef?.current) {
33
+ // im just not sure if older versions would error here,
34
+ // since measure layout probably changed since new arch
35
+ try {
36
+ nodeRef.current.measureLayout?.(scrollRef.current as any, (_x, y) => {
37
+ scrollRef.current?.scrollTo({ y: y - 100, animated: true });
38
+ });
39
+ } catch (error) {}
40
+ }
41
+ }, 500);
42
+ }, []);
43
+
44
+ return (
45
+ <SelectedNodeContext.Provider
46
+ value={{
47
+ nodeRef,
48
+ setNodeRef,
49
+ scrollToSelectedNode,
50
+ scrollRef,
51
+ }}
52
+ >
53
+ {children}
54
+ </SelectedNodeContext.Provider>
55
+ );
56
+ };
57
+
58
+ export const useSelectedNode = () => useContext(SelectedNodeContext);
package/src/Tree.tsx CHANGED
@@ -18,6 +18,7 @@ import type { ExpandAction, ExpandedState } from './hooks/useExpanded';
18
18
  import { useExpanded } from './hooks/useExpanded';
19
19
  import { getGroupStatus, statusMapping } from './util/status';
20
20
  import { createId, getAncestorIds, getDescendantIds, isStoryHoistable } from './util/tree';
21
+ import { useSelectedNode } from './SelectedNodeProvider';
21
22
 
22
23
  interface NodeProps {
23
24
  item: Item;
@@ -52,6 +53,17 @@ export const Node = React.memo<NodeProps>(function Node({
52
53
  setExpanded,
53
54
  onSelectStoryId,
54
55
  }) {
56
+ const { setNodeRef } = useSelectedNode();
57
+
58
+ const setRef = useCallback(
59
+ (node: View | null) => {
60
+ if (isSelected && node) {
61
+ setNodeRef(node);
62
+ }
63
+ },
64
+ [isSelected, setNodeRef]
65
+ );
66
+
55
67
  if (!isDisplayed) {
56
68
  return null;
57
69
  }
@@ -59,11 +71,10 @@ export const Node = React.memo<NodeProps>(function Node({
59
71
  const id = createId(item.id, refId);
60
72
 
61
73
  if (item.type === 'story') {
62
- const LeafNode = StoryNode;
63
-
64
74
  return (
65
75
  <LeafNodeStyleWrapper>
66
- <LeafNode
76
+ <StoryNode
77
+ ref={setRef}
67
78
  selected={isSelected}
68
79
  key={id}
69
80
  id={id}
@@ -73,7 +84,7 @@ export const Node = React.memo<NodeProps>(function Node({
73
84
  }}
74
85
  >
75
86
  {(item.renderLabel as (i: typeof item) => React.ReactNode)?.(item) || item.name}
76
- </LeafNode>
87
+ </StoryNode>
77
88
  </LeafNodeStyleWrapper>
78
89
  );
79
90
  }
package/src/TreeNode.tsx CHANGED
@@ -1,11 +1,12 @@
1
1
  import { styled, useTheme } from '@storybook/react-native-theming';
2
2
  import { ComponentIcon } from './icon/ComponentIcon';
3
3
  import { GroupIcon } from './icon/GroupIcon';
4
-
5
4
  import { StoryIcon } from './icon/StoryIcon';
6
5
  import { CollapseIcon } from './icon/CollapseIcon';
7
- import React, { ComponentProps, FC, useMemo } from 'react';
6
+ import { ComponentProps, FC, forwardRef, useMemo } from 'react';
8
7
  import { transparentize } from 'polished';
8
+ import { View } from 'react-native';
9
+ import React from 'react';
9
10
 
10
11
  export interface NodeProps {
11
12
  children: React.ReactNode | React.ReactNode[];
@@ -131,22 +132,26 @@ export const ComponentNode: FC<ComponentProps<typeof BranchNode>> = React.memo(
131
132
  }
132
133
  );
133
134
 
134
- export const StoryNode: FC<ComponentProps<typeof LeafNode>> = React.memo(function StoryNode({
135
- children,
136
- ...props
137
- }) {
138
- const theme = useTheme();
135
+ export const StoryNode = React.memo(
136
+ forwardRef<View, ComponentProps<typeof LeafNode>>(function StoryNode(
137
+ { children, ...props },
138
+ ref
139
+ ) {
140
+ const theme = useTheme();
139
141
 
140
- const color = useMemo(() => {
141
- return props.selected ? theme.color.lightest : theme.color.seafoam;
142
- }, [props.selected, theme.color.lightest, theme.color.seafoam]);
142
+ const color = useMemo(() => {
143
+ return props.selected ? theme.color.lightest : theme.color.seafoam;
144
+ }, [props.selected, theme.color.lightest, theme.color.seafoam]);
143
145
 
144
- return (
145
- <LeafNode {...props}>
146
- <Wrapper>
147
- <StoryIcon width={14} height={14} color={color} />
148
- </Wrapper>
149
- <LeafNodeText selected={props.selected}>{children}</LeafNodeText>
150
- </LeafNode>
151
- );
152
- });
146
+ return (
147
+ <LeafNode {...props} ref={ref}>
148
+ <Wrapper>
149
+ <StoryIcon width={14} height={14} color={color} />
150
+ </Wrapper>
151
+ <LeafNodeText selected={props.selected}>{children}</LeafNodeText>
152
+ </LeafNode>
153
+ );
154
+ })
155
+ );
156
+
157
+ StoryNode.displayName = 'StoryNode';