Skip to content

Commit

Permalink
[Emotion] Nested EuiThemeProviders now render a wrapper setting the…
Browse files Browse the repository at this point in the history
… correct inherited text `color` (#6775)

* Set up nested theme context provider/consumer + `span` wrapper

context sets up:
- whether the current theme should render a `span` (i.e., is global theme or is nested)
- sets up a resable `className` string that can be reused across both React elements and DOM nodes (important for portals)

* Update docs to remove now-unnecessary `color` CSS

+ document new behavior

* Add `wrapperProps` customization for new span wrapper

* Add `cloneElement` support
- for consumers who have a single child / where an extra rendered wrapper will cause layout issues

+ update `EuiButton`s with `color="ghost"` to use cloneElement

* [misc] fix incorrect `color="ghost"` usage in docs skip link

* [misc cleanup] remove mounted snapshot

- it's causing a snapshot diff when it shouldn't/doesn't need to; in general we should no longer be taking mounted snapshots

* [docs] Update colorMode demo to include portals for testing

+ add a 3rd level nested demo w/ `cloneElement`

* Fix portals not correctly inheriting `color` from its parent EuiTheme

-  there's already a `data-euiportal` wrapper around all portals to bogart, so no need to create one

- in an attempt to reduce snapshot churn in Kibana's side of things, I've opted to only render a CSS class if the theme color is different from the `body` color

+ [tech debt] switch `portal.test.tsx` tests to RTL render

* Fix portals with overlay masks overriding theme color
- e.g., EuiModal, EuiFlyout

+ [tech debt] Add missing overlay mask tests using RTL

* Update EuiBottomBar
- should use `cloneElement` behavior instead of a wrapper

- no longer needs to set `color`

+ [tech debt] update tests to use RTL

* [docs][cleanup] Remove unused `inverse` src-docs file

- appears to have been replaced by `color_mode/inverse` instead

* changelog
  • Loading branch information
cee-chen authored May 17, 2023
1 parent 2948c7c commit f516c12
Show file tree
Hide file tree
Showing 27 changed files with 914 additions and 3,740 deletions.
1 change: 0 additions & 1 deletion src-docs/src/views/app_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export const AppView = ({ children, currentRoute }) => {
{`${hash ? '— ' : ''}${currentRoute.name} — Elastic UI Framework`}
</EuiScreenReaderLive>
<EuiSkipLink
color="ghost"
destinationId="start-of-content"
position="fixed"
overrideLinkBehavior
Expand Down
30 changes: 12 additions & 18 deletions src-docs/src/views/theme/color_mode/color_mode_example.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { Link } from 'react-router-dom';

import { GuideSectionTypes } from '../../../components';

Expand Down Expand Up @@ -42,25 +41,20 @@ export const ColorModeExample = {
the current color mode.
</p>
<p>
When nesting or overriding <strong>EuiThemeProvider</strong> bear in
mind that the{' '}
<Link to="/display/text">
<strong>EuiText</strong>
</Link>{' '}
inherits the color that is set in the global styles or a parent
component. This is why you should change the color of{' '}
<strong>EuiText</strong> to <EuiCode>{'"default"'}</EuiCode> when
you want it to correctly adapt to the <EuiCode>colorMode</EuiCode>{' '}
of the nested <strong>EuiThemeProvider</strong>.
When nesting or overriding <strong>EuiThemeProvider</strong>, a
wrapping <EuiCode>{'<span>'}</EuiCode> element that sets the correct
default text color (normally set at the global{' '}
<EuiCode>{'<body>'}</EuiCode> level) will be rendered. You can
customize the display of this wrapping element by passing{' '}
<EuiCode>wrapperProps</EuiCode>.
</p>
<p>
<Link to="/display/icons">
<strong>EuiIcon</strong>
</Link>{' '}
behaves similarly. By default, it gets the color of its parent
component. Thus, you can override the color to{' '}
<EuiCode>{'"text"'}</EuiCode> or wrap it in a{' '}
<strong>EuiText</strong> to inherit its color.
Alternatively, if a wrapper will significantly impact the DOM
layout/flow of your content, and if your child is a single React
component, you may pass{' '}
<EuiCode>{'wrapperProps={{ cloneElement: true }}'}</EuiCode> to
avoid rendering an extra wrapper and to clone the correct color
classes onto your child content.
</p>
</>
),
Expand Down
180 changes: 157 additions & 23 deletions src-docs/src/views/theme/color_mode/inverse.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,181 @@
import React from 'react';
import React, { useState, FC } from 'react';
import {
EuiThemeProvider,
useEuiTheme,
EuiIcon,
EuiSpacer,
EuiText,
EuiThemeProvider,
EuiCode,
EuiPanel,
EuiFlexGroup,
EuiFlexItem,
EuiButton,
EuiPopover,
EuiModal,
EuiModalHeader,
EuiModalHeaderTitle,
EuiModalBody,
EuiModalFooter,
EuiFlyout,
EuiFlyoutHeader,
EuiTitle,
EuiFlyoutBody,
} from '../../../../../src';

export default () => {
return (
<>
<EuiThemeProvider colorMode="light">
<EuiPanel>
<EuiText color="default">
<p>
<EuiIcon type="faceHappy" /> The colors of this panel will always
be in <strong>light</strong> mode
</p>
</EuiText>
</EuiPanel>
<ThemedChildren />
</EuiThemeProvider>
<EuiSpacer />

<EuiThemeProvider colorMode="dark">
<EuiPanel>
<EuiText color="default">
<p>
<EuiIcon type="faceHappy" /> The colors of this panel will always
be in <strong>dark</strong> mode
</p>
</EuiText>
</EuiPanel>
<ThemedChildren />
</EuiThemeProvider>
<EuiSpacer />

<EuiThemeProvider colorMode="inverse">
<EuiPanel>
<EuiText color="default">
<ThemedChildren>
<EuiSpacer size="m" />
<EuiText>
<p>
<EuiIcon type="faceHappy" /> The colors of this panel are the
opposite (<strong>inverse</strong>) of the current color mode
This panel is in <strong>inverse</strong> mode (the opposite of
the global color mode), and renders a 3rd level nested theme
provider that inverses color mode yet again.
</p>
</EuiText>
</EuiPanel>
<EuiSpacer size="m" />

<EuiThemeProvider
colorMode="inverse"
wrapperProps={{ cloneElement: true }}
>
<ThemedChildren>
<EuiText>
<p>
This panel demonstrates the <EuiCode>cloneElement</EuiCode>{' '}
property.
</p>
</EuiText>
</ThemedChildren>
</EuiThemeProvider>
</ThemedChildren>
</EuiThemeProvider>
</>
);
};

const ThemedChildren: FC = ({ children, ...rest }) => {
const { colorMode: _colorMode } = useEuiTheme();
const colorMode = _colorMode.toLowerCase();
return (
<EuiPanel {...rest}>
<EuiFlexGroup gutterSize="m">
<EuiFlexItem>
<EuiText>
<p>
<EuiIcon type="faceHappy" /> The colors of this panel and its
portalled content are in <strong>{colorMode}</strong> mode.
</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<Popover>
This popover should render in <strong>{colorMode}</strong> mode.
</Popover>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<Modal>
This modal should render in <strong>{colorMode}</strong> mode.
</Modal>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<Flyout>
This flyout should render in <strong>{colorMode}</strong> mode.
</Flyout>
</EuiFlexItem>
</EuiFlexGroup>
{children}
</EuiPanel>
);
};

const Popover: FC = ({ children }) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
return (
<EuiPopover
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
button={
<EuiButton
onClick={() => setIsPopoverOpen((isOpen) => !isOpen)}
fullWidth
size="s"
>
Open popover
</EuiButton>
}
display="block"
>
{children}
</EuiPopover>
);
};

const Modal: FC = ({ children }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<>
<EuiButton onClick={() => setIsModalOpen(true)} size="s">
Open modal
</EuiButton>
{isModalOpen && (
<EuiModal onClose={() => setIsModalOpen(false)}>
<EuiModalHeader>
<EuiModalHeaderTitle>Modal title</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiText>
<p>{children}</p>
</EuiText>
</EuiModalBody>
<EuiModalFooter>
<EuiButton onClick={() => setIsModalOpen(false)} fill>
Close
</EuiButton>
</EuiModalFooter>
</EuiModal>
)}
</>
);
};

const Flyout: FC = ({ children }) => {
const [isFlyoutOpen, setIsFlyoutOpen] = useState(false);
return (
<>
<EuiButton onClick={() => setIsFlyoutOpen(true)} size="s">
Open flyout
</EuiButton>
{isFlyoutOpen && (
<EuiFlyout
ownFocus
onClose={() => setIsFlyoutOpen(false)}
aria-labelledby="flyoutTitle"
>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2 id="flyoutTitle">Flyout title</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiText>
<p>{children}</p>
</EuiText>
</EuiFlyoutBody>
</EuiFlyout>
)}
</>
);
};
1 change: 0 additions & 1 deletion src-docs/src/views/theme/color_mode/inverse_complex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const Box: FunctionComponent<{ children: ReactNode }> = ({ children }) => {
css={{
background: euiTheme.colors.lightShade,
padding: euiTheme.size.xl,
color: euiTheme.colors.text,
}}
>
<p>{children}</p>
Expand Down
48 changes: 0 additions & 48 deletions src-docs/src/views/theme/inverse.tsx

This file was deleted.

Loading

0 comments on commit f516c12

Please sign in to comment.