diff --git a/packages/eui/changelogs/upcoming/7820.md b/packages/eui/changelogs/upcoming/7820.md new file mode 100644 index 00000000000..9198600ece9 --- /dev/null +++ b/packages/eui/changelogs/upcoming/7820.md @@ -0,0 +1 @@ +- Updated `setEuiDevProviderWarning` to additionally accept a custom callback function, which warning messages will be passed to diff --git a/packages/eui/src-docs/src/views/provider/provider_example.js b/packages/eui/src-docs/src/views/provider/provider_example.js index cd7e33ba3ee..0808935cb91 100644 --- a/packages/eui/src-docs/src/views/provider/provider_example.js +++ b/packages/eui/src-docs/src/views/provider/provider_example.js @@ -15,7 +15,7 @@ import { GuideSectionPropsTable } from '../../components/guide_section/guide_sec import Setup from './provider_setup'; import GlobalStyles from './provider_styles'; -import Warnings from './provider_warning'; +import Warnings, { CallbackExample } from './provider_warning'; import { EuiComponentDefaultsProps, euiProviderComponentDefaultsSnippet, @@ -237,8 +237,7 @@ export const ProviderExample = {

- setEuiDevProviderWarning - accepts three levels: + setEuiDevProviderWarning accepts three levels:

+ +

+ It also accepts a callback function instead of a default warning + level. The warning message string will be passed to your callback, + where any custom action can be performed on it. Example usage: +

+ ), }, diff --git a/packages/eui/src-docs/src/views/provider/provider_warning.tsx b/packages/eui/src-docs/src/views/provider/provider_warning.tsx index 3d5b1fc5dc3..debd683eb55 100644 --- a/packages/eui/src-docs/src/views/provider/provider_warning.tsx +++ b/packages/eui/src-docs/src/views/provider/provider_warning.tsx @@ -35,3 +35,16 @@ const AppWithDuplicateProvider = () => ( ); }; + +export const CallbackExample = () => ( + + {`import { setEuiDevProviderWarning } from '@elastic/eui'; + +const customWarningHandler = (message: string) => { + sendWarningToTelemetryService(message); + console.debug(message); +}; + +setEuiDevProviderWarning(customWarningHandler);`} + +); diff --git a/packages/eui/src/components/accessibility/screen_reader_live/__snapshots__/screen_reader_live.test.tsx.snap b/packages/eui/src/components/accessibility/screen_reader_live/__snapshots__/screen_reader_live.test.tsx.snap index 079c3c633f8..c649a8ed157 100644 --- a/packages/eui/src/components/accessibility/screen_reader_live/__snapshots__/screen_reader_live.test.tsx.snap +++ b/packages/eui/src/components/accessibility/screen_reader_live/__snapshots__/screen_reader_live.test.tsx.snap @@ -129,7 +129,8 @@ exports[`EuiScreenReaderLive with dynamic properties alternates rendering screen role="status" >

- Number of active options: 1 + Number of active options: + 1

diff --git a/packages/eui/src/components/accessibility/screen_reader_live/screen_reader_live.test.tsx b/packages/eui/src/components/accessibility/screen_reader_live/screen_reader_live.test.tsx index 1275942597d..2bdad3e7889 100644 --- a/packages/eui/src/components/accessibility/screen_reader_live/screen_reader_live.test.tsx +++ b/packages/eui/src/components/accessibility/screen_reader_live/screen_reader_live.test.tsx @@ -7,9 +7,7 @@ */ import React, { useState } from 'react'; -import { mount } from 'enzyme'; - -import { findTestSubject } from '../../../test'; +import { fireEvent } from '@testing-library/react'; import { render } from '../../../test/rtl'; import { EuiScreenReaderLive } from './screen_reader_live'; @@ -94,25 +92,23 @@ describe('EuiScreenReaderLive', () => { }); it('alternates rendering screen reader content into the second live region when changed/toggled', () => { - const component = mount(); + const { container, getByTestSubject } = render(); - findTestSubject(component, 'increment').simulate('click'); + fireEvent.click(getByTestSubject('increment')); - expect(component.render()).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); }); describe('with focus behavior', () => { it('sets focus correctly', () => { - const component = mount( + const { container } = render( {content} ); - const focusableDiv = component.find('div').at(0); - - expect(focusableDiv.is(':focus')).toBe(true); + expect(container.firstChild).toHaveFocus(); }); }); }); diff --git a/packages/eui/src/components/accessibility/skip_link/skip_link.test.tsx b/packages/eui/src/components/accessibility/skip_link/skip_link.test.tsx index 9cc00a8a66b..4dc30f7ce1e 100644 --- a/packages/eui/src/components/accessibility/skip_link/skip_link.test.tsx +++ b/packages/eui/src/components/accessibility/skip_link/skip_link.test.tsx @@ -7,8 +7,7 @@ */ import React from 'react'; -import { mount } from 'enzyme'; -import { fireEvent } from '@testing-library/react'; +import { fireEvent, createEvent } from '@testing-library/react'; import { render } from '../../../test/rtl'; import { requiredProps } from '../../../test'; @@ -34,14 +33,18 @@ describe('EuiSkipLink', () => { const focusSpy = jest.fn(); mockElement.focus = focusSpy; - const component = mount( - + const { getByTestSubject } = render( + ); - const preventDefault = jest.fn(); - component.find('a').simulate('click', { preventDefault }); + const event = createEvent.click(getByTestSubject('skip-link')); + fireEvent(getByTestSubject('skip-link'), event); - expect(preventDefault).toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(true); expect(focusSpy).toHaveBeenCalled(); }); @@ -49,14 +52,18 @@ describe('EuiSkipLink', () => { const scrollSpy = jest.fn(); mockElement.scrollIntoView = scrollSpy; - const component = mount( - + const { getByTestSubject } = render( + ); - component.find('a').simulate('click'); + fireEvent.click(getByTestSubject('skip-link')); expect(scrollSpy).not.toHaveBeenCalled(); mockElement.getBoundingClientRect = () => ({ top: 1000 } as any); - component.find('a').simulate('click'); + fireEvent.click(getByTestSubject('skip-link')); expect(scrollSpy).toHaveBeenCalled(); }); diff --git a/packages/eui/src/components/accordion/__snapshots__/accordion.test.tsx.snap b/packages/eui/src/components/accordion/__snapshots__/accordion.test.tsx.snap index 94a560bdfc6..451b489a4a3 100644 --- a/packages/eui/src/components/accordion/__snapshots__/accordion.test.tsx.snap +++ b/packages/eui/src/components/accordion/__snapshots__/accordion.test.tsx.snap @@ -1,217 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EuiAccordion behavior closes when clicked twice 1`] = ` -
-
- - -
-
-
-

- You can not see me. -

-
-
-
-`; - -exports[`EuiAccordion behavior does not open when isDisabled 1`] = ` -
-
- - -
-
-
-

- You cannot see me. -

-
-
-
-`; - -exports[`EuiAccordion behavior opens when clicked once 1`] = ` -
-
- - -
-
-
-

- You can see me. -

-
-
-
-`; - -exports[`EuiAccordion behavior opens when div is clicked if element is a div 1`] = ` -
-
- - -
-
-
-

- You can see me. -

-
-
-
-`; - exports[`EuiAccordion is rendered 1`] = `
{ it('accepts and calls an optional callback on click', () => { const onToggleHandler = jest.fn(); - const component = mount( + const { getAllByRole } = render( { /> ); - component.find('button').at(0).simulate('click'); + fireEvent.click(getAllByRole('button')[0]); expect(onToggleHandler).toBeCalled(); expect(onToggleHandler).toBeCalledWith(true); }); @@ -256,66 +256,84 @@ describe('EuiAccordion', () => { }); describe('behavior', () => { + const expectAccordionClosed = () => { + const button = document.querySelector('.euiAccordion__button'); + expect(button).toHaveAttribute('aria-expanded', 'false'); + + const children = document.querySelector('.euiAccordion__childWrapper')!; + expect(children.className).toContain('isClosed'); + expect(children).toHaveAttribute('inert', ''); + }; + + const expectAccordionOpen = () => { + const button = document.querySelector('.euiAccordion__button')!; + expect(button).toHaveAttribute('aria-expanded', 'true'); + + const children = document.querySelector('.euiAccordion__childWrapper')!; + expect(children.className).toContain('isOpen'); + expect(children).not.toHaveAttribute('inert'); + }; + it('opens when clicked once', () => { - const component = mount( + const { getAllByRole } = render(

You can see me.

); - component.find('button').at(0).simulate('click'); + fireEvent.click(getAllByRole('button')[0]); - expect(component.render()).toMatchSnapshot(); + expectAccordionOpen(); }); it('does not open when isDisabled', () => { - const component = mount( + const { getAllByRole } = render(

You cannot see me.

); - component.find('button').at(0).simulate('click'); + fireEvent.click(getAllByRole('button')[0]); - expect(component.render()).toMatchSnapshot(); + expectAccordionClosed(); }); it('opens when div is clicked if element is a div', () => { - const component = mount( + const { getAllByRole } = render(

You can see me.

); - component.find('button').at(0).simulate('click'); + fireEvent.click(getAllByRole('button')[0]); - expect(component.render()).toMatchSnapshot(); + expectAccordionOpen(); }); it('closes when clicked twice', () => { - const component = mount( + const { getAllByRole } = render(

You can not see me.

); - component.find('button').at(0).simulate('click'); - component.find('button').at(0).simulate('click'); + fireEvent.click(getAllByRole('button')[0]); + fireEvent.click(getAllByRole('button')[0]); - expect(component.render()).toMatchSnapshot(); + expectAccordionClosed(); }); it('accepts and calls an optional callback on open and close', () => { const onToggleHandler = jest.fn(); - const component = mount( + const { getAllByRole } = render( ); - component.find('button').at(0).simulate('click'); + fireEvent.click(getAllByRole('button')[0]); expect(onToggleHandler).toBeCalled(); expect(onToggleHandler).toBeCalledWith(true); - component.find('button').at(0).simulate('click'); + fireEvent.click(getAllByRole('button')[0]); expect(onToggleHandler).toBeCalled(); expect(onToggleHandler).toBeCalledWith(false); }); diff --git a/packages/eui/src/components/basic_table/__snapshots__/custom_item_action.test.tsx.snap b/packages/eui/src/components/basic_table/__snapshots__/custom_item_action.test.tsx.snap index f8b1953b4e4..0a327e8258d 100644 --- a/packages/eui/src/components/basic_table/__snapshots__/custom_item_action.test.tsx.snap +++ b/packages/eui/src/components/basic_table/__snapshots__/custom_item_action.test.tsx.snap @@ -2,7 +2,7 @@ exports[`CustomItemAction render 1`] = `
test diff --git a/packages/eui/src/components/basic_table/__snapshots__/in_memory_table.test.tsx.snap b/packages/eui/src/components/basic_table/__snapshots__/in_memory_table.test.tsx.snap index c22cc2cbc85..dd93726382c 100644 --- a/packages/eui/src/components/basic_table/__snapshots__/in_memory_table.test.tsx.snap +++ b/packages/eui/src/components/basic_table/__snapshots__/in_memory_table.test.tsx.snap @@ -216,32 +216,6 @@ exports[`EuiInMemoryTable empty array 1`] = `
`; -exports[`EuiInMemoryTable with executeQueryOptions 1`] = ` - - } - onChange={[Function]} - tableLayout="fixed" -/> -`; - exports[`EuiInMemoryTable with items 1`] = `
{ describe('behavior', () => { describe('selected items', () => { - let props: EuiBasicTableProps<{ id: string; name: string }>; - let component: ReactWrapper; - - beforeEach(() => { - props = { - items: [ - { id: '1', name: 'name1' }, - { id: '2', name: 'name2' }, - ], - itemId: 'id', - columns: [ - { - field: 'name', - name: 'Name', - description: 'description', - }, - ], - selection: { - onSelectionChange: () => {}, + const props: EuiBasicTableProps<{ id: string; name: string }> = { + items: [ + { id: '1', name: 'name1' }, + { id: '2', name: 'name2' }, + ], + itemId: 'id', + columns: [ + { + field: 'name', + name: 'Name', + description: 'description', }, - onChange: () => {}, - }; - - component = mount(); - }); + ], + selection: { + onSelectionChange: () => {}, + }, + onChange: () => {}, + }; test('check the select all checkbox when all are selected', () => { - findTestSubject(component, 'checkboxSelectRow-1').simulate('change', { - target: { checked: true }, - }); - findTestSubject(component, 'checkboxSelectRow-2').simulate('change', { - target: { checked: true }, - }); - expect( - findTestSubject(component, 'checkboxSelectAll').prop('checked') - ).toBe(true); + const { getByTestSubject } = render(); + + fireEvent.click(getByTestSubject('checkboxSelectRow-1')); + fireEvent.click(getByTestSubject('checkboxSelectRow-2')); + expect(getByTestSubject('checkboxSelectAll')).toBeChecked(); }); test('uncheck the select all checkbox when some are selected', () => { - findTestSubject(component, 'checkboxSelectRow-1').simulate('change', { - target: { checked: true }, - }); - expect( - findTestSubject(component, 'checkboxSelectAll').prop('checked') - ).toBe(false); + const { getByTestSubject } = render(); + + fireEvent.click(getByTestSubject('checkboxSelectRow-1')); + expect(getByTestSubject('checkboxSelectAll')).not.toBeChecked(); }); test('are all selected when the select all checkbox is checked', () => { - findTestSubject(component, 'checkboxSelectAll').simulate('change', { - target: { checked: true }, - }); - expect( - findTestSubject(component, 'checkboxSelectRow-1').prop('checked') - ).toBe(true); - expect( - findTestSubject(component, 'checkboxSelectRow-2').prop('checked') - ).toBe(true); + const { getByTestSubject } = render(); + + fireEvent.click(getByTestSubject('checkboxSelectAll')); + expect(getByTestSubject('checkboxSelectRow-1')).toBeChecked(); + expect(getByTestSubject('checkboxSelectRow-2')).toBeChecked(); }); test('select all checkbox becomes unchecked when selected items are deleted', () => { - findTestSubject(component, 'checkboxSelectAll').simulate('change', { - target: { checked: true }, - }); - props.items = []; - component.setProps(props); - expect( - findTestSubject(component, 'checkboxSelectAll').prop('checked') - ).toBe(false); + const { getByTestSubject, rerender } = render( + + ); + fireEvent.click(getByTestSubject('checkboxSelectAll')); + expect(getByTestSubject('checkboxSelectAll')).toBeChecked(); + + rerender(); + expect(getByTestSubject('checkboxSelectAll')).not.toBeChecked(); }); }); }); diff --git a/packages/eui/src/components/basic_table/custom_item_action.test.tsx b/packages/eui/src/components/basic_table/custom_item_action.test.tsx index 3da90485f01..7f06dc681ed 100644 --- a/packages/eui/src/components/basic_table/custom_item_action.test.tsx +++ b/packages/eui/src/components/basic_table/custom_item_action.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { render } from '../../test/rtl'; import { CustomItemAction, CustomItemActionProps } from './custom_item_action'; describe('CustomItemAction', () => { @@ -21,8 +21,8 @@ describe('CustomItemAction', () => { className: 'test', }; - const component = shallow(); + const { container } = render(); - expect(component).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); }); diff --git a/packages/eui/src/components/basic_table/in_memory_table.test.tsx b/packages/eui/src/components/basic_table/in_memory_table.test.tsx index 6e0371bedfe..9ee07e98cbd 100644 --- a/packages/eui/src/components/basic_table/in_memory_table.test.tsx +++ b/packages/eui/src/components/basic_table/in_memory_table.test.tsx @@ -7,7 +7,6 @@ */ import React from 'react'; -import { mount, shallow } from 'enzyme'; import { fireEvent } from '@testing-library/react'; import { render } from '../../test/rtl'; import { requiredProps } from '../../test'; @@ -36,8 +35,6 @@ interface ComplexItem { }; } -// TODO: Convert remaining shallow/mount tests to RTL - describe('EuiInMemoryTable', () => { test('empty array', () => { const props: EuiInMemoryTableProps = { @@ -93,26 +90,6 @@ describe('EuiInMemoryTable', () => { expect(container.querySelector('.euiBasicTable-loading')).toBeTruthy(); }); - test('with executeQueryOptions', () => { - const props: EuiInMemoryTableProps = { - ...requiredProps, - items: [], - columns: [ - { - field: 'name', - name: 'Name', - description: 'description', - }, - ], - executeQueryOptions: { - defaultFields: ['name'], - }, - }; - const component = shallow(); - - expect(component.find(EuiInMemoryTable).dive()).toMatchSnapshot(); - }); - test('with items', () => { const props: EuiInMemoryTableProps = { ...requiredProps, @@ -309,6 +286,11 @@ describe('EuiInMemoryTable', () => { }); describe('sorting', () => { + const getCellTextArray = () => + Array.from( + document.querySelectorAll('tbody .euiTableCellContent__text') + ).map((cell) => cell.textContent); + test('with field sorting (off by default)', () => { const props: EuiInMemoryTableProps = { ...requiredProps, @@ -327,13 +309,9 @@ describe('EuiInMemoryTable', () => { ], sorting: true, }; - const component = mount(); + render(); - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['name3', 'name1', 'name2']); + expect(getCellTextArray()).toEqual(['name3', 'name1', 'name2']); }); test('with field sorting (on by default)', () => { @@ -359,13 +337,9 @@ describe('EuiInMemoryTable', () => { }, }, }; - const component = mount(); + render(); - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['name1', 'name2', 'name3']); + expect(getCellTextArray()).toEqual(['name1', 'name2', 'name3']); }); test('with name sorting', () => { @@ -391,13 +365,9 @@ describe('EuiInMemoryTable', () => { }, }, }; - const component = mount(); + render(); - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['name3', 'name2', 'name1']); + expect(getCellTextArray()).toEqual(['name3', 'name2', 'name1']); }); test('verify field sorting precedes name sorting', () => { @@ -429,14 +399,17 @@ describe('EuiInMemoryTable', () => { }, }, }; - const component = mount(); + render(); // name TDs should be sorted desc, id TDs should be asc, - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['name3', '1', 'name2', '2', 'name1', '3']); + expect(getCellTextArray()).toEqual([ + 'name3', + '1', + 'name2', + '2', + 'name1', + '3', + ]); }); test('verify an invalid sort field does not blow everything up', () => { @@ -463,7 +436,7 @@ describe('EuiInMemoryTable', () => { }, }; expect(() => { - mount(); + render(); }).not.toThrow(); }); @@ -496,54 +469,114 @@ describe('EuiInMemoryTable', () => { }, }; - const component = mount(); + const { rerender } = render(); // initial sorting: id asc - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['1', 'name1', '2', 'name2', '3', 'name3']); + expect(getCellTextArray()).toEqual([ + '1', + 'name1', + '2', + 'name2', + '3', + 'name3', + ]); // sorting: id desc - component.setProps({ - sorting: { sort: { field: 'id', direction: SortDirection.DESC } }, - }); - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['3', 'name3', '2', 'name2', '1', 'name1']); + rerender( + + ); + expect(getCellTextArray()).toEqual([ + '3', + 'name3', + '2', + 'name2', + '1', + 'name1', + ]); // sorting: name asc - component.setProps({ - sorting: { sort: { field: 'name', direction: SortDirection.ASC } }, - }); - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['1', 'name1', '2', 'name2', '3', 'name3']); + rerender( + + ); + expect(getCellTextArray()).toEqual([ + '1', + 'name1', + '2', + 'name2', + '3', + 'name3', + ]); // sorting: name desc - component.setProps({ - sorting: { sort: { field: 'name', direction: SortDirection.DESC } }, - }); - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['3', 'name3', '2', 'name2', '1', 'name1']); + rerender( + + ); + expect(getCellTextArray()).toEqual([ + '3', + 'name3', + '2', + 'name2', + '1', + 'name1', + ]); // can return to initial sorting: id asc - component.setProps({ - sorting: { sort: { field: 'id', direction: SortDirection.ASC } }, + rerender( + + ); + expect(getCellTextArray()).toEqual([ + '1', + 'name1', + '2', + 'name2', + '3', + 'name3', + ]); + }); + + describe('custom column sorting', () => { + it('calls the sortable function and uses its return value for sorting', () => { + const sortable = jest.fn(({ id }: any) => id); // Sorts by the ID instead of the name + + const props: EuiInMemoryTableProps = { + ...requiredProps, + items: [ + { id: 7, name: 'Alfred' }, + { id: 3, name: 'Betty' }, + { id: 5, name: 'Charlie' }, + ], + itemId: 'id', + columns: [ + { + field: 'name', + name: 'Name', + sortable, + }, + ], + sorting: { + sort: { + field: 'name', + direction: SortDirection.ASC, + }, + }, + }; + render(); + + expect(sortable).toHaveBeenCalled(); + expect(getCellTextArray()).toEqual(['Betty', 'Charlie', 'Alfred']); }); - expect( - component - .find('tbody .euiTableCellContent__text') - .map((cell) => cell.text()) - ).toEqual(['1', 'name1', '2', 'name2', '3', 'name3']); }); }); @@ -942,34 +975,28 @@ describe('EuiInMemoryTable', () => { className: 'testTable', }; - const component = mount(); + const { container } = render(); // should render with all three results visible - expect(component.find('.testTable EuiTableRow').length).toBe(3); + expect(container.querySelectorAll('tbody tr')).toHaveLength(3); - const searchField = component.find('input.euiFieldSearch'); + const searchField = container.querySelector('input.euiFieldSearch')!; - searchField.simulate('keyUp', { - target: { - value: 'is:active', - }, + fireEvent.keyUp(searchField, { + target: { value: 'is:active' }, key: keys.ENTER, }); - component.update(); // should render with the two active results - expect(component.find('.testTable EuiTableRow').length).toBe(2); + expect(container.querySelectorAll('tbody tr')).toHaveLength(2); - searchField.simulate('keyUp', { - target: { - value: 'active:false', - }, + fireEvent.keyUp(searchField, { + target: { value: 'active:false' }, key: keys.ENTER, }); - component.update(); // should render with the one inactive result - expect(component.find('.testTable EuiTableRow').length).toBe(1); + expect(container.querySelectorAll('tbody tr')).toHaveLength(1); }); it('passes down the executeQueryOptions properly', () => { @@ -1012,9 +1039,9 @@ describe('EuiInMemoryTable', () => { message: No items found!, }; - const noDefaultFieldsComponent = mount(); + const { container } = render(); // should render with the no items found text - expect(noDefaultFieldsComponent.find('.customMessage').length).toBe(1); + expect(container.querySelector('.customMessage')).toBeInTheDocument(); // With defaultFields and a search query, we should only see one const props2: EuiInMemoryTableProps = { @@ -1056,44 +1083,10 @@ describe('EuiInMemoryTable', () => { message: No items found!, }; - const defaultFieldComponent = mount(); - expect(defaultFieldComponent.find('.testTable EuiTableRow').length).toBe( - 1 + const { container: container2 } = render( + ); - }); - }); - - describe('custom column sorting', () => { - it('calls the sortable function and uses its return value for sorting', () => { - const props: EuiInMemoryTableProps = { - ...requiredProps, - items: [ - { id: 7, name: 'Alfred' }, - { id: 3, name: 'Betty' }, - { id: 5, name: 'Charlie' }, - ], - itemId: 'id', - columns: [ - { - field: 'name', - name: 'Name', - sortable: ({ id }: any) => id, - }, - ], - sorting: { - sort: { - field: 'name', - direction: SortDirection.ASC, - }, - }, - }; - const component = mount(); - - expect((component.find('EuiBasicTable').props() as any).items).toEqual([ - { id: 3, name: 'Betty' }, - { id: 5, name: 'Charlie' }, - { id: 7, name: 'Alfred' }, - ]); + expect(container2.querySelectorAll('tbody tr')).toHaveLength(1); }); }); diff --git a/packages/eui/src/components/button/button.test.tsx b/packages/eui/src/components/button/button.test.tsx index 367f3a72bf5..c97c3fd4c64 100644 --- a/packages/eui/src/components/button/button.test.tsx +++ b/packages/eui/src/components/button/button.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { mount } from 'enzyme'; +import { fireEvent } from '@testing-library/react'; import { shouldRenderCustomStyles } from '../../test/internal'; import { requiredProps } from '../../test/required_props'; import { render } from '../../test/rtl'; @@ -166,16 +166,16 @@ describe('EuiButton', () => { describe('onClick', () => { it('supports onClick and href', () => { const handler = jest.fn(); - const component = mount(); - component.find('a').simulate('click'); - expect(handler.mock.calls.length).toEqual(1); + const { getByRole } = render(); + fireEvent.click(getByRole('link')); + expect(handler).toHaveBeenCalledTimes(1); }); it('supports onClick as a button', () => { const handler = jest.fn(); - const component = mount(); - component.find('button').simulate('click'); - expect(handler.mock.calls.length).toEqual(1); + const { getByRole } = render(); + fireEvent.click(getByRole('button')); + expect(handler).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/eui/src/components/card/card.test.tsx b/packages/eui/src/components/card/card.test.tsx index fe78a65de72..28334a47482 100644 --- a/packages/eui/src/components/card/card.test.tsx +++ b/packages/eui/src/components/card/card.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { mount } from 'enzyme'; +import { fireEvent } from '@testing-library/react'; import { requiredProps } from '../../test'; import { shouldRenderCustomStyles } from '../../test/internal'; import { render } from '../../test/rtl'; @@ -126,33 +126,33 @@ describe('EuiCard', () => { describe('onClick', () => { it('supports onClick as a link', () => { const handler = jest.fn(); - const component = mount( + const { getByRole } = render( ); - component.find('a').simulate('click'); - expect(handler.mock.calls.length).toEqual(1); + fireEvent.click(getByRole('link')); + expect(handler).toHaveBeenCalledTimes(1); }); it('supports onClick as a button', () => { const handler = jest.fn(); - const component = mount( + const { getByRole } = render( ); - component.find('button').simulate('click'); - expect(handler.mock.calls.length).toEqual(1); + fireEvent.click(getByRole('button')); + expect(handler).toHaveBeenCalledTimes(1); }); it('should only call onClick once when title is a React node', () => { const handler = jest.fn(); - const component = mount( + const { getByTestSubject } = render( Hoi} description="There" onClick={handler} /> ); - component.find('[data-test-subj="click"]').simulate('click'); - expect(handler.mock.calls.length).toEqual(1); + fireEvent.click(getByTestSubject('click')); + expect(handler).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/eui/src/components/code/__snapshots__/utils.test.tsx.snap b/packages/eui/src/components/code/__snapshots__/utils.test.tsx.snap index 89eef9260f8..b3690a25ae4 100644 --- a/packages/eui/src/components/code/__snapshots__/utils.test.tsx.snap +++ b/packages/eui/src/components/code/__snapshots__/utils.test.tsx.snap @@ -1451,29 +1451,30 @@ Array [ `; exports[`shared utils nodeToHtml handles rendering custom annotation types 1`] = ` -
- +
- - Hello world - - -
+ + +
+ `; exports[`shared utils nodeToHtml recursively converts refactor nodes to React JSX 1`] = ` -
- - Hello world - -
+ + Hello world + `; diff --git a/packages/eui/src/components/code/code_block.test.tsx b/packages/eui/src/components/code/code_block.test.tsx index a9db61f3230..237caf6ca7e 100644 --- a/packages/eui/src/components/code/code_block.test.tsx +++ b/packages/eui/src/components/code/code_block.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { mount, shallow } from 'enzyme'; +import { fireEvent } from '@testing-library/react'; import { requiredProps } from '../../test/required_props'; import { render } from '../../test/rtl'; @@ -124,7 +124,7 @@ describe('EuiCodeBlock', () => { describe('fullscreen', () => { it('displays content in fullscreen mode', () => { - const component = mount( + const { getByLabelText, baseElement } = render( { const value = "hello" ); - component.find('button[aria-label="Expand"]').simulate('click'); - component.update(); + fireEvent.click(getByLabelText('Expand')); expect( - component.find('div.euiCodeBlockFullScreen').render() + baseElement.querySelector('.euiCodeBlockFullScreen') ).toMatchSnapshot(); }); it('closes fullscreen mode when the escape key is pressed', () => { - const component = mount( + const { getByLabelText, baseElement } = render( { const value = "world" ); - component.find('button[aria-label="Expand"]').simulate('click'); - component.update(); - component - .find('div.euiCodeBlockFullScreen') - .find('pre.euiCodeBlock__pre') - .simulate('keyDown', { key: 'Escape' }); - expect(component.find('.euiCodeBlockFullScreen')).toHaveLength(0); + fireEvent.click(getByLabelText('Expand')); + + fireEvent.keyDown( + baseElement.querySelector( + '.euiCodeBlockFullScreen .euiCodeBlock__pre' + )!, + { key: 'Escape' } + ); + expect( + baseElement.querySelector('.euiCodeBlockFullScreen') + ).not.toBeInTheDocument(); }); }); @@ -177,11 +180,11 @@ describe('EuiCodeBlock', () => { describe('type checks', () => { it('requires overflowHeight', () => { // @ts-expect-error should expect overflowHeight - shallow(); + render(); }); it('only allows whiteSpace of pre', () => { - shallow( + render( // @ts-expect-error should only accept "pre" { /> ); // OK - shallow( + render( ); }); diff --git a/packages/eui/src/components/code/utils.test.tsx b/packages/eui/src/components/code/utils.test.tsx index 02afbf3cce3..b3cd2112918 100644 --- a/packages/eui/src/components/code/utils.test.tsx +++ b/packages/eui/src/components/code/utils.test.tsx @@ -7,8 +7,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; -import { renderHook } from '../../test/rtl'; +import { render, renderHook } from '../../test/rtl'; import { useEuiTheme } from '../../services'; import { @@ -66,8 +65,8 @@ describe('shared utils', () => { 0, [] ); - const component = shallow(
{output}
); - expect(component).toMatchSnapshot(); + const { container } = render(<>{output}); + expect(container.firstChild).toMatchSnapshot(); }); it('handles rendering custom annotation types', () => { @@ -88,8 +87,8 @@ describe('shared utils', () => { 0, [] ); - const component = shallow(
{output}
); - expect(component).toMatchSnapshot(); + const { container } = render(<>{output}); + expect(container.firstChild).toMatchSnapshot(); }); }); }); diff --git a/packages/eui/src/services/theme/warning.test.ts b/packages/eui/src/services/theme/warning.test.ts index 46a7bb13bc3..e83e57acb64 100644 --- a/packages/eui/src/services/theme/warning.test.ts +++ b/packages/eui/src/services/theme/warning.test.ts @@ -80,5 +80,18 @@ describe('EUI provider dev warnings', () => { expect(consoleLogSpy).not.toHaveBeenCalled(); expect(consoleWarnSpy).not.toHaveBeenCalled(); }); + + it('passes messages to callback functions', () => { + const devCallback = jest.fn(); + setEuiDevProviderWarning(devCallback); + + emitEuiProviderWarning(providerMessage); + + expect(devCallback).toHaveBeenCalledWith('hello world'); + expect(devCallback).toHaveBeenCalledTimes(1); + + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); }); }); diff --git a/packages/eui/src/services/theme/warning.ts b/packages/eui/src/services/theme/warning.ts index f4d2ac7f9b5..bcff37ed4ac 100644 --- a/packages/eui/src/services/theme/warning.ts +++ b/packages/eui/src/services/theme/warning.ts @@ -7,16 +7,22 @@ */ type LEVELS = 'log' | 'warn' | 'error'; +type ProviderCallback = (message: string | Error) => void; -let providerWarning: LEVELS | undefined = undefined; +let providerWarning: LEVELS | ProviderCallback | undefined = undefined; -export const setEuiDevProviderWarning = (level: LEVELS | undefined) => - (providerWarning = level); +export const setEuiDevProviderWarning = (warningType: typeof providerWarning) => + (providerWarning = warningType); export const getEuiDevProviderWarning = () => providerWarning; // Not a public top-level EUI export, currently for internal use export const emitEuiProviderWarning = (providerMessage: string) => { + // Handle callback types + if (typeof providerWarning === 'function') { + return providerWarning(providerMessage); + } + // Handle level types switch (providerWarning) { case 'log': console.log(providerMessage); diff --git a/wiki/contributing-to-eui/running-eui-locally.md b/wiki/contributing-to-eui/running-eui-locally.md index ccba91defca..591eeb54de4 100644 --- a/wiki/contributing-to-eui/running-eui-locally.md +++ b/wiki/contributing-to-eui/running-eui-locally.md @@ -22,14 +22,27 @@ nvm install ### Dependencies -EUI uses `yarn` for dependency management. We use `npm` for release purposes only. +EUI uses [Yarn v4](https://yarnpkg.com/getting-started/install) for dependency management. We use `npm` for release purposes only. -EUI only uses [yarn@v1 (classic)](https://classic.yarnpkg.com/en/docs/install), and not yarn v2 or above. Ensure you are on the correct version via `yarn -v` before installing all dependencies: +Unlike Yarn Classic and other package managers, Yarn v4 isn't supposed to be installed globally. +Instead, it utilizes [corepack](https://nodejs.org/api/corepack.html) - a modern way to manage package managers built straight into Node.js. + +First, enable corepack: +```shell +corepack enable +``` + +`yarn` is now configured on your machine, and you can use it just like any other CLI command. +Corepack will always use the right Yarn version, even when switching branches. + +With `yarn` set up and ready to go, use it to install EUI dependencies: ```shell yarn ``` +Remember to run `yarn` whenever you're switching branches to ensure all dependencies are installed and have correct versions. + #### Puppeteer issues If you're on an Apple arm64 machine and receive an error on `yarn` about Puppeteer/the chromium binary not being available, you have two options: