mirror of
https://github.com/vxcontrol/pentagi.git
synced 2026-09-21 01:35:57 +00:00
fix(detail-navigation): re-seed the sheet search mirror from the controller on open
DetailNavigationSheet keeps a local input mirror (localQuery) seeded once from
controller.searchQuery — a deliberate perf decouple (92a9e59) so typing in the
sheet doesn't re-render the whole detail page. But in the controller's documented
controlled mode (a page-level search box that owns searchQuery), an external
change made while the sheet was closed left the mirror stale: reopening the sheet
showed the old text.
Re-seed the mirror from the controller on each open, folded into the existing
open-transition reconciliation. The sheet is modal, so searchQuery can only
change externally while closed — on-open re-seeding suffices and never clobbers
in-progress typing. Add a controlled-mode test that reproduces the stale input
(red before, green after).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
47c1ea108b
commit
8ca83137a1
@@ -505,3 +505,65 @@ describe('DetailNavigationSheet — virtualization (>100 items)', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Controlled mode (the documented "page-level search box" use case): the parent
|
||||
// owns searchQuery, so the sheet's local input mirror must re-seed from the
|
||||
// controller when it changes outside the sheet.
|
||||
const ControlledSheet = ({ open, query }: { open: boolean; query: string }) => {
|
||||
const nav = useDetailNavigation<Item>({
|
||||
currentId: 'c',
|
||||
getHref,
|
||||
getLabel,
|
||||
getSearchableText,
|
||||
items: ITEMS,
|
||||
onOpenChange: () => {},
|
||||
onSearchQueryChange: () => {},
|
||||
open,
|
||||
searchDebounceMs: 0,
|
||||
searchQuery: query,
|
||||
});
|
||||
|
||||
return (
|
||||
<DetailNavigationSheet<Item>
|
||||
controller={nav}
|
||||
sheetTitle="Items"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderControlled = (props: { open: boolean; query: string }) =>
|
||||
render(<ControlledSheet {...props} />, {
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<MemoryRouter initialEntries={['/items/c']}>
|
||||
<TooltipProvider>
|
||||
<Routes>
|
||||
<Route
|
||||
element={<>{children}</>}
|
||||
path="/items/:id"
|
||||
/>
|
||||
</Routes>
|
||||
</TooltipProvider>
|
||||
</MemoryRouter>
|
||||
),
|
||||
});
|
||||
|
||||
describe('DetailNavigationSheet — controlled search resync', () => {
|
||||
it('reflects an external searchQuery change made while the sheet was closed', () => {
|
||||
const { rerender } = renderControlled({ open: false, query: '' });
|
||||
|
||||
rerender(
|
||||
<ControlledSheet
|
||||
open={false}
|
||||
query="Bravo"
|
||||
/>,
|
||||
);
|
||||
rerender(
|
||||
<ControlledSheet
|
||||
open
|
||||
query="Bravo"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole<HTMLInputElement>('textbox').value).toBe('Bravo');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,6 +140,14 @@ export function DetailNavigationSheet<T extends { id: string }>({
|
||||
|
||||
if (lastOpen !== open) {
|
||||
setLastOpen(open);
|
||||
|
||||
// Re-seed the input mirror from the controller on open so a controlled
|
||||
// searchQuery changed from outside isn't shown stale. The sheet is modal,
|
||||
// so searchQuery only changes externally while closed — on-open re-seed
|
||||
// suffices and never clobbers in-progress typing.
|
||||
if (open) {
|
||||
setLocalQuery(searchQuery);
|
||||
}
|
||||
}
|
||||
|
||||
if (desiredFocusId !== focusedId) {
|
||||
|
||||
Reference in New Issue
Block a user