diff --git a/package.json b/package.json index 03dea24c..5674d148 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "test": "rc-test" }, "dependencies": { - "@rc-component/select": "~1.2.0-alpha.2", + "@rc-component/select": "~1.2.0-alpha.3", "@rc-component/tree": "~1.0.0", "@rc-component/util": "^1.3.0", "clsx": "^2.1.1" diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 9c3e08bf..f3390bea 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -152,7 +152,15 @@ export type GetOptionType< Multiple extends boolean | React.ReactNode = false, > = false extends Multiple ? OptionType[] : OptionType[][]; -type SemanticName = 'input' | 'prefix' | 'suffix'; +type SemanticName = + | 'input' + | 'prefix' + | 'suffix' + | 'placeholder' + | 'content' + | 'item' + | 'itemContent' + | 'itemRemove'; type PopupSemantic = 'list' | 'listItem'; export interface CascaderProps< OptionType extends DefaultOptionType = DefaultOptionType, @@ -451,16 +459,8 @@ const Cascader = React.forwardRef((props, re prefixCls={prefixCls} autoClearSearchValue={autoClearSearchValue} popupMatchSelectWidth={popupMatchSelectWidth} - classNames={{ - prefix: classNames?.prefix, - suffix: classNames?.suffix, - input: classNames?.input, - }} - styles={{ - prefix: styles?.prefix, - suffix: styles?.suffix, - input: styles?.input, - }} + classNames={classNames} + styles={styles} popupStyle={{ ...popupStyle, ...customPopupStyle, diff --git a/tests/__snapshots__/search.spec.tsx.snap b/tests/__snapshots__/search.spec.tsx.snap index a65b366b..068d2622 100644 --- a/tests/__snapshots__/search.spec.tsx.snap +++ b/tests/__snapshots__/search.spec.tsx.snap @@ -1,38 +1,32 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Cascader.Search should correct render Cascader with same field name of label and value 1`] = ` - + ,
- - + , +] `; diff --git a/tests/fieldNames.spec.tsx b/tests/fieldNames.spec.tsx index a485d1cd..2315b145 100644 --- a/tests/fieldNames.spec.tsx +++ b/tests/fieldNames.spec.tsx @@ -39,7 +39,7 @@ describe('Cascader.FieldNames', () => { ); // Open - wrapper.find('.rc-cascader-selector').simulate('mousedown'); + wrapper.find('.rc-cascader').first().simulate('mousedown'); expect(wrapper.isOpen()).toBeTruthy(); // Check values @@ -76,7 +76,7 @@ describe('Cascader.FieldNames', () => { />, ); - expect(wrapper.find('.rc-cascader-selection-item').text()).toEqual('Bamboo / Little / Toy'); + expect(wrapper.find('.rc-cascader-content-value').text()).toEqual('Bamboo / Little / Toy'); expect(wrapper.find('.rc-cascader-menu')).toHaveLength(3); expect(wrapper.find('.rc-cascader-menu-item-active')).toHaveLength(3); @@ -97,7 +97,7 @@ describe('Cascader.FieldNames', () => { />, ); - expect(wrapper.find('.rc-cascader-selection-item').text()).toEqual( + expect(wrapper.find('.rc-cascader-content-value').text()).toEqual( 'Bamboo->Little->Toy & bamboo>>little>>toy', ); }); diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 1f94f4b6..a59d0f37 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -5,8 +5,9 @@ import Cascader from '../src'; import { addressOptions, addressOptionsForUneven, optionsForActiveMenuItems } from './demoOptions'; import { mount } from './enzyme'; import { toRawValues } from '../src/utils/commonUtil'; -import { fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render } from '@testing-library/react'; import KeyCode from '@rc-component/util/lib/KeyCode'; +import { expectOpen, selectOption } from './util'; describe('Cascader.Basic', () => { let selectedValue: any; @@ -27,67 +28,71 @@ describe('Cascader.Basic', () => { }); it('should toggle select panel when click it', () => { - const wrapper = mount( + const { container } = render( , ); - expect(wrapper.isOpen()).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeTruthy(); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeFalsy(); + expectOpen(container, false); + + fireEvent.mouseDown(container.querySelector('input')!); + fireEvent.mouseUp(container.querySelector('input')!); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, true); + + fireEvent.mouseDown(container.querySelector('input')!); + fireEvent.mouseUp(container.querySelector('input')!); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, false); }); it('should call onChange when finish select', () => { - const wrapper = mount( + const { container } = render( , ); - wrapper.find('input').simulate('click'); + fireEvent.click(container.querySelector('input')!); // Menu 1 - let menus = wrapper.find('.rc-cascader-menu'); + let menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + const menu1Items = menus[0].querySelectorAll('.rc-cascader-menu-item'); expect(menu1Items.length).toBe(3); expect(selectedValue).toBeFalsy(); - wrapper.clickOption(0, 0); + selectOption(container, 0, 0); expect( - wrapper.find('.rc-cascader-menu-item').first().hasClass('rc-cascader-menu-item-active'), + container.querySelector('.rc-cascader-menu-item')!.classList.contains('rc-cascader-menu-item-active') ).toBeTruthy(); // Menu 2 - menus = wrapper.find('.rc-cascader-menu'); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); - const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); + const menu2Items = menus[1].querySelectorAll('.rc-cascader-menu-item'); expect(menu2Items.length).toBe(2); - expect(wrapper.isOpen()).toBeTruthy(); + expectOpen(container, true); expect(selectedValue).toBeFalsy(); - wrapper.clickOption(1, 0); + selectOption(container, 1, 0); expect( - wrapper - .find('.rc-cascader-menu') - .at(1) - .find('.rc-cascader-menu-item') - .first() - .hasClass('rc-cascader-menu-item-active'), + container + .querySelectorAll('.rc-cascader-menu')[1] + .querySelector('.rc-cascader-menu-item')! + .classList.contains('rc-cascader-menu-item-active') ).toBeTruthy(); // Menu 3 - menus = wrapper.find('.rc-cascader-menu'); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(3); - const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + const menu3Items = menus[2].querySelectorAll('.rc-cascader-menu-item'); expect(menu3Items.length).toBe(1); - expect(wrapper.isOpen()).toBeTruthy(); + expectOpen(container, true); expect(selectedValue).toBeFalsy(); - wrapper.clickOption(2, 0); - expect(wrapper.isOpen()).toBeFalsy(); + selectOption(container, 2, 0); + expectOpen(container, false); expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); }); @@ -163,111 +168,109 @@ describe('Cascader.Basic', () => { }); it('should support expand previous item when hover', () => { - const wrapper = mount( + const { container } = render( , ); - wrapper.find('input').simulate('click'); + fireEvent.click(container.querySelector('input')!); // Menu 1 - let menus = wrapper.find('.rc-cascader-menu'); + let menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + const menu1Items = menus[0].querySelectorAll('.rc-cascader-menu-item'); expect(menu1Items.length).toBe(3); expect(selectedValue).toBeFalsy(); - menu1Items.at(0).simulate('mouseEnter'); - jest.runAllTimers(); - wrapper.update(); + selectOption(container, 0, 0, 'mouseEnter'); + act(() => { + jest.runAllTimers(); + }); expect( - wrapper - .find('.rc-cascader-menu') - .at(0) - .find('.rc-cascader-menu-item') - .first() - .hasClass('rc-cascader-menu-item-active'), + container + .querySelectorAll('.rc-cascader-menu')[0] + .querySelector('.rc-cascader-menu-item')! + .classList.contains('rc-cascader-menu-item-active') ).toBeTruthy(); // Menu 2 - menus = wrapper.find('.rc-cascader-menu'); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); - const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); + const menu2Items = menus[1].querySelectorAll('.rc-cascader-menu-item'); expect(menu2Items.length).toBe(2); - expect(wrapper.isOpen()).toBeTruthy(); + expectOpen(container, true); expect(selectedValue).toBeFalsy(); - menu2Items.at(0).simulate('mouseEnter'); - jest.runAllTimers(); - wrapper.update(); + selectOption(container, 1, 0, 'mouseEnter'); + act(() => { + jest.runAllTimers(); + }); expect( - wrapper - .find('.rc-cascader-menu') - .at(1) - .find('.rc-cascader-menu-item') - .first() - .hasClass('rc-cascader-menu-item-active'), + container + .querySelectorAll('.rc-cascader-menu')[1] + .querySelector('.rc-cascader-menu-item')! + .classList.contains('rc-cascader-menu-item-active') ).toBeTruthy(); // Menu 3 - menus = wrapper.find('.rc-cascader-menu'); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(3); - const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + const menu3Items = menus[2].querySelectorAll('.rc-cascader-menu-item'); expect(menu3Items.length).toBe(1); - expect(wrapper.isOpen()).toBeTruthy(); + expectOpen(container, true); expect(selectedValue).toBeFalsy(); - wrapper.clickOption(2, 0); - expect(wrapper.isOpen()).toBeFalsy(); + selectOption(container, 2, 0); + expectOpen(container, false); expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); }); it('should clear active selection when no finish select', () => { - const wrapper = mount( + const { container } = render( , ); - wrapper.find('input').simulate('click'); - wrapper.clickOption(0, 0); - let menus = wrapper.find('.rc-cascader-menu'); + fireEvent.click(container.querySelector('input')!); + selectOption(container, 0, 0); + let menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeFalsy(); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, false); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeTruthy(); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, true); - menus = wrapper.find('.rc-cascader-menu'); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(1); }); it('should set back to defaultValue when no finish select', () => { - const wrapper = mount( + const { container } = render( , ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); + fireEvent.click(container.querySelector('input')!); + let menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(3); - wrapper.clickOption(0, 0); - menus = wrapper.find('.rc-cascader-menu'); + selectOption(container, 0, 0); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeFalsy(); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, false); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeTruthy(); - menus = wrapper.find('.rc-cascader-menu'); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, true); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(3); }); it('should set the value on each selection', () => { - const wrapper = mount( + const { container } = render( { , ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); + fireEvent.click(container.querySelector('input')!); + let menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(3); - wrapper.clickOption(0, 0); - menus = wrapper.find('.rc-cascader-menu'); + selectOption(container, 0, 0); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeFalsy(); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, false); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeTruthy(); - menus = wrapper.find('.rc-cascader-menu'); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, true); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); expect(selectedValue.length).toBe(1); expect(selectedValue[0]).toBe('fj'); }); it('should not change value inside when it is a controlled component', () => { - const wrapper = mount( + const { container } = render( , ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); + fireEvent.click(container.querySelector('input')!); + let menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); - wrapper.clickOption(0, 0); - menus = wrapper.find('.rc-cascader-menu'); + selectOption(container, 0, 0); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(2); - wrapper.clickOption(1, 0); - menus = wrapper.find('.rc-cascader-menu'); + selectOption(container, 1, 0); + menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(3); - wrapper.clickOption(2, 0); - expect(wrapper.isOpen()).toBeFalsy(); + selectOption(container, 2, 0); + expectOpen(container, false); - wrapper.find('input').simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(wrapper.isOpen()).toBeTruthy(); + fireEvent.click(container.querySelector('input')!); + menus = container.querySelectorAll('.rc-cascader-menu'); + expectOpen(container, true); expect(menus.length).toBe(2); }); @@ -478,21 +481,21 @@ describe('Cascader.Basic', () => { // https://github.com/ant-design/ant-design/issues/7480 it('should call onChange on click when expandTrigger=hover with changeOnSelect', () => { - const wrapper = mount( + const { container } = render( , ); - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); + fireEvent.click(container.querySelector('input')!); + const menus = container.querySelectorAll('.rc-cascader-menu'); expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + const menu1Items = menus[0].querySelectorAll('.rc-cascader-menu-item'); expect(menu1Items.length).toBe(3); - wrapper.clickOption(0, 0); + selectOption(container, 0, 0); expect(selectedValue[0]).toBe('fj'); - expect(wrapper.isOpen()).toBeFalsy(); + expectOpen(container, false); }); it('should not call onChange on hover when expandTrigger=hover with changeOnSelect', () => { @@ -536,17 +539,17 @@ describe('Cascader.Basic', () => { }); it('should close popup on double click when changeOnSelect is set', () => { - const wrapper = mount( + const { container } = render( , ); - expect(wrapper.isOpen()).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.isOpen()).toBeTruthy(); - wrapper.clickOption(0, 0, 'doubleClick'); - expect(wrapper.isOpen()).toBeFalsy(); + expectOpen(container, false); + fireEvent.click(container.querySelector('input')!); + expectOpen(container, true); + selectOption(container, 0, 0, 'doubleClick'); + expectOpen(container, false); }); // https://github.com/ant-design/ant-design/issues/9793 @@ -773,7 +776,7 @@ describe('Cascader.Basic', () => { it('defaultValue not exist', () => { const wrapper = mount(); - expect(wrapper.find('.rc-cascader-selection-item').text()).toEqual('not / exist'); + expect(wrapper.find('.rc-cascader-content-value').text()).toEqual('not / exist'); }); it('number value', () => { @@ -784,7 +787,7 @@ describe('Cascader.Basic', () => { wrapper.clickOption(0, 0); expect(onValueChange).toHaveBeenCalledWith([1], expect.anything()); - expect(wrapper.find('.rc-cascader-selection-item').text()).toEqual('One'); + expect(wrapper.find('.rc-cascader-content-value').text()).toEqual('One'); }); it('empty children is last children', () => { @@ -834,7 +837,7 @@ describe('Cascader.Basic', () => { />, ); - expect(wrapper.find('.rc-cascader-selection-item').text()).toEqual('Normal / Child'); + expect(wrapper.find('.rc-cascader-content-value').text()).toEqual('Normal / Child'); }); it('multiple', () => { @@ -1156,13 +1159,13 @@ describe('Cascader.Basic', () => { }); it('support aria-* and data-*', () => { - const options: CascaderProps["options"] = [ + const options: CascaderProps['options'] = [ { label: '福建', value: 'fj', - "aria-label": '福建', - "aria-labelledby": 'fj', - "data-type": 'fj', + 'aria-label': '福建', + 'aria-labelledby': 'fj', + 'data-type': 'fj', children: [ { label: '福州', diff --git a/tests/search.spec.tsx b/tests/search.spec.tsx index 3c9fed0e..57d28177 100644 --- a/tests/search.spec.tsx +++ b/tests/search.spec.tsx @@ -6,6 +6,7 @@ import Cascader from '../src'; import { optionsForActiveMenuItems } from './demoOptions'; import type { ReactWrapper } from './enzyme'; import { mount } from './enzyme'; +import { expectOpen } from './util'; describe('Cascader.Search', () => { function doSearch(wrapper: ReactWrapper, search: string) { @@ -45,6 +46,14 @@ describe('Cascader.Search', () => { }, ]; + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + it('default search', () => { const onSearch = jest.fn(); const onChange = jest.fn(); @@ -284,7 +293,7 @@ describe('Cascader.Search', () => { // Click fireEvent.click(document.querySelector('.rc-cascader-menu-item-content') as HTMLElement); - expect(document.querySelector('.rc-cascader-dropdown-hidden')).toBeTruthy(); + expectOpen(container, false); expect(document.querySelector('.rc-cascader-menu-item-content')?.textContent).toBe( 'Label Bamboo / Label Little / Toy Fish', ); @@ -356,22 +365,26 @@ describe('Cascader.Search', () => { it('onSearch and searchValue in showSearch', () => { const onSearch = jest.fn(); - const wrapper = mount(); + const { container } = render(); // Leaf - doSearch(wrapper, 'toy'); - let itemList = wrapper.find('div.rc-cascader-menu-item-content'); + fireEvent.change(container.querySelector('input') as HTMLElement, { + target: { value: 'toy' }, + }); + let itemList = container.querySelectorAll('div.rc-cascader-menu-item-content'); expect(itemList).toHaveLength(2); - expect(itemList.at(0).text()).toEqual('Label Bamboo / Label Little / Toy Fish'); - expect(itemList.at(1).text()).toEqual('Label Bamboo / Label Little / Toy Cards'); + expect(itemList[0].textContent).toEqual('Label Bamboo / Label Little / Toy Fish'); + expect(itemList[1].textContent).toEqual('Label Bamboo / Label Little / Toy Cards'); expect(onSearch).toHaveBeenCalledWith('toy'); // Parent - doSearch(wrapper, 'Label Little'); - itemList = wrapper.find('div.rc-cascader-menu-item-content'); + fireEvent.change(container.querySelector('input') as HTMLElement, { + target: { value: 'Label Little' }, + }); + itemList = container.querySelectorAll('div.rc-cascader-menu-item-content'); expect(itemList).toHaveLength(2); - expect(itemList.at(0).text()).toEqual('Label Bamboo / Label Little / Toy Fish'); - expect(itemList.at(1).text()).toEqual('Label Bamboo / Label Little / Toy Cards'); + expect(itemList[0].textContent).toEqual('Label Bamboo / Label Little / Toy Fish'); + expect(itemList[1].textContent).toEqual('Label Bamboo / Label Little / Toy Cards'); expect(onSearch).toHaveBeenCalledWith('Label Little'); }); @@ -395,10 +408,9 @@ describe('Cascader.Search', () => { />, ); expect(container.querySelectorAll('.rc-cascader-menu-item')).toHaveLength(1); - expect( - (container.querySelector('.rc-cascader-selection-search-input') as HTMLInputElement)?.value, - ).toBe('little'); + expect(container.querySelector('input') as HTMLInputElement).toHaveValue('little'); }); + it('autoClearSearchValue in showSearch', () => { const { container } = render( { />, ); - const inputNode = container.querySelector( - '.rc-cascader-selection-search-input', - ); + const inputNode = container.querySelector('input'); fireEvent.change(inputNode as HTMLInputElement, { target: { value: 'little' } }); expect(inputNode).toHaveValue('little'); fireEvent.click(document.querySelector('.rc-cascader-checkbox') as HTMLElement); diff --git a/tests/selector.spec.tsx b/tests/selector.spec.tsx index e7387714..7261b184 100644 --- a/tests/selector.spec.tsx +++ b/tests/selector.spec.tsx @@ -3,6 +3,7 @@ import { fireEvent, render } from '@testing-library/react'; import { mount } from './enzyme'; import Cascader from '../src'; import { addressOptions } from './demoOptions'; +import { expectOpen } from './util'; // Mock `useActive` hook jest.mock('../src/OptionList/useActive', () => (multiple: boolean, open: boolean) => { @@ -23,7 +24,7 @@ describe('Cascader.Selector', () => { , ); - fireEvent.mouseDown(container.querySelector('.rc-cascader-clear-icon') as HTMLElement); + fireEvent.mouseDown(container.querySelector('.rc-cascader-clear') as HTMLElement); expect(onChange).toHaveBeenCalledWith(undefined, undefined); }); @@ -41,25 +42,25 @@ describe('Cascader.Selector', () => { ); // Open and select - fireEvent.mouseDown(container.querySelector('.rc-cascader-selector') as HTMLElement); - expect(container.querySelector('.rc-cascader-open')).toBeTruthy(); + fireEvent.mouseDown(container.querySelector('.rc-cascader') as HTMLElement); + expectOpen(container); fireEvent.click(container.querySelector('.rc-cascader-menu-item-content') as HTMLElement); fireEvent.click(container.querySelectorAll('.rc-cascader-menu-item-content')[1]); - expect(container.querySelector('.rc-cascader-open')).toBeFalsy(); + expectOpen(container, false); // Clear - fireEvent.mouseDown(container.querySelector('.rc-cascader-clear-icon') as HTMLElement); + fireEvent.mouseDown(container.querySelector('.rc-cascader-clear') as HTMLElement); expect((global as any).activeValueCells).toEqual([]); }); it('multiple', () => { const onChange = jest.fn(); - const wrapper = mount( + const { container } = render( , ); - wrapper.find('.rc-cascader-clear-icon').simulate('mouseDown'); + fireEvent.mouseDown(container.querySelector('.rc-cascader-clear') as HTMLElement); expect(onChange).toHaveBeenCalledWith([], []); }); }); @@ -70,7 +71,7 @@ describe('Cascader.Selector', () => { , ); - wrapper.find('.rc-cascader-selection-item-remove-icon').first().simulate('click'); + wrapper.find('.rc-cascader-selection-item-remove').first().simulate('click'); expect(onChange).toHaveBeenCalledWith([['exist']], expect.anything()); }); diff --git a/tests/semantic.spec.tsx b/tests/semantic.spec.tsx index 2dd24ca9..7f6034cc 100644 --- a/tests/semantic.spec.tsx +++ b/tests/semantic.spec.tsx @@ -9,6 +9,8 @@ describe('Cascader.Search', () => { prefix: 'test-prefix', suffix: 'test-suffix', input: 'test-input', + placeholder: 'test-placeholder', + content: 'test-content', popup: { list: 'test-popup-list', listItem: 'test-popup-list-item', @@ -18,6 +20,8 @@ describe('Cascader.Search', () => { prefix: { color: 'green' }, suffix: { color: 'blue' }, input: { color: 'purple' }, + placeholder: { fontSize: '12px' }, + content: { backgroundColor: 'lightgray' }, popup: { list: { background: 'red' }, listItem: { color: 'yellow' }, @@ -34,20 +38,120 @@ describe('Cascader.Search', () => { optionRender={option => `${option.label} - test`} />, ); - const input = container.querySelector('.rc-cascader-selection-search-input'); + + const input = container.querySelector('.rc-cascader-input'); const prefix = container.querySelector('.rc-cascader-prefix'); - const suffix = container.querySelector('.rc-cascader-arrow'); + const suffix = container.querySelector('.rc-cascader-suffix'); + const placeholder = container.querySelector('.rc-cascader-placeholder'); + const content = container.querySelector('.rc-cascader-content'); const list = container.querySelector('.rc-cascader-menu'); const listItem = container.querySelector('.rc-cascader-menu-item'); + + // Test styles for supported semantic elements expect(input).toHaveStyle(testStyles.input); expect(prefix).toHaveStyle(testStyles.prefix); expect(suffix).toHaveStyle(testStyles.suffix); + if (placeholder) { + expect(placeholder).toHaveStyle(testStyles.placeholder); + } + expect(content).toHaveStyle(testStyles.content); expect(list).toHaveStyle(testStyles.popup.list); expect(listItem).toHaveStyle(testStyles.popup.listItem); + + // Test class names for supported semantic elements + expect(input?.className).toContain(testClassNames.input); + expect(prefix?.className).toContain(testClassNames.prefix); + expect(suffix?.className).toContain(testClassNames.suffix); + if (placeholder) { + expect(placeholder?.className).toContain(testClassNames.placeholder); + } + expect(content?.className).toContain(testClassNames.content); + expect(list?.className).toContain(testClassNames.popup.list); + expect(listItem?.className).toContain(testClassNames.popup.listItem); + }); + + it('Should support semantic for multiple mode', () => { + const testClassNames = { + prefix: 'test-prefix', + suffix: 'test-suffix', + input: 'test-input', + placeholder: 'test-placeholder', + content: 'test-content', + item: 'test-item', + itemContent: 'test-item-content', + itemRemove: 'test-item-remove', + popup: { + list: 'test-popup-list', + listItem: 'test-popup-list-item', + }, + }; + const testStyles = { + prefix: { color: 'green' }, + suffix: { color: 'blue' }, + input: { color: 'purple' }, + placeholder: { fontSize: '12px' }, + content: { backgroundColor: 'lightgray' }, + item: { border: '1px solid red' }, + itemContent: { fontWeight: 'bold' }, + itemRemove: { color: 'red' }, + popup: { + list: { background: 'red' }, + listItem: { color: 'yellow' }, + }, + }; + const { container } = render( + 'icon'} + open + checkable + value={[['bamboo']]} + options={[ + { label: 'bamboo', value: 'bamboo', children: [{ label: 'leaf', value: 'leaf' }] }, + ]} + optionRender={option => `${option.label} - test`} + />, + ); + + const input = container.querySelector('.rc-cascader-input'); + const prefix = container.querySelector('.rc-cascader-prefix'); + const suffix = container.querySelector('.rc-cascader-suffix'); + const placeholder = container.querySelector('.rc-cascader-placeholder'); + const content = container.querySelector('.rc-cascader-content'); + const list = container.querySelector('.rc-cascader-menu'); + const listItem = container.querySelector('.rc-cascader-menu-item'); + const item = container.querySelector('.rc-cascader-selection-item'); + const itemContent = container.querySelector('.rc-cascader-selection-item-content'); + const itemRemove = container.querySelector('.rc-cascader-selection-item-remove'); + + // Test styles for supported semantic elements + expect(input).toHaveStyle(testStyles.input); + expect(prefix).toHaveStyle(testStyles.prefix); + expect(suffix).toHaveStyle(testStyles.suffix); + if (placeholder) { + expect(placeholder).toHaveStyle(testStyles.placeholder); + } + expect(content).toHaveStyle(testStyles.content); + expect(list).toHaveStyle(testStyles.popup.list); + expect(listItem).toHaveStyle(testStyles.popup.listItem); + expect(item).toHaveStyle(testStyles.item); + expect(itemContent).toHaveStyle(testStyles.itemContent); + expect(itemRemove).toHaveStyle(testStyles.itemRemove); + + // Test class names for supported semantic elements expect(input?.className).toContain(testClassNames.input); expect(prefix?.className).toContain(testClassNames.prefix); expect(suffix?.className).toContain(testClassNames.suffix); + if (placeholder) { + expect(placeholder?.className).toContain(testClassNames.placeholder); + } + expect(content?.className).toContain(testClassNames.content); expect(list?.className).toContain(testClassNames.popup.list); expect(listItem?.className).toContain(testClassNames.popup.listItem); + expect(item?.className).toContain(testClassNames.item); + expect(itemContent?.className).toContain(testClassNames.itemContent); + expect(itemRemove?.className).toContain(testClassNames.itemRemove); }); }); diff --git a/tests/setup.js b/tests/setup.js index 2bc763a2..849377c4 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -27,3 +27,29 @@ Object.assign(Enzyme.ReactWrapper.prototype, { }); window.HTMLElement.prototype.scrollIntoView = jest.fn(); + +window.MessageChannel = class { + constructor() { + const createPort = () => { + const port = { + onmessage: null, + postMessage: message => { + setTimeout(() => { + if (port._target && typeof port._target.onmessage === 'function') { + port._target.onmessage({ data: message }); + } + }, 0); + }, + _target: null, + }; + return port; + }; + + const port1 = createPort(); + const port2 = createPort(); + port1._target = port2; + port2._target = port1; + this.port1 = port1; + this.port2 = port2; + } +}; diff --git a/tests/util.ts b/tests/util.ts new file mode 100644 index 00000000..b7a91b0d --- /dev/null +++ b/tests/util.ts @@ -0,0 +1,34 @@ +import { act, fireEvent } from '@testing-library/react'; + +export function expectOpen(dom: HTMLElement, open = true) { + act(() => { + jest.advanceTimersByTime(100000); + }); + + const popup = dom.querySelector('.rc-cascader-dropdown')!; + const isOpen = !!(popup && !popup.className.includes('rc-cascader-dropdown-hidden')); + + expect(isOpen).toBe(open); +} + +export function selectOption(container: HTMLElement, menuIndex: number, optionIndex: number, eventType = 'click') { + const menus = container.querySelectorAll('.rc-cascader-menu'); + const menu = menus[menuIndex]; + if (!menu) { + throw new Error(`Menu ${menuIndex} not found`); + } + + const options = menu.querySelectorAll('.rc-cascader-menu-item'); + const option = options[optionIndex]; + if (!option) { + throw new Error(`Option ${optionIndex} in menu ${menuIndex} not found`); + } + + if (eventType === 'doubleClick') { + fireEvent.doubleClick(option); + } else if (eventType === 'mouseEnter') { + fireEvent.mouseEnter(option); + } else { + fireEvent.click(option); + } +}