diff --git a/packages/react/src/components/Dropdown/Dropdown.tsx b/packages/react/src/components/Dropdown/Dropdown.tsx index 51de97b91f87..7ff281908c70 100644 --- a/packages/react/src/components/Dropdown/Dropdown.tsx +++ b/packages/react/src/components/Dropdown/Dropdown.tsx @@ -467,9 +467,7 @@ const Dropdown = React.forwardRef( // NOTE: does not prevent click evt.preventDefault(); // focus on the element as per readonly input behavior - if (mergedRef.current !== undefined) { - mergedRef.current.focus(); - } + mergedRef?.current?.focus(); }, onKeyDown: (evt: React.KeyboardEvent) => { const selectAccessKeys = ['ArrowDown', 'ArrowUp', ' ', 'Enter']; @@ -487,7 +485,6 @@ const Dropdown = React.forwardRef( ) { setIsTyping(true); } - if ( (isTyping && evt.code === 'Space') || !['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(evt.key) diff --git a/packages/react/src/components/Dropdown/Dropdown-test.js b/packages/react/src/components/Dropdown/__tests__/Dropdown-test.js similarity index 51% rename from packages/react/src/components/Dropdown/Dropdown-test.js rename to packages/react/src/components/Dropdown/__tests__/Dropdown-test.js index fa0b52467a82..2100d4888445 100644 --- a/packages/react/src/components/Dropdown/Dropdown-test.js +++ b/packages/react/src/components/Dropdown/__tests__/Dropdown-test.js @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; -import { render, screen } from '@testing-library/react'; +import React, { useRef } from 'react'; +import { render, screen, act, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { assertMenuOpen, @@ -15,10 +15,11 @@ import { generateItems, generateGenericItem, waitForPosition, -} from '../ListBox/test-helpers'; -import Dropdown from '../Dropdown'; -import DropdownSkeleton from '../Dropdown/Dropdown.Skeleton'; -import { AILabel } from '../AILabel'; +} from '../../ListBox/test-helpers'; +import Dropdown from '..'; +import DropdownSkeleton from '../Dropdown.Skeleton'; +import { AILabel } from '../../AILabel'; +import { Simulate } from 'react-dom/test-utils'; const prefix = 'cds'; @@ -35,6 +36,9 @@ describe('Dropdown', () => { titleText: 'Dropdown label', }; }); + afterEach(() => { + jest.useRealTimers(); // Restore real timers after each test + }); it('should initially render with the menu not open', async () => { render(); @@ -94,6 +98,29 @@ describe('Dropdown', () => { expect(screen.getByText('Item 1')).toBeInTheDocument(); }); + it('should render selectedItem as an element of type number', async () => { + const itemToElement = jest.fn((item) => { + return
{item.label}
; + }); + let mockProps1 = { ...mockProps, label: 1 }; + render(); + await openMenu(); + expect(itemToElement).toHaveBeenCalled(); + await waitForPosition(); + expect(screen.getByText(1)).toBeInTheDocument(); + }); + + it('should render when defaulItemtoString passed with null value', async () => { + let mockProps2 = { ...mockProps, label: [] }; + const { container } = render(); + const labelElement = screen.queryByText('shankar'); + expect(labelElement).not.toBeInTheDocument(); + const emptySpanTargeting = container.querySelector( + '.cds--list-box__label:not(:empty)' + ); + expect(emptySpanTargeting).toBeNull(); + }); + describe('title', () => { it('renders a title', async () => { render(); @@ -154,10 +181,37 @@ describe('Dropdown', () => { }); it('should respect readOnly prop', async () => { - render(); + let onChange = jest.fn(); + let onKeyDown = jest.fn(); + let mockProps1 = { ...mockProps }; + const ref = React.createRef(); + let dropDownItem = render( + + ); await openMenu(); // menu should not open assertMenuClosed(); + await waitForPosition(); + + const button = screen.getByRole('combobox'); + + if (button) { + fireEvent.click(button); + fireEvent.keyDown(screen.getByRole('combobox'), { key: 'ArrowDown' }); + fireEvent.keyDown(screen.getByRole('combobox'), { code: 'ArrowDown' }); + } + + expect(onChange).toHaveBeenCalledTimes(0); + expect(onKeyDown).toHaveBeenCalledTimes(2); + expect(onKeyDown).toHaveBeenCalledWith( + expect.objectContaining({ key: 'ArrowDown' }) + ); await openMenu(); // menu should not open expect(screen.queryByText('Item 0')).not.toBeInTheDocument(); expect(mockProps.onChange).toHaveBeenCalledTimes(0); @@ -166,6 +220,94 @@ describe('Dropdown', () => { mockProps.onChange.mockClear(); }); + it('should respect readOnly prop with false and respect call setTimeout function', async () => { + let onChange = jest.fn(); + let onKeyDown = jest.fn(); + let mockProps2 = { ...mockProps }; + const ref = React.createRef(); + + render( + + ); + + //const button = dropDownItem.container.querySelector('#downshift-\\:r19\\:-toggle-button'); + const button = screen.getByRole('combobox'); + await waitForPosition(); + if (button) { + act(() => { + jest.useFakeTimers(); + fireEvent.keyDown(screen.getByRole('combobox'), { + key: 'Space', + code: 'Space', + }); + fireEvent.keyDown(screen.getByRole('combobox'), { + key: 'ArrowLeft', + code: 'ArrowLeft', + }); + jest.advanceTimersByTime(3000); + }); + } + if (button) { + act(() => { + fireEvent.keyDown(screen.getByRole('combobox'), { + key: 'Space', + code: 'Space', + }); + fireEvent.keyDown(screen.getByRole('combobox'), { + key: 'ArrowLeft', + code: 'ArrowLeft', + }); + }); + } + //await openMenu(); + + expect(onKeyDown).toHaveBeenCalledWith( + expect.objectContaining({ code: 'Space' }) + ); + expect(onKeyDown).toHaveBeenCalledWith( + expect.objectContaining({ code: 'ArrowLeft' }) + ); + expect(onChange).toHaveBeenCalledTimes(0); + assertMenuClosed(); + jest.useRealTimers(); // Restore the original timer behavior + }); + + it('should respect readOnly prop with false argument and respect clear activated Timeout', async () => { + let onChange = jest.fn(); + let onKeyDown = jest.fn(); + render(); + + await waitForPosition(); + const button = screen.getByRole('combobox'); + if (button) { + act(() => { + fireEvent.keyDown(screen.getByRole('combobox'), { + key: 'Space', + code: 'Space', + }); + fireEvent.keyDown(screen.getByRole('combobox'), { + key: 'ArrowLeft', + code: 'ArrowLeft', + }); + }); + } + expect(onKeyDown).toHaveBeenCalledWith( + expect.objectContaining({ code: 'Space' }) + ); + expect(onKeyDown).toHaveBeenCalledWith( + expect.objectContaining({ code: 'ArrowLeft' }) + ); + //expect(screen.queryByText('Item 0')).not.toBeInTheDocument(); + expect(onChange).toHaveBeenCalledTimes(0); + assertMenuClosed(); + }); + describe('should display initially selected item found in `initialSelectedItem`', () => { it('using an object type for the `initialSelectedItem` prop', async () => { render( @@ -213,6 +355,27 @@ describe('Dropdown', () => { }); }); +describe('Dropdown', () => { + let mockProps; + beforeEach(() => { + mockProps = { + id: 'test-dropdown', + items: generateItems(5, generateGenericItem), + onChange: jest.fn(), + label: 'input', + placeholder: 'Filter...', + type: 'default', + titleText: 'Dropdown label', + }; + }); + + it('should initially render with the menu not open ', async () => { + render(); + await waitForPosition(); + assertMenuClosed(); + }); +}); + describe('DropdownSkeleton', () => { describe('Renders as expected', () => { it('Has the expected classes', () => { @@ -223,3 +386,41 @@ describe('DropdownSkeleton', () => { }); }); }); + +describe('Test useEffect ', () => { + let mockProps; + beforeEach(() => { + mockProps = { + id: 'test-dropdown', + items: generateItems(5, generateGenericItem), + onChange: jest.fn(), + label: 'input', + placeholder: 'Filter...', + type: 'default', + titleText: 'Dropdown label', + }; + }); + + it('for auto-align features', async () => { + const initialStyles = { top: '10px', left: '20px' }; + const updatedStyles = { top: '30px', left: '40px' }; + const { getByTestId, rerender } = render( + + ); + //Initially, styles should not be set because autoAlign is false + //const floatingElement = getByTestId('test-dropdown'); + rerender( + + ); + await waitForPosition(); + assertMenuClosed(); + }); +});