diff --git a/packages/manager/.changeset/pr-12623-fixed-1754501467029.md b/packages/manager/.changeset/pr-12623-fixed-1754501467029.md new file mode 100644 index 00000000000..e9f8cf73653 --- /dev/null +++ b/packages/manager/.changeset/pr-12623-fixed-1754501467029.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Wrong stackScriptID used when clicking Deploy New Linode during an active search ([#12623](https://github.com/linode/manager/pull/12623)) diff --git a/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx b/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx index f568d261c30..5e888f935ee 100644 --- a/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx +++ b/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx @@ -75,14 +75,30 @@ export const DebouncedSearchTextField = React.memo( const [textFieldValue, setTextFieldValue] = React.useState(''); - // Memoize the debounced onChange handler to prevent unnecessary re-creations. - const debouncedOnChange = React.useMemo( - () => - debounce(debounceTime ?? 400, (e) => { + const debouncedRef = React.useRef>(null); + + React.useEffect(() => { + // Cancel any pending call from a previous instance. + debouncedRef.current?.cancel(); + + debouncedRef.current = debounce( + debounceTime ?? 400, + (e: React.ChangeEvent) => { onSearch(e.target.value); setTextFieldValue(e.target.value); - }), - [debounceTime, onSearch] + } + ); + + return () => { + debouncedRef.current?.cancel(); + }; + }, [debounceTime, onSearch]); + + const handleChange = React.useCallback( + (e: React.ChangeEvent) => { + debouncedRef.current?.(e); + }, + [] ); // Synchronize the internal state with the prop value when the value prop changes. @@ -99,7 +115,7 @@ export const DebouncedSearchTextField = React.memo( defaultValue={defaultValue} hideLabel={hideLabel} label={label} - onChange={debouncedOnChange} + onChange={handleChange} placeholder={placeholder || 'Filter by query'} slotProps={{ input: { @@ -110,13 +126,12 @@ export const DebouncedSearchTextField = React.memo( { + debouncedRef.current?.cancel(); setTextFieldValue(''); onSearch(''); }} size="small" - sx={{ - padding: 0, - }} + sx={{ padding: 0 }} > diff --git a/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.test.tsx b/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.test.tsx index 021bfdba2de..1d16187a1ba 100644 --- a/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.test.tsx +++ b/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.test.tsx @@ -128,6 +128,11 @@ describe('AlertResources component tests', () => { }); // search with invalid text and a region await userEvent.type(searchInput, 'dummy'); + // Wait for debounced search to complete and filter results + await waitFor(() => { + expect(queryByText(linodes[0].label)).not.toBeInTheDocument(); + expect(queryByText(linodes[1].label)).not.toBeInTheDocument(); + }); await userEvent.click(getByPlaceholderText('Select Regions')); await userEvent.click(getByTestId(regions[0].id)); await userEvent.click(getByRole('button', { name: 'Close' }));