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

Add support for href, target, and rel properties for EuiContextMenu items. #911

Merged
merged 2 commits into from
Jun 8, 2018
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 @@ -9,6 +9,7 @@
- Added more customization options to `EuiAvatar` ([#903](https://github.com/elastic/eui/pull/903))
- Added more color options to `EuiButtonIcon` ([#907](https://github.com/elastic/eui/pull/907))
- Added icon for EMS (Elastic Map Service) (`emsApp`) ([#914](https://github.com/elastic/eui/pull/914))
- Added support for `href`, `target`, and `rel` properties for `EuiContextMenu` items ([#911](https://github.com/elastic/eui/pull/911))

**Bug fixes**

Expand Down
21 changes: 9 additions & 12 deletions src-docs/src/views/context_menu/context_menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export default class extends Component {

const panelTree = {
id: 0,
title: 'View options',
title: 'This is a context menu',
items: [{
name: 'Show fullscreen',
name: 'Handle an onClick',
icon: (
<EuiIcon
type="search"
Expand All @@ -47,19 +47,20 @@ export default class extends Component {
),
onClick: () => { this.closePopover(); window.alert('Show fullscreen'); },
}, {
name: 'Share this dashboard',
name: 'Go to a link',
icon: 'user',
href: 'http://elastic.co',
target: '_blank',
}, {
name: 'Nest panels',
icon: 'user',
panel: {
id: 1,
title: 'Share this dashboard',
title: 'Nest panels',
items: [{
name: 'PDF reports',
icon: 'user',
onClick: () => { this.closePopover(); window.alert('PDF reports'); },
}, {
name: 'CSV reports',
icon: 'user',
onClick: () => { this.closePopover(); window.alert('CSV reports'); },
}, {
name: 'Embed code',
icon: 'user',
Expand Down Expand Up @@ -96,10 +97,6 @@ export default class extends Component {
onClick: () => { this.closePopover(); window.alert('Permalinks'); },
}],
},
}, {
name: 'Edit / add panels',
icon: 'user',
onClick: () => { this.closePopover(); window.alert('Edit / add panels'); },
}, {
name: 'You can add a tooltip',
icon: 'user',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@ exports[`EuiContextMenuItem props hasPanel is rendered 1`] = `
</button>
`;

exports[`EuiContextMenuItem props href renders a link 1`] = `
<a
aria-label="aria-label"
class="euiContextMenuItem testClass1 testClass2"
data-test-subj="test subject string"
href="url"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
/>
</span>
</a>
`;

exports[`EuiContextMenuItem props icon is rendered 1`] = `
<button
class="euiContextMenuItem"
Expand All @@ -88,3 +105,57 @@ exports[`EuiContextMenuItem props icon is rendered 1`] = `
</span>
</button>
`;

exports[`EuiContextMenuItem props onClick renders a button 1`] = `
<button
aria-label="aria-label"
class="euiContextMenuItem testClass1 testClass2"
data-test-subj="test subject string"
type="button"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
/>
</span>
</button>
`;

exports[`EuiContextMenuItem props rel is rendered 1`] = `
<a
aria-label="aria-label"
class="euiContextMenuItem testClass1 testClass2"
data-test-subj="test subject string"
href="url"
rel="help"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
/>
</span>
</a>
`;

exports[`EuiContextMenuItem props target is rendered 1`] = `
<a
aria-label="aria-label"
class="euiContextMenuItem testClass1 testClass2"
data-test-subj="test subject string"
href="url"
rel="noopener noreferrer"
target="_blank"
>
<span
class="euiContextMenu__itemLayout"
>
<span
class="euiContextMenuItem__text"
/>
</span>
</a>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
<div className=\\"euiContextMenuPanel\\" onKeyDown={[Function]} tabIndex=\\"0\\" onAnimationEnd={[Function]}>
<div>
<EuiContextMenuItem data-counter={0} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={0}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={0}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option A
Expand All @@ -126,7 +126,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
</button>
</EuiContextMenuItem>
<EuiContextMenuItem data-counter={1} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={1}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={1}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option B
Expand All @@ -144,7 +144,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
<div className=\\"euiContextMenuPanel\\" onKeyDown={[Function]} tabIndex=\\"0\\" onAnimationEnd={[Function]}>
<div>
<EuiContextMenuItem data-counter={0} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={0}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={0}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option A
Expand All @@ -153,7 +153,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
</button>
</EuiContextMenuItem>
<EuiContextMenuItem data-counter={1} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={1}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={1}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option B
Expand Down Expand Up @@ -191,7 +191,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
<div className=\\"euiContextMenuPanel\\" onKeyDown={[Function]} tabIndex=\\"0\\" onAnimationEnd={[Function]}>
<div>
<EuiContextMenuItem data-counter={0} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={0}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={0}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option A
Expand All @@ -200,7 +200,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
</button>
</EuiContextMenuItem>
<EuiContextMenuItem data-counter={1} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={1}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={1}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option B
Expand All @@ -218,7 +218,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
<div className=\\"euiContextMenuPanel\\" onKeyDown={[Function]} tabIndex=\\"0\\" onAnimationEnd={[Function]}>
<div>
<EuiContextMenuItem data-counter={2} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={2}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={2}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option A
Expand All @@ -227,7 +227,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
</button>
</EuiContextMenuItem>
<EuiContextMenuItem data-counter={3} toolTipPosition=\\"right\\" buttonRef={[Function: bound ]}>
<button className=\\"euiContextMenuItem\\" type=\\"button\\" disabled={[undefined]} data-counter={3}>
<button disabled={[undefined]} className=\\"euiContextMenuItem\\" type=\\"button\\" data-counter={3}>
<span className=\\"euiContextMenu__itemLayout\\">
<span className=\\"euiContextMenuItem__text\\">
Option B
Expand Down
1 change: 1 addition & 0 deletions src/components/context_menu/_context_menu_item.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.euiContextMenuItem {
display: block;
padding: $euiSizeM;
width: 100%;
text-align: left;
Expand Down
62 changes: 47 additions & 15 deletions src/components/context_menu/context_menu_item.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import classNames from 'classnames';
import { EuiIcon } from '../icon';
import { EuiToolTip } from '../tool_tip';

import { getSecureRelForTarget } from '../../services';

export class EuiContextMenuItem extends Component {
static propTypes = {
children: PropTypes.node,
Expand Down Expand Up @@ -35,6 +37,9 @@ export class EuiContextMenuItem extends Component {
* Dictates the position of the tooltip.
*/
toolTipPosition: PropTypes.string,
href: PropTypes.string,
target: PropTypes.string,
rel: PropTypes.string,
};

render() {
Expand All @@ -49,6 +54,9 @@ export class EuiContextMenuItem extends Component {
toolTipTitle,
toolTipContent,
toolTipPosition,
href,
target,
rel,
...rest
} = this.props;

Expand Down Expand Up @@ -90,24 +98,48 @@ export class EuiContextMenuItem extends Component {
'euiContextMenuItem-isDisabled': disabled,
});

const button = (
<button
className={classes}
type="button"
ref={buttonRef}
disabled={disabled}
{...rest}
>
<span className="euiContextMenu__itemLayout">
{iconInstance}
<span className="euiContextMenuItem__text">
{children}
</span>
{arrow}
const buttonInner = (
<span className="euiContextMenu__itemLayout">
{iconInstance}
<span className="euiContextMenuItem__text">
{children}
</span>
</button>
{arrow}
</span>
);

let button;
// <a> elements don't respect the `disabled` attribute. So if we're disabled, we'll just pretend
// this is a button and piggyback off its disabled styles.
if (href && !disabled) {
const secureRel = getSecureRelForTarget(target, rel);

button = (
<a
className={classes}
href={href}
target={target}
rel={secureRel}
ref={buttonRef}
{...rest}
>
{buttonInner}
</a>
);
} else {
button = (
<button
disabled={disabled}
className={classes}
type="button"
ref={buttonRef}
{...rest}
>
{buttonInner}
</button>
);
}

if (toolTipContent) {
return (
<EuiToolTip
Expand Down
47 changes: 41 additions & 6 deletions src/components/context_menu/context_menu_item.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ describe('EuiContextMenuItem', () => {
</EuiContextMenuItem>
);

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

describe('props', () => {
Expand All @@ -24,8 +23,7 @@ describe('EuiContextMenuItem', () => {
<EuiContextMenuItem icon={<span className="euiIcon fa-user" />} />
);

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

Expand All @@ -35,12 +33,19 @@ describe('EuiContextMenuItem', () => {
<EuiContextMenuItem disabled />
);

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

describe('onClick', () => {
test('renders a button', () => {
const component = render(
<EuiContextMenuItem {...requiredProps} onClick={() => {}} />
);

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

test(`isn't called upon instantiation`, () => {
const onClickHandler = sinon.stub();

Expand Down Expand Up @@ -76,6 +81,36 @@ describe('EuiContextMenuItem', () => {
});
});

describe('href', () => {
test('renders a link', () => {
const component = render(
<EuiContextMenuItem {...requiredProps} href='url' />
);

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

describe('rel', () => {
test('is rendered', () => {
const component = render(
<EuiContextMenuItem {...requiredProps} href='url' rel='help' />
);

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

describe('target', () => {
test('is rendered', () => {
const component = render(
<EuiContextMenuItem {...requiredProps} href='url' target='_blank' />
);

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

describe('hasPanel', () => {
test('is rendered', () => {
const component = render(
Expand Down