From 45e2acff9cbc7afbc5fa92e53decb58e2adcc69c Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 02:53:49 +0000 Subject: [PATCH 1/9] test(storybook): make Select tests portal-aware and less flaky - Query listbox/options from document.body for portaled menus - Wait for triggers with findBy* before interaction - Use role-based queries and verify combobox text updates - Fix custom region selection to use an existing option (California) Refs: ensure ARIA roles (combobox/listbox/option) are respected --- .vscode/settings.json | 10 +-- apps/docs/src/remix-hook-form/select.test.tsx | 67 +++++++++++-------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9052de7d..7e91eb70 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,11 +24,5 @@ "source.fixAll.biome": "explicit", "source.organizeImports.biome": "explicit" }, - "tailwindCSS.classAttributes": [ - "class", - "className", - "ngClass", - "class:list", - "wrapperClassName" - ] -} \ No newline at end of file + "tailwindCSS.classAttributes": ["class", "className", "ngClass", "class:list", "wrapperClassName"] +} diff --git a/apps/docs/src/remix-hook-form/select.test.tsx b/apps/docs/src/remix-hook-form/select.test.tsx index 271aa2f9..9d50df84 100644 --- a/apps/docs/src/remix-hook-form/select.test.tsx +++ b/apps/docs/src/remix-hook-form/select.test.tsx @@ -6,32 +6,34 @@ import { userEvent, within } from '@storybook/testing-library'; export const testUSStateSelection = async ({ canvasElement }: StoryContext) => { const canvas = within(canvasElement); - // Find and click the US state dropdown - const stateDropdown = canvas.getByLabelText('US State'); - await userEvent.click(stateDropdown); + // Wait for and click the US state trigger (combobox) + const stateTrigger = await canvas.findByLabelText('US State'); + await userEvent.click(stateTrigger); - // Select a state (e.g., California) - const californiaOption = await canvas.findByText('California'); + // Dropdown content is portaled; query from document.body + const listbox = await within(document.body).findByRole('listbox'); + const californiaOption = within(listbox).getByRole('option', { name: 'California' }); await userEvent.click(californiaOption); - // Verify the selection - expect(stateDropdown).toHaveTextContent('California'); + // Verify the trigger text updates + await expect(canvas.findByRole('combobox', { name: 'US State' })).resolves.toHaveTextContent('California'); }; // Test selecting a Canadian province export const testCanadaProvinceSelection = async ({ canvasElement }: StoryContext) => { const canvas = within(canvasElement); - // Find and click the Canada province dropdown - const provinceDropdown = canvas.getByLabelText('Canadian Province'); - await userEvent.click(provinceDropdown); + // Wait for and click the province trigger + const provinceTrigger = await canvas.findByLabelText('Canadian Province'); + await userEvent.click(provinceTrigger); - // Select a province (e.g., Ontario) - const ontarioOption = await canvas.findByText('Ontario'); + // Query in the portaled content + const listbox = await within(document.body).findByRole('listbox'); + const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); await userEvent.click(ontarioOption); - // Verify the selection - expect(provinceDropdown).toHaveTextContent('Ontario'); + // Verify the trigger text updates + await expect(canvas.findByRole('combobox', { name: 'Canadian Province' })).resolves.toHaveTextContent('Ontario'); }; // Test form submission @@ -39,22 +41,31 @@ export const testFormSubmission = async ({ canvasElement }: StoryContext) => { const canvas = within(canvasElement); // Select a state - const stateDropdown = canvas.getByLabelText('US State'); - await userEvent.click(stateDropdown); - const californiaOption = await canvas.findByText('California'); - await userEvent.click(californiaOption); + const stateTrigger = await canvas.findByLabelText('US State'); + await userEvent.click(stateTrigger); + { + const listbox = await within(document.body).findByRole('listbox'); + const californiaOption = within(listbox).getByRole('option', { name: 'California' }); + await userEvent.click(californiaOption); + } // Select a province - const provinceDropdown = canvas.getByLabelText('Canadian Province'); - await userEvent.click(provinceDropdown); - const ontarioOption = await canvas.findByText('Ontario'); - await userEvent.click(ontarioOption); - - // Select a custom region - const regionDropdown = canvas.getByLabelText('Custom Region'); - await userEvent.click(regionDropdown); - const customOption = await canvas.findByText('New York'); - await userEvent.click(customOption); + const provinceTrigger = await canvas.findByLabelText('Canadian Province'); + await userEvent.click(provinceTrigger); + { + const listbox = await within(document.body).findByRole('listbox'); + const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); + await userEvent.click(ontarioOption); + } + + // Select a custom region (use an option that exists in the story's options) + const regionTrigger = await canvas.findByLabelText('Custom Region'); + await userEvent.click(regionTrigger); + { + const listbox = await within(document.body).findByRole('listbox'); + const customOption = within(listbox).getByRole('option', { name: 'California' }); + await userEvent.click(customOption); + } // Submit the form const submitButton = canvas.getByRole('button', { name: 'Submit' }); From 0e0e960834240276a2d4df3b52915a4a10a7836a Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 03:03:48 +0000 Subject: [PATCH 2/9] Fix CreatableOption test: wait for component render before interaction - Replace getByLabelText with findByLabelText to wait for component render - Ensures test stability by avoiding race conditions on initial render - Follows same pattern as other fixed select tests in the PR --- apps/docs/src/remix-hook-form/select.stories.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/docs/src/remix-hook-form/select.stories.tsx b/apps/docs/src/remix-hook-form/select.stories.tsx index 9984d466..a157a5f3 100644 --- a/apps/docs/src/remix-hook-form/select.stories.tsx +++ b/apps/docs/src/remix-hook-form/select.stories.tsx @@ -508,7 +508,8 @@ export const CreatableOption: Story = { const canvas = within(canvasElement); await step('Create new option when no exact match', async () => { - const regionSelect = canvas.getByLabelText('Custom Region'); + // Wait for the component to render before interacting + const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); const listbox = await within(document.body).findByRole('listbox'); // The search input is outside the listbox container; query from the portal root @@ -529,7 +530,8 @@ export const CreatableOption: Story = { }); await step('No creatable when exact match exists', async () => { - const regionSelect = canvas.getByLabelText('Custom Region'); + // Wait for the component to render before interacting + const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); const listbox = await within(document.body).findByRole('listbox'); // The search input is outside the listbox container; query from the portal root From 1d37c023ffd0e56ae9a4fe08c2ca5d838487b08c Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 03:13:06 +0000 Subject: [PATCH 3/9] Fix Select component ARIA roles for Storybook tests - Add role='listbox' to CommandList component in Select - Add role='option' to all CommandItem components (regular options and creatable options) - This ensures proper ARIA semantics for screen readers and test queries - Fixes failing CreatableOption test that was looking for listbox role The issue was that the Select component was using cmdk library components without explicit ARIA roles, causing tests to fail when querying for role='listbox' and role='option' elements. --- packages/components/src/ui/select.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/components/src/ui/select.tsx b/packages/components/src/ui/select.tsx index 9766ddb4..9c69b4c5 100644 --- a/packages/components/src/ui/select.tsx +++ b/packages/components/src/ui/select.tsx @@ -162,7 +162,7 @@ export function Select({ /> )} - + No results. {options.map((option, index) => { @@ -185,6 +185,7 @@ export function Select({ }} value={option.label} id={`${listboxId}-option-${index}`} + role="option" {...commonProps} className={cn(itemClassName)} // Attach ref to CommandItem (even with asChild) so we can focus the selected item on open @@ -210,6 +211,7 @@ export function Select({ }} value={option.label} id={`${listboxId}-option-${index}`} + role="option" {...commonProps} className={cn( 'w-full text-left cursor-pointer select-none py-3 px-3 transition-colors duration-150 flex items-center gap-2 rounded', @@ -240,6 +242,7 @@ export function Select({ key={`__create__-${q}`} data-value={`__create__-${q}`} value={q} + role="option" onSelect={async () => { if (!onCreateOption) return; const created = await onCreateOption(q); From e60441c0a0079ffbd933ae485ea6fe94feb9f5d4 Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 03:21:07 +0000 Subject: [PATCH 4/9] Fix CreatableOption test: improve async handling for portaled dropdown - Replace setTimeout with proper await for listbox and search input elements - Use findByRole and findByPlaceholderText for better async handling - Ensures dropdown and search input are fully rendered before interaction - Fixes race condition that was causing 'Unable to find role=listbox' error Co-authored-by: Jake Ruesink --- apps/docs/src/remix-hook-form/select.stories.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/docs/src/remix-hook-form/select.stories.tsx b/apps/docs/src/remix-hook-form/select.stories.tsx index ddf823bd..c7e03597 100644 --- a/apps/docs/src/remix-hook-form/select.stories.tsx +++ b/apps/docs/src/remix-hook-form/select.stories.tsx @@ -511,11 +511,12 @@ export const CreatableOption: Story = { // Wait for the component to render before interacting const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); - // Add a small delay to ensure the dropdown has time to render - await new Promise((resolve) => setTimeout(resolve, 100)); + + // Wait for the dropdown to appear in the portal const listbox = await within(document.body).findByRole('listbox'); + // The search input is outside the listbox container; query from the portal root - const input = within(document.body).getByPlaceholderText('Search...'); + const input = await within(document.body).findByPlaceholderText('Search...'); await userEvent.click(input); await userEvent.clear(input); await userEvent.type(input, 'Atlantis'); @@ -535,11 +536,12 @@ export const CreatableOption: Story = { // Wait for the component to render before interacting const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); - // Add a small delay to ensure the dropdown has time to render - await new Promise((resolve) => setTimeout(resolve, 100)); + + // Wait for the dropdown to appear in the portal const listbox = await within(document.body).findByRole('listbox'); + // The search input is outside the listbox container; query from the portal root - const input = within(document.body).getByPlaceholderText('Search...'); + const input = await within(document.body).findByPlaceholderText('Search...'); await userEvent.click(input); await userEvent.clear(input); await userEvent.type(input, 'California'); From 3a4ae4c25467d1991e4347a2c72eba031fd6a2b6 Mon Sep 17 00:00:00 2001 From: Jake Ruesink Date: Sun, 21 Sep 2025 22:47:02 -0500 Subject: [PATCH 5/9] Fix: improve listbox retrieval in CreatableOption test Updated the CreatableOption test to wait for the dropdown to open before attempting to find the listbox role. This change replaces the previous timing delay with a timeout parameter, enhancing test reliability and reducing flakiness. --- apps/docs/src/remix-hook-form/select.stories.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/docs/src/remix-hook-form/select.stories.tsx b/apps/docs/src/remix-hook-form/select.stories.tsx index c7e03597..1c1296aa 100644 --- a/apps/docs/src/remix-hook-form/select.stories.tsx +++ b/apps/docs/src/remix-hook-form/select.stories.tsx @@ -511,10 +511,9 @@ export const CreatableOption: Story = { // Wait for the component to render before interacting const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); - - // Wait for the dropdown to appear in the portal - const listbox = await within(document.body).findByRole('listbox'); - + + // Wait for the dropdown to open and find the listbox + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); // The search input is outside the listbox container; query from the portal root const input = await within(document.body).findByPlaceholderText('Search...'); await userEvent.click(input); @@ -536,10 +535,9 @@ export const CreatableOption: Story = { // Wait for the component to render before interacting const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); - - // Wait for the dropdown to appear in the portal - const listbox = await within(document.body).findByRole('listbox'); - + + // Wait for the dropdown to open and find the listbox + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); // The search input is outside the listbox container; query from the portal root const input = await within(document.body).findByPlaceholderText('Search...'); await userEvent.click(input); From c3a00c5d2e1067e9d5b745ba4eafcb5e2ec5e8c6 Mon Sep 17 00:00:00 2001 From: Jake Ruesink Date: Sun, 21 Sep 2025 22:58:44 -0500 Subject: [PATCH 6/9] Fix: enhance listbox retrieval and validation in select stories Updated the select stories to include a timeout when retrieving the listbox role, ensuring that the dropdown has fully opened before interaction. Added assertions to verify the presence of specific options, improving test reliability and reducing flakiness across multiple story scenarios. --- .../src/remix-hook-form/select.stories.tsx | 67 +++++++++++++++---- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/apps/docs/src/remix-hook-form/select.stories.tsx b/apps/docs/src/remix-hook-form/select.stories.tsx index 1c1296aa..4542157f 100644 --- a/apps/docs/src/remix-hook-form/select.stories.tsx +++ b/apps/docs/src/remix-hook-form/select.stories.tsx @@ -273,9 +273,13 @@ export const USStateSelection: Story = { const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); - // Dropdown content renders in a portal; query via document.body roles - const listbox = await within(document.body).findByRole('listbox'); + // Wait for the dropdown to open and find the listbox with timeout + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); + + // Find and click the California option const californiaOption = within(listbox).getByRole('option', { name: 'California' }); + expect(californiaOption).toBeInTheDocument(); await userEvent.click(californiaOption); // Wait for the trigger text to update after portal selection @@ -301,9 +305,13 @@ export const CanadaProvinceSelection: Story = { const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); - // Query in portal content by role - const listbox = await within(document.body).findByRole('listbox'); + // Wait for the dropdown to open and find the listbox with timeout + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); + + // Find and click the Ontario option const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); + expect(ontarioOption).toBeInTheDocument(); await userEvent.click(ontarioOption); // Wait for the trigger text to update after portal selection @@ -329,8 +337,10 @@ export const FormSubmission: Story = { const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); { - const listbox = await within(document.body).findByRole('listbox'); + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); const californiaOption = within(listbox).getByRole('option', { name: 'California' }); + expect(californiaOption).toBeInTheDocument(); await userEvent.click(californiaOption); } @@ -338,8 +348,10 @@ export const FormSubmission: Story = { const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); { - const listbox = await within(document.body).findByRole('listbox'); + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); + expect(ontarioOption).toBeInTheDocument(); await userEvent.click(ontarioOption); } @@ -347,8 +359,10 @@ export const FormSubmission: Story = { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); { - const listbox = await within(document.body).findByRole('listbox'); + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); const customOption = within(listbox).getByRole('option', { name: 'California' }); + expect(customOption).toBeInTheDocument(); await userEvent.click(customOption); } }); @@ -407,8 +421,14 @@ export const SearchDisabled: Story = { await step('Open select and ensure no search input', async () => { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - const listbox = await within(document.body).findByRole('listbox'); - expect(within(listbox).queryByPlaceholderText('Search...')).not.toBeInTheDocument(); + + // Wait for the dropdown to open + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); + + // Verify no search input is present when searchable is disabled + const searchInput = within(listbox).queryByPlaceholderText('Search...'); + expect(searchInput).not.toBeInTheDocument(); }); }, }; @@ -454,8 +474,9 @@ export const CustomSearchPlaceholder: Story = { await step('Open select and see custom placeholder', async () => { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); + // The search input is rendered alongside the listbox in the portal, not inside the listbox itself. - const searchInput = await within(document.body).findByPlaceholderText('Type to filter…'); + const searchInput = await within(document.body).findByPlaceholderText('Type to filter…', {}, { timeout: 5000 }); expect(searchInput).toBeInTheDocument(); }); }, @@ -512,17 +533,28 @@ export const CreatableOption: Story = { const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); - // Wait for the dropdown to open and find the listbox + // Wait for the dropdown to open and find the listbox with more specific timeout and error handling const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + + // Verify the listbox is properly rendered + expect(listbox).toBeInTheDocument(); + expect(listbox).toHaveAttribute('role', 'listbox'); + // The search input is outside the listbox container; query from the portal root const input = await within(document.body).findByPlaceholderText('Search...'); + expect(input).toBeInTheDocument(); + await userEvent.click(input); await userEvent.clear(input); await userEvent.type(input, 'Atlantis'); - const createItem = await within(listbox).findByRole('option', { name: 'Select "Atlantis"' }); + // Wait for the creatable option to appear + const createItem = await within(listbox).findByRole('option', { name: 'Select "Atlantis"' }, { timeout: 2000 }); + expect(createItem).toBeInTheDocument(); + await userEvent.click(createItem); + // Verify the selection was applied await expect(canvas.findByRole('combobox', { name: 'Custom Region' })).resolves.toHaveTextContent('Atlantis'); // Submit and verify server received the created option value @@ -538,13 +570,22 @@ export const CreatableOption: Story = { // Wait for the dropdown to open and find the listbox const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); + // The search input is outside the listbox container; query from the portal root const input = await within(document.body).findByPlaceholderText('Search...'); + expect(input).toBeInTheDocument(); + await userEvent.click(input); await userEvent.clear(input); await userEvent.type(input, 'California'); - expect(within(listbox).queryByRole('option', { name: 'Select "California"' })).not.toBeInTheDocument(); + // Verify no creatable option appears when exact match exists + const createOption = within(listbox).queryByRole('option', { name: 'Select "California"' }); + expect(createOption).not.toBeInTheDocument(); + + // Close the dropdown + await userEvent.click(regionSelect); }); }, }; From d3321f98bc1636cedad8000280d8c7be315004a5 Mon Sep 17 00:00:00 2001 From: Jake Ruesink Date: Sun, 21 Sep 2025 23:04:13 -0500 Subject: [PATCH 7/9] Fix: enhance listbox interaction and error handling in select stories Updated select stories to improve listbox retrieval with fallback mechanisms for better reliability. Added error handling to ensure that dropdown options are accessible even if the initial listbox approach fails. This change enhances test stability and reduces flakiness across various scenarios. --- .../src/remix-hook-form/select.stories.tsx | 229 ++++++++++++------ 1 file changed, 161 insertions(+), 68 deletions(-) diff --git a/apps/docs/src/remix-hook-form/select.stories.tsx b/apps/docs/src/remix-hook-form/select.stories.tsx index 4542157f..5209d52b 100644 --- a/apps/docs/src/remix-hook-form/select.stories.tsx +++ b/apps/docs/src/remix-hook-form/select.stories.tsx @@ -210,37 +210,56 @@ const RegionSelectExample = () => { await userEvent.click(submitButton); // Verify validation error messages appear - await expect(canvas.findByText('Please select a state')).resolves.toBeInTheDocument(); - await expect(canvas.findByText('Please select a province')).resolves.toBeInTheDocument(); - await expect(canvas.findByText('Please select a region')).resolves.toBeInTheDocument(); + // Use getByText with fallback to findByText for better WebKit compatibility + expect(canvas.getByText('Please select a state')).toBeInTheDocument(); + expect(canvas.getByText('Please select a province')).toBeInTheDocument(); + expect(canvas.getByText('Please select a region')).toBeInTheDocument(); }); await step('Test successful submission', async () => { // Select a state const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); - { - const listbox = await within(document.body).findByRole('listbox'); + + try { + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); const californiaOption = within(listbox).getByRole('option', { name: 'California' }); await userEvent.click(californiaOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const californiaOption = canvas.getByRole('option', { name: 'California' }); + await userEvent.click(californiaOption); } // Select a province const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); - { - const listbox = await within(document.body).findByRole('listbox'); + + try { + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); await userEvent.click(ontarioOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const ontarioOption = canvas.getByRole('option', { name: 'Ontario' }); + await userEvent.click(ontarioOption); } // Select a custom region const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - { - const listbox = await within(document.body).findByRole('listbox'); + + try { + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); const customOption = within(listbox).getByRole('option', { name: 'California' }); await userEvent.click(customOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const customOption = canvas.getByRole('option', { name: 'California' }); + await userEvent.click(customOption); } // Submit @@ -273,14 +292,21 @@ export const USStateSelection: Story = { const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); - // Wait for the dropdown to open and find the listbox with timeout - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); + try { + // Wait for the dropdown to open and find the listbox with timeout + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // Find and click the California option - const californiaOption = within(listbox).getByRole('option', { name: 'California' }); - expect(californiaOption).toBeInTheDocument(); - await userEvent.click(californiaOption); + // Find and click the California option + const californiaOption = within(listbox).getByRole('option', { name: 'California' }); + expect(californiaOption).toBeInTheDocument(); + await userEvent.click(californiaOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const californiaOption = canvas.getByRole('option', { name: 'California' }); + await userEvent.click(californiaOption); + } // Wait for the trigger text to update after portal selection await expect(canvas.findByRole('combobox', { name: 'US State' })).resolves.toHaveTextContent('California'); @@ -305,14 +331,21 @@ export const CanadaProvinceSelection: Story = { const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); - // Wait for the dropdown to open and find the listbox with timeout - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); + try { + // Wait for the dropdown to open and find the listbox with timeout + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // Find and click the Ontario option - const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); - expect(ontarioOption).toBeInTheDocument(); - await userEvent.click(ontarioOption); + // Find and click the Ontario option + const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); + expect(ontarioOption).toBeInTheDocument(); + await userEvent.click(ontarioOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const ontarioOption = canvas.getByRole('option', { name: 'Ontario' }); + await userEvent.click(ontarioOption); + } // Wait for the trigger text to update after portal selection await expect(canvas.findByRole('combobox', { name: 'Canadian Province' })).resolves.toHaveTextContent('Ontario'); @@ -336,34 +369,52 @@ export const FormSubmission: Story = { // Select a state const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); - { + + try { const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); expect(listbox).toBeInTheDocument(); const californiaOption = within(listbox).getByRole('option', { name: 'California' }); expect(californiaOption).toBeInTheDocument(); await userEvent.click(californiaOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const californiaOption = canvas.getByRole('option', { name: 'California' }); + await userEvent.click(californiaOption); } // Select a province const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); - { + + try { const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); expect(listbox).toBeInTheDocument(); const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); expect(ontarioOption).toBeInTheDocument(); await userEvent.click(ontarioOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const ontarioOption = canvas.getByRole('option', { name: 'Ontario' }); + await userEvent.click(ontarioOption); } // Select a custom region const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - { + + try { const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); expect(listbox).toBeInTheDocument(); const customOption = within(listbox).getByRole('option', { name: 'California' }); expect(customOption).toBeInTheDocument(); await userEvent.click(customOption); + } catch (error) { + // Fallback: try clicking the option directly if listbox approach fails + console.warn('Listbox approach failed, trying direct option selection', error); + const customOption = canvas.getByRole('option', { name: 'California' }); + await userEvent.click(customOption); } }); @@ -422,13 +473,18 @@ export const SearchDisabled: Story = { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - // Wait for the dropdown to open - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); + try { + // Wait for the dropdown to open + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // Verify no search input is present when searchable is disabled - const searchInput = within(listbox).queryByPlaceholderText('Search...'); - expect(searchInput).not.toBeInTheDocument(); + // Verify no search input is present when searchable is disabled + const searchInput = within(listbox).queryByPlaceholderText('Search...'); + expect(searchInput).not.toBeInTheDocument(); + } catch (error) { + // If listbox approach fails, that's okay - we're testing search disabled + console.warn('Listbox approach failed for search disabled test', error); + } }); }, }; @@ -475,9 +531,16 @@ export const CustomSearchPlaceholder: Story = { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - // The search input is rendered alongside the listbox in the portal, not inside the listbox itself. - const searchInput = await within(document.body).findByPlaceholderText('Type to filter…', {}, { timeout: 5000 }); - expect(searchInput).toBeInTheDocument(); + try { + // The search input is rendered alongside the listbox in the portal, not inside the listbox itself. + const searchInput = await within(document.body).findByPlaceholderText('Type to filter…', {}, { timeout: 5000 }); + expect(searchInput).toBeInTheDocument(); + } catch (error) { + // Fallback: try to find the search input within the canvas + console.warn('Portal search input approach failed, trying canvas search', error); + const searchInput = canvas.getByPlaceholderText('Type to filter…'); + expect(searchInput).toBeInTheDocument(); + } }); }, }; @@ -533,26 +596,41 @@ export const CreatableOption: Story = { const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); - // Wait for the dropdown to open and find the listbox with more specific timeout and error handling - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - - // Verify the listbox is properly rendered - expect(listbox).toBeInTheDocument(); - expect(listbox).toHaveAttribute('role', 'listbox'); - - // The search input is outside the listbox container; query from the portal root - const input = await within(document.body).findByPlaceholderText('Search...'); - expect(input).toBeInTheDocument(); - - await userEvent.click(input); - await userEvent.clear(input); - await userEvent.type(input, 'Atlantis'); - - // Wait for the creatable option to appear - const createItem = await within(listbox).findByRole('option', { name: 'Select "Atlantis"' }, { timeout: 2000 }); - expect(createItem).toBeInTheDocument(); + try { + // Wait for the dropdown to open and find the listbox with more specific timeout and error handling + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - await userEvent.click(createItem); + // Verify the listbox is properly rendered + expect(listbox).toBeInTheDocument(); + expect(listbox).toHaveAttribute('role', 'listbox'); + + // The search input is outside the listbox container; query from the portal root + const input = await within(document.body).findByPlaceholderText('Search...'); + expect(input).toBeInTheDocument(); + + await userEvent.click(input); + await userEvent.clear(input); + await userEvent.type(input, 'Atlantis'); + + // Wait for the creatable option to appear + const createItem = await within(listbox).findByRole('option', { name: 'Select "Atlantis"' }, { timeout: 2000 }); + expect(createItem).toBeInTheDocument(); + + await userEvent.click(createItem); + } catch (error) { + // Fallback: try to find and interact with elements within the canvas + console.warn('Portal approach failed, trying canvas-based interaction', error); + + // Try to find and type in the search input within the canvas + const input = canvas.getByPlaceholderText('Search...'); + await userEvent.click(input); + await userEvent.clear(input); + await userEvent.type(input, 'Atlantis'); + + // Try to find and click the create option + const createItem = canvas.getByRole('option', { name: 'Select "Atlantis"' }); + await userEvent.click(createItem); + } // Verify the selection was applied await expect(canvas.findByRole('combobox', { name: 'Custom Region' })).resolves.toHaveTextContent('Atlantis'); @@ -568,21 +646,36 @@ export const CreatableOption: Story = { const regionSelect = await canvas.findByLabelText('Custom Region'); await userEvent.click(regionSelect); - // Wait for the dropdown to open and find the listbox - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); - - // The search input is outside the listbox container; query from the portal root - const input = await within(document.body).findByPlaceholderText('Search...'); - expect(input).toBeInTheDocument(); - - await userEvent.click(input); - await userEvent.clear(input); - await userEvent.type(input, 'California'); + try { + // Wait for the dropdown to open and find the listbox + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // Verify no creatable option appears when exact match exists - const createOption = within(listbox).queryByRole('option', { name: 'Select "California"' }); - expect(createOption).not.toBeInTheDocument(); + // The search input is outside the listbox container; query from the portal root + const input = await within(document.body).findByPlaceholderText('Search...'); + expect(input).toBeInTheDocument(); + + await userEvent.click(input); + await userEvent.clear(input); + await userEvent.type(input, 'California'); + + // Verify no creatable option appears when exact match exists + const createOption = within(listbox).queryByRole('option', { name: 'Select "California"' }); + expect(createOption).not.toBeInTheDocument(); + } catch (error) { + // Fallback: try canvas-based approach + console.warn('Portal approach failed for exact match test', error); + + // Try to find and type in the search input within the canvas + const input = canvas.getByPlaceholderText('Search...'); + await userEvent.click(input); + await userEvent.clear(input); + await userEvent.type(input, 'California'); + + // Verify no creatable option appears when exact match exists + const createOption = canvas.queryByRole('option', { name: 'Select "California"' }); + expect(createOption).not.toBeInTheDocument(); + } // Close the dropdown await userEvent.click(regionSelect); From e173cefac723ae60414197f495883d3ad80b33f8 Mon Sep 17 00:00:00 2001 From: Jake Ruesink Date: Sun, 21 Sep 2025 23:08:53 -0500 Subject: [PATCH 8/9] Fix: improve loading waits in select stories for better test reliability Enhanced select stories by adding explicit waits for component loading before interactions. This change ensures that the dropdowns are fully rendered and interactive, reducing flakiness in tests and improving overall reliability across various scenarios. --- .../src/remix-hook-form/select.stories.tsx | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/apps/docs/src/remix-hook-form/select.stories.tsx b/apps/docs/src/remix-hook-form/select.stories.tsx index 5209d52b..8f449622 100644 --- a/apps/docs/src/remix-hook-form/select.stories.tsx +++ b/apps/docs/src/remix-hook-form/select.stories.tsx @@ -205,6 +205,9 @@ const RegionSelectExample = () => { }); await step('Test validation errors on invalid submission', async () => { + // Wait for component to be fully loaded + await canvas.findByLabelText('US State'); + // Submit form without selecting any options const submitButton = canvas.getByRole('button', { name: 'Submit' }); await userEvent.click(submitButton); @@ -288,6 +291,9 @@ export const USStateSelection: Story = { const canvas = within(canvasElement); await step('Select a US state', async () => { + // Wait for component to be fully loaded + await canvas.findByLabelText('US State'); + // Find and click the US state dropdown const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); @@ -327,6 +333,9 @@ export const CanadaProvinceSelection: Story = { const canvas = within(canvasElement); await step('Select a Canadian province', async () => { + // Wait for component to be fully loaded + await canvas.findByLabelText('Canadian Province'); + // Find and click the Canada province dropdown const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); @@ -366,6 +375,9 @@ export const FormSubmission: Story = { const canvas = within(canvasElement); await step('Select all regions', async () => { + // Wait for component to be fully loaded + await canvas.findByLabelText('US State'); + // Select a state const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); @@ -470,6 +482,9 @@ export const SearchDisabled: Story = { play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); await step('Open select and ensure no search input', async () => { + // Wait for component to be fully loaded + await canvas.findByLabelText('Custom Region'); + const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); @@ -528,6 +543,9 @@ export const CustomSearchPlaceholder: Story = { play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); await step('Open select and see custom placeholder', async () => { + // Wait for component to be fully loaded + await canvas.findByLabelText('Custom Region'); + const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); @@ -592,8 +610,13 @@ export const CreatableOption: Story = { const canvas = within(canvasElement); await step('Create new option when no exact match', async () => { - // Wait for the component to render before interacting - const regionSelect = await canvas.findByLabelText('Custom Region'); + // Wait for the component to fully load - check for loading screen absence + await canvas.findByLabelText('Custom Region'); + + // Additional wait to ensure the component is fully interactive + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); try { @@ -642,8 +665,13 @@ export const CreatableOption: Story = { }); await step('No creatable when exact match exists', async () => { - // Wait for the component to render before interacting - const regionSelect = await canvas.findByLabelText('Custom Region'); + // Wait for the component to fully load - check for loading screen absence + await canvas.findByLabelText('Custom Region'); + + // Additional wait to ensure the component is fully interactive + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); try { From d24d4e670544599dd82d75701b4c53fe0f895f0d Mon Sep 17 00:00:00 2001 From: Jake Ruesink Date: Sun, 21 Sep 2025 23:15:01 -0500 Subject: [PATCH 9/9] Fix: streamline listbox interactions in select stories for improved test reliability Refactored select stories to remove redundant error handling and fallback mechanisms, ensuring a more straightforward approach to listbox interactions. This change enhances the clarity of the tests and maintains reliability by ensuring that dropdowns are fully rendered before interaction, reducing flakiness across various scenarios. --- .../src/remix-hook-form/select.stories.tsx | 207 ++++++------------ 1 file changed, 64 insertions(+), 143 deletions(-) diff --git a/apps/docs/src/remix-hook-form/select.stories.tsx b/apps/docs/src/remix-hook-form/select.stories.tsx index 8f449622..d837c73d 100644 --- a/apps/docs/src/remix-hook-form/select.stories.tsx +++ b/apps/docs/src/remix-hook-form/select.stories.tsx @@ -298,21 +298,14 @@ export const USStateSelection: Story = { const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); - try { - // Wait for the dropdown to open and find the listbox with timeout - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); + // Wait for the dropdown to open and find the listbox with timeout + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // Find and click the California option - const californiaOption = within(listbox).getByRole('option', { name: 'California' }); - expect(californiaOption).toBeInTheDocument(); - await userEvent.click(californiaOption); - } catch (error) { - // Fallback: try clicking the option directly if listbox approach fails - console.warn('Listbox approach failed, trying direct option selection', error); - const californiaOption = canvas.getByRole('option', { name: 'California' }); - await userEvent.click(californiaOption); - } + // Find and click the California option + const californiaOption = within(listbox).getByRole('option', { name: 'California' }); + expect(californiaOption).toBeInTheDocument(); + await userEvent.click(californiaOption); // Wait for the trigger text to update after portal selection await expect(canvas.findByRole('combobox', { name: 'US State' })).resolves.toHaveTextContent('California'); @@ -340,21 +333,14 @@ export const CanadaProvinceSelection: Story = { const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); - try { - // Wait for the dropdown to open and find the listbox with timeout - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); + // Wait for the dropdown to open and find the listbox with timeout + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // Find and click the Ontario option - const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); - expect(ontarioOption).toBeInTheDocument(); - await userEvent.click(ontarioOption); - } catch (error) { - // Fallback: try clicking the option directly if listbox approach fails - console.warn('Listbox approach failed, trying direct option selection', error); - const ontarioOption = canvas.getByRole('option', { name: 'Ontario' }); - await userEvent.click(ontarioOption); - } + // Find and click the Ontario option + const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); + expect(ontarioOption).toBeInTheDocument(); + await userEvent.click(ontarioOption); // Wait for the trigger text to update after portal selection await expect(canvas.findByRole('combobox', { name: 'Canadian Province' })).resolves.toHaveTextContent('Ontario'); @@ -382,52 +368,31 @@ export const FormSubmission: Story = { const stateSelect = canvas.getByLabelText('US State'); await userEvent.click(stateSelect); - try { - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); - const californiaOption = within(listbox).getByRole('option', { name: 'California' }); - expect(californiaOption).toBeInTheDocument(); - await userEvent.click(californiaOption); - } catch (error) { - // Fallback: try clicking the option directly if listbox approach fails - console.warn('Listbox approach failed, trying direct option selection', error); - const californiaOption = canvas.getByRole('option', { name: 'California' }); - await userEvent.click(californiaOption); - } + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); + const californiaOption = within(listbox).getByRole('option', { name: 'California' }); + expect(californiaOption).toBeInTheDocument(); + await userEvent.click(californiaOption); // Select a province const provinceSelect = canvas.getByLabelText('Canadian Province'); await userEvent.click(provinceSelect); - try { - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); - const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' }); - expect(ontarioOption).toBeInTheDocument(); - await userEvent.click(ontarioOption); - } catch (error) { - // Fallback: try clicking the option directly if listbox approach fails - console.warn('Listbox approach failed, trying direct option selection', error); - const ontarioOption = canvas.getByRole('option', { name: 'Ontario' }); - await userEvent.click(ontarioOption); - } + const provinceListbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(provinceListbox).toBeInTheDocument(); + const ontarioOption = within(provinceListbox).getByRole('option', { name: 'Ontario' }); + expect(ontarioOption).toBeInTheDocument(); + await userEvent.click(ontarioOption); // Select a custom region const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - try { - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); - const customOption = within(listbox).getByRole('option', { name: 'California' }); - expect(customOption).toBeInTheDocument(); - await userEvent.click(customOption); - } catch (error) { - // Fallback: try clicking the option directly if listbox approach fails - console.warn('Listbox approach failed, trying direct option selection', error); - const customOption = canvas.getByRole('option', { name: 'California' }); - await userEvent.click(customOption); - } + const regionListbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(regionListbox).toBeInTheDocument(); + const customOption = within(regionListbox).getByRole('option', { name: 'California' }); + expect(customOption).toBeInTheDocument(); + await userEvent.click(customOption); }); await step('Submit the form', async () => { @@ -488,18 +453,13 @@ export const SearchDisabled: Story = { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - try { - // Wait for the dropdown to open - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); + // Wait for the dropdown to open + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // Verify no search input is present when searchable is disabled - const searchInput = within(listbox).queryByPlaceholderText('Search...'); - expect(searchInput).not.toBeInTheDocument(); - } catch (error) { - // If listbox approach fails, that's okay - we're testing search disabled - console.warn('Listbox approach failed for search disabled test', error); - } + // Verify no search input is present when searchable is disabled + const searchInput = within(listbox).queryByPlaceholderText('Search...'); + expect(searchInput).not.toBeInTheDocument(); }); }, }; @@ -549,16 +509,9 @@ export const CustomSearchPlaceholder: Story = { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - try { - // The search input is rendered alongside the listbox in the portal, not inside the listbox itself. - const searchInput = await within(document.body).findByPlaceholderText('Type to filter…', {}, { timeout: 5000 }); - expect(searchInput).toBeInTheDocument(); - } catch (error) { - // Fallback: try to find the search input within the canvas - console.warn('Portal search input approach failed, trying canvas search', error); - const searchInput = canvas.getByPlaceholderText('Type to filter…'); - expect(searchInput).toBeInTheDocument(); - } + // The search input is rendered alongside the listbox in the portal, not inside the listbox itself. + const searchInput = await within(document.body).findByPlaceholderText('Type to filter…', {}, { timeout: 5000 }); + expect(searchInput).toBeInTheDocument(); }); }, }; @@ -611,6 +564,7 @@ export const CreatableOption: Story = { await step('Create new option when no exact match', async () => { // Wait for the component to fully load - check for loading screen absence + // This prevents the "sb-loader" (loading screen) from interfering with interactions await canvas.findByLabelText('Custom Region'); // Additional wait to ensure the component is fully interactive @@ -619,41 +573,23 @@ export const CreatableOption: Story = { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - try { - // Wait for the dropdown to open and find the listbox with more specific timeout and error handling - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - - // Verify the listbox is properly rendered - expect(listbox).toBeInTheDocument(); - expect(listbox).toHaveAttribute('role', 'listbox'); + // Wait for the dropdown to open and find the listbox with timeout + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // The search input is outside the listbox container; query from the portal root - const input = await within(document.body).findByPlaceholderText('Search...'); - expect(input).toBeInTheDocument(); + // The search input is outside the listbox container; query from the portal root + const input = await within(document.body).findByPlaceholderText('Search...'); + expect(input).toBeInTheDocument(); - await userEvent.click(input); - await userEvent.clear(input); - await userEvent.type(input, 'Atlantis'); + await userEvent.click(input); + await userEvent.clear(input); + await userEvent.type(input, 'Atlantis'); - // Wait for the creatable option to appear - const createItem = await within(listbox).findByRole('option', { name: 'Select "Atlantis"' }, { timeout: 2000 }); - expect(createItem).toBeInTheDocument(); + // Wait for the creatable option to appear + const createItem = await within(listbox).findByRole('option', { name: 'Select "Atlantis"' }, { timeout: 2000 }); + expect(createItem).toBeInTheDocument(); - await userEvent.click(createItem); - } catch (error) { - // Fallback: try to find and interact with elements within the canvas - console.warn('Portal approach failed, trying canvas-based interaction', error); - - // Try to find and type in the search input within the canvas - const input = canvas.getByPlaceholderText('Search...'); - await userEvent.click(input); - await userEvent.clear(input); - await userEvent.type(input, 'Atlantis'); - - // Try to find and click the create option - const createItem = canvas.getByRole('option', { name: 'Select "Atlantis"' }); - await userEvent.click(createItem); - } + await userEvent.click(createItem); // Verify the selection was applied await expect(canvas.findByRole('combobox', { name: 'Custom Region' })).resolves.toHaveTextContent('Atlantis'); @@ -674,36 +610,21 @@ export const CreatableOption: Story = { const regionSelect = canvas.getByLabelText('Custom Region'); await userEvent.click(regionSelect); - try { - // Wait for the dropdown to open and find the listbox - const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); - expect(listbox).toBeInTheDocument(); + // Wait for the dropdown to open and find the listbox + const listbox = await within(document.body).findByRole('listbox', {}, { timeout: 5000 }); + expect(listbox).toBeInTheDocument(); - // The search input is outside the listbox container; query from the portal root - const input = await within(document.body).findByPlaceholderText('Search...'); - expect(input).toBeInTheDocument(); + // The search input is outside the listbox container; query from the portal root + const input = await within(document.body).findByPlaceholderText('Search...'); + expect(input).toBeInTheDocument(); - await userEvent.click(input); - await userEvent.clear(input); - await userEvent.type(input, 'California'); + await userEvent.click(input); + await userEvent.clear(input); + await userEvent.type(input, 'California'); - // Verify no creatable option appears when exact match exists - const createOption = within(listbox).queryByRole('option', { name: 'Select "California"' }); - expect(createOption).not.toBeInTheDocument(); - } catch (error) { - // Fallback: try canvas-based approach - console.warn('Portal approach failed for exact match test', error); - - // Try to find and type in the search input within the canvas - const input = canvas.getByPlaceholderText('Search...'); - await userEvent.click(input); - await userEvent.clear(input); - await userEvent.type(input, 'California'); - - // Verify no creatable option appears when exact match exists - const createOption = canvas.queryByRole('option', { name: 'Select "California"' }); - expect(createOption).not.toBeInTheDocument(); - } + // Verify no creatable option appears when exact match exists + const createOption = within(listbox).queryByRole('option', { name: 'Select "California"' }); + expect(createOption).not.toBeInTheDocument(); // Close the dropdown await userEvent.click(regionSelect);