= {
+ title: 'EuiCollapsibleNavGroup',
+ // @ts-ignore This still works for Storybook controls, even though Typescript complains
+ component: EuiCollapsibleNavGroup,
+ argTypes: {
+ // Only show when `title` is set
+ titleSize: { if: { arg: 'title' } },
+ titleElement: { if: { arg: 'title' } },
+ iconType: { if: { arg: 'title' } },
+ iconSize: { if: { arg: 'title' } },
+ iconProps: { if: { arg: 'title' } },
+ // EuiAccordion props that should only show if `isCollapsible`
+ // (and `title`, but Storybook doesn't support multiple conditional controls)
+ buttonElement: { if: { arg: 'isCollapsible' } },
+ buttonProps: { if: { arg: 'isCollapsible' } },
+ buttonClassName: { if: { arg: 'isCollapsible' } },
+ buttonContentClassName: { if: { arg: 'isCollapsible' } },
+ buttonContent: { if: { arg: 'isCollapsible' } },
+ arrowProps: { if: { arg: 'isCollapsible' } },
+ extraAction: { if: { arg: 'isCollapsible' } },
+ initialIsOpen: { if: { arg: 'isCollapsible' } },
+ onToggle: { if: { arg: 'isCollapsible' } },
+ paddingSize: { if: { arg: 'isCollapsible' } },
+ arrowDisplay: { if: { arg: 'isCollapsible' } },
+ forceState: { if: { arg: 'isCollapsible' } },
+ isLoading: { if: { arg: 'isCollapsible' } },
+ isLoadingMessage: { if: { arg: 'isCollapsible' } },
+ isDisabled: { if: { arg: 'isCollapsible' } },
+ element: { if: { arg: 'isCollapsible' } },
+ },
+};
+
+export default meta;
+type Story = StoryObj
;
+
+export const Accordion: Story = {
+ args: {
+ children: 'This is an accordion group with a title',
+ background: 'none',
+ title: 'Nav group - accordion',
+ iconType: 'logoElastic',
+ iconSize: 'l',
+ titleElement: 'h3',
+ titleSize: 'xxs',
+ initialIsOpen: true,
+ isCollapsible: true,
+ },
+};
+
+export const NonAccordion: StoryObj = {
+ args: {
+ children: 'This is a group with a title',
+ background: 'none',
+ title: 'Nav group - non-accordion',
+ iconType: 'logoElastic',
+ iconSize: 'l',
+ titleElement: 'h3',
+ titleSize: 'xxs',
+ isCollapsible: false,
+ },
+};
+
+export const NoTitle: Story = {
+ args: {
+ children: 'This is a group without a title',
+ background: 'none',
+ },
+};
diff --git a/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.styles.ts b/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.styles.ts
new file mode 100644
index 00000000000..b7de2749a0f
--- /dev/null
+++ b/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.styles.ts
@@ -0,0 +1,62 @@
+/*
+ * 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 { UseEuiTheme, shade } from '../../../services';
+import { logicalCSS } from '../../../global_styling';
+
+export const euiCollapsibleNavGroupStyles = ({
+ euiTheme,
+ colorMode,
+}: UseEuiTheme) => {
+ return {
+ euiCollapsibleNavGroup: css`
+ &:not(:first-child) {
+ ${logicalCSS('border-top', euiTheme.border.thin)}
+ }
+ `,
+ // Background colors
+ none: css``,
+ light: css`
+ background-color: ${euiTheme.colors.body};
+ `,
+ dark: css`
+ background-color: ${colorMode === 'DARK'
+ ? shade(euiTheme.colors.lightestShade, 0.5)
+ : shade(euiTheme.colors.darkestShade, 0.2)};
+
+ .euiCollapsibleNavGroup__title,
+ .euiCollapsibleNavGroup__heading,
+ .euiAccordion__iconButton {
+ color: ${euiTheme.colors.ghost};
+ }
+ `,
+ // Header padding
+ isCollapsible: css`
+ /* This class does not accept a custom className */
+ .euiAccordion__triggerWrapper {
+ padding: ${euiTheme.size.base};
+ }
+ `,
+ notCollapsible: css`
+ /* If the heading is not in an accordion, it needs the padding */
+ .euiCollapsibleNavGroup__heading {
+ padding: ${euiTheme.size.base};
+ }
+ `,
+ // Children padding
+ childrenWrapper: {
+ euiCollapsibleNavGroup__children: css`
+ padding: ${euiTheme.size.s};
+ `,
+ withHeading: css`
+ ${logicalCSS('padding-top', 0)}
+ `,
+ },
+ };
+};
diff --git a/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.test.tsx b/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.test.tsx
index 39a89e9dec8..b7b5cb813d4 100644
--- a/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.test.tsx
+++ b/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.test.tsx
@@ -7,7 +7,7 @@
*/
import React from 'react';
-import { render } from 'enzyme';
+import { render } from '../../../test/rtl';
import { requiredProps } from '../../../test/required_props';
import { shouldRenderCustomStyles } from '../../../test/internal';
@@ -20,32 +20,32 @@ describe('EuiCollapsibleNavGroup', () => {
);
test('is rendered', () => {
- const component = render(
+ const { container } = render(
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
describe('props', () => {
test('title is rendered', () => {
- const component = render(
+ const { container } = render(
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
test('iconType is rendered', () => {
- const component = render(
+ const { container } = render(
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
test('iconSize is rendered', () => {
- const component = render(
+ const { container } = render(
{
/>
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
test('iconProps renders data-test-subj', () => {
- const component = render(
+ const { container } = render(
{
/>
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
describe('background', () => {
BACKGROUNDS.forEach((color) => {
test(`${color} is rendered`, () => {
- const component = render(
+ const { container } = render(
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
});
});
test('titleElement can change the rendered element to h2', () => {
- const component = render(
+ const { container } = render(
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
test('titleSize can be larger', () => {
- const component = render(
+ const { container } = render(
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
});
describe('when isCollapsible is true', () => {
test('will render an accordion', () => {
- const component = render(
+ const { container } = render(
{
/>
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
test('accepts accordion props', () => {
- const component = render(
+ const { container } = render(
{
/>
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
});
@@ -147,7 +147,7 @@ describe('EuiCollapsibleNavGroup', () => {
});
test('if iconType is passed without a title', () => {
- const component = render(
+ const { container } = render(
);
@@ -155,7 +155,7 @@ describe('EuiCollapsibleNavGroup', () => {
expect(consoleStub.mock.calls[0][0]).toMatch(
'not render an icon without `title`'
);
- expect(component).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
});
});
diff --git a/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.tsx b/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.tsx
index 8849645c2cf..b65c1d44aa7 100644
--- a/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.tsx
+++ b/src/components/collapsible_nav/collapsible_nav_group/collapsible_nav_group.tsx
@@ -9,22 +9,21 @@
import React, { FunctionComponent, ReactNode, HTMLAttributes } from 'react';
import classNames from 'classnames';
import { CommonProps, ExclusiveUnion } from '../../common';
-import { useGeneratedHtmlId } from '../../../services';
+import {
+ EuiThemeProvider,
+ useEuiTheme,
+ useGeneratedHtmlId,
+} from '../../../services';
import { EuiAccordion, EuiAccordionProps } from '../../accordion';
import { EuiIcon, IconType, IconSize, EuiIconProps } from '../../icon';
import { EuiFlexGroup, EuiFlexItem } from '../../flex';
import { EuiTitle, EuiTitleProps, EuiTitleSize } from '../../title';
-type Background = 'none' | 'light' | 'dark';
-const backgroundToClassNameMap: { [color in Background]: string } = {
- none: '',
- light: 'euiCollapsibleNavGroup--light',
- dark: 'euiCollapsibleNavGroup--dark',
-};
-export const BACKGROUNDS = Object.keys(
- backgroundToClassNameMap
-) as Background[];
+import { euiCollapsibleNavGroupStyles } from './collapsible_nav_group.styles';
+
+export const BACKGROUNDS = ['none', 'light', 'dark'] as const;
+type Background = (typeof BACKGROUNDS)[number];
export interface EuiCollapsibleNavGroupInterface extends CommonProps {
/**
@@ -116,14 +115,15 @@ export const EuiCollapsibleNavGroup: FunctionComponent<
const groupID = useGeneratedHtmlId({ conditionalId: id });
const titleID = `${groupID}__title`;
- const classes = classNames(
- 'euiCollapsibleNavGroup',
- backgroundToClassNameMap[background],
- {
- 'euiCollapsibleNavGroup--withHeading': title,
- },
- className
- );
+ const euiTheme = useEuiTheme();
+ const styles = euiCollapsibleNavGroupStyles(euiTheme);
+ const cssStyles = [
+ styles.euiCollapsibleNavGroup,
+ isCollapsible ? styles.isCollapsible : styles.notCollapsible,
+ background && styles[background],
+ ];
+
+ const classes = classNames('euiCollapsibleNavGroup', className);
// Warn if consumer passes an iconType without a title
if (iconType && !title) {
@@ -132,8 +132,14 @@ export const EuiCollapsibleNavGroup: FunctionComponent<
);
}
+ const childrenStyles = [
+ styles.childrenWrapper.euiCollapsibleNavGroup__children,
+ title && styles.childrenWrapper.withHeading,
+ ];
const content = children && (
- {children}
+
+ {children}
+
);
const headingClasses = 'euiCollapsibleNavGroup__heading';
@@ -157,27 +163,36 @@ export const EuiCollapsibleNavGroup: FunctionComponent<
) : undefined;
+ let render;
if (isCollapsible && title) {
- return (
+ render = (
{content}
);
} else {
- return (
-
+ render = (
+
{titleContent &&
{titleContent}
}
{content}
);
}
+
+ return background === 'dark' ? (
+
+ {render}
+
+ ) : (
+ render
+ );
};
diff --git a/src/components/index.scss b/src/components/index.scss
index 999878192e4..2d60e75ee4c 100644
--- a/src/components/index.scss
+++ b/src/components/index.scss
@@ -2,7 +2,6 @@
@import 'accordion/index';
@import 'button/index';
-@import 'collapsible_nav/index';
@import 'color_picker/index';
@import 'combo_box/index';
@import 'context_menu/index';
diff --git a/upcoming_changelogs/6865.md b/upcoming_changelogs/6865.md
new file mode 100644
index 00000000000..e072cc3de73
--- /dev/null
+++ b/upcoming_changelogs/6865.md
@@ -0,0 +1,3 @@
+**CSS-in-JS conversions**
+
+- Converted `EuiCollapsibleNav` and `EuiCollapsibleNavGroup` to Emotion