Skip to content

Commit

Permalink
Add support for href, target, and rel properties for `EuiContex…
Browse files Browse the repository at this point in the history
…tMenu` items.
  • Loading branch information
cjcenizal committed Jun 6, 2018
1 parent 6d08912 commit e56cc5f
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Added multiple style-only adjustments to `EuiFormControlLayout` buttons/icons ([#894](https://github.com/elastic/eui/pull/894))
- Shifted `readOnly` inputs to not have left padding unless it has an icon ([#894](https://github.com/elastic/eui/pull/894))
- Added more customization options to `EuiAvatar` ([#903](https://github.com/elastic/eui/pull/903))
- 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
66 changes: 50 additions & 16 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,23 +98,49 @@ 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}
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}
>
<span className="euiContextMenu__itemLayout">
{iconInstance}
<span className="euiContextMenuItem__text">
{children}
</span>
{arrow}
</span>
{arrow}
</span>
</button>
);
</a>
);
} else {
button = (
<button
disabled={disabled}
className={classes}
type="button"
ref={buttonRef}
{...rest}
>
<span className="euiContextMenu__itemLayout">
{iconInstance}
<span className="euiContextMenuItem__text">
{children}
</span>
{arrow}
</span>
</button>
);
}

if (toolTipContent) {
return (
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

0 comments on commit e56cc5f

Please sign in to comment.