diff --git a/src-docs/src/index.html b/src-docs/src/index.html index f4e964b07d6..6d39172aa4d 100644 --- a/src-docs/src/index.html +++ b/src-docs/src/index.html @@ -15,6 +15,8 @@ + +
diff --git a/src-docs/src/views/app_context.js b/src-docs/src/views/app_context.js index c414339d756..18f4416850f 100644 --- a/src-docs/src/views/app_context.js +++ b/src-docs/src/views/app_context.js @@ -17,11 +17,15 @@ import favicon16Dev from '../images/favicon/dev/favicon-16x16.png'; import favicon32Dev from '../images/favicon/dev/favicon-32x32.png'; import favicon96Dev from '../images/favicon/dev/favicon-96x96.png'; -const emotionCache = createCache({ - key: 'eui-docs', +const generalEmotionCache = createCache({ + key: 'css', container: document.querySelector('meta[name="emotion-styles"]'), }); -emotionCache.compat = true; +generalEmotionCache.compat = true; +const utilityCache = createCache({ + key: 'util', + container: document.querySelector('meta[name="emotion-styles-utility"]'), +}); export const AppContext = ({ children }) => { const { theme } = useContext(ThemeContext); @@ -41,7 +45,10 @@ export const AppContext = ({ children }) => { return ( t.value === theme)?.provider} colorMode={theme.includes('light') ? 'light' : 'dark'} > diff --git a/src-docs/src/views/provider/provider_example.js b/src-docs/src/views/provider/provider_example.js index 3ac7aebd13a..c7c0e555d4a 100644 --- a/src-docs/src/views/provider/provider_example.js +++ b/src-docs/src/views/provider/provider_example.js @@ -95,12 +95,25 @@ export const ProviderExample = { The @emotion/cache library {' '} provides configuration options that help with specifying the - injection location. We recommend using a{' '} - {''} tag to achieve this. + injection location. We recommend using {''}{' '} + tags to achieve this.

+

+ For most applications, the above configuration will be enough to + have styles ordered correctly. In advanced more cases, you may use + the default,global, and{' '} + utility properties on the{' '} + cache prop to further define where specific + styles should be inserted. See{' '} + + the props documentation + {' '} + for details. +

+

Any other options available with{' '} { My App - + +

@@ -28,13 +29,15 @@ export default () => { import { EuiProvider } from '@elastic/eui' import createCache from '@emotion/cache'; -const cache = createCache({ - key: 'myApp', - container: document.querySelector('meta[name="emotion-style-insert"]'), +const euiCache = createCache({ + key: 'eui', + container: document.querySelector('meta[name="eui-style-insert"]'), }); cache.compat = true; - + {/* Content */}
`} diff --git a/src-docs/webpack.config.js b/src-docs/webpack.config.js index 9d831994b92..16cab69d2b8 100644 --- a/src-docs/webpack.config.js +++ b/src-docs/webpack.config.js @@ -82,7 +82,10 @@ const webpackConfig = { loaders: employCache([ { loader: 'style-loader', - options: { injectType: 'lazySingletonStyleTag' }, + options: { + injectType: 'lazySingletonStyleTag', + insert: 'meta[name="sass-styles-compiled"]', + }, }, 'css-loader', 'postcss-loader', diff --git a/src/components/provider/__snapshots__/provider.test.tsx.snap b/src/components/provider/__snapshots__/provider.test.tsx.snap index 41d40612911..dc576bb68b3 100644 --- a/src/components/provider/__snapshots__/provider.test.tsx.snap +++ b/src/components/provider/__snapshots__/provider.test.tsx.snap @@ -1,1301 +1,2413 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`EuiProvider applying modifications propagates \`modify\` 1`] = ` - + + + + + + + + + +`; + +exports[`EuiProvider changing color modes propagates \`colorMode\` 1`] = ` + + - - -`; - -exports[`EuiProvider changing color modes propagates \`colorMode\` 1`] = ` - + + + + + + + + +`; + +exports[`EuiProvider is rendered 1`] = ` + + - - + > + + + + + + + + `; -exports[`EuiProvider is rendered 1`] = ` -, + "ctr": 0, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "default", + "nonce": undefined, + "prepend": undefined, + "tags": Array [], }, - "base": 16, - "border": Object { - "color": Computed { - "computer": [Function], - "dependencies": Array [ - "colors.lightShade", - ], - }, - "editable": Computed { - "computer": [Function], - "dependencies": Array [ - "border.width", - "border.color", - ], - }, - "radius": Object { - "medium": Computed { + } + } +> + + + + + + + + + +`; + +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": "testing", - "nonce": undefined, - "prepend": undefined, - "tags": Array [], - }, - } - } - theme={ - Object { - "animation": Object { - "bounce": "cubic-bezier(.34, 1.61, .7, 1)", - "extraFast": "90ms", - "extraSlow": "500ms", - "fast": "150ms", - "normal": "250ms", - "resistance": "cubic-bezier(.694, .0482, .335, 1)", - "slow": "350ms", - }, - "base": 16, - "border": Object { - "color": Computed { - "computer": [Function], - "dependencies": Array [ - "colors.lightShade", - ], - }, - "editable": Computed { - "computer": [Function], - "dependencies": Array [ - "border.width", - "border.color", - ], - }, - "radius": Object { - "medium": Computed { + + + , + "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": "utility", + "nonce": undefined, + "prepend": undefined, + "tags": Array [], + }, + } + } + > + + + + `; -exports[`EuiProvider using \`null\` theme option does not add global styles 1`] = ``; +exports[`EuiProvider using \`null\` theme option does not add global styles 1`] = ` + + + +`; diff --git a/src/components/provider/cache/__snapshots__/cache_provider.test.tsx.snap b/src/components/provider/cache/__snapshots__/cache_provider.test.tsx.snap new file mode 100644 index 00000000000..ced846ba2e7 --- /dev/null +++ b/src/components/provider/cache/__snapshots__/cache_provider.test.tsx.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiProvider adds CacheProvider from Emotion when configured with a cache 1`] = ` +, + "ctr": 0, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "testing", + "nonce": undefined, + "prepend": undefined, + "tags": Array [], + }, + } + } +> +
+ +`; + +exports[`EuiProvider does not add CacheProvider from Emotion when configured without a cache 1`] = ` + +
+ +`; + +exports[`EuiProvider renders a Fragment when no children are provided 1`] = ``; diff --git a/src/components/provider/cache/cache_provider.test.tsx b/src/components/provider/cache/cache_provider.test.tsx new file mode 100644 index 00000000000..d1fa6729211 --- /dev/null +++ b/src/components/provider/cache/cache_provider.test.tsx @@ -0,0 +1,44 @@ +/* + * 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 { shallow } from 'enzyme'; +import createCache from '@emotion/cache'; + +import { EuiCacheProvider } from './cache_provider'; + +describe('EuiProvider', () => { + const cache = createCache({ + key: 'testing', + }); + it('adds CacheProvider from Emotion when configured with a cache', () => { + const component = shallow( + +
+ + ); + + expect(component).toMatchSnapshot(); + }); + + it('does not add CacheProvider from Emotion when configured without a cache', () => { + const component = shallow( + +
+ + ); + + expect(component).toMatchSnapshot(); + }); + + it('renders a Fragment when no children are provided', () => { + const component = shallow(); + + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/components/provider/cache/cache_provider.tsx b/src/components/provider/cache/cache_provider.tsx new file mode 100644 index 00000000000..7c0bd618ce8 --- /dev/null +++ b/src/components/provider/cache/cache_provider.tsx @@ -0,0 +1,26 @@ +/* + * 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, { PropsWithChildren } from 'react'; +import { EmotionCache } from '@emotion/cache'; +import { CacheProvider } from '@emotion/react'; + +export interface EuiCacheProviderProps { + cache?: false | EmotionCache; +} + +export const EuiCacheProvider = ({ + cache, + children, +}: PropsWithChildren) => { + return children && cache ? ( + {children} + ) : ( + <>{children} + ); +}; diff --git a/src/components/provider/cache/index.ts b/src/components/provider/cache/index.ts new file mode 100644 index 00000000000..ce66007af02 --- /dev/null +++ b/src/components/provider/cache/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 './cache_provider'; diff --git a/src/components/provider/provider.test.tsx b/src/components/provider/provider.test.tsx index f908e9d59eb..edcafcc5b39 100644 --- a/src/components/provider/provider.test.tsx +++ b/src/components/provider/provider.test.tsx @@ -28,11 +28,47 @@ describe('EuiProvider', () => { }); describe('providing an @emotion cache config', () => { - const emotionCache = createCache({ - key: 'testing', + const defaultCache = createCache({ + key: 'default', }); + const globalCache = createCache({ + key: 'global', + }); + const utilityCache = createCache({ + key: 'utility', + }); + it('applies the cache to all styles', () => { + const component = shallow(); + + expect(component).toMatchSnapshot(); + }); + it('applies the cache to global styles', () => { - const component = shallow(); + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); + }); + + it('applies the cache to utility styles', () => { + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); + }); + + it('applies the cache to each location separately', () => { + const component = shallow( + + ); expect(component).toMatchSnapshot(); }); diff --git a/src/components/provider/provider.tsx b/src/components/provider/provider.tsx index feba9435be9..748da28aba0 100644 --- a/src/components/provider/provider.tsx +++ b/src/components/provider/provider.tsx @@ -13,12 +13,18 @@ import { EuiGlobalStyles, EuiGlobalStylesProps, } from '../../global_styling/reset/global_styles'; +import { EuiUtilityClasses } from '../../global_styling/utility/utility'; import { EuiThemeProvider, EuiThemeProviderProps, EuiThemeSystem, } from '../../services'; import { EuiThemeAmsterdam } from '../../themes'; +import { EuiCacheProvider } from './cache'; + +const isEmotionCacheObject = ( + obj: EmotionCache | Object +): obj is EmotionCache => obj.hasOwnProperty('key'); export interface EuiProviderProps extends Omit, 'children' | 'theme'>, @@ -34,26 +40,70 @@ export interface EuiProviderProps */ globalStyles?: false | ((params: any) => JSX.Element | null); /** - * Provide a cache configuration from `@emotion/cache` + * Provide utility classes. + * Pass `false` to remove the default EUI utility classes. + */ + utilityClasses?: false | ((params: any) => JSX.Element | null); + /** + * Provide a cache configuration(s) from `@emotion/cache`. + * + * - `default` will encompass all Emotion styles, including consumer defined appliction styles, not handled by nested cache instances. + * - `global` will scope all EUI global and reset styles. + * - `utility` will scope all EUI utility class styles. + * + * A cache instance provided as the sole value will function the same as the `default` cache. */ - cache?: EmotionCache; + cache?: + | EmotionCache + | { + default?: EmotionCache; + global?: EmotionCache; + utility?: EmotionCache; + }; } export const EuiProvider = ({ cache, theme = EuiThemeAmsterdam, - globalStyles: GlobalStyles = EuiGlobalStyles, + globalStyles: Globals = EuiGlobalStyles, + utilityClasses: Utilities = EuiUtilityClasses, colorMode, modify, children, -}: PropsWithChildren>) => ( - - {theme !== null && GlobalStyles !== false ? : null} - {children} - -); +}: PropsWithChildren>) => { + let defaultCache; + let globalCache; + let utilityCache; + if (cache) { + if (isEmotionCacheObject(cache)) { + defaultCache = cache; + } else { + defaultCache = cache.default; + globalCache = cache.global; + utilityCache = cache.utility; + } + } + return ( + + + {theme && ( + <> + } + /> + } + /> + + )} + {children} + + + ); +}; diff --git a/src/global_styling/utility/utility.tsx b/src/global_styling/utility/utility.tsx new file mode 100644 index 00000000000..9c590485f66 --- /dev/null +++ b/src/global_styling/utility/utility.tsx @@ -0,0 +1,12 @@ +/* + * 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 { Global, css } from '@emotion/react'; + +export const EuiUtilityClasses = () => ; diff --git a/src/services/theme/hooks.tsx b/src/services/theme/hooks.tsx index 4873ae612ae..b34e80c49b5 100644 --- a/src/services/theme/hooks.tsx +++ b/src/services/theme/hooks.tsx @@ -43,7 +43,8 @@ export interface WithEuiThemeProps

{ export const withEuiTheme = ( Component: React.ComponentType> ) => { - const componentName = Component.displayName || Component.name || 'Component'; + const componentName = + Component.displayName || Component.name || 'ComponentWithTheme'; const Render = ( props: Omit>, ref: React.Ref>> @@ -64,7 +65,7 @@ export const withEuiTheme = ( const WithEuiTheme = forwardRef(Render); - WithEuiTheme.displayName = `WithEuiTheme(${componentName})`; + WithEuiTheme.displayName = componentName; return WithEuiTheme; }; diff --git a/src/services/theme/provider.tsx b/src/services/theme/provider.tsx index 6d2c8ba1ddb..508e01f7b74 100644 --- a/src/services/theme/provider.tsx +++ b/src/services/theme/provider.tsx @@ -13,7 +13,6 @@ import React, { useState, PropsWithChildren, } from 'react'; -import { CacheProvider, EmotionCache } from '@emotion/react'; import isEqual from 'lodash/isEqual'; import { @@ -34,7 +33,6 @@ export interface EuiThemeProviderProps { theme?: EuiThemeSystem; colorMode?: EuiThemeColorMode; modify?: EuiThemeModifications; - cache?: EmotionCache; children: any; } @@ -42,7 +40,6 @@ export const EuiThemeProvider = ({ theme: _system, colorMode: _colorMode, modify: _modifications, - cache, children, }: PropsWithChildren>) => { const parentSystem = useContext(EuiSystemContext); @@ -123,11 +120,7 @@ export const EuiThemeProvider = ({ - {cache ? ( - {children} - ) : ( - children - )} + {children} diff --git a/upcoming_changelogs/5853.md b/upcoming_changelogs/5853.md new file mode 100644 index 00000000000..83d696287ba --- /dev/null +++ b/upcoming_changelogs/5853.md @@ -0,0 +1,4 @@ +- Added configuration options to `EuiProvider.cache` to enable more granular style insertion +- Added a utility classes component +- Added utility classes configuration options to `EuiProvider` +