Skip to content

Commit

Permalink
[EuiBreadcrumb] Allow popover content to close the breadcrumb popover (
Browse files Browse the repository at this point in the history
  • Loading branch information
cee-chen authored Mar 7, 2024
1 parent 4408b42 commit 415971d
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 51 deletions.
1 change: 1 addition & 0 deletions changelogs/upcoming/7555.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `EuiBreadcrumbs`'s `popoverContent` API now accepts a render function that will be passed a `closePopover` callback, allowing consumers to close the breadcrumb popover from their popover content
8 changes: 8 additions & 0 deletions src-docs/src/views/breadcrumbs/breadcrumbs_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,14 @@ export const BreadcrumbsExample = {
accepted as they are controlled automatically by{' '}
<strong>EuiBreadcrumbs</strong>.
</p>
<p>
If you need the ability to close the breadcrumb popover from within
your popover content, <EuiCode>popoverContent</EuiCode> accepts a
render function that will be passed a{' '}
<EuiCode>closePopover</EuiCode> callback, which you can invoke to
close the popover. See the Deployment breadcrumb below for example
usage.
</p>
<EuiCallOut
color="warning"
iconType="accessibility"
Expand Down
59 changes: 28 additions & 31 deletions src-docs/src/views/breadcrumbs/popover_content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,37 +41,34 @@ export default () => {
const breadcrumbs: EuiBreadcrumb[] = [
{
text: 'My deployment',
popoverContent: (
<>
<EuiPopoverTitle paddingSize="s">Select a deployment</EuiPopoverTitle>
<EuiContextMenuPanel
size="s"
items={[
<EuiContextMenuItem
key="A"
href="#"
onClick={(e) => e.preventDefault()}
>
Go to Deployment A
</EuiContextMenuItem>,
<EuiContextMenuItem
key="B"
href="#"
onClick={(e) => e.preventDefault()}
>
Go to Deployment B
</EuiContextMenuItem>,
<EuiContextMenuItem
key="C"
href="#"
onClick={(e) => e.preventDefault()}
>
Go to all deployments
</EuiContextMenuItem>,
]}
/>
</>
),
// Passing a render function allows closing the breadcrumb popover from within your content
popoverContent: (closePopover) => {
const onClick = (e: React.MouseEvent) => {
e.preventDefault();
closePopover();
};
return (
<>
<EuiPopoverTitle paddingSize="s">
Select a deployment
</EuiPopoverTitle>
<EuiContextMenuPanel
size="s"
items={[
<EuiContextMenuItem key="A" href="#" onClick={onClick}>
Go to Deployment A
</EuiContextMenuItem>,
<EuiContextMenuItem key="B" href="#" onClick={onClick}>
Go to Deployment B
</EuiContextMenuItem>,
<EuiContextMenuItem key="C" href="#" onClick={onClick}>
Go to all deployments
</EuiContextMenuItem>,
]}
/>
</>
);
},
popoverProps: { panelPaddingSize: 'none' },
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiBreadcrumbContent renders breadcrumbs with \`popoverContent\` with popovers 1`] = `
exports[`EuiBreadcrumbContent breadcrumbs with popovers renders with \`popoverContent\` 1`] = `
<body>
<div>
<div
Expand Down
57 changes: 42 additions & 15 deletions src/components/breadcrumbs/breadcrumb.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

import React from 'react';
import { fireEvent } from '@testing-library/react';
import { render, waitForEuiPopoverOpen } from '../../test/rtl';
import {
render,
waitForEuiPopoverOpen,
waitForEuiPopoverClose,
} from '../../test/rtl';

import { EuiBreadcrumbContent } from './breadcrumb';

Expand All @@ -35,21 +39,44 @@ describe('EuiBreadcrumbContent', () => {
expect(container).toMatchSnapshot();
});

it('renders breadcrumbs with `popoverContent` with popovers', async () => {
const { baseElement, getByTestSubject } = render(
<EuiBreadcrumbContent
type="page"
text="Toggles a popover"
data-test-subj="popoverToggle"
popoverContent="Hello popover world"
popoverProps={{ 'data-test-subj': 'popover' }}
/>
);
fireEvent.click(getByTestSubject('popoverToggle'));
await waitForEuiPopoverOpen();
describe('breadcrumbs with popovers', () => {
it('renders with `popoverContent`', async () => {
const { baseElement, getByTestSubject } = render(
<EuiBreadcrumbContent
type="page"
text="Toggles a popover"
data-test-subj="popoverToggle"
popoverContent="Hello popover world"
popoverProps={{ 'data-test-subj': 'popover' }}
/>
);
fireEvent.click(getByTestSubject('popoverToggle'));
await waitForEuiPopoverOpen();

expect(getByTestSubject('popover')).toBeInTheDocument();
expect(baseElement).toMatchSnapshot();
expect(getByTestSubject('popover')).toBeInTheDocument();
expect(baseElement).toMatchSnapshot();
});

it('passes a popover close callback to `popoverContent` render functions', async () => {
const { getByTestSubject } = render(
<EuiBreadcrumbContent
type="page"
text="Controlled breadcrumb popover"
data-test-subj="popoverToggle"
popoverContent={(closePopover) => (
<button onClick={closePopover} data-test-subj="popoverClose">
Close popover
</button>
)}
/>
);

fireEvent.click(getByTestSubject('popoverToggle'));
await waitForEuiPopoverOpen();

fireEvent.click(getByTestSubject('popoverClose'));
await waitForEuiPopoverClose();
});
});

describe('highlightLastBreadcrumb', () => {
Expand Down
13 changes: 9 additions & 4 deletions src/components/breadcrumbs/breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ export type EuiBreadcrumbProps = Omit<
*/
'aria-current'?: AriaAttributes['aria-current'];
/**
* Creates a breadcrumb that toggles a popover dialog
* Creates a breadcrumb that toggles a popover dialog. Takes any rendered node(s),
* or a render function that will pass callback allowing you to close the
* breadcrumb popover from within your popover content.
*
* If passed, both `href` and `onClick` will be ignored - the breadcrumb's
* click behavior should only trigger a popover.
*/
popoverContent?: ReactNode;
popoverContent?: ReactNode | ((closePopover: () => void) => ReactNode);
/**
* Allows customizing the popover if necessary. Accepts any props that
* [EuiPopover](/#/layout/popover) accepts, except for props that control state.
Expand Down Expand Up @@ -166,11 +168,12 @@ export const EuiBreadcrumbContent: FunctionComponent<
const styleProps = { className: classes, css: cssStyles };

if (isPopoverBreadcrumb) {
const closePopover = () => setIsPopoverOpen(false);
return (
<EuiPopover
{...popoverProps}
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
closePopover={closePopover}
css={!isLastBreadcrumb && styles.euiBreadcrumb__popoverWrapper}
button={
<EuiLink
Expand All @@ -190,7 +193,9 @@ export const EuiBreadcrumbContent: FunctionComponent<
</EuiLink>
}
>
{popoverContent}
{typeof popoverContent === 'function'
? popoverContent(closePopover)
: popoverContent}
</EuiPopover>
);
} else if (isInteractiveBreadcrumb) {
Expand Down

0 comments on commit 415971d

Please sign in to comment.