From a8df8cce0b284266431b5bfc1a25b75a5c3f8620 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 13:28:51 -0700 Subject: [PATCH 01/12] [setup] DRY out warning logic and write more robust unit tests for it --- src/services/theme/hooks.tsx | 18 ++----- src/services/theme/warning.test.ts | 84 ++++++++++++++++++++++++++++++ src/services/theme/warning.ts | 17 ++++++ 3 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 src/services/theme/warning.test.ts diff --git a/src/services/theme/hooks.tsx b/src/services/theme/hooks.tsx index ec74ac1b8ed..132d3a35f4f 100644 --- a/src/services/theme/hooks.tsx +++ b/src/services/theme/hooks.tsx @@ -14,7 +14,7 @@ import { EuiColorModeContext, defaultComputedTheme, } from './context'; -import { getEuiDevProviderWarning } from './warning'; +import { emitEuiProviderWarning } from './warning'; import { EuiThemeColorModeStandard, EuiThemeModifications, @@ -39,20 +39,8 @@ export const useEuiTheme = (): UseEuiTheme => { const modifications = useContext(EuiModificationsContext); const isFallback = theme === defaultComputedTheme; - const warningLevel = getEuiDevProviderWarning(); - if (isFallback && typeof warningLevel !== 'undefined') { - switch (warningLevel) { - case 'log': - console.log(providerMessage); - break; - case 'warn': - console.warn(providerMessage); - break; - case 'error': - throw new Error(providerMessage); - default: - break; - } + if (isFallback) { + emitEuiProviderWarning(providerMessage); } const assembledTheme = useMemo( diff --git a/src/services/theme/warning.test.ts b/src/services/theme/warning.test.ts new file mode 100644 index 00000000000..46a7bb13bc3 --- /dev/null +++ b/src/services/theme/warning.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + setEuiDevProviderWarning, + getEuiDevProviderWarning, + emitEuiProviderWarning, +} from './warning'; + +describe('EUI provider dev warnings', () => { + const defaultWarningLevel = getEuiDevProviderWarning(); + afterEach(() => { + setEuiDevProviderWarning(defaultWarningLevel); + jest.resetAllMocks(); + }); + + describe('getting and setting the current dev warning level', () => { + describe('getEuiDevProviderWarning', () => { + it('defaults to undefined', () => { + expect(getEuiDevProviderWarning()).toEqual(undefined); + }); + }); + + describe('setEuiDevProviderWarning', () => { + it('allows configuring the global provider warning level', () => { + setEuiDevProviderWarning('log'); + expect(getEuiDevProviderWarning()).toEqual('log'); + }); + }); + }); + + describe('emitEuiProviderWarning', () => { + // Silence logs and warnings from CLI output + const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); + const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + afterAll(() => { + consoleLogSpy.mockRestore(); + consoleWarnSpy.mockRestore(); + }); + + const providerMessage = 'hello world'; + + it('does nothing if the warning level is undefined', () => { + emitEuiProviderWarning(providerMessage); + + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + + it('emits a console log when level is log', () => { + setEuiDevProviderWarning('log'); + + emitEuiProviderWarning(providerMessage); + + expect(consoleLogSpy).toHaveBeenCalledWith('hello world'); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + + it('emits a console warning when level is warn', () => { + setEuiDevProviderWarning('warn'); + + emitEuiProviderWarning(providerMessage); + + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).toHaveBeenCalledWith('hello world'); + }); + + it('throws an error when level is error', () => { + setEuiDevProviderWarning('error'); + + expect(() => emitEuiProviderWarning(providerMessage)).toThrowError( + 'hello world' + ); + + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/services/theme/warning.ts b/src/services/theme/warning.ts index 04311bc0d81..f4d2ac7f9b5 100644 --- a/src/services/theme/warning.ts +++ b/src/services/theme/warning.ts @@ -14,3 +14,20 @@ export const setEuiDevProviderWarning = (level: LEVELS | undefined) => (providerWarning = level); export const getEuiDevProviderWarning = () => providerWarning; + +// Not a public top-level EUI export, currently for internal use +export const emitEuiProviderWarning = (providerMessage: string) => { + switch (providerWarning) { + case 'log': + console.log(providerMessage); + break; + case 'warn': + console.warn(providerMessage); + break; + case 'error': + throw new Error(providerMessage); + case undefined: + default: + break; + } +}; From 89e61debd0e7561de12697e7139a6b4af9532ef4 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 14:41:02 -0700 Subject: [PATCH 02/12] [cleanup] Move `EuiThemeProvider` nested tests to theme provider file - they don't really belong in `EuiProvider` --- .../__snapshots__/provider.test.tsx.snap | 37 ---------- src/components/provider/provider.test.tsx | 57 --------------- .../__snapshots__/provider.test.tsx.snap | 38 ++++++++++ src/services/theme/provider.test.tsx | 71 +++++++++++++++++++ 4 files changed, 109 insertions(+), 94 deletions(-) create mode 100644 src/services/theme/__snapshots__/provider.test.tsx.snap create mode 100644 src/services/theme/provider.test.tsx diff --git a/src/components/provider/__snapshots__/provider.test.tsx.snap b/src/components/provider/__snapshots__/provider.test.tsx.snap index 5c03d60237f..d83523850cd 100644 --- a/src/components/provider/__snapshots__/provider.test.tsx.snap +++ b/src/components/provider/__snapshots__/provider.test.tsx.snap @@ -1057,43 +1057,6 @@ exports[`EuiProvider is rendered 1`] = ` `; -exports[`EuiProvider nested EuiThemeProviders allows avoiding the extra span wrapper with \`wrapperProps.cloneElement\` 1`] = ` -
- Top-level provider - -
- clone provider color onto div -
-
-`; - -exports[`EuiProvider nested EuiThemeProviders allows customizing the span wrapper with \`wrapperProps\` 1`] = ` -
- Top-level provider - - - Nested - -
-`; - -exports[`EuiProvider nested EuiThemeProviders renders with a span wrapper that sets the inherited text color 1`] = ` -
- Top-level provider - - - Nested - -
-`; - exports[`EuiProvider providing an @emotion cache config applies the cache to all styles 1`] = ` { @@ -109,59 +107,4 @@ describe('EuiProvider', () => { expect(component).toMatchSnapshot(); }); }); - - describe('nested EuiThemeProviders', () => { - it('renders with a span wrapper that sets the inherited text color', () => { - const { container } = render( - - Top-level provider{' '} - Nested - - ); - - expect(container).toMatchSnapshot(); - }); - - it('allows customizing the span wrapper with `wrapperProps`', () => { - const customCss = css` - display: flex; - `; - - const { container } = render( - - Top-level provider{' '} - - Nested - - - ); - - expect(container).toMatchSnapshot(); - expect(container.querySelector('.test')).toBeTruthy(); - }); - - it('allows avoiding the extra span wrapper with `wrapperProps.cloneElement`', () => { - const { container } = render( - - Top-level provider{' '} - -
clone provider color onto div
-
-
- ); - - expect(container).toMatchSnapshot(); - expect(container.querySelector('.hello.world')).toBeTruthy(); - }); - }); }); diff --git a/src/services/theme/__snapshots__/provider.test.tsx.snap b/src/services/theme/__snapshots__/provider.test.tsx.snap new file mode 100644 index 00000000000..56de2bb16d2 --- /dev/null +++ b/src/services/theme/__snapshots__/provider.test.tsx.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiThemeProvider nested EuiThemeProviders allows avoiding the extra span wrapper with \`wrapperProps.cloneElement\` 1`] = ` +
+ Top-level provider + +
+ clone provider color onto div +
+
+`; + +exports[`EuiThemeProvider nested EuiThemeProviders allows customizing the span wrapper with \`wrapperProps\` 1`] = ` +
+ Top-level provider + + + Nested + +
+`; + +exports[`EuiThemeProvider nested EuiThemeProviders renders with a span wrapper that sets the inherited text color 1`] = ` +
+ Top-level provider + + + Nested + +
+`; diff --git a/src/services/theme/provider.test.tsx b/src/services/theme/provider.test.tsx new file mode 100644 index 00000000000..093b09963aa --- /dev/null +++ b/src/services/theme/provider.test.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; // Note - don't use the EUI custom RTL `render`, as it auto-wraps an `EuiProvider` +import { css } from '@emotion/react'; + +import { EuiProvider } from '../../components/provider'; +import { EuiThemeProvider } from './provider'; + +describe('EuiThemeProvider', () => { + describe('nested EuiThemeProviders', () => { + it('renders with a span wrapper that sets the inherited text color', () => { + const { container } = render( + + Top-level provider{' '} + Nested + + ); + + expect(container).toMatchSnapshot(); + }); + + it('allows customizing the span wrapper with `wrapperProps`', () => { + const customCss = css` + display: flex; + `; + + const { container } = render( + + Top-level provider{' '} + + Nested + + + ); + + expect(container).toMatchSnapshot(); + expect(container.querySelector('.test')).toBeTruthy(); + }); + + it('allows avoiding the extra span wrapper with `wrapperProps.cloneElement`', () => { + const { container } = render( + + Top-level provider{' '} + +
clone provider color onto div
+
+
+ ); + + expect(container).toMatchSnapshot(); + expect(container.querySelector('.hello.world')).toBeTruthy(); + }); + }); +}); From f2d0903ba043ab9115c7f6e82c18bfba8245429e Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 14:57:54 -0700 Subject: [PATCH 03/12] [cleanup] Improve EuiThemeProvider's unit testing - make `EuiProvider`s tests more basic in comparison - use `toHaveStyleRule` instead of snapshots --- .../__snapshots__/provider.test.tsx.snap | 709 ------------------ src/components/provider/provider.test.tsx | 42 +- .../__snapshots__/provider.test.tsx.snap | 12 +- src/services/theme/provider.test.tsx | 70 +- 4 files changed, 105 insertions(+), 728 deletions(-) diff --git a/src/components/provider/__snapshots__/provider.test.tsx.snap b/src/components/provider/__snapshots__/provider.test.tsx.snap index d83523850cd..510a41558f6 100644 --- a/src/components/provider/__snapshots__/provider.test.tsx.snap +++ b/src/components/provider/__snapshots__/provider.test.tsx.snap @@ -1,714 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EuiProvider applying modifications propagates \`modify\` 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "speedy": [Function], - "tags": Array [], - }, - } - } -> - - - - - - - - - - -`; - -exports[`EuiProvider changing color modes propagates \`colorMode\` 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "speedy": [Function], - "tags": Array [], - }, - } - } -> - - - - - - - - - - -`; - exports[`EuiProvider is rendered 1`] = ` { }); }); - describe('changing color modes', () => { - it('propagates `colorMode`', () => { - const component = shallow(); + describe('EuiThemeProvider prop passing', () => { + const modify = { + colors: { + LIGHT: { lightShade: '#aaa' }, + DARK: { lightShade: '#333' }, + }, + }; + + it('passes `modify`', () => { + const { getByText } = render( + +
({ color: euiTheme.colors.lightShade })}> + Modified +
+
+ ); - expect(component).toMatchSnapshot(); + expect(getByText('Modified')).toHaveStyleRule('color', '#aaa'); }); - }); - describe('applying modifications', () => { - it('propagates `modify`', () => { - const component = shallow( - + it('passes `colorMode`', () => { + const { getByText } = render( + +
({ color: euiTheme.colors.lightShade })}> + Dark mode +
+
); - expect(component).toMatchSnapshot(); + expect(getByText('Dark mode')).toHaveStyleRule('color', '#333'); }); }); }); diff --git a/src/services/theme/__snapshots__/provider.test.tsx.snap b/src/services/theme/__snapshots__/provider.test.tsx.snap index 56de2bb16d2..720b4738012 100644 --- a/src/services/theme/__snapshots__/provider.test.tsx.snap +++ b/src/services/theme/__snapshots__/provider.test.tsx.snap @@ -30,9 +30,19 @@ exports[`EuiThemeProvider nested EuiThemeProviders renders with a span wrapper t Top-level provider Nested + + Double nested + + Triple nested + + `; diff --git a/src/services/theme/provider.test.tsx b/src/services/theme/provider.test.tsx index 093b09963aa..519cd645f87 100644 --- a/src/services/theme/provider.test.tsx +++ b/src/services/theme/provider.test.tsx @@ -14,12 +14,80 @@ import { EuiProvider } from '../../components/provider'; import { EuiThemeProvider } from './provider'; describe('EuiThemeProvider', () => { + describe('colorMode', () => { + it('sets `colorMode`', () => { + const { getByText } = render( + <> + +
({ color: euiTheme.colors.fullShade })}> + Light mode +
+ +
({ color: euiTheme.colors.fullShade })} + > + Inverse of light mode +
+
+
+ +
({ color: euiTheme.colors.fullShade })}> + Dark mode +
+ +
({ color: euiTheme.colors.fullShade })} + > + Inverse of dark mode +
+
+
+ + ); + + expect(getByText('Light mode')).toHaveStyleRule('color', '#000'); + expect(getByText('Dark mode')).toHaveStyleRule('color', '#FFF'); + expect(getByText('Inverse of light mode')).toHaveStyleRule( + 'color', + '#FFF' + ); + expect(getByText('Inverse of dark mode')).toHaveStyleRule( + 'color', + '#000' + ); + }); + }); + + describe('modify', () => { + it('allows overriding theme tokens', () => { + const { getByText } = render( + +
({ color: euiTheme.colors.primary })}> + Modified +
+
+ ); + + expect(getByText('Modified')).toHaveStyleRule('color', 'hotpink'); + }); + }); + describe('nested EuiThemeProviders', () => { it('renders with a span wrapper that sets the inherited text color', () => { const { container } = render( Top-level provider{' '} - Nested + + Nested + + Double nested + + Triple nested + + + ); From fe02eb1c6595986ab5c5e4be136d27ea70fddf25 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 15:01:47 -0700 Subject: [PATCH 04/12] [cleanup] Convert more EuiProvider tests to RTL from Enzyme --- .../__snapshots__/provider.test.tsx.snap | 380 ------------------ src/components/provider/provider.test.tsx | 36 +- 2 files changed, 28 insertions(+), 388 deletions(-) diff --git a/src/components/provider/__snapshots__/provider.test.tsx.snap b/src/components/provider/__snapshots__/provider.test.tsx.snap index 510a41558f6..cbf8151d0bf 100644 --- a/src/components/provider/__snapshots__/provider.test.tsx.snap +++ b/src/components/provider/__snapshots__/provider.test.tsx.snap @@ -1,353 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EuiProvider is rendered 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "speedy": [Function], - "tags": Array [], - }, - } - } -> - - - - - - - - - - -`; - exports[`EuiProvider providing an @emotion cache config applies the cache to all styles 1`] = ` `; - -exports[`EuiProvider using \`null\` theme option does not add global styles 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "speedy": [Function], - "tags": Array [], - }, - } - } -> - - - - -`; diff --git a/src/components/provider/provider.test.tsx b/src/components/provider/provider.test.tsx index 38335517965..d010506ab6f 100644 --- a/src/components/provider/provider.test.tsx +++ b/src/components/provider/provider.test.tsx @@ -14,17 +14,37 @@ import createCache from '@emotion/cache'; import { EuiProvider } from './provider'; describe('EuiProvider', () => { - it('is rendered', () => { - const component = shallow(); - - expect(component).toMatchSnapshot(); + it('renders children', () => { + const { container } = render( + +
Hello world
+
+ ); + + expect(container.firstChild).toMatchInlineSnapshot(` +
+ Hello world +
+ `); }); - describe('using `null` theme option', () => { - it('does not add global styles', () => { - const component = shallow(); + describe('global styles and reset CSS', () => { + it('renders by default', () => { + render(); - expect(component).toMatchSnapshot(); + const globalStyleElement = document.querySelector( + 'style[data-emotion="css-global"]' + ); + expect(globalStyleElement).not.toEqual(null); + }); + + it('does not render when `theme` is null', () => { + render(); + + const globalStyleElement = document.querySelector( + 'style[data-emotion="css-global"]' + ); + expect(globalStyleElement).toEqual(null); }); }); From 13a4bf3ce513e8f7bddc8d353f591585da97fbae Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 15:03:12 -0700 Subject: [PATCH 05/12] [cleanup] Convert `EuiProvider` cache tests to RTL from Enzyme --- .../__snapshots__/provider.test.tsx.snap | 1831 ----------------- src/components/provider/provider.test.tsx | 58 +- 2 files changed, 39 insertions(+), 1850 deletions(-) delete mode 100644 src/components/provider/__snapshots__/provider.test.tsx.snap diff --git a/src/components/provider/__snapshots__/provider.test.tsx.snap b/src/components/provider/__snapshots__/provider.test.tsx.snap deleted file mode 100644 index cbf8151d0bf..00000000000 --- a/src/components/provider/__snapshots__/provider.test.tsx.snap +++ /dev/null @@ -1,1831 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EuiProvider providing an @emotion cache config applies the cache to all styles 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "default", - "nonce": undefined, - "prepend": undefined, - "tags": Array [], - }, - } - } -> - - - - - - - - - - -`; - -exports[`EuiProvider providing an @emotion cache config applies the cache to each location separately 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "default", - "nonce": undefined, - "prepend": undefined, - "tags": Array [], - }, - } - } -> - - , - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "global", - "nonce": undefined, - "prepend": undefined, - "tags": Array [], - }, - } - } - > - - - , - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "utility", - "nonce": undefined, - "prepend": undefined, - "tags": Array [], - }, - } - } - > - - - - - -`; - -exports[`EuiProvider providing an @emotion cache config applies the cache to global styles 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "speedy": [Function], - "tags": Array [], - }, - } - } -> - - , - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "global", - "nonce": undefined, - "prepend": undefined, - "tags": Array [], - }, - } - } - > - - - - - - - - -`; - -exports[`EuiProvider providing an @emotion cache config applies the cache to utility styles 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "speedy": [Function], - "tags": Array [], - }, - } - } -> - - - - - , - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "utility", - "nonce": undefined, - "prepend": undefined, - "tags": Array [], - }, - } - } - > - - - - - -`; - -exports[`EuiProvider providing an @emotion cache config provides a default cache from Emotion when configured without a cache 1`] = ` -, - "ctr": 0, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "speedy": [Function], - "tags": Array [], - }, - } - } -> - - - - - - - - - - -`; diff --git a/src/components/provider/provider.test.tsx b/src/components/provider/provider.test.tsx index d010506ab6f..3de71b6daab 100644 --- a/src/components/provider/provider.test.tsx +++ b/src/components/provider/provider.test.tsx @@ -7,8 +7,8 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; import { render } from '@testing-library/react'; // Note - don't use the EUI custom RTL `render`, as it auto-wraps an `EuiProvider` +import { cache as emotionCache } from '@emotion/css'; import createCache from '@emotion/cache'; import { EuiProvider } from './provider'; @@ -59,47 +59,67 @@ describe('EuiProvider', () => { key: 'utility', }); - it('provides a default cache from Emotion when configured without a cache', () => { - const component = shallow(); + const getStyleByCss = (content: string) => { + return Array.from(document.querySelectorAll('style[data-emotion]')).find( + (el) => el?.textContent?.includes(content) + ) as HTMLStyleElement; + }; + + it('uses a default cache from Emotion when configured without a cache', () => { + render(); - expect(component).toMatchSnapshot(); - expect(component.prop('cache').key).toEqual('css'); - expect(component.prop('cache').compat).toEqual(true); + expect(emotionCache.key).toEqual('css'); + expect(getStyleByCss('html').dataset.emotion).toEqual('css-global'); + expect(getStyleByCss('.eui-displayBlock').dataset.emotion).toEqual( + 'css-global' + ); }); + it('applies the cache to all styles', () => { - const component = shallow(); + render(); - expect(component).toMatchSnapshot(); + expect(getStyleByCss('html').dataset.emotion).toEqual('default-global'); + expect(getStyleByCss('.eui-displayBlock').dataset.emotion).toEqual( + 'default-global' + ); }); it('applies the cache to global styles', () => { - const component = shallow( - - ); + render(); - expect(component).toMatchSnapshot(); + expect(getStyleByCss('html').dataset.emotion).toEqual('global-global'); + expect(getStyleByCss('.eui-displayBlock').dataset.emotion).toEqual( + 'css-global' + ); }); it('applies the cache to utility styles', () => { - const component = shallow( - - ); + render(); - expect(component).toMatchSnapshot(); + expect(getStyleByCss('html').dataset.emotion).toEqual('css-global'); + expect(getStyleByCss('.eui-displayBlock').dataset.emotion).toEqual( + 'utility-global' + ); }); it('applies the cache to each location separately', () => { - const component = shallow( + render( + > +
+ ); - expect(component).toMatchSnapshot(); + expect(getStyleByCss('.default-').dataset.emotion).toEqual('default'); + expect(getStyleByCss('html').dataset.emotion).toEqual('global-global'); + expect(getStyleByCss('.eui-displayBlock').dataset.emotion).toEqual( + 'utility-global' + ); }); }); From 73bf66cad8132794a4c596563d6b48686a55d400 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 15:16:20 -0700 Subject: [PATCH 06/12] Set up a context to check for nested `EuiProvider`s --- src/components/provider/nested/index.ts | 9 ++++ .../provider/nested/nested_context.test.tsx | 30 ++++++++++++ .../provider/nested/nested_context.tsx | 28 +++++++++++ src/components/provider/provider.tsx | 48 +++++++++++-------- 4 files changed, 94 insertions(+), 21 deletions(-) create mode 100644 src/components/provider/nested/index.ts create mode 100644 src/components/provider/nested/nested_context.test.tsx create mode 100644 src/components/provider/nested/nested_context.tsx diff --git a/src/components/provider/nested/index.ts b/src/components/provider/nested/index.ts new file mode 100644 index 00000000000..4c1cbd4c9b0 --- /dev/null +++ b/src/components/provider/nested/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './nested_context'; diff --git a/src/components/provider/nested/nested_context.test.tsx b/src/components/provider/nested/nested_context.test.tsx new file mode 100644 index 00000000000..1cfc2163642 --- /dev/null +++ b/src/components/provider/nested/nested_context.test.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { renderHook } from '@testing-library/react-hooks'; + +import { + EuiProviderNestedCheck, + useIsNestedEuiProvider, +} from './nested_context'; + +describe('useIsNestedEuiProvider', () => { + it('is false if an EuiProviderNestedCheck wrapper has not yet been instantiated', () => { + const { result } = renderHook(useIsNestedEuiProvider); + + expect(result.current).toEqual(false); + }); + + it('is true after an EuiProviderNestedCheck wrapper has been instantiated', () => { + const { result } = renderHook(useIsNestedEuiProvider, { + wrapper: EuiProviderNestedCheck, + }); + + expect(result.current).toEqual(true); + }); +}); diff --git a/src/components/provider/nested/nested_context.tsx b/src/components/provider/nested/nested_context.tsx new file mode 100644 index 00000000000..57550d8eb57 --- /dev/null +++ b/src/components/provider/nested/nested_context.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { createContext, useContext, PropsWithChildren } from 'react'; + +/** + * This util creates a context for EuiProviders to use and determine if they're + * the only (top-most) EuiProvider in the app. If they aren't (i.e., they're + * nested within another EuiProvider) we should throw a warning and not + * render instantiate the nested EuiProvider. + */ + +export const EuiNestedProviderContext = createContext(false); + +export const EuiProviderNestedCheck = ({ children }: PropsWithChildren<{}>) => ( + + {children} + +); + +export const useIsNestedEuiProvider = () => { + return !!useContext(EuiNestedProviderContext); +}; diff --git a/src/components/provider/provider.tsx b/src/components/provider/provider.tsx index 50b4c29f17b..9c98cee5093 100644 --- a/src/components/provider/provider.tsx +++ b/src/components/provider/provider.tsx @@ -22,6 +22,7 @@ import { } from '../../services'; import { EuiThemeAmsterdam } from '../../themes'; import { EuiCacheProvider } from './cache'; +import { EuiProviderNestedCheck } from './nested'; const isEmotionCacheObject = ( obj: EmotionCache | Object @@ -94,27 +95,32 @@ export const EuiProvider = ({ utilityCache = cache.utility; } } + return ( - - - {theme && ( - <> - } - /> - } - /> - - )} - {children} - - + + + + {theme && ( + <> + } + /> + } + /> + + )} + + {children} + + + + ); }; From 365e6e35b70f5f5fd36f661475dd6653e050fd97 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 15:18:07 -0700 Subject: [PATCH 07/12] Update `EuiProvider` to return early and emit a warning if nested usage is detected --- src/components/provider/provider.test.tsx | 57 +++++++++++++++++++++++ src/components/provider/provider.tsx | 12 ++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/components/provider/provider.test.tsx b/src/components/provider/provider.test.tsx index 3de71b6daab..84fc7a9c96e 100644 --- a/src/components/provider/provider.test.tsx +++ b/src/components/provider/provider.test.tsx @@ -11,6 +11,7 @@ import { render } from '@testing-library/react'; // Note - don't use the EUI cus import { cache as emotionCache } from '@emotion/css'; import createCache from '@emotion/cache'; +import { setEuiDevProviderWarning } from '../../services'; import { EuiProvider } from './provider'; describe('EuiProvider', () => { @@ -155,4 +156,60 @@ describe('EuiProvider', () => { expect(getByText('Dark mode')).toHaveStyleRule('color', '#333'); }); }); + + describe('nested EuiProviders', () => { + it('emits a log/error/warning per `euiDevProviderWarning` levels', () => { + const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); // Silence warning + setEuiDevProviderWarning('warn'); + + render( + + Top-level provider + Nested + + ); + + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + '`EuiProvider` should not be nested or used more than once' + ) + ); + + setEuiDevProviderWarning(undefined); + warnSpy.mockRestore(); + }); + + it('returns children as-is without rendering any nested contexts', () => { + const { container } = render( + + Top-level provider + + Nested + Nested again + + + ); + + expect(container).toMatchInlineSnapshot(` +
+ Top-level provider + Nested + Nested again +
+ `); + }); + + it('does not instantiate any extra logic, including setting cache behavior', () => { + const ignoredCache = createCache({ key: 'ignore' }); + + render( + + Top-level provider + Nested + + ); + + expect(ignoredCache.compat).not.toEqual(true); + }); + }); }); diff --git a/src/components/provider/provider.tsx b/src/components/provider/provider.tsx index 9c98cee5093..ecb35228a87 100644 --- a/src/components/provider/provider.tsx +++ b/src/components/provider/provider.tsx @@ -20,9 +20,10 @@ import { EuiThemeSystem, CurrentEuiBreakpointProvider, } from '../../services'; +import { emitEuiProviderWarning } from '../../services/theme/warning'; import { EuiThemeAmsterdam } from '../../themes'; import { EuiCacheProvider } from './cache'; -import { EuiProviderNestedCheck } from './nested'; +import { EuiProviderNestedCheck, useIsNestedEuiProvider } from './nested'; const isEmotionCacheObject = ( obj: EmotionCache | Object @@ -73,6 +74,15 @@ export const EuiProvider = ({ modify, children, }: PropsWithChildren>) => { + const isNested = useIsNestedEuiProvider(); + if (isNested) { + const providerMessage = `\`EuiProvider\` should not be nested or used more than once, other than at the top level of your app. + Use \`EuiThemeProvider\` instead for nested component-level theming: https://ela.st/euiprovider.`; + + emitEuiProviderWarning(providerMessage); + return children as any; + } + let defaultCache; let globalCache; let utilityCache; From 61e8630d8f3e3d46f79f9f91dd4f8c1058aad225 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 15:18:36 -0700 Subject: [PATCH 08/12] Fix types on EuiProvider's props table --- src/components/provider/provider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/provider/provider.tsx b/src/components/provider/provider.tsx index ecb35228a87..ed744cd000b 100644 --- a/src/components/provider/provider.tsx +++ b/src/components/provider/provider.tsx @@ -30,7 +30,7 @@ const isEmotionCacheObject = ( ): obj is EmotionCache => obj.hasOwnProperty('key'); export interface EuiProviderProps - extends Omit, 'children' | 'theme'>, + extends Pick, 'colorMode' | 'modify'>, EuiGlobalStylesProps { /** * Provide a specific EuiTheme; Defaults to EuiThemeAmsterdam; From a9a4725b0d337973b0f2df4a2e0e1978f4738dde Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 17:07:48 -0700 Subject: [PATCH 09/12] [docs] Improve `EuiProvider` and `EuiThemeProvider` docs - clarify in as many places as possible - remove uncertain language (e.g. 'currently', 'future') that were still not yet figured out when docs were written - try to remove overlap in docs between provider and theme provider, try to clarify nested usage of theme provider - add examples to warning section --- .../src/views/provider/provider_example.js | 114 ++++++++++-------- .../src/views/provider/provider_styles.tsx | 4 +- .../src/views/provider/provider_warning.tsx | 37 ++++++ src-docs/src/views/theme/theme_example.js | 29 ++--- 4 files changed, 109 insertions(+), 75 deletions(-) create mode 100644 src-docs/src/views/provider/provider_warning.tsx diff --git a/src-docs/src/views/provider/provider_example.js b/src-docs/src/views/provider/provider_example.js index 3c1665b5576..a6ac1a53ec6 100644 --- a/src-docs/src/views/provider/provider_example.js +++ b/src-docs/src/views/provider/provider_example.js @@ -14,6 +14,7 @@ import { GuideSectionPropsTable } from '../../components/guide_section/guide_sec import Setup from './provider_setup'; import GlobalStyles from './provider_styles'; +import Warnings from './provider_warning'; export const ProviderExample = { title: 'Provider', @@ -21,12 +22,9 @@ export const ProviderExample = {

EuiProvider contains all necessary context providers - required for full functionality and styling of EUI. It currently - includes the{' '} - - EuiThemeProvider - {' '} - for theming and writing custom styles. + required for full functionality and styling of EUI. A single instance of{' '} + EuiProvider should exist at the top level of your app, + where functionality will flow down the component tree.

), @@ -37,54 +35,64 @@ export const ProviderExample = {

For EUI to work correctly, set up EuiProvider at - the root of your application. + the root of your application:

- See{' '} - + EuiProvider includes global reset and utilites + styles and other app-wide contexts, functionality, and configuration + options. It should only be instantiated once. This + requirement is enforced internally - if another nested instance of{' '} + EuiProvider is detected, that instance will return + early without further processing, and will{' '} + warn if configured to do so. Nested + instances of EuiThemeProvider, however, are valid. +

+
+ ), + }, + { + title: 'Theming and global styles', + text: ( + +

+ To customize the global theme of your app, use the{' '} + theme, colorMode, and{' '} + modify props (documented in{' '} + EuiThemeProvider - {' '} - for full documentation as all relevant props will pass through. For - instance, it's likely that you will want to implement color - mode switching at this level: + + ). For instance, it's likely that you will want to implement + color mode switching at the top level:

{""} - - +

- It is not recommended to recreate the functionality of{' '} - EuiProvider by composing its constituent parts. - More context, functionality, and configurations will be added to{' '} - EuiProvider in future releases. Nested instances of{' '} - - EuiThemeProvider + If you do not wish your app to include EUI's default global reset + CSS or{' '} + utility CSS classes + , this is configurable via the globalStyles or{' '} + utilityClasses props. You can either pass in your + own as a React component returning an{' '} + + Emotion Global - , however, are valid. -

-
- ), - }, - { - title: 'Global styles', - text: ( - -

- The provider includes general reset and global styles, applied via - Emotion. These only need to be applied once so to - prevent these styles from loading in nested instances of the - provider, pass - {'globalStyles={false}'}. + , or remove them completely by setting the props to{' '} + false:

-

- @emotion/cache and style injection location + + {''} + + +

+ @emotion/cache and style injection location

In the case that your app has its own static stylesheet,{' '} @@ -108,9 +116,7 @@ export const ProviderExample = { utility properties on the{' '} cache prop to further define where specific styles should be inserted. See{' '} - - the props documentation - {' '} + the props documentation{' '} for details.

@@ -122,13 +128,9 @@ export const ProviderExample = { > the createCache API {' '} - will be respected by EUI. -

- -

- Note that EUI does not include the @emotion/cache{' '} - library, so you will need to add it to your application - dependencies. + will be respected by EUI. Note that EUI does not include the{' '} + @emotion/cache library, so you will need to add + it to your application dependencies.

), @@ -139,12 +141,18 @@ export const ProviderExample = {

For complex applications with multiple mount points or template - wrappers, it may be beneficial to enable logging when components do - not have access to a parent EuiProvider. + wrappers, it may be beneficial to enable logging. Doing so will + allow you to see warnings for duplicate{' '} + EuiProviders, as well as when components do not + have access to a parent EuiProvider. To enable + logging or erroring, use setEuiDevProviderWarning + :

+ + +

- setEuiDevProviderWarning is a function that will - enable adding logging or erroring if the Provider is missing. It + setEuiDevProviderWarning accepts three levels:

    @@ -157,7 +165,7 @@ export const ProviderExample = { console.warn
  • - 'error': Throw an + 'error': Throws an exception
diff --git a/src-docs/src/views/provider/provider_styles.tsx b/src-docs/src/views/provider/provider_styles.tsx index e147e94b8fb..9b4ca730d41 100644 --- a/src-docs/src/views/provider/provider_styles.tsx +++ b/src-docs/src/views/provider/provider_styles.tsx @@ -35,9 +35,7 @@ const euiCache = createCache({ }); cache.compat = true; - + {/* Content */} `} diff --git a/src-docs/src/views/provider/provider_warning.tsx b/src-docs/src/views/provider/provider_warning.tsx new file mode 100644 index 00000000000..3d5b1fc5dc3 --- /dev/null +++ b/src-docs/src/views/provider/provider_warning.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +import { EuiCodeBlock } from '../../../../src'; + +export default () => { + return ( + <> + + {`import { setEuiDevProviderWarning } from '@elastic/eui'; + +setEuiDevProviderWarning('warn');`} + + +

Examples of apps that would cause warnings:

+ + + {`const AppWithMissingProvider = () => ( + + {/* Will render, but will warn about missing EuiProvider */} + +); + +const App = () => ( + + {/* Content */} + +); +const AppWithDuplicateProvider = () => ( + + {/* Will warn about multiple providers */} + + +)`} + + + ); +}; diff --git a/src-docs/src/views/theme/theme_example.js b/src-docs/src/views/theme/theme_example.js index 969a9f6c230..eb88f718b90 100644 --- a/src-docs/src/views/theme/theme_example.js +++ b/src-docs/src/views/theme/theme_example.js @@ -1,4 +1,5 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import { GuideSectionTypes } from '../../components'; @@ -35,11 +36,17 @@ export const ThemeExample = { EUI is in the progress of switching it's core styles processor from Sass to Emotion. To take full advantage of this context layer, wrap the root of your - application with{' '} + application with a single{' '} EuiProvider - . + . While EuiProvider should not be included more than + once, you may use multiple nested EuiThemeProviders{' '} + to customize section-specific or component-specific{' '} + + color modes + {' '} + or theme overrides.

@@ -51,23 +58,7 @@ export const ThemeExample = { <>

The context layer that enables theming (including the default theme - styles) comes from EuiThemeProvider. It is a thin - wrapper around and caching layer built onto{' '} - React.Context.Provider. -

-

- Typically your app will only need a single instance at the top level - and the functionality will flow down the component tree. We - recommend using{' '} - - EuiProvider - {' '} - at this level as it includes reset styles and future configuration - options. It is also possible to use several nested theme providers. - In this case each nested provider will inherit from its closest - ancestor provider. -

-

+ styles) comes from EuiThemeProvider.{' '} EuiThemeProvider accepts three props, all of which have default values and are therefore optional. To use the default EUI theme, no configuration is required. From 16f08a4ec0101bed64e943732dd435dcb8311d52 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Thu, 13 Jul 2023 17:21:31 -0700 Subject: [PATCH 10/12] changelog --- upcoming_changelogs/6949.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 upcoming_changelogs/6949.md diff --git a/upcoming_changelogs/6949.md b/upcoming_changelogs/6949.md new file mode 100644 index 00000000000..f7909a37f41 --- /dev/null +++ b/upcoming_changelogs/6949.md @@ -0,0 +1,3 @@ +**Breaking changes** + +- `EuiProvider` will no longer render multiple or duplicate nested instances of itself. If a nested `EuiProvider` is detected, that instance will return early without further processing, and will warn if configured to do so via `setEuiDevProviderWarning`. For nested theming, use `EuiThemeProvider` instead. From 1db5f2229dfe2122276be9dc8504d067226f7605 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Fri, 14 Jul 2023 18:37:52 -0700 Subject: [PATCH 11/12] Fix Cypress tests failing due to nested EuiProviders --- .../breakpoint/current_breakpoint.spec.tsx | 5 +++-- .../breakpoint/is_within_hooks.spec.tsx | 21 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/services/breakpoint/current_breakpoint.spec.tsx b/src/services/breakpoint/current_breakpoint.spec.tsx index ce0cdca7af6..d64842bdf5f 100644 --- a/src/services/breakpoint/current_breakpoint.spec.tsx +++ b/src/services/breakpoint/current_breakpoint.spec.tsx @@ -11,6 +11,7 @@ /// import React from 'react'; +import { mount } from 'cypress/react'; // cy.mount is configured to automatically wrap , which we're already using manually here import { EuiProvider } from '../../components/provider'; import { useCurrentEuiBreakpoint } from './'; @@ -28,7 +29,7 @@ describe('useCurrentEuiBreakpoint', () => { describe('with default EUI theme breakpoints', () => { beforeEach(() => { cy.viewport(1600, 600); - cy.mount( + mount( @@ -70,7 +71,7 @@ describe('useCurrentEuiBreakpoint', () => { describe('with custom breakpoints', () => { beforeEach(() => { - cy.mount( + mount( import React, { FunctionComponent } from 'react'; +import { mount } from 'cypress/react'; // mount is configured to automatically wrap , which we're already using manually here import { EuiProvider } from '../../components/provider'; import { _EuiThemeBreakpoint } from '../../global_styling/variables/breakpoint'; @@ -31,7 +32,7 @@ describe('useIsWithinBreakpoints', () => { it('returns true if the current breakpoint size is in the passed sizes array', () => { cy.viewport(300, 600); - cy.mount( + mount( @@ -41,7 +42,7 @@ describe('useIsWithinBreakpoints', () => { it('returns false if the current breakpoint size is outside the passed sizes array', () => { cy.viewport(1400, 600); - cy.mount( + mount( @@ -51,7 +52,7 @@ describe('useIsWithinBreakpoints', () => { it('returns false always if isResponsive is passed as false', () => { cy.viewport(300, 600); - cy.mount( + mount( @@ -61,7 +62,7 @@ describe('useIsWithinBreakpoints', () => { it('correctly handles custom breakpoint sizes', () => { cy.viewport(1500, 600); - cy.mount( + mount( { it('returns true if the current breakpoint size is smaller than the passed max size', () => { cy.viewport(300, 600); - cy.mount( + mount( @@ -100,7 +101,7 @@ describe('useIsWithinMaxBreakpoint', () => { it('returns false if the current breakpoint size is larger than the passed max size', () => { cy.viewport(1400, 600); - cy.mount( + mount( @@ -110,7 +111,7 @@ describe('useIsWithinMaxBreakpoint', () => { it('correctly handles custom breakpoint sizes', () => { cy.viewport(1400, 600); - cy.mount( + mount( { it('returns true if the current breakpoint size is larger than the passed min size', () => { cy.viewport(800, 600); - cy.mount( + mount( @@ -147,7 +148,7 @@ describe('useIsWithinMinBreakpoint', () => { it('returns false if the current breakpoint size is smaller than the passed min size', () => { cy.viewport(600, 600); - cy.mount( + mount( @@ -157,7 +158,7 @@ describe('useIsWithinMinBreakpoint', () => { it('correctly handles custom breakpoint sizes', () => { cy.viewport(600, 600); - cy.mount( + mount( Date: Mon, 17 Jul 2023 09:42:53 -0700 Subject: [PATCH 12/12] [PR feedback] Allow `cy.mount` to have a configurable provider props --- cypress/support/index.d.ts | 7 +- cypress/support/setup/mount.js | 5 +- .../breakpoint/current_breakpoint.spec.tsx | 37 ++---- .../breakpoint/is_within_hooks.spec.tsx | 111 ++++++------------ 4 files changed, 55 insertions(+), 105 deletions(-) diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts index f20e645816e..b2d2eb20f2b 100644 --- a/cypress/support/index.d.ts +++ b/cypress/support/index.d.ts @@ -1,6 +1,8 @@ +import type { ReactNode } from 'react'; import { mount } from 'cypress/react'; import { ContextObject, Result, RunOptions } from 'axe-core'; import { realPress } from 'cypress-real-events/commands/realPress'; +import type { EuiProviderProps } from '../../src/components/provider'; type KeyOrShortcut = Parameters[0]; type RealPressOptions = Parameters[1]; @@ -30,7 +32,10 @@ declare global { /** * Mounts components with a basic `EuiProvider` wrapper */ - mount: typeof mount; + mount: ( + children: ReactNode, + options?: { providerProps?: Partial> } + ) => ReturnType; /** * This ensures the correct testing window has focus when using Cypress Real Events. diff --git a/cypress/support/setup/mount.js b/cypress/support/setup/mount.js index 192ce0641b6..c0f7edf3e3f 100644 --- a/cypress/support/setup/mount.js +++ b/cypress/support/setup/mount.js @@ -10,6 +10,7 @@ import React from 'react'; import { mount as cypressMount } from 'cypress/react'; import { EuiProvider } from '../../../src'; -Cypress.Commands.add('mount', (children) => { - return cypressMount({children}); +Cypress.Commands.add('mount', (children, options = {}) => { + const { providerProps } = options; + return cypressMount({children}); }); diff --git a/src/services/breakpoint/current_breakpoint.spec.tsx b/src/services/breakpoint/current_breakpoint.spec.tsx index d64842bdf5f..74998e9a88c 100644 --- a/src/services/breakpoint/current_breakpoint.spec.tsx +++ b/src/services/breakpoint/current_breakpoint.spec.tsx @@ -11,9 +11,7 @@ /// import React from 'react'; -import { mount } from 'cypress/react'; // cy.mount is configured to automatically wrap , which we're already using manually here -import { EuiProvider } from '../../components/provider'; import { useCurrentEuiBreakpoint } from './'; describe('useCurrentEuiBreakpoint', () => { @@ -29,11 +27,7 @@ describe('useCurrentEuiBreakpoint', () => { describe('with default EUI theme breakpoints', () => { beforeEach(() => { cy.viewport(1600, 600); - mount( - - - - ); + cy.mount(); cy.wait(50); // Throttle race conditions - won't typically happen in production, but Cypress does everything extremely fast }); @@ -71,23 +65,18 @@ describe('useCurrentEuiBreakpoint', () => { describe('with custom breakpoints', () => { beforeEach(() => { - mount( - - - - ); + const customBreakpoints = { + xxs: 0, + xs: 250, + s: 500, + m: 1000, + l: 1500, + xl: 2000, + xxl: 2500, + }; + cy.mount(, { + providerProps: { modify: { breakpoint: customBreakpoints } }, + }); cy.wait(50); // Throttle race conditions - won't typically happen in production, but Cypress does everything extremely fast }); diff --git a/src/services/breakpoint/is_within_hooks.spec.tsx b/src/services/breakpoint/is_within_hooks.spec.tsx index ca685a27cb8..33e9d2cbd28 100644 --- a/src/services/breakpoint/is_within_hooks.spec.tsx +++ b/src/services/breakpoint/is_within_hooks.spec.tsx @@ -11,9 +11,7 @@ /// import React, { FunctionComponent } from 'react'; -import { mount } from 'cypress/react'; // mount is configured to automatically wrap , which we're already using manually here -import { EuiProvider } from '../../components/provider'; import { _EuiThemeBreakpoint } from '../../global_styling/variables/breakpoint'; import { useIsWithinBreakpoints, @@ -32,51 +30,34 @@ describe('useIsWithinBreakpoints', () => { it('returns true if the current breakpoint size is in the passed sizes array', () => { cy.viewport(300, 600); - mount( - - - - ); + cy.mount(); cy.get('[data-test-subj]').should('exist'); }); it('returns false if the current breakpoint size is outside the passed sizes array', () => { cy.viewport(1400, 600); - mount( - - - - ); + cy.mount(); cy.get('[data-test-subj]').should('not.exist'); }); it('returns false always if isResponsive is passed as false', () => { cy.viewport(300, 600); - mount( - - - - ); + cy.mount(); cy.get('[data-test-subj]').should('not.exist'); }); it('correctly handles custom breakpoint sizes', () => { + const customBreakpoints = { + xs: 0, + s: 500, + m: 1000, + l: 1500, + xl: 2000, + }; cy.viewport(1500, 600); - mount( - - - - ); + cy.mount(, { + providerProps: { modify: { breakpoint: customBreakpoints } }, + }); cy.get('[data-test-subj]').should('exist'); }); }); @@ -91,39 +72,26 @@ describe('useIsWithinMaxBreakpoint', () => { it('returns true if the current breakpoint size is smaller than the passed max size', () => { cy.viewport(300, 600); - mount( - - - - ); + cy.mount(); cy.get('[data-test-subj]').should('exist'); }); it('returns false if the current breakpoint size is larger than the passed max size', () => { cy.viewport(1400, 600); - mount( - - - - ); + cy.mount(); cy.get('[data-test-subj]').should('not.exist'); }); it('correctly handles custom breakpoint sizes', () => { + const customBreakpoints = { + m: 1500, + l: 1800, + xl: 2000, + }; cy.viewport(1400, 600); - mount( - - - - ); + cy.mount(, { + providerProps: { modify: { breakpoint: customBreakpoints } }, + }); cy.get('[data-test-subj]').should('exist'); }); }); @@ -138,39 +106,26 @@ describe('useIsWithinMinBreakpoint', () => { it('returns true if the current breakpoint size is larger than the passed min size', () => { cy.viewport(800, 600); - mount( - - - - ); + cy.mount(); cy.get('[data-test-subj]').should('exist'); }); it('returns false if the current breakpoint size is smaller than the passed min size', () => { cy.viewport(600, 600); - mount( - - - - ); + cy.mount(); cy.get('[data-test-subj]').should('not.exist'); }); it('correctly handles custom breakpoint sizes', () => { + const customBreakpoints = { + m: 600, + l: 800, + xl: 1000, + }; cy.viewport(600, 600); - mount( - - - - ); + cy.mount(, { + providerProps: { modify: { breakpoint: customBreakpoints } }, + }); cy.get('[data-test-subj]').should('exist'); }); });