From 81ebd191fc75d901aa0efcb73b05fbd3cf94946a Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Mon, 28 Aug 2023 08:55:31 -0700 Subject: [PATCH] Add QA tests/stories --- .../__snapshots__/provider.test.tsx.snap | 6 ++ src/services/theme/provider.stories.tsx | 99 +++++++++++++++++++ src/services/theme/provider.test.tsx | 81 ++++++++++++++- 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/services/theme/provider.stories.tsx diff --git a/src/services/theme/__snapshots__/provider.test.tsx.snap b/src/services/theme/__snapshots__/provider.test.tsx.snap index 720b4738012..47336d01d76 100644 --- a/src/services/theme/__snapshots__/provider.test.tsx.snap +++ b/src/services/theme/__snapshots__/provider.test.tsx.snap @@ -1,5 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`EuiThemeProvider CSS variables allows child components to set non-global theme CSS variables 1`] = ` + +`; + exports[`EuiThemeProvider nested EuiThemeProviders allows avoiding the extra span wrapper with \`wrapperProps.cloneElement\` 1`] = `
Top-level provider diff --git a/src/services/theme/provider.stories.tsx b/src/services/theme/provider.stories.tsx new file mode 100644 index 00000000000..b1a8d301571 --- /dev/null +++ b/src/services/theme/provider.stories.tsx @@ -0,0 +1,99 @@ +/* + * 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, { FunctionComponent, useContext, useEffect } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { EuiNestedThemeContext } from './context'; +import { EuiThemeProvider, EuiThemeProviderProps } from './provider'; + +const meta: Meta> = { + title: 'EuiThemeProvider', + component: EuiThemeProvider, +}; + +export default meta; +type Story = StoryObj>; + +export const WrapperCloneElement: Story = { + render: () => ( + <> + +
+ This example should only have 1 main wrapper rendered. +
+
+ + ), +}; + +export const CSSVariablesNearest: Story = { + render: () => ( + <> + + This component sets the nearest theme provider (the global theme) with a + red CSS variable color. Inspect the `:root` styles to see the variable + set. + + + + This component sets the nearest local theme provider with a blue CSS + variable color. Inspect the parent theme wrapper to see the variable + set. + + + + ), +}; + +export const CSSVariablesGlobal: Story = { + render: () => ( + <> + + This component sets the nearest theme provider (the global theme) with a + red CSS variable color. However, it should be overridden by the next + component. + + + + This component sets the global theme with a blue CSS variable color. + It should override the previous component. Inspect the `:root` styles + to see this behavior + + + + ), +}; + +/** + * Component for QA/testing purposes that mocks an EUI component + * that sets global or theme-level CSS variables + */ +const MockComponent: FunctionComponent<{ + global?: boolean; + color: string; + children: any; +}> = ({ global, color, children }) => { + const { setGlobalCSSVariables, setNearestThemeCSSVariables } = useContext( + EuiNestedThemeContext + ); + + useEffect(() => { + if (global) { + setGlobalCSSVariables({ '--testColor': color }); + } else { + setNearestThemeCSSVariables({ '--testColor': color }); + } + }, [global, color, setGlobalCSSVariables, setNearestThemeCSSVariables]); + + return ( +

+ {children} +

+ ); +}; diff --git a/src/services/theme/provider.test.tsx b/src/services/theme/provider.test.tsx index 519cd645f87..3b0368723c5 100644 --- a/src/services/theme/provider.test.tsx +++ b/src/services/theme/provider.test.tsx @@ -6,11 +6,12 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { FunctionComponent, useContext, useEffect } 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 { EuiNestedThemeContext } from './context'; import { EuiThemeProvider } from './provider'; describe('EuiThemeProvider', () => { @@ -136,4 +137,82 @@ describe('EuiThemeProvider', () => { expect(container.querySelector('.hello.world')).toBeTruthy(); }); }); + + describe('CSS variables', () => { + const MockEuiComponent: FunctionComponent<{ global?: boolean }> = ({ + global, + }) => { + const { + globalCSSVariables, + setGlobalCSSVariables, + setNearestThemeCSSVariables, + } = useContext(EuiNestedThemeContext); + + useEffect(() => { + if (global) { + setGlobalCSSVariables({ '--hello': 'global-world' }); + } else { + setNearestThemeCSSVariables({ '--hello': 'world' }); + } + }, [global, setGlobalCSSVariables, setNearestThemeCSSVariables]); + + // Our current version of jsdom doesn't yet support :root (currently on v11, + // need to be on at least v20), so we'll mock something to assert on in the interim + return <>{JSON.stringify(globalCSSVariables)}; + }; + + const getThemeProvider = (container: HTMLElement) => + container.querySelector('.euiThemeProvider')!; + const getThemeClassName = (container: HTMLElement) => + getThemeProvider(container).className; + + it('allows child components to set non-global theme CSS variables', () => { + const { container } = render( + + + + + + ); + expect(getThemeClassName(container)).toContain('euiCSSVariables'); + expect(container.firstChild).toHaveStyleRule('--hello', 'world'); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('sets global CSS variables when the nearest theme provider is the top-level one', () => { + const { container } = render( + + + + ); + expect(container.textContent).toContain('{"--hello":"world"}'); + }); + + it('allows child components to set global CSS variables from any nested theme provider', () => { + const { container } = render( + + + + + + ); + expect(getThemeClassName(container)).not.toContain('euiCSSVariables'); + expect(container.textContent).toContain('{"--hello":"global-world"}'); + }); + + it('can set both global and nearest theme variables without conflicting', () => { + const { container } = render( + + + + + + + + ); + expect(getThemeClassName(container)).toContain('euiCSSVariables'); + expect(getThemeProvider(container)).toHaveStyleRule('--hello', 'world'); + expect(container.textContent).toContain('{"--hello":"global-world"}'); + }); + }); });