Skip to content

Commit

Permalink
[EuiContextMenu] Add line separator as menu item (#4018)
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich authored Sep 16, 2020
1 parent 7e3364e commit ee78ad0
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ No public interface changes since `29.0.0`.
- 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

0 comments on commit ee78ad0

Please sign in to comment.