diff --git a/apps/storybook/stories/Review5555.stories.tsx b/apps/storybook/stories/Review5555.stories.tsx new file mode 100644 index 00000000000..14521261931 --- /dev/null +++ b/apps/storybook/stories/Review5555.stories.tsx @@ -0,0 +1,312 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +import {Profiler, useMemo, useState} from 'react'; +import type {Meta, StoryObj} from '@storybook/react'; +import { + BaseTypeahead, + Typeahead, + type SearchableItem, + type SearchSource, +} from '@astryxdesign/core/Typeahead'; +import {Tokenizer} from '@astryxdesign/core/Tokenizer'; + +interface ReviewHarness { + calls: Record; + commits: Record; + pending: Record void>; + tokenRenders: number; + resetCommits: () => void; + resetTokenRenders: () => void; + settleAll: () => void; +} + +declare global { + interface Window { + __review5555?: ReviewHarness; + } +} + +const items: SearchableItem[] = [ + {id: 'apple', label: 'Apple'}, + {id: 'banana', label: 'Banana'}, + {id: 'alice', label: 'Alice Johnson'}, + {id: 'bob', label: 'Bob Smith'}, +]; + +function harness(): ReviewHarness { + if (window.__review5555 == null) { + const state: ReviewHarness = { + calls: {}, + commits: {}, + pending: {}, + tokenRenders: 0, + resetCommits() { + state.commits = {}; + }, + resetTokenRenders() { + state.tokenRenders = 0; + }, + settleAll() { + const pending = Object.values(state.pending); + state.pending = {}; + pending.forEach(resolve => resolve()); + }, + }; + window.__review5555 = state; + } + return window.__review5555; +} + +function deferredSource(key: string): SearchSource { + return { + search: query => { + const state = harness(); + state.calls[key] = (state.calls[key] ?? 0) + 1; + return new Promise(resolve => { + state.pending[key] = () => + resolve( + items.filter(item => + item.label.toLowerCase().includes(query.toLowerCase()), + ), + ); + }); + }, + bootstrap: () => Promise.resolve(items), + }; +} + +function countCommit(id: string): void { + const state = harness(); + state.commits[id] = (state.commits[id] ?? 0) + 1; +} + +function RenderScaleCase({count}: {count: number}) { + const source = useMemo(() => deferredSource(`scale-${count}`), [count]); + const [value, setValue] = useState( + Array.from({length: count}, (_, index) => ({ + id: `selected-${index}`, + label: `Selected ${index + 1}`, + })), + ); + return ( +
+ { + harness().tokenRenders += 1; + return {item.label}; + }} + debounceMs={0} + /> +
+ ); +} + +const meta: Meta = { + title: 'Review/PR5555', + parameters: {layout: 'padded'}, +}; + +export default meta; +type Story = StoryObj; + +export const BusyPair: Story = { + render: () => { + const typeaheadSource = useMemo( + () => deferredSource('typeahead'), + [], + ); + const tokenizerSource = useMemo( + () => deferredSource('tokenizer'), + [], + ); + const baseSource = useMemo(() => deferredSource('base'), []); + const [typeaheadValue, setTypeaheadValue] = + useState(items[0]); + const [tokenizerValue, setTokenizerValue] = useState([ + items[2], + ]); + + return ( +
+
+ countCommit('typeahead')}> + + +
+ +
+ countCommit('tokenizer')}> + 1 selected} + debounceMs={0} + /> + +
+ +
+ Direct BaseTypeahead consumer + countCommit('base')}> + {}} + ariaLabelledBy="review-base-label" + debounceMs={0} + /> + +
+
+ ); + }, +}; + +export const RenderCounts: Story = { + render: () => { + const typeaheadSource = useMemo( + () => deferredSource('count-typeahead'), + [], + ); + const tokenizerSource = useMemo( + () => deferredSource('count-tokenizer'), + [], + ); + const [typeaheadValue, setTypeaheadValue] = + useState(null); + const [tokenizerValue, setTokenizerValue] = useState([]); + + return ( +
+
+ countCommit('typeahead')}> + + +
+
+ countCommit('tokenizer')}> + + +
+
+ ); + }, +}; + +export const RenderScale1: Story = { + render: () => , +}; + +export const RenderScale6: Story = { + render: () => , +}; + +export const RenderScale20: Story = { + render: () => , +}; + +export const ThresholdCreate: Story = { + render: () => { + const typeaheadSource = useMemo( + () => deferredSource('threshold-typeahead'), + [], + ); + const tokenizerSource = useMemo( + () => deferredSource('threshold-tokenizer'), + [], + ); + const [typeaheadValue, setTypeaheadValue] = + useState(null); + const [tokens, setTokens] = useState([]); + + return ( +
+
+ +
+
+ +
+
+ ); + }, +};