From e054d2d1a019ca5aaeb73949be140703de34196b Mon Sep 17 00:00:00 2001 From: Tomohisa Igarashi Date: Wed, 1 May 2024 21:41:01 -0400 Subject: [PATCH] fix: Use a Typeahead component for the DataFormat and LoadBalancer Field Fixes: #903 --- .../cypress/support/next-commands/design.ts | 4 +- .../Form/dataFormat/DataFormatEditor.test.tsx | 47 +++- .../Form/dataFormat/DataFormatEditor.tsx | 233 ++++++++++++++---- .../Form/expression/ExpressionEditor.tsx | 16 +- .../loadBalancer/LoadBalancerEditor.test.tsx | 58 ++++- .../Form/loadBalancer/LoadBalancerEditor.tsx | 233 ++++++++++++++---- .../Visualization/Canvas/CanvasForm.test.tsx | 16 +- 7 files changed, 490 insertions(+), 117 deletions(-) diff --git a/packages/ui-tests/cypress/support/next-commands/design.ts b/packages/ui-tests/cypress/support/next-commands/design.ts index d67c61acc..dbfd2e1fd 100644 --- a/packages/ui-tests/cypress/support/next-commands/design.ts +++ b/packages/ui-tests/cypress/support/next-commands/design.ts @@ -133,7 +133,9 @@ Cypress.Commands.add('selectDataformat', (dataformat: string) => { }); Cypress.Commands.add('selectCustomMetadataEditor', (type: string, format: string) => { - cy.get(`div[data-testid="${type}-config-card"] button.pf-v5-c-menu-toggle`).should('be.visible').click(); + cy.get(`div[data-testid="${type}-config-card"] div.pf-v5-c-menu-toggle button.pf-v5-c-menu-toggle__button`) + .should('be.visible') + .click(); const regex = new RegExp(`^${format}$`); cy.get('span.pf-v5-c-menu__item-text').contains(regex).should('exist').scrollIntoView().click(); }); diff --git a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx index d608e575a..798729177 100644 --- a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx +++ b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx @@ -1,6 +1,5 @@ import * as catalogIndex from '@kaoto/camel-catalog/index.json'; -import { fireEvent, render, screen } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; +import { fireEvent, render, screen, act } from '@testing-library/react'; import { CatalogKind, ICamelDataformatDefinition, KaotoSchemaDefinition } from '../../../models'; import { IVisualizationNode, VisualComponentSchema } from '../../../models/visualization/base-visual-entity'; import { CamelCatalogService } from '../../../models/visualization/flows'; @@ -11,7 +10,7 @@ import { DataFormatEditor } from './DataFormatEditor'; describe('DataFormatEditor', () => { let mockNode: CanvasNode; let dataformatCatalog: Record; - beforeAll(async () => { + beforeEach(async () => { jest.spyOn(console, 'error').mockImplementation(() => {}); dataformatCatalog = await import('@kaoto/camel-catalog/' + catalogIndex.catalogs.dataformats.file); /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -50,9 +49,9 @@ describe('DataFormatEditor', () => { it('should render', async () => { render(); - const buttons = screen.getAllByRole('button'); + const buttons = screen.getAllByRole('button', { name: 'Menu toggle' }); await act(async () => { - fireEvent.click(buttons[1]); + fireEvent.click(buttons[0]); }); const json = screen.getByTestId('dataformat-dropdownitem-json'); fireEvent.click(json.getElementsByTagName('button')[0]); @@ -60,6 +59,44 @@ describe('DataFormatEditor', () => { expect(form.innerHTML).toContain('Allow Unmarshall Type'); }); + it('should filter candidates with a text input', async () => { + render(); + const buttons = screen.getAllByRole('button', { name: 'Menu toggle' }); + await act(async () => { + fireEvent.click(buttons[0]); + }); + let dropdownItems = screen.queryAllByTestId(/dataformat-dropdownitem-.*/); + expect(dropdownItems.length).toBeGreaterThan(40); + const inputElement = screen.getAllByRole('combobox')[0]; + await act(async () => { + fireEvent.change(inputElement, { target: { value: 'json' } }); + }); + dropdownItems = screen.getAllByTestId(/dataformat-dropdownitem-.*/); + expect(dropdownItems).toHaveLength(3); + }); + + it('should clear filter and close the dropdown with close button', async () => { + render(); + const buttons = screen.getAllByRole('button', { name: 'Menu toggle' }); + await act(async () => { + fireEvent.click(buttons[0]); + }); + let inputElement = screen.getAllByRole('combobox')[0]; + await act(async () => { + fireEvent.change(inputElement, { target: { value: 'json' } }); + }); + let dropdownItems = screen.getAllByTestId(/dataformat-dropdownitem-.*/); + expect(dropdownItems).toHaveLength(3); + const clearButton = screen.getByLabelText('Clear input value'); + await act(async () => { + fireEvent.click(clearButton); + }); + dropdownItems = screen.getAllByTestId(/dataformat-dropdownitem-.*/); + expect(dropdownItems.length).toBeGreaterThan(40); + inputElement = screen.getAllByRole('combobox')[0]; + expect(inputElement).toHaveValue(''); + }); + it('should render for all dataformats without an error', () => { Object.entries(dataformatCatalog).forEach(([name, dataformat]) => { try { diff --git a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx index eedaecef0..ae5f66fa3 100644 --- a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx +++ b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx @@ -1,25 +1,28 @@ import { + Button, Card, CardBody, CardExpandableContent, CardHeader, CardTitle, - Dropdown, - DropdownItem, - DropdownList, MenuToggle, MenuToggleElement, - Text, - TextContent, - TextVariants, + Select, + SelectList, + SelectOption, + SelectOptionProps, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, } from '@patternfly/react-core'; -import { FunctionComponent, Ref, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { FunctionComponent, Ref, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { EntitiesContext } from '../../../providers'; import { MetadataEditor } from '../../MetadataEditor'; import { CanvasNode } from '../../Visualization/Canvas/canvas.models'; import { SchemaService } from '../schema.service'; import './DataFormatEditor.scss'; import { DataFormatService } from './dataformat.service'; +import { TimesIcon } from '@patternfly/react-icons'; interface DataFormatEditorProps { selectedNode: CanvasNode; @@ -45,11 +48,56 @@ export const DataFormatEditor: FunctionComponent = (props visualComponentSchema?.definition, ); const [selected, setSelected] = useState(dataFormat?.model.name || ''); + const [inputValue, setInputValue] = useState(dataFormat?.model.title || ''); + const initialDataFormatOptions = useMemo(() => { + return Object.values(dataFormatCatalogMap).map((option) => { + return { + value: option.model.name, + children: option.model.title, + description: option.model.description, + }; + }); + }, [dataFormatCatalogMap]); + const [selectOptions, setSelectOptions] = useState(initialDataFormatOptions); + const [focusedItemIndex, setFocusedItemIndex] = useState(null); + const [activeItem, setActiveItem] = useState(null); + const [filterValue, setFilterValue] = useState(''); + const textInputRef = useRef(); useEffect(() => { dataFormat ? setSelected(dataFormat.model.name) : setSelected(''); }, [dataFormat]); + useEffect(() => { + let newSelectOptions: SelectOptionProps[] = initialDataFormatOptions; + + // Filter menu items based on the text input value when one exists + if (filterValue) { + const lowerFilterValue = filterValue.toLowerCase(); + newSelectOptions = initialDataFormatOptions.filter((menuItem) => { + return ( + String(menuItem.value).toLowerCase().includes(lowerFilterValue) || + String(menuItem.children).toLowerCase().includes(lowerFilterValue) || + String(menuItem.description).toLowerCase().includes(lowerFilterValue) + ); + }); + // When no options are found after filtering, display 'No results found' + if (!newSelectOptions.length) { + newSelectOptions = [ + { isDisabled: false, children: `No results found for "${filterValue}"`, value: 'no results' }, + ]; + } + // Open the menu when the input value changes and the new value is not empty + if (!isOpen) { + setIsOpen(true); + } + } + + setSelectOptions(newSelectOptions); + setActiveItem(null); + setFocusedItemIndex(null); + }, [filterValue, initialDataFormatOptions, isOpen]); + const dataFormatSchema = useMemo(() => { return DataFormatService.getDataFormatSchema(dataFormat); }, [dataFormat]); @@ -71,25 +119,123 @@ export const DataFormatEditor: FunctionComponent = (props const onSelect = useCallback( (_event: React.MouseEvent | undefined, value: string | number | undefined) => { + const option = selectOptions.find((option) => option.children === value); + if (option && value !== 'no results') { + setInputValue(value as string); + setFilterValue(''); + handleOnChange(option!.value as string, {}); + setSelected(option!.children as string); + } setIsOpen(false); - if ((!dataFormat && value === '') || value === dataFormat?.model.name) return; - setSelected(value as string); - handleOnChange(value as string, {}); + setFocusedItemIndex(null); + setActiveItem(null); }, - [handleOnChange, dataFormat], + [selectOptions, handleOnChange], ); - const toggle = useCallback( - (toggleRef: Ref) => ( - - {selected || ( - - {SchemaService.DROPDOWN_PLACEHOLDER} - - )} - - ), - [isOpen, onToggleClick, selected], + const onTextInputChange = (_event: React.FormEvent, value: string) => { + setInputValue(value); + setFilterValue(value); + }; + + const handleMenuArrowKeys = (key: string) => { + let indexToFocus; + + if (isOpen) { + if (key === 'ArrowUp') { + // When no index is set or at the first index, focus to the last, otherwise decrement focus index + if (focusedItemIndex === null || focusedItemIndex === 0) { + indexToFocus = selectOptions.length - 1; + } else { + indexToFocus = focusedItemIndex - 1; + } + } + + if (key === 'ArrowDown') { + // When no index is set or at the last index, focus to the first, otherwise increment focus index + if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) { + indexToFocus = 0; + } else { + indexToFocus = focusedItemIndex + 1; + } + } + + setFocusedItemIndex(indexToFocus!); + const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus!]; + setActiveItem(`select-typeahead-${focusedItem.value.replace(' ', '-')}`); + } + }; + + const onInputKeyDown = (event: React.KeyboardEvent) => { + const enabledMenuItems = selectOptions.filter((option) => !option.isDisabled); + const [firstMenuItem] = enabledMenuItems; + const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem; + + switch (event.key) { + // Select the first available option + case 'Enter': + if (isOpen && focusedItem.value !== 'no results') { + setInputValue(String(focusedItem.children)); + setFilterValue(''); + setSelected(String(focusedItem.children)); + } + + setIsOpen((prevIsOpen) => !prevIsOpen); + setFocusedItemIndex(null); + setActiveItem(null); + break; + case 'Tab': + case 'Escape': + setIsOpen(false); + setActiveItem(null); + break; + case 'ArrowUp': + case 'ArrowDown': + event.preventDefault(); + handleMenuArrowKeys(event.key); + break; + } + }; + + const toggle = (toggleRef: Ref) => ( + + + + + + {!!inputValue && ( + + )} + + + ); return ( @@ -101,31 +247,32 @@ export const DataFormatEditor: FunctionComponent = (props - { + setIsOpen(false); + }} toggle={toggle} - isScrollable={true} > - - {Object.values(dataFormatCatalogMap).map((df) => { - return ( - - {df.model.title} - - ); - })} - - + + {selectOptions.map((option, index) => ( + setSelected(option.children as string)} + id={`select-typeahead-${option.value.replace(' ', '-')}`} + {...option} + value={option.children} + /> + ))} + + {dataFormat && ( = ({ // Filter menu items based on the text input value when one exists if (filterValue) { - newSelectOptions = languageCatalogMap.filter( - (menuItem) => - String(menuItem.value).toLowerCase().includes(filterValue.toLowerCase()) || - String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase()) || - String(menuItem.description).toLowerCase().includes(filterValue.toLowerCase()), - ); + const lowerFilterValue = filterValue.toLowerCase(); + newSelectOptions = languageCatalogMap.filter((menuItem) => { + return ( + String(menuItem.value).toLowerCase().includes(lowerFilterValue) || + String(menuItem.children).toLowerCase().includes(lowerFilterValue) || + String(menuItem.description).toLowerCase().includes(lowerFilterValue) + ); + }); // When no options are found after filtering, display 'No results found' if (!newSelectOptions.length) { newSelectOptions = [ @@ -100,7 +102,7 @@ export const ExpressionEditor: FunctionComponent = ({ setSelectOptions(newSelectOptions); setActiveItem(null); setFocusedItemIndex(null); - }, [filterValue]); + }, [filterValue, isOpen, languageCatalogMap]); const onTextInputChange = (_event: React.FormEvent, value: string) => { setInputValue(value); diff --git a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx index aaa8969a7..730138101 100644 --- a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx +++ b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx @@ -1,6 +1,5 @@ import * as catalogIndex from '@kaoto/camel-catalog/index.json'; -import { fireEvent, render, screen } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; +import { fireEvent, render, screen, act } from '@testing-library/react'; import { CatalogKind, ICamelLoadBalancerDefinition, KaotoSchemaDefinition } from '../../../models'; import { IVisualizationNode, VisualComponentSchema } from '../../../models/visualization/base-visual-entity'; import { CamelCatalogService } from '../../../models/visualization/flows'; @@ -11,7 +10,7 @@ import { LoadBalancerEditor } from './LoadBalancerEditor'; describe('LoadBalancerEditor', () => { let mockNode: CanvasNode; let loadbalancerCatalog: Record; - beforeAll(async () => { + beforeEach(async () => { loadbalancerCatalog = await import('@kaoto/camel-catalog/' + catalogIndex.catalogs.loadbalancers.file); /* eslint-disable @typescript-eslint/no-explicit-any */ delete (loadbalancerCatalog as any).default; @@ -40,7 +39,9 @@ describe('LoadBalancerEditor', () => { type: 'node', data: { vizNode: { - getComponentSchema: () => visualComponentSchema, + getComponentSchema: () => { + return { ...visualComponentSchema }; + }, updateModel: (_value: unknown) => {}, } as IVisualizationNode, }, @@ -49,11 +50,54 @@ describe('LoadBalancerEditor', () => { it('should render', async () => { render(); - const launchExpressionModalBtn = screen.getAllByRole('button')[1]; + const buttons = screen.getAllByRole('button', { name: 'Menu toggle' }); + await act(async () => { + fireEvent.click(buttons[0]); + }); + const failover = screen.getByTestId('loadbalancer-dropdownitem-failover'); + await act(async () => { + fireEvent.click(failover.getElementsByTagName('button')[0]); + }); + const form = screen.getByTestId('metadata-editor-form-loadbalancer'); + expect(form.innerHTML).toContain('Maximum Failover Attempts'); + }); + + it('should filter candidates with a text input', async () => { + render(); + const buttons = screen.getAllByRole('button', { name: 'Menu toggle' }); + await act(async () => { + fireEvent.click(buttons[0]); + }); + let dropdownItems = screen.queryAllByTestId(/loadbalancer-dropdownitem-.*/); + expect(dropdownItems.length).toBeGreaterThan(6); + const inputElement = screen.getAllByRole('combobox')[0]; + await act(async () => { + fireEvent.change(inputElement, { target: { value: 'round' } }); + }); + dropdownItems = screen.getAllByTestId(/loadbalancer-dropdownitem-.*/); + expect(dropdownItems).toHaveLength(1); + }); + + it('should clear filter and close the dropdown with close button', async () => { + render(); + const buttons = screen.getAllByRole('button', { name: 'Menu toggle' }); + await act(async () => { + fireEvent.click(buttons[0]); + }); + let inputElement = screen.getAllByRole('combobox')[0]; + await act(async () => { + fireEvent.change(inputElement, { target: { value: 'round' } }); + }); + let dropdownItems = screen.getAllByTestId(/loadbalancer-dropdownitem-.*/); + expect(dropdownItems).toHaveLength(1); + const clearButton = screen.getByLabelText('Clear input value'); await act(async () => { - fireEvent.click(launchExpressionModalBtn); + fireEvent.click(clearButton); }); - expect(screen.getByTestId('loadbalancer-dropdown')).toBeTruthy(); + dropdownItems = screen.queryAllByTestId(/loadbalancer-dropdownitem-.*/); + expect(dropdownItems.length).toBeGreaterThan(6); + inputElement = screen.getAllByRole('combobox')[0]; + expect(inputElement).toHaveValue(''); }); it('should render for all loadbalancers without an error', () => { diff --git a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx index f7ef156fd..35c074270 100644 --- a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx +++ b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx @@ -1,25 +1,28 @@ import { + Button, Card, CardBody, CardExpandableContent, CardHeader, CardTitle, - Dropdown, - DropdownItem, - DropdownList, MenuToggle, MenuToggleElement, - Text, - TextContent, - TextVariants, + Select, + SelectList, + SelectOption, + SelectOptionProps, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, } from '@patternfly/react-core'; -import { FunctionComponent, Ref, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { FunctionComponent, Ref, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { EntitiesContext } from '../../../providers'; import { MetadataEditor } from '../../MetadataEditor'; import { CanvasNode } from '../../Visualization/Canvas/canvas.models'; import { LoadBalancerService } from './loadbalancer.service'; import { SchemaService } from '../schema.service'; import './LoadBalancerEditor.scss'; +import { TimesIcon } from '@patternfly/react-icons'; interface LoadBalancerEditorProps { selectedNode: CanvasNode; } @@ -44,10 +47,56 @@ export const LoadBalancerEditor: FunctionComponent = (p visualComponentSchema?.definition, ); const [selected, setSelected] = useState(loadBalancer?.model.name || ''); + const [inputValue, setInputValue] = useState(loadBalancer?.model.title || ''); + const initialLoadBalancerOptions = useMemo(() => { + return Object.values(loadBalancerCatalogMap).map((option) => { + return { + value: option.model.name, + children: option.model.title, + description: option.model.description, + }; + }); + }, [loadBalancerCatalogMap]); + const [selectOptions, setSelectOptions] = useState(initialLoadBalancerOptions); + const [focusedItemIndex, setFocusedItemIndex] = useState(null); + const [activeItem, setActiveItem] = useState(null); + const [filterValue, setFilterValue] = useState(''); + const textInputRef = useRef(); + useEffect(() => { loadBalancer ? setSelected(loadBalancer.model.name) : setSelected(''); }, [loadBalancer]); + useEffect(() => { + let newSelectOptions: SelectOptionProps[] = initialLoadBalancerOptions; + + // Filter menu items based on the text input value when one exists + if (filterValue) { + const lowerFilterValue = filterValue.toLowerCase(); + newSelectOptions = initialLoadBalancerOptions.filter((menuItem) => { + return ( + String(menuItem.value).toLowerCase().includes(lowerFilterValue) || + String(menuItem.children).toLowerCase().includes(lowerFilterValue) || + String(menuItem.description).toLowerCase().includes(lowerFilterValue) + ); + }); + // When no options are found after filtering, display 'No results found' + if (!newSelectOptions.length) { + newSelectOptions = [ + { isDisabled: false, children: `No results found for "${filterValue}"`, value: 'no results' }, + ]; + } + // Open the menu when the input value changes and the new value is not empty + if (!isOpen) { + setIsOpen(true); + } + } + + setSelectOptions(newSelectOptions); + setActiveItem(null); + setFocusedItemIndex(null); + }, [filterValue, initialLoadBalancerOptions, isOpen]); + const loadBalancerSchema = useMemo(() => { return LoadBalancerService.getLoadBalancerSchema(loadBalancer); }, [loadBalancer]); @@ -74,25 +123,123 @@ export const LoadBalancerEditor: FunctionComponent = (p const onSelect = useCallback( (_event: React.MouseEvent | undefined, value: string | number | undefined) => { + const option = selectOptions.find((option) => option.children === value); + if (option && value !== 'no results') { + setInputValue(value as string); + setFilterValue(''); + handleOnChange(option!.value as string, {}); + setSelected(option!.children as string); + } setIsOpen(false); - if ((!loadBalancer && value === '') || value === loadBalancer?.model.name) return; - setSelected(value as string); - handleOnChange(value as string, {}); + setFocusedItemIndex(null); + setActiveItem(null); }, - [handleOnChange, loadBalancer], + [handleOnChange, selectOptions], ); - const toggle = useCallback( - (toggleRef: Ref) => ( - - {selected || ( - - {SchemaService.DROPDOWN_PLACEHOLDER} - - )} - - ), - [onToggleClick, isOpen, selected], + const onTextInputChange = (_event: React.FormEvent, value: string) => { + setInputValue(value); + setFilterValue(value); + }; + + const handleMenuArrowKeys = (key: string) => { + let indexToFocus; + + if (isOpen) { + if (key === 'ArrowUp') { + // When no index is set or at the first index, focus to the last, otherwise decrement focus index + if (focusedItemIndex === null || focusedItemIndex === 0) { + indexToFocus = selectOptions.length - 1; + } else { + indexToFocus = focusedItemIndex - 1; + } + } + + if (key === 'ArrowDown') { + // When no index is set or at the last index, focus to the first, otherwise increment focus index + if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) { + indexToFocus = 0; + } else { + indexToFocus = focusedItemIndex + 1; + } + } + + setFocusedItemIndex(indexToFocus!); + const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus!]; + setActiveItem(`select-typeahead-${focusedItem.value.replace(' ', '-')}`); + } + }; + + const onInputKeyDown = (event: React.KeyboardEvent) => { + const enabledMenuItems = selectOptions.filter((option) => !option.isDisabled); + const [firstMenuItem] = enabledMenuItems; + const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem; + + switch (event.key) { + // Select the first available option + case 'Enter': + if (isOpen && focusedItem.value !== 'no results') { + setInputValue(String(focusedItem.children)); + setFilterValue(''); + setSelected(String(focusedItem.children)); + } + + setIsOpen((prevIsOpen) => !prevIsOpen); + setFocusedItemIndex(null); + setActiveItem(null); + break; + case 'Tab': + case 'Escape': + setIsOpen(false); + setActiveItem(null); + break; + case 'ArrowUp': + case 'ArrowDown': + event.preventDefault(); + handleMenuArrowKeys(event.key); + break; + } + }; + + const toggle = (toggleRef: Ref) => ( + + + + + + {!!inputValue && ( + + )} + + + ); return ( @@ -103,30 +250,32 @@ export const LoadBalancerEditor: FunctionComponent = (p - { + setIsOpen(false); + }} toggle={toggle} - isScrollable={true} > - - {Object.values(loadBalancerCatalogMap).map((lb) => { - return ( - - {lb.model.title} - - ); - })} - - + + {selectOptions.map((option, index) => ( + setSelected(option.children as string)} + id={`select-typeahead-${option.value.replace(' ', '-')}`} + {...option} + value={option.children} + /> + ))} + + {loadBalancer && (
{ , ); - const button = screen - .getAllByRole('button') - .filter((button) => button.innerHTML.includes(SchemaService.DROPDOWN_PLACEHOLDER)); + const button = screen.getAllByRole('button', { name: 'Menu toggle' }); await act(async () => { fireEvent.click(button[0]); }); @@ -509,9 +507,7 @@ describe('CanvasForm', () => { expect(camelRoute.from.steps[0].marshal!.avro).toBeUndefined(); expect(camelRoute.from.steps[0].marshal!.id).toEqual('modified'); - const button = screen - .getAllByRole('button') - .filter((button) => button.innerHTML.includes(SchemaService.DROPDOWN_PLACEHOLDER)); + const button = screen.getAllByRole('button', { name: 'Menu toggle' }); await act(async () => { fireEvent.click(button[0]); }); @@ -560,9 +556,7 @@ describe('CanvasForm', () => { , ); - const button = screen - .getAllByRole('button') - .filter((button) => button.innerHTML.includes(SchemaService.DROPDOWN_PLACEHOLDER)); + const button = screen.getAllByRole('button', { name: 'Menu toggle' }); await act(async () => { fireEvent.click(button[0]); }); @@ -619,9 +613,7 @@ describe('CanvasForm', () => { expect(camelRoute.from.steps[0].loadBalance!.weighted).toBeUndefined(); expect(camelRoute.from.steps[0].loadBalance!.id).toEqual('modified'); - const button = screen - .getAllByRole('button') - .filter((button) => button.innerHTML.includes(SchemaService.DROPDOWN_PLACEHOLDER)); + const button = screen.getAllByRole('button', { name: 'Menu toggle' }); await act(async () => { fireEvent.click(button[0]); });