diff --git a/src/components/index.scss b/src/components/index.scss index ced54713a92..12530e2af31 100644 --- a/src/components/index.scss +++ b/src/components/index.scss @@ -29,4 +29,3 @@ @import 'steps/index'; @import 'suggest/index'; @import 'table/index'; -@import 'tabs/index'; diff --git a/src/components/page/page_header/__snapshots__/page_header.test.tsx.snap b/src/components/page/page_header/__snapshots__/page_header.test.tsx.snap index 814cb118f90..1d845e1e86b 100644 --- a/src/components/page/page_header/__snapshots__/page_header.test.tsx.snap +++ b/src/components/page/page_header/__snapshots__/page_header.test.tsx.snap @@ -324,30 +324,30 @@ exports[`EuiPageHeader props page content props are passed down is rendered 1`] />
); diff --git a/src/components/tabs/tabbed_content/__snapshots__/tabbed_content.test.tsx.snap b/src/components/tabs/tabbed_content/__snapshots__/tabbed_content.test.tsx.snap index a78d6cf2f27..6abf6017711 100644 --- a/src/components/tabs/tabbed_content/__snapshots__/tabbed_content.test.tsx.snap +++ b/src/components/tabs/tabbed_content/__snapshots__/tabbed_content.test.tsx.snap @@ -3,19 +3,19 @@ exports[`EuiTabbedContent behavior when selected tab state isn't controlled by the owner, select the first tab by default 1`] = `
@@ -94,76 +90,1615 @@ exports[`EuiTabbedContent behavior when uncontrolled, the selected tab should up onFocus={[Function]} >
- + + + + + + + + + + + + + + + + + + + , + "ctr": 18, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "1hjld2u-euiTabs-m-bottomBorder", + "next": undefined, + "styles": "display:flex;max-inline-size: 100%;; + overflow-x: auto; + overflow-inline: auto; +; + overflow-y: hidden; + overflow-block: hidden; +;position:relative;flex-shrink:0;;label:euiTabs;;;gap:16px;;label:m;;;box-shadow:inset 0 -1px 0 #D3DAE6;;label:bottomBorder;;;", + "toString": [Function], + } + } + /> +
- - - - + + + - - prepend - - - - Kibana - - - - append - - - + + + + + + + + + + + + + + + + + + + + , + "ctr": 18, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "9jv5g3-euiTab-selected-m", + "next": undefined, + "styles": "display:flex;cursor:pointer;flex-direction:row;align-items:center;font-weight:600;gap:8px;&:focus{background-color:transparent;outline-offset:-2px;};label:euiTab;;;;box-shadow:inset 0 calc(2px * -1) 0 #07C;;label:selected;;;;padding:0 4px;;label:m;;;", + "toString": [Function], + } + } + /> + + + +
@@ -246,19 +1777,19 @@ exports[`EuiTabbedContent is rendered with required props and tabs 1`] = ` exports[`EuiTabbedContent props autoFocus initial is rendered 1`] = `
@@ -306,19 +1833,19 @@ exports[`EuiTabbedContent props autoFocus initial is rendered 1`] = ` exports[`EuiTabbedContent props autoFocus selected is rendered 1`] = `
@@ -366,19 +1889,19 @@ exports[`EuiTabbedContent props autoFocus selected is rendered 1`] = ` exports[`EuiTabbedContent props initialSelectedTab renders a selected tab 1`] = `
@@ -426,19 +1945,19 @@ exports[`EuiTabbedContent props initialSelectedTab renders a selected tab 1`] = exports[`EuiTabbedContent props selectedTab renders a selected tab 1`] = `
@@ -486,19 +2001,19 @@ exports[`EuiTabbedContent props selectedTab renders a selected tab 1`] = ` exports[`EuiTabbedContent props size can be small 1`] = `
diff --git a/src/components/tabs/tabbed_content/tabbed_content.tsx b/src/components/tabs/tabbed_content/tabbed_content.tsx index 1a5361019c4..eaead4fe06c 100644 --- a/src/components/tabs/tabbed_content/tabbed_content.tsx +++ b/src/components/tabs/tabbed_content/tabbed_content.tsx @@ -16,7 +16,7 @@ import React, { import { htmlIdGenerator } from '../../../services'; -import { EuiTabs, EuiTabsSizes } from '../tabs'; +import { EuiTabs } from '../tabs'; import { EuiTab, EuiTabProps } from '../tab'; import { CommonProps } from '../../common'; @@ -58,7 +58,7 @@ export type EuiTabbedContentProps = CommonProps & * Use this prop if you want to control selection state within the owner component */ selectedTab?: EuiTabbedContentTab; - size?: EuiTabsSizes; + size?: 's' | 'm' | 'l' | 'xl'; /** * Each tab needs id and content properties, so we can associate it with its panel for accessibility. * The name property (a node) is also required to display to the user. diff --git a/src/components/tabs/tabs.styles.ts b/src/components/tabs/tabs.styles.ts new file mode 100644 index 00000000000..f17759246e8 --- /dev/null +++ b/src/components/tabs/tabs.styles.ts @@ -0,0 +1,48 @@ +/* + * 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 { css } from '@emotion/react'; +import { + logicalCSS, + logicalCSSWithFallback, + mathWithUnits, +} from '../../global_styling'; +import { UseEuiTheme } from '../../services'; + +export const euiTabsStyles = (euiThemeContext: UseEuiTheme) => { + const { euiTheme } = euiThemeContext; + + return { + euiTabs: css` + display: flex; + ${logicalCSS('max-width', '100%')}; + ${logicalCSSWithFallback('overflow-x', 'auto')}; + ${logicalCSSWithFallback('overflow-y', 'hidden')}; + position: relative; + flex-shrink: 0; + `, + bottomBorder: css` + box-shadow: inset 0 + ${mathWithUnits(euiTheme.border.width.thin, (x) => x * -1)} 0 + ${euiTheme.border.color}; + `, + // sizes + s: css` + gap: ${euiTheme.size.m}; + `, + m: css` + gap: ${euiTheme.size.base}; + `, + l: css` + gap: ${euiTheme.size.l}; + `, + xl: css` + gap: ${euiTheme.size.xl}; + `, + }; +}; diff --git a/src/components/tabs/tabs.test.tsx b/src/components/tabs/tabs.test.tsx index c1572a2ba7d..e12574e6954 100644 --- a/src/components/tabs/tabs.test.tsx +++ b/src/components/tabs/tabs.test.tsx @@ -9,10 +9,15 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../test/required_props'; +import { shouldRenderCustomStyles } from '../../test/internal'; -import { EuiTabs, SIZES } from './tabs'; +import { EuiTabs } from './tabs'; + +const SIZES = ['s', 'm', 'l', 'xl'] as const; describe('EuiTabs', () => { + shouldRenderCustomStyles(children); + test('renders', () => { const component = children; diff --git a/src/components/tabs/tabs.tsx b/src/components/tabs/tabs.tsx index a580ae12b7a..6e11e1fe2cf 100644 --- a/src/components/tabs/tabs.tsx +++ b/src/components/tabs/tabs.tsx @@ -13,18 +13,14 @@ import React, { ReactNode, } from 'react'; import classNames from 'classnames'; -import { CommonProps, keysOf } from '../common'; +import { CommonProps } from '../common'; +import { useEuiTheme } from '../../services'; +import { cloneElementWithCss } from '../../services/theme/clone_element'; -const sizeToClassNameMap = { - s: 'euiTabs--small', - m: null, - l: 'euiTabs--large', - xl: 'euiTabs--xlarge', -}; +import { euiTabsStyles } from './tabs.styles'; -export const SIZES = keysOf(sizeToClassNameMap); - -export type EuiTabsSizes = keyof typeof sizeToClassNameMap; +export const SIZES = ['s', 'm', 'l', 'xl'] as const; +export type EuiTabSizes = typeof SIZES[number]; export type EuiTabsProps = CommonProps & HTMLAttributes & { @@ -45,7 +41,7 @@ export type EuiTabsProps = CommonProps & * Sizes affect both font size and overall size. * Only use the `xl` size when displayed as page titles. */ - size?: EuiTabsSizes; + size?: EuiTabSizes; }; export type EuiTabRef = HTMLDivElement; @@ -62,24 +58,36 @@ export const EuiTabs = forwardRef>( }: PropsWithChildren, ref ) => { - const classes = classNames( - 'euiTabs', - sizeToClassNameMap[size], - { - 'euiTabs--expand': expand, - 'euiTabs--bottomBorder': bottomBorder, - }, - className - ); + const euiTheme = useEuiTheme(); + + const classes = classNames('euiTabs', className); + + const tabsStyles = euiTabsStyles(euiTheme); + const computedStyles = [ + tabsStyles.euiTabs, + tabsStyles[size], + bottomBorder && tabsStyles.bottomBorder, + ]; + + const tabItems = React.Children.map(children, (child) => { + if (React.isValidElement(child)) { + return cloneElementWithCss(child, { + // we're passing the parent `size` and `expand` down to the children + size: size, + expand: expand, + }); + } + }); return (
- {children} + {tabItems}
); } diff --git a/src/themes/amsterdam/overrides/_index.scss b/src/themes/amsterdam/overrides/_index.scss index 721733fee48..8d1e11becf6 100644 --- a/src/themes/amsterdam/overrides/_index.scss +++ b/src/themes/amsterdam/overrides/_index.scss @@ -24,4 +24,3 @@ @import 'range_tooltip'; @import 'side_nav'; @import 'steps'; -@import 'tabs'; diff --git a/src/themes/amsterdam/overrides/_tabs.scss b/src/themes/amsterdam/overrides/_tabs.scss deleted file mode 100644 index 623ef2abdab..00000000000 --- a/src/themes/amsterdam/overrides/_tabs.scss +++ /dev/null @@ -1,80 +0,0 @@ -// Fixed heights ensure proper alignment with pixel grid - -// MEDIUM -.euiTab, -.euiTabs--condensed .euiTab { - padding: 0 $euiSizeXS; - - .euiTab__content { - @include euiTitle('xxs'); - line-height: $euiSizeXXL; - } - - & + .euiTab { - margin-left: $euiSize; - } - - &:focus { - background-color: transparent; - } -} - -.euiTabs, -.euiTabs--condensed.euiTabs { - // SMALL - &--small .euiTab { - padding: 0 $euiSizeXS; - - .euiTab__content { - @include euiTitle('xxxs'); - line-height: $euiSizeXL; - } - - & + .euiTab { - margin-left: $euiSizeM; - } - } - - // LARGE - &--large .euiTab { - padding: 0 $euiSizeXS; - - .euiTab__content { - @include euiTitle('xs'); - line-height: $euiSizeXXL + $euiSizeS; - } - - & + .euiTab { - margin-left: $euiSizeL; - } - } - - // X-LARGE - &--xlarge .euiTab { - padding: 0 $euiSizeXS; - - .euiTab__content { - @include fontSize($euiSize + $euiSizeXS); - line-height: $euiSizeXXL + $euiSizeS; - } - - & + .euiTab { - margin-left: $euiSizeXL; - } - } - - // SELECTED - .euiTab-isSelected { - color: $euiColorPrimaryText; - - &:hover, - &:focus { - text-decoration: underline; - cursor: pointer; - } - - &:focus-visible { - box-shadow: none; - } - } -} diff --git a/upcoming_changelogs/6311.md b/upcoming_changelogs/6311.md new file mode 100644 index 00000000000..5f34fea279d --- /dev/null +++ b/upcoming_changelogs/6311.md @@ -0,0 +1,3 @@ +**CSS-in-JS conversions** + +- Converted `EuiTabs` to Emotion