diff --git a/.changeset/close-menu-on-story-select.md b/.changeset/close-menu-on-story-select.md new file mode 100644 index 0000000000..9dcb4ac371 --- /dev/null +++ b/.changeset/close-menu-on-story-select.md @@ -0,0 +1,6 @@ +--- +'@storybook/react-native-ui': patch +'@storybook/react-native-ui-lite': patch +--- + +The mobile story list now closes once you pick a story from it, as it does in Storybook for the web, so the selected story is visible straight away. Expanding or collapsing a group still leaves the list open. diff --git a/packages/react-native-ui-lite/src/Layout.tsx b/packages/react-native-ui-lite/src/Layout.tsx index af6128aea5..209eaa713d 100644 --- a/packages/react-native-ui-lite/src/Layout.tsx +++ b/packages/react-native-ui-lite/src/Layout.tsx @@ -260,6 +260,10 @@ export const Layout = ({ const channel = addons.getChannel(); channel.emit(SET_CURRENT_STORY, { storyId: newStoryId }); + + // As on Storybook for the web, the mobile story list closes once a story is chosen from it. + // The drawer isn't rendered on desktop, where the ref stays empty. + mobileMenuDrawerRef.current?.setMobileMenuOpen(false); }, []); return ( diff --git a/packages/react-native-ui/jest.config.js b/packages/react-native-ui/jest.config.js index c226d1506b..c9e77be656 100644 --- a/packages/react-native-ui/jest.config.js +++ b/packages/react-native-ui/jest.config.js @@ -7,7 +7,7 @@ const config = { resolver: 'react-native-reanimated/jest/resolver', ...(process.env.JEST_CACHE_DIRECTORY && { cacheDirectory: process.env.JEST_CACHE_DIRECTORY }), transformIgnorePatterns: [ - 'node_modules/(?!((jest-)?react-native|@react-native(-community)?|@gorhom|@storybook))', + 'node_modules/(?!((jest-)?react-native|@react-native(-community)?|@gorhom|@storybook|storybook))', ], }; module.exports = config; diff --git a/packages/react-native-ui/src/Layout.test.tsx b/packages/react-native-ui/src/Layout.test.tsx new file mode 100644 index 0000000000..0b5876aff6 --- /dev/null +++ b/packages/react-native-ui/src/Layout.test.tsx @@ -0,0 +1,69 @@ +import { theme, ThemeProvider } from '@storybook/react-native-theming'; +import { LayoutProvider, StorageProvider } from '@storybook/react-native-ui-common'; +import { fireEvent, render, screen } from '@testing-library/react-native'; +import { Text } from 'react-native'; +import { SET_CURRENT_STORY } from 'storybook/internal/core-events'; +import type { API_IndexHash } from 'storybook/internal/types'; +import { Layout } from './Layout'; +import { mockDataset } from './mockdata'; + +const mockEmit = jest.fn(); + +jest.mock('storybook/manager-api', () => ({ + addons: { + getChannel: () => ({ emit: mockEmit }), + getElements: () => ({}), + }, +})); + +const mockSetMobileMenuOpen = jest.fn(); + +// Record what the layout asks of the drawer. Like the real one, which keeps its content mounted +// while closed, this renders the story list straight away. +jest.mock('./MobileMenuDrawer', () => { + const React = require('react'); + + return { + MobileMenuDrawer: React.forwardRef(function MobileMenuDrawer({ children }, ref) { + React.useImperativeHandle(ref, () => ({ setMobileMenuOpen: mockSetMobileMenuOpen })); + + return children; + }), + }; +}); + +const storage = { + getItem: async () => null, + setItem: async () => {}, +}; + +const renderUI = async () => { + await render( + + + + + Story content + + + + + ); +}; + +describe('Layout', () => { + it('closes the mobile story list once a story is selected, but not when a group is toggled', async () => { + await renderUI(); + + await fireEvent.press(screen.getByText('Group 1'), { preventDefault: jest.fn() }); + + expect(mockEmit).not.toHaveBeenCalled(); + expect(mockSetMobileMenuOpen).not.toHaveBeenCalled(); + + await fireEvent.press(screen.getByText('Child B1')); + + expect(mockEmit).toHaveBeenCalledWith(SET_CURRENT_STORY, { storyId: 'group-1--child-b1' }); + expect(mockSetMobileMenuOpen).toHaveBeenCalledTimes(1); + expect(mockSetMobileMenuOpen).toHaveBeenCalledWith(false); + }); +}); diff --git a/packages/react-native-ui/src/Layout.tsx b/packages/react-native-ui/src/Layout.tsx index 56fbc7ac70..affe3ee98c 100644 --- a/packages/react-native-ui/src/Layout.tsx +++ b/packages/react-native-ui/src/Layout.tsx @@ -234,6 +234,10 @@ export const Layout = ({ const channel = addons.getChannel(); channel.emit(SET_CURRENT_STORY, { storyId: newStoryId }); + + // As on Storybook for the web, the mobile story list closes once a story is chosen from it. + // The drawer isn't rendered on desktop, where the ref stays empty. + mobileMenuDrawerRef.current?.setMobileMenuOpen(false); }, []); return (