Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 126 additions & 41 deletions apps/docs/src/remix-hook-form/select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,42 +205,64 @@ 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);

// 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
Expand Down Expand Up @@ -269,13 +291,20 @@ 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);

// 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
Expand All @@ -297,13 +326,20 @@ 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);

// 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
Expand All @@ -325,32 +361,38 @@ 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);
{
const listbox = await within(document.body).findByRole('listbox');
const californiaOption = within(listbox).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);
{
const listbox = await within(document.body).findByRole('listbox');
const ontarioOption = within(listbox).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);
{
const listbox = await within(document.body).findByRole('listbox');
const customOption = within(listbox).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 () => {
Expand Down Expand Up @@ -405,10 +447,19 @@ 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);
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();
});
},
};
Expand Down Expand Up @@ -452,10 +503,14 @@ 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);

// 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();
});
},
Expand Down Expand Up @@ -508,20 +563,35 @@ export const CreatableOption: Story = {
const canvas = within(canvasElement);

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
await new Promise((resolve) => setTimeout(resolve, 1000));

const regionSelect = canvas.getByLabelText('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));
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();

// 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...');
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
Expand All @@ -531,18 +601,33 @@ export const CreatableOption: Story = {
});

await step('No creatable when exact match exists', async () => {
// 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);
// Add a small delay to ensure the dropdown has time to render
await new Promise((resolve) => setTimeout(resolve, 100));
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 });
expect(listbox).toBeInTheDocument();

// 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...');
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);
});
},
};
67 changes: 39 additions & 28 deletions apps/docs/src/remix-hook-form/select.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,66 @@ 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
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' });
Expand Down
Loading