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

[EuiContextMenu] Add line separator as menu item #4018

Merged
merged 14 commits into from
Sep 16, 2020
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Added `boolean` type to the `notification` prop of `EuiHeaderSectionItemButton` to show a simple dot ([#4008](https://github.com/elastic/eui/pull/4008))
- Added `popoverButton` and `popoverButtonBreakpoints` props to `EuiSelectableTemplateSitewide` for responsive capabilities ([#4008](https://github.com/elastic/eui/pull/4008))
- Added `isWithinMaxBreakpoint` service ([#4008](https://github.com/elastic/eui/pull/4008))
- Added horizontal line separator to `EuiContextMenu` ([#4018](https://github.com/elastic/eui/pull/4018))

**Bug fixes**

Expand Down
12 changes: 11 additions & 1 deletion src-docs/src/views/context_menu/context_menu_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { GuideSectionTypes } from '../../components';
import {
EuiCode,
EuiContextMenu,
EuiContextMenuPanel,
EuiContextMenuItem,
EuiContextMenuPanel,
} from '../../../../src/components';

import ContextMenu from './context_menu';
Expand Down Expand Up @@ -122,6 +122,16 @@ export const ContextMenuExample = {
<EuiCode language="ts">width: [number of pixels]</EuiCode> to the
panel tree.
</p>
<p>
You can add separator lines in the <EuiCode>items</EuiCode> prop if
you define an item as{' '}
<EuiCode language="ts">{'{isSeparator: true}'}</EuiCode>. This will
pass the rest of its fields as props to a{' '}
<Link to="/layout/horizontal-rule">
<strong>EuiHorizontalRule</strong>
</Link>{' '}
component.
</p>
</div>
),
demo: <ContextMenuWithContent />,
Expand Down
4 changes: 4 additions & 0 deletions src-docs/src/views/context_menu/context_menu_with_content.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export default () => {
window.alert('Show fullscreen');
},
},
{
isSeparator: true,
key: 'sep',
},
{
name: 'See more',
icon: 'plusInCircle',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiContextMenu can pass-through horizontal rule props 1`] = `
<div
class="euiContextMenu"
>
<div
class="euiContextMenuPanel euiContextMenu__panel"
tabindex="0"
>
<div
class="euiPopoverTitle"
>
<span
class="euiContextMenu__itemLayout"
>
Testing separator
</span>
</div>
<div>
<div>
<hr
class="euiHorizontalRule euiHorizontalRule--half euiHorizontalRule--marginSmall"
/>
</div>
</div>
</div>
</div>
`;

exports[`EuiContextMenu is rendered 1`] = `
<div
aria-label="aria-label"
Expand All @@ -8,6 +36,62 @@ exports[`EuiContextMenu is rendered 1`] = `
/>
`;

exports[`EuiContextMenu panel item can be a separator line 1`] = `
<div
class="euiContextMenu"
>
<div
class="euiContextMenuPanel euiContextMenu__panel"
tabindex="0"
>
<div
class="euiPopoverTitle"
>
<span
class="euiContextMenu__itemLayout"
>
Testing separator
</span>
</div>
<div>
<div>
<button
class="euiContextMenuItem"
type="button"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
>
Foo
</span>
</span>
</button>
<hr
class="euiHorizontalRule euiHorizontalRule--full"
/>
<button
class="euiContextMenuItem"
type="button"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
>
Bar
</span>
</span>
</button>
</div>
</div>
</div>
</div>
`;

exports[`EuiContextMenu panel item can contain JSX 1`] = `
<div
class="euiContextMenu"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiContextMenu can pass-through horizontal rule props 1`] = `
<div
class="euiContextMenu"
>
<div
class="euiContextMenuPanel euiContextMenu__panel"
tabindex="0"
>
<div
class="euiPopoverTitle"
>
<span
class="euiContextMenu__itemLayout"
>
Testing separator
</span>
</div>
<div>
<div>
<hr
class="euiHorizontalRule euiHorizontalRule--half euiHorizontalRule--marginSmall"
/>
</div>
</div>
</div>
</div>
`;

exports[`EuiContextMenu is rendered 1`] = `
<div
aria-label="aria-label"
Expand All @@ -8,6 +36,62 @@ exports[`EuiContextMenu is rendered 1`] = `
/>
`;

exports[`EuiContextMenu panel item can be a separator line 1`] = `
<div
class="euiContextMenu"
>
<div
class="euiContextMenuPanel euiContextMenu__panel"
tabindex="0"
>
<div
class="euiPopoverTitle"
>
<span
class="euiContextMenu__itemLayout"
>
Testing separator
</span>
</div>
<div>
<div>
<button
class="euiContextMenuItem"
type="button"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
>
Foo
</span>
</span>
</button>
<hr
class="euiHorizontalRule euiHorizontalRule--full"
/>
<button
class="euiContextMenuItem"
type="button"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
>
Bar
</span>
</span>
</button>
</div>
</div>
</div>
</div>
`;

exports[`EuiContextMenu panel item can contain JSX 1`] = `
<div
class="euiContextMenu"
Expand Down
45 changes: 45 additions & 0 deletions src/components/context_menu/context_menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,51 @@ describe('EuiContextMenu', () => {
expect(component).toMatchSnapshot();
});

it('panel item can be a separator line', () => {
const component = render(
<EuiContextMenu
panels={[
{
id: 3,
title: 'Testing separator',
items: [
{ name: 'Foo', key: 'foo' },
{ isSeparator: true },
{ name: 'Bar', key: 'bar' },
],
},
]}
initialPanelId={3}
/>
);

expect(component).toMatchSnapshot();
});

it('can pass-through horizontal rule props', () => {
const component = render(
<EuiContextMenu
panels={[
{
id: 3,
title: 'Testing separator',
items: [
{
isSeparator: true,
key: 'separator',
margin: 's',
size: 'half',
},
],
},
]}
initialPanelId={3}
/>
);

expect(component).toMatchSnapshot();
});

describe('props', () => {
describe('panels and initialPanelId', () => {
it('renders the referenced panel', () => {
Expand Down
31 changes: 28 additions & 3 deletions src/components/context_menu/context_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import React, {
} from 'react';
import classNames from 'classnames';

import { CommonProps } from '../common';
import { CommonProps, ExclusiveUnion } from '../common';
import {
EuiContextMenuPanel,
EuiContextMenuPanelTransitionDirection,
Expand All @@ -35,10 +35,11 @@ import {
EuiContextMenuItem,
EuiContextMenuItemProps,
} from './context_menu_item';
import { EuiHorizontalRule, EuiHorizontalRuleProps } from '../horizontal_rule';

export type EuiContextMenuPanelId = string | number;

export type EuiContextMenuPanelItemDescriptor = Omit<
export type EuiContextMenuPanelItemDescriptorEntry = Omit<
EuiContextMenuItemProps,
'hasPanel'
> & {
Expand All @@ -47,6 +48,17 @@ export type EuiContextMenuPanelItemDescriptor = Omit<
panel?: EuiContextMenuPanelId;
};

export interface EuiContextMenuPanelItemSeparator
extends EuiHorizontalRuleProps {
isSeparator: true;
key?: string;
}

export type EuiContextMenuPanelItemDescriptor = ExclusiveUnion<
EuiContextMenuPanelItemDescriptorEntry,
EuiContextMenuPanelItemSeparator
>;

export interface EuiContextMenuPanelDescriptor {
id: EuiContextMenuPanelId;
title?: string;
Expand All @@ -61,6 +73,11 @@ export type EuiContextMenuProps = CommonProps &
initialPanelId?: EuiContextMenuPanelId;
};

const isItemSeparator = (
item: EuiContextMenuPanelItemDescriptor
): item is EuiContextMenuPanelItemSeparator =>
(item as EuiContextMenuPanelItemSeparator).isSeparator === true;

function mapIdsToPanels(panels: EuiContextMenuPanelDescriptor[]) {
const map: { [id: string]: EuiContextMenuPanelDescriptor } = {};

Expand All @@ -77,6 +94,7 @@ function mapIdsToPreviousPanels(panels: EuiContextMenuPanelDescriptor[]) {
panels.forEach(panel => {
if (Array.isArray(panel.items)) {
panel.items.forEach(item => {
if (isItemSeparator(item)) return;
const isCloseable = item.panel !== undefined;
if (isCloseable) {
idToPreviousPanelIdMap[item.panel!] = panel.id;
Expand All @@ -98,6 +116,7 @@ function mapPanelItemsToPanels(panels: EuiContextMenuPanelDescriptor[]) {

if (panel.items) {
panel.items.forEach((item, index) => {
if (isItemSeparator(item)) return;
if (item.panel) {
idAndItemIndexToPanelIdMap[panel.id][index] = item.panel;
}
Expand Down Expand Up @@ -227,7 +246,8 @@ export class EuiContextMenu extends Component<EuiContextMenuProps, State> {
// Set focus on the item which shows the panel we're leaving.
const previousPanel = this.state.idToPanelMap[previousPanelId];
const focusedItemIndex = previousPanel.items!.findIndex(
item => item.panel === this.state.incomingPanelId
item =>
!isItemSeparator(item) && item.panel === this.state.incomingPanelId
);

if (focusedItemIndex !== -1) {
Expand Down Expand Up @@ -277,6 +297,11 @@ export class EuiContextMenu extends Component<EuiContextMenuProps, State> {

renderItems(items: EuiContextMenuPanelItemDescriptor[] = []) {
return items.map((item, index) => {
if (isItemSeparator(item)) {
const { isSeparator: omit, key = index, ...rest } = item;
return <EuiHorizontalRule key={key} margin="none" {...rest} />;
}

const {
panel,
name,
Expand Down
1 change: 0 additions & 1 deletion src/components/context_menu/context_menu_item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export class EuiContextMenuItem extends Component<Props> {
rel,
...rest
} = this.props;

let iconInstance;

if (icon) {
Expand Down
Loading