diff --git a/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Dark_Theme_With_Sitewide_Search.png b/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Dark_Theme_With_Sitewide_Search.png index bca689620de..99b74495f2c 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Dark_Theme_With_Sitewide_Search.png and b/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Dark_Theme_With_Sitewide_Search.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Elastic_Navigation_Pattern.png b/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Elastic_Navigation_Pattern.png index 8ee62610e2e..d5943352375 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Elastic_Navigation_Pattern.png and b/packages/eui/.loki/reference/chrome_desktop_Layout_EuiHeader_EuiHeader_Elastic_Navigation_Pattern.png differ diff --git a/packages/eui/changelogs/upcoming/8806.md b/packages/eui/changelogs/upcoming/8806.md new file mode 100644 index 00000000000..43536976f3e --- /dev/null +++ b/packages/eui/changelogs/upcoming/8806.md @@ -0,0 +1,6 @@ +- Added `theme` prop to `EuiSelectableTemplateSitewide` to support granular control over the applied `colorMode` for the search and popover components + +**Breaking changes** + +- Removed custom style overrides for `EuiSelectableTemplateSitewide` search inside `EuiHeader` - Use the `theme` prop on `EuiSelectableTemplateSitewide` instead to control the color mode output. + diff --git a/packages/eui/src/components/header/header.stories.tsx b/packages/eui/src/components/header/header.stories.tsx index 0b90e57f612..c19907a486a 100644 --- a/packages/eui/src/components/header/header.stories.tsx +++ b/packages/eui/src/components/header/header.stories.tsx @@ -151,6 +151,10 @@ export const DarkThemeWithSitewideSearch: Story = { isOpen: true, repositionOnScroll: true, // Necessary when placing search in a fixed component }} + colorModes={{ + search: 'dark', + popover: 'global', + }} />, ], }, @@ -477,6 +481,10 @@ const ElasticNavigationPatternExample = () => { const search = ( { const { euiTheme } = euiThemeContext; @@ -69,6 +73,10 @@ import { euiFormVariables } from '../form/form.styles'; const euiHeaderDarkStyles = (euiThemeContext: UseEuiTheme) => { const { euiTheme, highContrastMode } = euiThemeContext; + const isRefreshVariant = isEuiThemeRefreshVariant( + euiThemeContext, + 'formVariant' + ); const { controlPlaceholderText } = euiFormVariables(euiThemeContext); const backgroundColor = euiTheme.components.headerDarkBackground; @@ -83,37 +91,7 @@ const euiHeaderDarkStyles = (euiThemeContext: UseEuiTheme) => { )(backgroundColor), }; - return ` - background-color: ${backgroundColor}; - - .euiHeaderLogo__text, - .euiHeaderLink, - .euiHeaderSectionItemButton { - color: ${euiTheme.colors.ghost}; - } - - .euiHeaderLink-isActive { - color: ${makeHighContrastColor(euiTheme.colors.primary)(backgroundColor)}; - } - - .euiHeaderLogo, - .euiHeaderLink, - .euiHeaderSectionItemButton { - &:focus { - background-color: ${ - euiTheme.components.headerDarkSectionItemBackgroundFocus - }; - } - } - - .euiHeaderSectionItemButton__notification--badge { - box-shadow: 0 0 0 ${euiTheme.border.width.thin} ${backgroundColor}; - } - - .euiHeaderSectionItemButton__notification--dot { - stroke: ${backgroundColor}; - } - + const formLayoutStyles = ` .euiSelectableTemplateSitewide .euiFormControlLayout { background-color: transparent; @@ -165,6 +143,40 @@ const euiHeaderDarkStyles = (euiThemeContext: UseEuiTheme) => { color: inherit; } } + } + `; + + return ` + background-color: ${backgroundColor}; + + .euiHeaderLogo__text, + .euiHeaderLink, + .euiHeaderSectionItemButton { + color: ${euiTheme.colors.ghost}; } + + .euiHeaderLink-isActive { + color: ${makeHighContrastColor(euiTheme.colors.primary)(backgroundColor)}; + } + + .euiHeaderLogo, + .euiHeaderLink, + .euiHeaderSectionItemButton { + &:focus { + background-color: ${ + euiTheme.components.headerDarkSectionItemBackgroundFocus + }; + } + } + + .euiHeaderSectionItemButton__notification--badge { + box-shadow: 0 0 0 ${euiTheme.border.width.thin} ${backgroundColor}; + } + + .euiHeaderSectionItemButton__notification--dot { + stroke: ${backgroundColor}; + } + + ${!isRefreshVariant && formLayoutStyles} `; }; diff --git a/packages/eui/src/components/selectable/selectable_templates/__snapshots__/selectable_template_sitewide.test.tsx.snap b/packages/eui/src/components/selectable/selectable_templates/__snapshots__/selectable_template_sitewide.test.tsx.snap index a3c006ecd83..72dae0a22a8 100644 --- a/packages/eui/src/components/selectable/selectable_templates/__snapshots__/selectable_template_sitewide.test.tsx.snap +++ b/packages/eui/src/components/selectable/selectable_templates/__snapshots__/selectable_template_sitewide.test.tsx.snap @@ -6,48 +6,52 @@ exports[`EuiSelectableTemplateSitewide is rendered 1`] = ` data-test-subj="test subject string" >
-
- + class="euiFormControlLayoutCustomIcon emotion-euiFormControlLayoutCustomIcon" + > + +
+
-
-
-

- - Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. -

+

+ + Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. +

+ `; @@ -57,7 +61,7 @@ exports[`EuiSelectableTemplateSitewide props popoverButton is rendered 1`] = ` class="euiSelectable euiSelectableTemplateSitewide emotion-euiSelectable-euiSelectableTemplateSitewide" >
-
- + class="euiFormControlLayoutCustomIcon emotion-euiFormControlLayoutCustomIcon" + > + +
+
-
-
-

- - Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. -

+

+ + Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. +

+
`; @@ -128,48 +136,52 @@ exports[`EuiSelectableTemplateSitewide props popoverProps is rendered 1`] = ` class="euiSelectable euiSelectableTemplateSitewide emotion-euiSelectable-euiSelectableTemplateSitewide" >
-
- + class="euiFormControlLayoutCustomIcon emotion-euiFormControlLayoutCustomIcon" + > + +
+
-
-
-

- - Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. -

+

+ + Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. +

+ `; @@ -179,48 +191,52 @@ exports[`EuiSelectableTemplateSitewide props popoverTitle is rendered 1`] = ` class="euiSelectable euiSelectableTemplateSitewide emotion-euiSelectable-euiSelectableTemplateSitewide" >
-
- + class="euiFormControlLayoutCustomIcon emotion-euiFormControlLayoutCustomIcon" + > + +
+
-
-
-

- - Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. -

+

+ + Use the Up and Down arrow keys to move focus over options. Press Enter to select. Press Escape to collapse options. +

+ `; diff --git a/packages/eui/src/components/selectable/selectable_templates/index.ts b/packages/eui/src/components/selectable/selectable_templates/index.ts index 186a11aa799..39c78d079c6 100644 --- a/packages/eui/src/components/selectable/selectable_templates/index.ts +++ b/packages/eui/src/components/selectable/selectable_templates/index.ts @@ -7,7 +7,10 @@ */ export type { EuiSelectableTemplateSitewideProps } from './selectable_template_sitewide'; -export { EuiSelectableTemplateSitewide } from './selectable_template_sitewide'; +export { + type EuiSelectableTemplateSitewideTheme, + EuiSelectableTemplateSitewide, +} from './selectable_template_sitewide'; export type { EuiSelectableTemplateSitewideOption, diff --git a/packages/eui/src/components/selectable/selectable_templates/selectable_template_sidewide.stories.tsx b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sidewide.stories.tsx index 7431b365644..05558698850 100644 --- a/packages/eui/src/components/selectable/selectable_templates/selectable_template_sidewide.stories.tsx +++ b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sidewide.stories.tsx @@ -91,6 +91,12 @@ const meta: Meta = { }, }, }, + args: { + colorModes: { + search: 'default', + popover: 'default', + }, + }, }; export default meta; diff --git a/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.styles.ts b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.styles.ts index 0df47b90855..a24617c31f3 100644 --- a/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.styles.ts +++ b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.styles.ts @@ -48,14 +48,14 @@ export const euiSelectableTemplateSitewideStyles = ( display: block; ${logicalCSS('margin-top', euiTheme.size.xs)} ${euiFontSize(euiThemeContext, 'xs')} - color: ${euiTheme.colors.subduedText}; + color: ${euiTheme.colors.textSubdued}; `, euiSelectableTemplateSitewide__optionMeta: css` &:not(:last-of-type)::after { content: '•'; ${logicalCSS('margin-horizontal', euiTheme.size.xs)} - color: ${euiTheme.colors.subduedText}; + color: ${euiTheme.colors.textSubdued}; } `, metaTypes: { @@ -80,3 +80,15 @@ export const euiSelectableTemplateSitewideStyles = ( }, }; }; + +export const euiSelectableTemplateSitewidePopoverStyles = ( + euiThemeContext: UseEuiTheme +) => { + const { euiTheme } = euiThemeContext; + + return { + euiSelectableTemplateSitewide__popover: css` + color: ${euiTheme.colors.textParagraph}; + `, + }; +}; diff --git a/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.tsx b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.tsx index 21eafeb2ed9..aa358ea506c 100644 --- a/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.tsx +++ b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide.tsx @@ -14,21 +14,27 @@ import React, { useCallback, CSSProperties, ReactElement, + useContext, } from 'react'; import classNames from 'classnames'; import { + COLOR_MODES_STANDARD, + EuiNestedThemeContext, + EuiThemeColorMode, + EuiThemeColorModeStandard, + EuiThemeProvider, useCombinedRefs, useCurrentEuiBreakpoint, useEuiMemoizedStyles, + useEuiTheme, + useEuiThemeRefreshVariant, } from '../../../services'; import { EuiBreakpointSize } from '../../../services/breakpoint'; import { ENTER } from '../../../services/keys'; import { useEuiI18n, EuiI18n } from '../../i18n'; -import { EuiPopoverTitle, EuiPopoverFooter } from '../../popover'; -import { EuiPopover, Props as PopoverProps } from '../../popover/popover'; +import { Props as PopoverProps } from '../../popover/popover'; import { EuiLoadingSpinner } from '../../loading'; -import { EuiSpacer } from '../../spacer'; import { EuiSelectable, EuiSelectableProps } from '../selectable'; import { EuiSelectableMessage } from '../selectable_message'; @@ -38,6 +44,12 @@ import { euiSelectableTemplateSitewideRenderOptions, } from './selectable_template_sitewide_option'; import { euiSelectableTemplateSitewideStyles } from './selectable_template_sitewide.styles'; +import { EuiSelectableTemplateSitewidePopover } from './selectable_template_sitewide_popover'; + +export type EuiSelectableTemplateSitewideTheme = + | 'default' + | 'global' + | EuiThemeColorMode; export type EuiSelectableTemplateSitewideProps = Partial< Omit, 'options'> @@ -69,6 +81,18 @@ export type EuiSelectableTemplateSitewideProps = Partial< * If `undefined`, the `popoverButton` will always show (if provided) */ popoverButtonBreakpoints?: EuiBreakpointSize[]; + + /** + * Manually sets the color mode for the search input and popover. It supports the common `colorMode` + * values: `light`, `dark`, `inverse` and additionally `default` and `global`. + * + * `default` applies the local (nearest) context `colorMode`. + * `global` applies the global context `colorMode` + */ + colorModes?: { + search: EuiSelectableTemplateSitewideTheme; + popover: EuiSelectableTemplateSitewideTheme; + }; }; export const EuiSelectableTemplateSitewide: FunctionComponent< @@ -85,8 +109,58 @@ export const EuiSelectableTemplateSitewide: FunctionComponent< isLoading, popoverButton, popoverButtonBreakpoints, + colorModes = { search: 'default', popover: 'default' }, ...rest }) => { + const { colorMode } = useEuiTheme(); + const isRefreshVariant = useEuiThemeRefreshVariant('formVariant'); + + const { hasDifferentColorFromGlobalTheme } = useContext( + EuiNestedThemeContext + ); + + const _searchColorMode = colorModes?.search?.toLowerCase(); + const _popoverColorMode = colorModes?.popover?.toLowerCase(); + + const searchColorMode = useMemo(() => { + const isStaticTheme = [ + COLOR_MODES_STANDARD.light.toLowerCase(), + COLOR_MODES_STANDARD.dark.toLowerCase(), + ].includes(_searchColorMode); + + return isStaticTheme + ? (_searchColorMode as EuiThemeColorModeStandard) + : _searchColorMode === 'inverse' + ? colorMode === COLOR_MODES_STANDARD.dark + ? COLOR_MODES_STANDARD.light + : COLOR_MODES_STANDARD.dark + : colorMode; + }, [colorMode, _searchColorMode]); + + const popoverColorMode = useMemo(() => { + const isStaticTheme = [ + COLOR_MODES_STANDARD.light.toLowerCase(), + COLOR_MODES_STANDARD.dark.toLowerCase(), + ].includes(_popoverColorMode); + const inverseColorMode = + colorMode === COLOR_MODES_STANDARD.dark + ? COLOR_MODES_STANDARD.light + : COLOR_MODES_STANDARD.dark; + const globalColorMode = hasDifferentColorFromGlobalTheme + ? colorMode === COLOR_MODES_STANDARD.dark + ? COLOR_MODES_STANDARD.light + : COLOR_MODES_STANDARD.dark + : colorMode; + + return isStaticTheme + ? (_popoverColorMode as EuiThemeColorModeStandard) + : _popoverColorMode === 'inverse' + ? inverseColorMode + : _popoverColorMode === 'global' + ? globalColorMode + : colorMode; + }, [hasDifferentColorFromGlobalTheme, colorMode, _popoverColorMode]); + /** * i18n text */ @@ -259,34 +333,43 @@ export const EuiSelectableTemplateSitewide: FunctionComponent< {...rest} searchable > - {(list, search) => ( - -
- {popoverTitle || popoverTrigger ? ( - - {popoverTitle} - {popoverTitle && search && } - {search} - - ) : undefined} - {list} - {popoverFooter && ( - - {popoverFooter} - - )} -
-
- )} + {(list, search) => { + const _search = + isRefreshVariant && !popoverTrigger ? ( + + {search} + + ) : ( + search + ); + + // uses standalone subcomponent to ensure scoped style/theme context + const popover = ( + + ); + + return isRefreshVariant ? ( + + {popover} + + ) : ( + popover + ); + }} ); }; diff --git a/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide_popover.tsx b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide_popover.tsx new file mode 100644 index 00000000000..854c8aca9a4 --- /dev/null +++ b/packages/eui/src/components/selectable/selectable_templates/selectable_template_sitewide_popover.tsx @@ -0,0 +1,78 @@ +/* + * 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, { CSSProperties, FunctionComponent, ReactNode } from 'react'; +import { useEuiMemoizedStyles } from '../../../services'; +import { + EuiPopover, + EuiPopoverFooter, + EuiPopoverProps, + EuiPopoverTitle, +} from '../../popover'; +import { EuiSpacer } from '../../spacer'; +import type { EuiSelectableTemplateSitewideProps } from './selectable_template_sitewide'; +import { euiSelectableTemplateSitewidePopoverStyles } from './selectable_template_sitewide.styles'; + +type EuiSelectableTemplateSitewidePopoverProps = Partial & { + search: ReactNode; + list: ReactNode; + trigger?: ReactNode; + title?: EuiSelectableTemplateSitewideProps['popoverTitle']; + footer?: EuiSelectableTemplateSitewideProps['popoverFooter']; + width: CSSProperties['width']; + isOpen: boolean; +}; + +export const EuiSelectableTemplateSitewidePopover: FunctionComponent< + EuiSelectableTemplateSitewidePopoverProps +> = ({ + trigger, + search, + list, + title, + footer, + width, + isOpen, + panelRef, + closePopover, + ...rest +}) => { + const styles = useEuiMemoizedStyles( + euiSelectableTemplateSitewidePopoverStyles + ); + + return ( + +
+ {title || trigger ? ( + + {title} + {title && search && } + {search} + + ) : undefined} + {list} + {footer && ( + {footer} + )} +
+
+ ); +}; diff --git a/packages/website/docs/components/templates/sitewide-search/index.mdx b/packages/website/docs/components/templates/sitewide-search/index.mdx index 4a3aa5cf26a..90035c44690 100644 --- a/packages/website/docs/components/templates/sitewide-search/index.mdx +++ b/packages/website/docs/components/templates/sitewide-search/index.mdx @@ -382,6 +382,47 @@ export default () => { }; ``` +### ColorModes + +**EuiSelectableTemplateSitewide** by default has the `colorMode` of the nearest **EuiThemeProvider** applied. +If you want to override the this manually, you can pass a `colorModes` prop to the component. This allows you +to apply different color modes to the search and popover components. + +import { Example } from '@site/src/components'; + + + ```tsx + + ``` + + +```tsx interactive +export default () => { + return ( + + ); +}; +``` + + ## Options The `options` are the most opinionated portion of the template. Their display is determined by the props passed in and extends the normal `EuiSelectable.option` type. All parts are optional with the exception of the label (A).