Skip to content

Commit

Permalink
Remove isGroupTitle API in favor of renderItem
Browse files Browse the repository at this point in the history
+ provide Storybook examples of custom Kibana usage
  • Loading branch information
cee-chen committed Sep 26, 2023
1 parent b38001b commit c12fb27
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { EuiHeader, EuiHeaderSection, EuiHeaderSectionItem } from '../header';
import { EuiPageTemplate } from '../page_template';
import { EuiFlyout, EuiFlyoutBody, EuiFlyoutFooter } from '../flyout';
import { EuiButton } from '../button';
import { EuiTitle } from '../title';

import { EuiCollapsibleNavItem } from './collapsible_nav_item';
import {
Expand Down Expand Up @@ -58,6 +59,22 @@ const OpenCollapsibleNav: FunctionComponent<
);
};

const KibanaNavTitle: FunctionComponent<{
title: string;
}> = ({ title }) => (
<EuiTitle
size="xxxs"
className="eui-textTruncate"
css={({ euiTheme }) => ({
marginTop: euiTheme.size.base,
paddingBlock: euiTheme.size.xs,
paddingInline: euiTheme.size.s,
})}
>
<div>{title}</div>
</EuiTitle>
);

export const KibanaExample: Story = {
render: ({ ...args }) => (
<OpenCollapsibleNav {...args}>
Expand All @@ -78,15 +95,15 @@ export const KibanaExample: Story = {
href="#"
items={[
{ title: 'Get started', href: '#' },
{ title: 'Explore', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Explore" /> },
{ title: 'Discover', href: '#' },
{ title: 'Dashboards', href: '#' },
{ title: 'Visualize library', href: '#' },
{ title: 'Content', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Content" /> },
{ title: 'Indices', href: '#' },
{ title: 'Transforms', href: '#' },
{ title: 'Indexing API', href: '#' },
{ title: 'Security', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Security" /> },
{ title: 'API keys', href: '#' },
]}
/>
Expand Down Expand Up @@ -115,7 +132,7 @@ export const KibanaExample: Story = {
{ title: 'Alerts', href: '#' },
{ title: 'Cases', href: '#' },
{ title: 'SLOs', href: '#' },
{ title: 'Signals', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Signals" /> },
{ title: 'Logs', href: '#' },
{
title: 'Tracing',
Expand All @@ -126,7 +143,7 @@ export const KibanaExample: Story = {
{ title: 'Dependencies', href: '#' },
],
},
{ title: 'Toolbox', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Toolbox" /> },
{ title: 'Visualize library', href: '#' },
{ title: 'Dashboards', href: '#' },
{
Expand Down Expand Up @@ -217,18 +234,20 @@ export const KibanaExample: Story = {
{ title: 'Overview', href: '#' },
{ title: 'Notifications', href: '#' },
{ title: 'Memory usage', href: '#' },
{ title: 'Anomaly detection', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Anomaly detection" /> },
{ title: 'Jobs', href: '#' },
{ title: 'Anomaly explorer', href: '#' },
{ title: 'Single metric viewer', href: '#' },
{ title: 'Settings', href: '#' },
{ title: 'Data frame analytics', isGroupTitle: true },
{
renderItem: () => <KibanaNavTitle title="Data frame analytics" />,
},
{ title: 'Jobs', href: '#' },
{ title: 'Results explorer', href: '#' },
{ title: 'Analytics map', href: '#' },
{ title: 'Model management', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Model management" /> },
{ title: 'Trained models', href: '#' },
{ title: 'Data visualizer', isGroupTitle: true },
{ renderItem: () => <KibanaNavTitle title="Data visualizer" /> },
{ title: 'File', href: '#' },
{ title: 'Data view', href: '#' },
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import { EuiAccordion } from '../../accordion';

import {
EuiCollapsibleNavSubItem,
EuiCollapsibleNavSubItemProps,
_SharedEuiCollapsibleNavItemProps,
_EuiCollapsibleNavItemDisplayProps,
EuiCollapsibleNavItemProps,
} from './collapsible_nav_item';
import { EuiCollapsibleNavLink } from './collapsible_nav_link';
import { euiCollapsibleNavAccordionStyles } from './collapsible_nav_accordion.styles';
Expand All @@ -33,10 +33,7 @@ type EuiCollapsibleNavAccordionProps = Omit<
> &
_EuiCollapsibleNavItemDisplayProps & {
buttonContent: ReactNode;
// On the main `EuiCollapsibleNavItem` component, this uses `EuiCollapsibleNavSubItemProps`
// to allow for section headings, but by the time `items` reaches this component, we
// know for sure it's an actual accordion item and not a section heading
items: EuiCollapsibleNavItemProps[];
items: EuiCollapsibleNavSubItemProps[];
};

/**
Expand Down Expand Up @@ -91,13 +88,10 @@ export const EuiCollapsibleNavAccordion: FunctionComponent<
/**
* Child items
*/
// If any of the sub items have an icon, default to an
// icon of `empty` so that all text lines up vertically
const itemsHaveIcons = useMemo(
() => items.some((item) => !!item.icon),
[items]
);
const icon = itemsHaveIcons ? 'empty' : undefined;

const childrenCssStyles = [
styles.children.euiCollapsibleNavAccordion__children,
Expand All @@ -109,12 +103,19 @@ export const EuiCollapsibleNavAccordion: FunctionComponent<
css={childrenCssStyles}
className="euiCollapsibleNavAccordion__children"
>
{items.map((item, index) => (
// This is an intentional circular dependency between the accordion & parent item display.
// EuiSideNavItem is purposely recursive to support any amount of nested sub items,
// and split up into separate files/components for better dev readability
<EuiCollapsibleNavSubItem key={index} icon={icon} {...item} />
))}
{items.map((item, index) => {
// If any of the sub items have an icon, default to an
// icon of `empty` so that all text lines up vertically
if (!item.renderItem && itemsHaveIcons && !item.icon) {
item.icon = 'empty';
}
return (
// This is an intentional circular dependency between the accordion & parent item display.
// EuiSideNavItem is purposely recursive to support any amount of nested sub items,
// and split up into separate files/components for better dev readability
<EuiCollapsibleNavSubItem key={index} {...item} />
);
})}
</div>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';

import { EuiSpacer } from '../../spacer';
import { EuiCollapsibleNavBeta } from '../collapsible_nav_beta';

import {
Expand Down Expand Up @@ -100,10 +101,7 @@ export const EdgeCaseTesting: Story = {
{ ...args, title: 'Link', href: '#', isSelected: true },
{ ...args, title: 'Button', onClick: () => {} },
{ ...args, title: 'Span', href: '#' },
{
title: 'Section 2',
isGroupTitle: true,
},
{ renderItem: () => <EuiSpacer size="m" /> },
{
...args,
title: 'Test 2',
Expand All @@ -125,11 +123,7 @@ export const EdgeCaseTesting: Story = {
{ title: 'grandchild 2', href: '#' },
],
},
{
title: 'Section 3',
titleElement: 'h3',
isGroupTitle: true,
},
{ renderItem: () => <EuiSpacer size="m" /> },
{
...args,
title: 'Nested accordion with grandchildren',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
import { css } from '@emotion/react';

import { UseEuiTheme } from '../../../services';
import {
logicalCSS,
logicalShorthandCSS,
euiFontSize,
} from '../../../global_styling';
import { euiFontSize } from '../../../global_styling';
import { euiButtonColor } from '../../../themes/amsterdam/global_styling/mixins/button';

/**
Expand Down Expand Up @@ -47,17 +43,3 @@ export const euiCollapsibleNavItemTitleStyles = {
flex-grow: 1;
`,
};

export const euiCollapsibleNavSubItemGroupTitleStyles = ({
euiTheme,
}: UseEuiTheme) => {
return {
euiCollapsibleNavItem__groupTitle: css`
${logicalCSS('margin-top', euiTheme.size.base)}
${logicalShorthandCSS(
'padding',
`${euiTheme.size.xs} ${euiTheme.size.s}`
)}
`,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -145,51 +145,18 @@ describe('EuiCollapsibleNavItem', () => {
).toHaveLength(5);
});

it('renders group titles', () => {
const { container } = render(
<EuiCollapsibleNavItem
title="Item"
items={[
{ isGroupTitle: true, title: 'Section' },
{ title: 'Hello' },
{ title: 'World' },
]}
/>
);

expect(container.querySelector('.euiCollapsibleNavItem__groupTitle'))
.toMatchInlineSnapshot(`
<div
class="euiTitle euiCollapsibleNavItem__groupTitle eui-textTruncate emotion-euiTitle-xxxs-euiCollapsibleNavItem__groupTitle"
>
Section
</div>
`);
});

it('allows customizing the group title element', () => {
const { container } = render(
it('allows rendering totally custom sub items', () => {
const { getByTestSubject } = render(
<EuiCollapsibleNavItem
title="Item"
items={[
{
isGroupTitle: true,
title: 'Group title',
titleElement: 'h2',
},
{ renderItem: () => <div data-test-subj="custom" /> },
{ title: 'Link 1', titleElement: 'h3' },
]}
/>
);

expect(container.querySelector('.euiCollapsibleNavItem__groupTitle'))
.toMatchInlineSnapshot(`
<h2
class="euiTitle euiCollapsibleNavItem__groupTitle eui-textTruncate emotion-euiTitle-xxxs-euiCollapsibleNavItem__groupTitle"
>
Group title
</h2>
`);
expect(getByTestSubject('custom')).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@
* Side Public License, v 1.
*/

import React, { FunctionComponent, HTMLAttributes, useContext } from 'react';
import React, {
FunctionComponent,
HTMLAttributes,
ReactNode,
useContext,
} from 'react';
import classNames from 'classnames';

import { useEuiTheme } from '../../../services';
import { CommonProps, ExclusiveUnion } from '../../common';

import { EuiIcon, IconType, EuiIconProps } from '../../icon';
import { EuiLinkProps } from '../../link';
import { EuiAccordionProps } from '../../accordion';
import { EuiTitle } from '../../title';

import { EuiCollapsibleNavContext } from '../context';
import { EuiCollapsedNavItem } from './collapsed';
import { EuiCollapsibleNavAccordion } from './collapsible_nav_accordion';
import { EuiCollapsibleNavLink } from './collapsible_nav_link';
import {
euiCollapsibleNavItemTitleStyles,
euiCollapsibleNavSubItemGroupTitleStyles,
} from './collapsible_nav_item.styles';
import { euiCollapsibleNavItemTitleStyles } from './collapsible_nav_item.styles';

export type _SharedEuiCollapsibleNavItemProps = HTMLAttributes<HTMLElement> &
CommonProps & {
Expand All @@ -37,8 +37,8 @@ export type _SharedEuiCollapsibleNavItemProps = HTMLAttributes<HTMLElement> &
/**
* When passed, an `EuiAccordion` with nested child item links will be rendered.
*
* Accepts any #EuiCollapsibleNavItem prop, and also accepts an
* #EuiCollapsibleNavSubItemGroupTitle
* Accepts any #EuiCollapsibleNavItemProps. Or, to render completely custom
* subitem content, pass an object with a `renderItem` callback.
*/
items?: EuiCollapsibleNavSubItemProps[];
/**
Expand Down Expand Up @@ -79,20 +79,13 @@ export type EuiCollapsibleNavItemProps = {
iconProps?: Partial<EuiIconProps>;
} & _SharedEuiCollapsibleNavItemProps;

export type EuiCollapsibleNavSubItemGroupTitle = Pick<
EuiCollapsibleNavItemProps,
'title' | 'titleElement'
> & {
/**
* Pass this flag to seperate links by group title headings.
* Strongly consider using the `titleElement` prop for accessibility.
*/
isGroupTitle?: boolean;
export type EuiCollapsibleNavCustomSubItem = {
renderItem: () => ReactNode;
};

export type EuiCollapsibleNavSubItemProps = ExclusiveUnion<
EuiCollapsibleNavItemProps,
EuiCollapsibleNavSubItemGroupTitle
EuiCollapsibleNavCustomSubItem
>;

export type _EuiCollapsibleNavItemDisplayProps = {
Expand Down Expand Up @@ -182,31 +175,24 @@ const EuiCollapsibleNavItemTitle: FunctionComponent<
};

/**
* Sub-items can either be a group title, to visually separate sections
* of nav links, or they can simply be more links or accordions
* Sub-items can either be a totally custom rendered item,
* or they can simply be more links or accordions
*/
export const EuiCollapsibleNavSubItem: FunctionComponent<
EuiCollapsibleNavSubItemProps
> = ({ isGroupTitle, className, ...props }) => {
const euiTheme = useEuiTheme();
const styles = euiCollapsibleNavSubItemGroupTitleStyles(euiTheme);
> = ({ renderItem, className, ...props }) => {
const classes = classNames('euiCollapsibleNavSubItem', className);

if (isGroupTitle) {
const TitleElement = props.titleElement || 'div';
return (
<EuiTitle
size="xxxs"
css={styles.euiCollapsibleNavItem__groupTitle}
className="euiCollapsibleNavItem__groupTitle eui-textTruncate"
>
<TitleElement>{props.title}</TitleElement>
</EuiTitle>
);
if (renderItem) {
return <>{renderItem()}</>;
}

return (
<EuiCollapsibleNavItemDisplay className={classes} {...props} isSubItem />
<EuiCollapsibleNavItemDisplay
className={classes}
{...(props as EuiCollapsibleNavItemProps)}
isSubItem
/>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
export type {
EuiCollapsibleNavItemProps,
EuiCollapsibleNavSubItemProps,
EuiCollapsibleNavSubItemGroupTitle,
} from './collapsible_nav_item';

export { EuiCollapsibleNavItem } from './collapsible_nav_item';
Loading

0 comments on commit c12fb27

Please sign in to comment.