mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 07:53:09 +00:00
refactor(lists): share the remembered lists sub-tab between desktop and phone
useListsSubTab remembers whether the packing or the to-do sub tab was open, for ListsContainer and MTripShell. The desktop restores any stored value, the phone restores todo and otherwise falls back to packing.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { useListsSubTab } from './useListsSubTab';
|
||||
|
||||
// FE-HOOK-LISTSTAB-001 to FE-HOOK-LISTSTAB-006
|
||||
|
||||
describe('useListsSubTab', () => {
|
||||
it('FE-HOOK-LISTSTAB-001: opens on packing when nothing is stored for the trip', () => {
|
||||
const { result } = renderHook(() => useListsSubTab(7));
|
||||
expect(result.current[0]).toBe('packing');
|
||||
});
|
||||
|
||||
it('FE-HOOK-LISTSTAB-002: reopens the sub-tab stored for that trip, not another one', () => {
|
||||
sessionStorage.setItem('trip-lists-subtab-7', 'todo');
|
||||
sessionStorage.setItem('trip-lists-subtab-8', 'packing');
|
||||
expect(renderHook(() => useListsSubTab(7)).result.current[0]).toBe('todo');
|
||||
expect(renderHook(() => useListsSubTab(8)).result.current[0]).toBe('packing');
|
||||
expect(renderHook(() => useListsSubTab(7, { onlyKnownTabs: true })).result.current[0]).toBe('todo');
|
||||
});
|
||||
|
||||
it('FE-HOOK-LISTSTAB-003: the desktop takes whatever is stored, the phone only a tab it knows', () => {
|
||||
sessionStorage.setItem('trip-lists-subtab-7', 'files');
|
||||
expect(renderHook(() => useListsSubTab(7)).result.current[0]).toBe('files');
|
||||
expect(renderHook(() => useListsSubTab(7, { onlyKnownTabs: true })).result.current[0]).toBe('packing');
|
||||
});
|
||||
|
||||
it('FE-HOOK-LISTSTAB-004: an empty stored value falls back to packing on both', () => {
|
||||
sessionStorage.setItem('trip-lists-subtab-7', '');
|
||||
expect(renderHook(() => useListsSubTab(7)).result.current[0]).toBe('packing');
|
||||
expect(renderHook(() => useListsSubTab(7, { onlyKnownTabs: true })).result.current[0]).toBe('packing');
|
||||
});
|
||||
|
||||
it('FE-HOOK-LISTSTAB-005: switching shows the new tab and stores it under the trip key', () => {
|
||||
const { result } = renderHook(() => useListsSubTab(7, { onlyKnownTabs: true }));
|
||||
act(() => result.current[1]('todo'));
|
||||
expect(result.current[0]).toBe('todo');
|
||||
expect(sessionStorage.getItem('trip-lists-subtab-7')).toBe('todo');
|
||||
act(() => result.current[1]('packing'));
|
||||
expect(result.current[0]).toBe('packing');
|
||||
expect(sessionStorage.getItem('trip-lists-subtab-7')).toBe('packing');
|
||||
});
|
||||
|
||||
it('FE-HOOK-LISTSTAB-006: a choice made on one surface is what the other opens with', () => {
|
||||
const desktop = renderHook(() => useListsSubTab(3));
|
||||
act(() => desktop.result.current[1]('todo'));
|
||||
expect(renderHook(() => useListsSubTab(3, { onlyKnownTabs: true })).result.current[0]).toBe('todo');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
/** The two lists the Lists tab switches between. */
|
||||
export type ListsSubTab = 'packing' | 'todo';
|
||||
|
||||
const storageKey = (tripId: number) => `trip-lists-subtab-${tripId}`;
|
||||
|
||||
/**
|
||||
* The Lists tab's sub-tab, remembered per trip for the browser session. Both the
|
||||
* desktop container and the phone shell read and write the same key. The phone
|
||||
* shell only takes a stored value it knows (`onlyKnownTabs`); the desktop one
|
||||
* takes whatever is stored, as it always has.
|
||||
*/
|
||||
export function useListsSubTab(tripId: number, { onlyKnownTabs = false }: { onlyKnownTabs?: boolean } = {}) {
|
||||
const [subTab, setSubTabState] = useState<ListsSubTab>(() => {
|
||||
const saved = sessionStorage.getItem(storageKey(tripId));
|
||||
if (onlyKnownTabs) return saved === 'todo' ? 'todo' : 'packing';
|
||||
return (saved as ListsSubTab) || 'packing';
|
||||
});
|
||||
const setSubTab = (tab: ListsSubTab) => {
|
||||
setSubTabState(tab);
|
||||
sessionStorage.setItem(storageKey(tripId), tab);
|
||||
};
|
||||
return [subTab, setSubTab] as const;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { badgeLabel, distanceBadge } from './roadtrip/stageBadges'
|
||||
import type { CorridorReach } from '../../../components/Roadtrip/corridorSearchModel'
|
||||
import { useSettingsStore } from '../../../store/settingsStore'
|
||||
import { useAuthStore } from '../../../store/authStore'
|
||||
import { useListsSubTab, type ListsSubTab } from '../../../hooks/useListsSubTab'
|
||||
import { canManageDocSync } from '../../../components/Files/docsync/useDocSync'
|
||||
import { useDocSyncOffered } from '../../../components/Files/docsync/useDocSyncOffered'
|
||||
import type { Day, Trip } from '../../../types'
|
||||
@@ -46,7 +47,7 @@ export type MTripView = 'plan' | 'map'
|
||||
export type MTripMode = 'go' | 'edit' | 'browse'
|
||||
/** The road trip tab's own two halves: the chain, or the same map showing the stage. */
|
||||
export type MRtView = 'list' | 'map'
|
||||
export type MTripListsTab = 'packing' | 'todo'
|
||||
export type MTripListsTab = ListsSubTab
|
||||
export type MTripCollabTab = 'chat' | 'notes' | 'links' | 'polls'
|
||||
|
||||
/**
|
||||
@@ -221,10 +222,7 @@ export default function MTripShell({
|
||||
const [mode, setMode] = useState<MTripMode>('go')
|
||||
const [browseFromEdit, setBrowseFromEdit] = useState(false)
|
||||
const [sheet, setSheet] = useState<MTripSheetState | null>(null)
|
||||
const [listsTab, setListsTabState] = useState<MTripListsTab>(() => {
|
||||
const saved = sessionStorage.getItem(`trip-lists-subtab-${tripId}`)
|
||||
return saved === 'todo' ? 'todo' : 'packing'
|
||||
})
|
||||
const [listsTab, setListsTab] = useListsSubTab(tripId, { onlyKnownTabs: true })
|
||||
const [collabTab, setCollabTab] = useState<MTripCollabTab>('chat')
|
||||
const [transportsCompact, setTransportsCompact] = useState(false)
|
||||
const [bookingsCompact, setBookingsCompact] = useState(false)
|
||||
@@ -386,11 +384,6 @@ export default function MTripShell({
|
||||
|
||||
const mapFront = (trTab === 'plan' && view === 'map') || (trTab === 'roadtrip' && rtView === 'map')
|
||||
|
||||
const setListsTab = (tab: MTripListsTab) => {
|
||||
setListsTabState(tab)
|
||||
sessionStorage.setItem(`trip-lists-subtab-${tripId}`, tab)
|
||||
}
|
||||
|
||||
const openSheet = (id: string, payload?: unknown) => setSheet({ id, payload })
|
||||
const closeSheet = () => setSheet(null)
|
||||
|
||||
|
||||
@@ -6,15 +6,13 @@ import { useTranslation } from '../../i18n'
|
||||
import { useAuthStore } from '../../store/authStore'
|
||||
import { useTripStore } from '../../store/tripStore'
|
||||
import { useCanDo } from '../../store/permissionsStore'
|
||||
import { useListsSubTab } from '../../hooks/useListsSubTab'
|
||||
import type { PackingItem, TodoItem } from '../../types'
|
||||
import { PackingListPanel, TodoListPanel } from './plannerLazy'
|
||||
import { LazyPanel } from './LazyPanel'
|
||||
|
||||
export function ListsContainer({ tripId, packingItems, todoItems }: Readonly<{ tripId: number; packingItems: PackingItem[]; todoItems: TodoItem[] }>) {
|
||||
const [subTab, setSubTab] = useState<'packing' | 'todo'>(() => {
|
||||
return (sessionStorage.getItem(`trip-lists-subtab-${tripId}`) as 'packing' | 'todo') || 'packing'
|
||||
})
|
||||
const setSubTabPersist = (tab: 'packing' | 'todo') => { setSubTab(tab); sessionStorage.setItem(`trip-lists-subtab-${tripId}`, tab) }
|
||||
const [subTab, setSubTabPersist] = useListsSubTab(tripId)
|
||||
const [importPackingSignal, setImportPackingSignal] = useState(0)
|
||||
const [addCategorySignal, setAddCategorySignal] = useState(0)
|
||||
const [saveTemplateSignal, setSaveTemplateSignal] = useState(0)
|
||||
|
||||
Reference in New Issue
Block a user