Skip to content

Commit

Permalink
Adding href prop to EuiTab (elastic#2275)
Browse files Browse the repository at this point in the history
* Tab comp in typescript, href prop and example

* Improving code based on comments

* Tab comp in typescript, href prop and example

* Improving code based on comments

* Updating changelog

* typescripting

* Adding a snapshot test for when href is in use.
  • Loading branch information
elizabetdev authored and thompsongl committed Sep 10, 2019
1 parent 77eea67 commit cf47288
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 68 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## [`master`](https://github.com/elastic/eui/tree/master)

- Added href prop to `EuiTab` and converted to TypeScript ([#2275](https://github.com/elastic/eui/pull/2275))

**Bug fixes**

- Removed extra right side margin in `EuiSuperDatePicker` ([#2236](https://github.com/elastic/eui/pull/2236))
Expand Down
7 changes: 7 additions & 0 deletions src-docs/src/views/tabs/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class EuiTabsExample extends Component {
name: 'Monosodium Glutamate',
disabled: false,
},
{
id: 'elastic_link',
name: 'Elastic Website',
disabled: false,
href: 'https://www.elastic.co/',
},
];

this.state = {
Expand All @@ -43,6 +49,7 @@ class EuiTabsExample extends Component {
renderTabs() {
return this.tabs.map((tab, index) => (
<EuiTab
{...tab.href && { href: tab.href, target: '_blank' }}
onClick={() => this.onSelectedTabChanged(tab.id)}
isSelected={tab.id === this.state.selectedTabId}
disabled={tab.disabled}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiTab renders 1`] = `
exports[`EuiTab renders anchor 1`] = `
<a
aria-label="aria-label"
aria-selected="false"
class="euiTab testClass1 testClass2"
data-test-subj="test subject string"
href="/baz/bing"
rel="noreferrer"
role="tab"
>
<span
class="euiTab__content"
>
children
</span>
</a>
`;

exports[`EuiTab renders button 1`] = `
<button
aria-label="aria-label"
aria-selected="false"
Expand Down
2 changes: 1 addition & 1 deletion src/components/tabs/_tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

.euiTab {
@include fontSize($euiTabFontSize);
color: $euiTextColor;
background-color: transparent;
cursor: pointer;
line-height: $euiLineHeight;
Expand All @@ -33,7 +34,6 @@
transition: color $euiAnimSpeedNormal $euiAnimSlightResistance, background-color $euiAnimSpeedNormal $euiAnimSlightResistance;

&:hover:not(.euiTab-isSelected) {
color: $euiTextColor;
text-decoration: underline;
}

Expand Down
19 changes: 4 additions & 15 deletions src/components/tabs/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import {
MouseEventHandler,
ReactNode,
FunctionComponent,
HTMLAttributes,
} from 'react';
import { ReactNode, FunctionComponent, HTMLAttributes } from 'react';
import { CommonProps } from '../common';

import { Props as EuiTabProps } from './tab';

declare module '@elastic/eui' {
type TAB_SIZES = 's' | 'm';

type TAB_DISPLAYS = 'default' | 'condensed';

interface EuiTabProps {
onClick: MouseEventHandler<HTMLButtonElement>;
isSelected?: boolean;
disabled?: boolean;
}

interface EuiTabsProps {
size?: TAB_SIZES;
display?: TAB_DISPLAYS;
Expand All @@ -42,13 +33,11 @@ declare module '@elastic/eui' {
autoFocus?: TABBED_CONTENT_AUTOFOCUS;
}

export const EuiTab: FunctionComponent<
EuiTabProps & CommonProps & HTMLAttributes<HTMLDivElement>
>;
export const EuiTabs: FunctionComponent<
EuiTabsProps & CommonProps & HTMLAttributes<HTMLDivElement>
>;
export const EuiTabbedContent: FunctionComponent<
EuiTabbedContentProps & CommonProps & HTMLAttributes<HTMLDivElement>
>;
export const EuiTab: FunctionComponent<EuiTabProps>;
}
43 changes: 0 additions & 43 deletions src/components/tabs/tab.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React from 'react';
import { render, shallow } from 'enzyme';
import { requiredProps } from '../../test/required_props';
import sinon from 'sinon';

import { EuiTab } from './tab';

describe('EuiTab', () => {
test('renders', () => {
test('renders button', () => {
const component = (
<EuiTab onClick={() => {}} {...requiredProps}>
children
Expand All @@ -15,6 +14,15 @@ describe('EuiTab', () => {
expect(render(component)).toMatchSnapshot();
});

test('renders anchor', () => {
const component = (
<EuiTab href="/baz/bing" {...requiredProps}>
children
</EuiTab>
);
expect(render(component)).toMatchSnapshot();
});

test('renders isSelected', () => {
const component = (
<EuiTab onClick={() => {}} isSelected {...requiredProps}>
Expand All @@ -27,13 +35,13 @@ describe('EuiTab', () => {
describe('Props', () => {
describe('onClick', () => {
test('is called when the button is clicked', () => {
const onClickHandler = sinon.stub();
const onClickHandler = jest.fn();

const $button = shallow(<EuiTab onClick={onClickHandler} />);

$button.simulate('click');

sinon.assert.calledOnce(onClickHandler);
expect(onClickHandler).toBeCalled();
});
});
});
Expand Down
74 changes: 74 additions & 0 deletions src/components/tabs/tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, {
MouseEventHandler,
AnchorHTMLAttributes,
ButtonHTMLAttributes,
FunctionComponent,
} from 'react';
import classNames from 'classnames';
import { CommonProps, ExclusiveUnion } from '../common';
import { getSecureRelForTarget } from '../../services';

export interface EuiTabProps extends CommonProps {
isSelected?: boolean;
disabled?: boolean;
}

type EuiTabPropsForAnchor = EuiTabProps &
AnchorHTMLAttributes<HTMLAnchorElement> & {
href?: string;
onClick?: MouseEventHandler<HTMLAnchorElement>;
};

type EuiTabPropsForButton = EuiTabProps &
ButtonHTMLAttributes<HTMLButtonElement> & {
onClick?: MouseEventHandler<HTMLButtonElement>;
};

export type Props = ExclusiveUnion<EuiTabPropsForAnchor, EuiTabPropsForButton>;

export const EuiTab: FunctionComponent<Props> = ({
isSelected,
children,
className,
disabled,
href,
target,
rel,
...rest
}) => {
const classes = classNames('euiTab', className, {
'euiTab-isSelected': isSelected,
'euiTab-isDisabled': disabled,
});

// <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({ href, target, rel });

return (
<a
role="tab"
aria-selected={!!isSelected}
className={classes}
href={href}
target={target}
rel={secureRel}
{...rest as AnchorHTMLAttributes<HTMLAnchorElement>}>
<span className="euiTab__content">{children}</span>
</a>
);
}

return (
<button
role="tab"
aria-selected={!!isSelected}
type="button"
className={classes}
disabled={disabled}
{...rest as ButtonHTMLAttributes<HTMLButtonElement>}>
<span className="euiTab__content">{children}</span>
</button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ exports[`EuiTabbedContent behavior when uncontrolled, the selected tab should up
>
<EuiTab
aria-controls="42"
disabled={false}
id="es"
isSelected={false}
key="es"
Expand All @@ -95,7 +94,6 @@ exports[`EuiTabbedContent behavior when uncontrolled, the selected tab should up
aria-controls="42"
aria-selected={false}
className="euiTab"
disabled={false}
id="es"
onClick={[Function]}
role="tab"
Expand All @@ -111,7 +109,6 @@ exports[`EuiTabbedContent behavior when uncontrolled, the selected tab should up
<EuiTab
aria-controls="42"
data-test-subj="kibanaTab"
disabled={false}
id="kibana"
isSelected={true}
key="kibana"
Expand All @@ -122,7 +119,6 @@ exports[`EuiTabbedContent behavior when uncontrolled, the selected tab should up
aria-selected={true}
className="euiTab euiTab-isSelected"
data-test-subj="kibanaTab"
disabled={false}
id="kibana"
onClick={[Function]}
role="tab"
Expand Down

0 comments on commit cf47288

Please sign in to comment.