Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiBreadcrumb] Allow popover content to close the breadcrumb popover #7555

Merged
merged 4 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading