diff --git a/app/react-native/src/hooks.tsx b/app/react-native/src/hooks.tsx index 0f52d96f7e..8f09c2b223 100644 --- a/app/react-native/src/hooks.tsx +++ b/app/react-native/src/hooks.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import React, { useMemo } from 'react'; import type { StoryContext } from '@storybook/csf'; import { atom, useAtom, useAtomValue, useSetAtom, getDefaultStore } from 'jotai'; import { useTheme as useEmotionTheme } from 'emotion-theming'; @@ -63,7 +63,8 @@ export function useTheme() { } /** - * A boolean atom creator for an atom that can only be toggled between true/false. + * A boolean atom creator for an atom that can only be toggled between + * true/false. * * @see {@link https://jotai.org/docs/recipes/atom-creators#atomwithtoggle} */ @@ -78,7 +79,8 @@ export function atomWithToggle(initialValue?: boolean) { const isUIVisibleAtom = atomWithToggle(true); /** - * Hook that retrieves the current state, and a setter, for the `isUIVisible` atom. + * Hook that retrieves the current state, and a setter, for the `isUIVisible` + * atom. */ export function useIsUIVisible() { return useAtom(isUIVisibleAtom); @@ -111,3 +113,26 @@ export function syncExternalUI({ isUIVisible, isSplitPanelVisible }: SyncExterna jotaiStore.set(isSplitPanelVisibleAtom, isSplitPanelVisible); } } + +const selectedAddonAtom = atom(undefined as string); + +/** + * Hook that manages the state for the currently selected addon. + * + * This value persists across stories, so that the same addon will be selected + * when switching stories. + */ +export function useSelectedAddon(initialValue?: string) { + const result = useAtom(selectedAddonAtom); + const [_, set] = result; + React.useEffect(() => { + const jotaiStore = getDefaultStore(); + // Only apply the initial value once, and only if the atom doesn't have a + // value yet. + if (jotaiStore.get(selectedAddonAtom) === undefined) { + set(initialValue); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return result; +} diff --git a/app/react-native/src/preview/components/OnDeviceUI/addons/Addons.tsx b/app/react-native/src/preview/components/OnDeviceUI/addons/Addons.tsx index 85b94c644c..9c07d68cf2 100644 --- a/app/react-native/src/preview/components/OnDeviceUI/addons/Addons.tsx +++ b/app/react-native/src/preview/components/OnDeviceUI/addons/Addons.tsx @@ -1,6 +1,7 @@ -import React, { useState } from 'react'; +import React from 'react'; import { Text } from 'react-native'; import { addons } from '@storybook/addons'; +import { useSelectedAddon } from '../../../../hooks'; import AddonsList from './List'; import AddonWrapper from './Wrapper'; import { Box } from '../../Shared/layout'; @@ -11,7 +12,7 @@ interface AddonsProps { const Addons = ({ active }: AddonsProps) => { const panels = addons.getElements('panel'); - const [addonSelected, setAddonSelected] = useState(Object.keys(panels)[0] || null); + const [addonSelected, setAddonSelected] = useSelectedAddon(Object.keys(panels)[0]); if (Object.keys(panels).length === 0) { return (