Skip to content

Commit

Permalink
[UI Framework] [K7] KuiTabs component (elastic#13280)
Browse files Browse the repository at this point in the history
* Add KuiTabs React component.
  • Loading branch information
cjcenizal committed Aug 16, 2017
1 parent b31027e commit 40c266e
Show file tree
Hide file tree
Showing 16 changed files with 379 additions and 2 deletions.
61 changes: 61 additions & 0 deletions ui_framework/dist/ui_framework.css
Expand Up @@ -1150,6 +1150,67 @@ table {
.kuiTableRowCell--wrapText .kuiTableRowCell__content {
white-space: normal; }

.kuiTabs {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
border-bottom: 1px solid #D9D9D9; }

.kuiTab {
font-size: 16px;
font-size: 1rem;
line-height: 24px;
position: relative;
cursor: pointer;
padding: 12px 16px;
color: #666;
background-color: #FFF;
transition: all 250ms cubic-bezier(0.694, 0.0482, 0.335, 1); }
.kuiTab:hover:not(.kuiTab-isSelected) {
color: #3F3F3F;
text-decoration: underline; }
.kuiTab:focus {
background-color: #e6f2f6;
text-decoration: underline; }
.kuiTab.kuiTab-isSelected {
cursor: default;
color: #00A69B; }
.kuiTab.kuiTab-isSelected:after {
position: absolute;
bottom: -1px;
left: 0;
content: ' ';
width: 100%;
height: 2px;
background-color: #00A69B;
-webkit-animation: kuiTab 150ms cubic-bezier(0.694, 0.0482, 0.335, 1);
animation: kuiTab 150ms cubic-bezier(0.694, 0.0482, 0.335, 1); }

.kuiTab__content {
display: block;
transition: -webkit-transform 150ms cubic-bezier(0.34, 1.61, 0.7, 1);
transition: transform 150ms cubic-bezier(0.34, 1.61, 0.7, 1);
transition: transform 150ms cubic-bezier(0.34, 1.61, 0.7, 1), -webkit-transform 150ms cubic-bezier(0.34, 1.61, 0.7, 1);
-webkit-transform: translateY(0);
transform: translateY(0); }

@-webkit-keyframes kuiTab {
0% {
-webkit-transform: scaleX(0);
transform: scaleX(0); }
100% {
-webkit-transform: scaleX(1);
transform: scaleX(1); } }

@keyframes kuiTab {
0% {
-webkit-transform: scaleX(0);
transform: scaleX(0); }
100% {
-webkit-transform: scaleX(1);
transform: scaleX(1); } }

.kuiTitle {
font-size: 24px;
font-size: 1.5rem;
Expand Down
7 changes: 7 additions & 0 deletions ui_framework/doc_site/src/services/routes/routes.js
Expand Up @@ -36,6 +36,9 @@ import PopoverExample
import TableExample
from '../../views/table/table_example';

import TabsExample
from '../../views/tabs/tabs_example';

import TypographyExample
from '../../views/typography/typography_example';

Expand Down Expand Up @@ -84,6 +87,10 @@ const components = [{
name: 'Table',
component: TableExample,
hasReact: true,
}, {
name: 'Tabs',
component: TabsExample,
hasReact: true,
}, {
name: 'Typography',
component: TypographyExample,
Expand Down
58 changes: 58 additions & 0 deletions ui_framework/doc_site/src/views/tabs/tabs.js
@@ -0,0 +1,58 @@
import React from 'react';

import {
KuiTabs,
KuiTab,
} from '../../../../components';

class KuiTabsExample extends React.Component {
constructor(props) {
super(props);

this.tabs = [{
id: 'cobalt',
name: 'Cobalt',
}, {
id: 'dextrose',
name: 'Dextrose',
}, {
id: 'hydrogen',
name: 'Hydrogen',
}, {
id: 'monosodium_glutammate',
name: 'Monosodium Glutamate',
}];

this.state = {
selectedTabId: 'cobalt',
};
}

onSelectedTabChanged = id => {
this.setState({
selectedTabId: id,
});
}

renderTabs() {
return this.tabs.map((tab,index) => (
<KuiTab
onClick={() => this.onSelectedTabChanged(tab.id)}
isSelected={tab.id === this.state.selectedTabId}
key={index}
>
{tab.name}
</KuiTab>
));
}

render() {
return (
<KuiTabs>
{this.renderTabs()}
</KuiTabs>
);
}
}

export default KuiTabsExample;
39 changes: 39 additions & 0 deletions ui_framework/doc_site/src/views/tabs/tabs_example.js
@@ -0,0 +1,39 @@
import React from 'react';
import { renderToHtml } from '../../services';

import {
GuideDemo,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
GuideCode
} from '../../components';

import Tabs from './tabs';
const tabsSource = require('!!raw!./tabs');
const tabsHtml = renderToHtml(Tabs);

export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Tabs"
source={[{
type: GuideSectionTypes.JS,
code: tabsSource,
}, {
type: GuideSectionTypes.HTML,
code: tabsHtml,
}]}
>
<GuideText>
The <GuideCode>KuiTabs</GuideCode> component should have <GuideCode>KuiTab</GuideCode>
components as children.
</GuideText>

<GuideDemo>
<Tabs />
</GuideDemo>
</GuideSection>
</GuidePage>
);
5 changes: 5 additions & 0 deletions ui_framework/src/components/index.js
Expand Up @@ -76,6 +76,11 @@ export {
KuiTableRowCell,
} from './table';

export {
KuiTab,
KuiTabs,
} from './tabs';

export {
KuiTitle,
KuiText,
Expand Down
1 change: 1 addition & 0 deletions ui_framework/src/components/index.scss
Expand Up @@ -12,4 +12,5 @@
@import 'page/index';
@import 'popover/index';
@import 'table/index';
@import 'tabs/index';
@import 'typography/index';
21 changes: 21 additions & 0 deletions ui_framework/src/components/tabs/__snapshots__/tab.test.js.snap
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`KuiTab renders 1`] = `
<button
aria-label="aria-label"
class="kuiTab testClass1 testClass2"
data-test-subj="test subject string"
>
children
</button>
`;

exports[`KuiTab renders isSelected 1`] = `
<button
aria-label="aria-label"
class="kuiTab testClass1 testClass2 kuiTab-isSelected"
data-test-subj="test subject string"
>
children
</button>
`;
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`KuiTabs renders 1`] = `
<div
aria-label="aria-label"
class="kuiTabs testClass1 testClass2"
data-test-subj="test subject string"
/>
`;
4 changes: 4 additions & 0 deletions ui_framework/src/components/tabs/_index.scss
@@ -0,0 +1,4 @@
$tabBackgroundColor: #FFF;
$tabHoverBackgroundColor: #F2F2F2;

@import "tabs";
57 changes: 57 additions & 0 deletions ui_framework/src/components/tabs/_tabs.scss
@@ -0,0 +1,57 @@
.kuiTabs {
display: flex;
border-bottom: $kuiBorderThin;
}

.kuiTab {
@include kuiFontSize;

position: relative;
cursor: pointer;
padding: ($kuiSize - $kuiSizeXS) $kuiSize;
color: $kuiColorDarkShade;
background-color: $kuiColorEmptyShade;
transition: all $kuiAnimSpeedNormal $kuiAnimSlightResistance;

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

&:focus {
background-color: $kuiFocusBackgroundColor;
text-decoration: underline;
}

&.kuiTab-isSelected {
cursor: default;
color: $kuiColorSecondary;

&:after {
position: absolute;
bottom: -1px;
left: 0;
content: ' ';
width: 100%;
height: $kuiBorderWidthThick;
background-color: $kuiColorSecondary;
animation: kuiTab $kuiAnimSpeedFast $kuiAnimSlightResistance;
}
}
}

.kuiTab__content {
display: block;
transition: transform $kuiAnimSpeedFast $kuiAnimSlightBounce;
transform: translateY(0);
}


@keyframes kuiTab {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
2 changes: 2 additions & 0 deletions ui_framework/src/components/tabs/index.js
@@ -0,0 +1,2 @@
export { KuiTab } from './tab';
export { KuiTabs } from './tabs';
32 changes: 32 additions & 0 deletions ui_framework/src/components/tabs/tab.js
@@ -0,0 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

export const KuiTab = ({ isSelected, onClick, children, className, ...rest }) => {
const classes = classNames('kuiTab', className, {
'kuiTab-isSelected': isSelected
});

return (
<button
className={classes}
onClick={onClick}
{...rest}
>
<span className="kuiTab__content">
{children}
</span>
</button>
);
};

KuiTab.defaultProps = {
isSelected: false,
};

KuiTab.propTypes = {
isSelected: PropTypes.bool,
onClick: PropTypes.func.isRequired,
children: PropTypes.node,
className: PropTypes.string,
};
36 changes: 36 additions & 0 deletions ui_framework/src/components/tabs/tab.test.js
@@ -0,0 +1,36 @@
import React from 'react';
import { render, shallow } from 'enzyme';
import { requiredProps } from '../../test/required_props';
import sinon from 'sinon';

import {
KuiTab,
} from './tab';

describe('KuiTab', () => {
test('renders', () => {
const component = <KuiTab onClick={()=>{}} { ...requiredProps }>children</KuiTab>;
expect(render(component)).toMatchSnapshot();
});

test('renders isSelected', () => {
const component = <KuiTab onClick={()=>{}} isSelected { ...requiredProps }>children</KuiTab>;
expect(render(component)).toMatchSnapshot();
});

describe('Props', () => {
describe('onClick', () => {
test('is called when the button is clicked', () => {
const onClickHandler = sinon.stub();

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

$button.simulate('click');

sinon.assert.calledOnce(onClickHandler);
});
});
});
});
25 changes: 25 additions & 0 deletions ui_framework/src/components/tabs/tabs.js
@@ -0,0 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

export const KuiTabs = ({
children,
className,
...rest
}) => {
const classes = classNames('kuiTabs', className);

return (
<div
className={classes}
{...rest}
>
{children}
</div>
);
};

KuiTabs.propTypes = {
children: PropTypes.node,
className: PropTypes.string
};

0 comments on commit 40c266e

Please sign in to comment.