Skip to content

Commit

Permalink
feat(tabs): Makes tabs responsive
Browse files Browse the repository at this point in the history
[Finishes #99800334]

Signed-off-by: Caroline Taymor <ctaymor@pivotal.io>
Signed-off-by: Geoff Pleiss <gpleiss@pivotal.io>
  • Loading branch information
Caroline Taymor authored and Caroline Taymor and Dominick Reinhold committed Aug 17, 2015
1 parent 36c3eda commit 721cdbf
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 135 deletions.
3 changes: 0 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,3 @@ DEPENDENCIES
pry
rspec
selenium-webdriver

BUNDLED WITH
1.10.0.rc
254 changes: 133 additions & 121 deletions spec/pivotal-ui-react/tab/tab_spec.js
Original file line number Diff line number Diff line change
@@ -1,148 +1,160 @@
require('../spec_helper');
import {SimpleAltTabs, SimpleTabs, Tab} from '../../../src/pivotal-ui-react/tabs/tabs';
import EventEmitter from 'node-event-emitter';
import {itPropagatesAttributes} from '../support/shared_examples';

describe('Tabs', function() {
var Tab;
beforeEach(function() {
Tab = require('../../../src/pivotal-ui-react/tabs/tabs').Tab;
afterEach(function() {
React.unmountComponentAtNode(root);
});

describe('SimpleTabs', function() {
let SimpleTabs = require('../../../src/pivotal-ui-react/tabs/tabs').SimpleTabs;

beforeEach(function() {
React.render(
(
<SimpleTabs defaultActiveKey={1}>
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2"/>
</SimpleTabs>
),
root
);
});

afterEach(function() {
React.unmountComponentAtNode(root);
});
function itBehavesLikeTabs(Component, tabType) {
let emitter;

it('creates tabs in a simpletab container', function() {
expect('.tab-simple nav ul.nav.nav-tabs li.active').toContainText('Tab1');
expect('.tab-simple .tab-content .tab-pane.fade.active.in').toContainText('Content1');
});
describe('props', function() {
let onSelectSpy;

describe('when no ID is given to the Tabs component', function() {
beforeEach(function() {
React.render(
(
<SimpleTabs defaultActiveKey={1}>
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2"/>
</SimpleTabs>
),
root
);
});
onSelectSpy = jasmine.createSpy('onSelectSpy');

it('sets up the correct aria-controls relationship', function() {
let pane1 = $(root).find('.tab-simple .tab-pane:first');
expect(pane1.length).toEqual(1);
expect(pane1.attr('id')).toBeTruthy();
expect('.tab-simple nav ul.nav.nav-tabs li:first a').toHaveAttr('aria-controls', pane1.attr('id'));
});
});

describe('when an ID is given to the Tabs component', function() {
beforeEach(function() {
React.render(
(
<SimpleTabs defaultActiveKey={1} id="tabs-id" >
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2"/>
</SimpleTabs>
),
root
);
<Component defaultActiveKey={2} className="test-class" id="test-id" style={{opacity: 0.5}} responsiveBreakpoint="md" smallScreenClassName="small-class" largeScreenClassName="large-class" onSelect={onSelectSpy}>
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2">Content2</Tab>
</Component>,
root);
});

it('uses the given id for the tabs element in the DOM', function() {
expect('.tab-simple > #tabs-id').toExist();
afterEach(function() {
React.unmountComponentAtNode(root);
});

it('sets up the correct aria-controls relationship', function() {
let pane1 = $(root).find('.tab-simple .tab-pane:first');
expect(pane1.length).toEqual(1);
expect(pane1.attr('id')).toBeTruthy();
expect('.tab-simple nav ul.nav.nav-tabs li:first a').toHaveAttr('aria-controls', pane1.attr('id'));
});
});
});
itPropagatesAttributes('#root > div', {className: 'test-class', id: 'test-id', style: {opacity: '0.5'}});

describe('SimpleAltTabs', function() {
let SimpleAltTabs = require('../../../src/pivotal-ui-react/tabs/tabs').SimpleAltTabs;

beforeEach(function() {
React.render((
<SimpleAltTabs defaultActiveKey={1}>,
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2"/>
</SimpleAltTabs>
),
root
);
});

afterEach(function() {
React.unmountComponentAtNode(root);
});

it('creates tabs in a simpletab container', function() {
expect('.tab-simple-alt nav ul.nav.nav-tabs li.active').toContainText('Tab1');
expect('.tab-simple-alt .tab-content .tab-pane.fade.active.in').toContainText('Content1');
});
it('sets the responsive breakpoint', function() {
expect(`.${tabType}`).toHaveClass('hidden-md');
expect('.panel-group').toHaveClass('visible-md-block');
});

describe('when no ID is given to the Tabs component', function() {
beforeEach(function() {
React.render(
(
<SimpleAltTabs defaultActiveKey={1}>
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2"/>
</SimpleAltTabs>
),
root
);
it('passes screen-size specific classes', function() {
expect(`.${tabType}`).toHaveClass('large-class');
expect('.panel-group').toHaveClass('small-class');
});

it('sets up the correct aria-controls relationship', function() {
let pane1 = $(root).find('.tab-simple-alt .tab-pane:first');
expect(pane1.length).toEqual(1);
expect(pane1.attr('id')).toBeTruthy();
expect('.tab-simple-alt nav ul.nav.nav-tabs li:first a').toHaveAttr('aria-controls', pane1.attr('id'));
it('uses the supplied onSelect method when clicking on large-screen tabs', function() {
$(`.${tabType} li a:eq(0)`).simulate('click');
expect(onSelectSpy).toHaveBeenCalled();
});
});

describe('when an ID is given to the Tabs component', function() {
beforeEach(function() {
React.render(
(
<SimpleAltTabs defaultActiveKey={1} id="tabs-id" >
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2"/>
</SimpleAltTabs>
),
root
);
it('uses the supplied onSelect method when clicking on small-screen tabs', function() {
$(`.panel-group .panel-title a:eq(0)`).simulate('click');
expect(onSelectSpy).toHaveBeenCalled();
});

it('uses the given id for the tabs element in the DOM', function() {
expect('.tab-simple-alt > #tabs-id').toExist();
describe('when an ID is given to the Tabs component', function() {
it('sets up the correct aria-controls relationship', function() {
let pane1 = $(root).find(`.${tabType} .tab-pane:first`);
expect(pane1.length).toEqual(1);
expect(pane1.attr('id')).toBeTruthy();
expect(`.${tabType} nav ul.nav.nav-tabs li:first a`).toHaveAttr('aria-controls', pane1.attr('id'));
});
});

it('sets up the correct aria-controls relationship', function() {
let pane1 = $(root).find('.tab-simple-alt .tab-pane:first');
expect(pane1.length).toEqual(1);
expect(pane1.attr('id')).toBeTruthy();
expect('.tab-simple-alt nav ul.nav.nav-tabs li:first a').toHaveAttr('aria-controls', pane1.attr('id'));
describe('default behavior', function() {
beforeEach(function() {
emitter = new EventEmitter();

const TestComponent = React.createClass({
getInitialState() {
return {defaultActiveKey: 2};
},

componentDidMount() {
emitter.on('changeActiveKey', (key) => this.setState({defaultActiveKey: key}));
},

render() {
return (
<Component defaultActiveKey={this.state.defaultActiveKey}>
<Tab eventKey={1} title="Tab1">Content1</Tab>
<Tab eventKey={2} title="Tab2">Content2</Tab>
</Component>
);
}
});

React.render(<TestComponent />, root);
});

it('creates tabs in the correct container', function() {
expect(`.${tabType} nav ul.nav.nav-tabs li.active`).toContainText('Tab2');
expect(`.${tabType} .tab-content .tab-pane.fade.active.in`).toContainText('Content2');
});

describe('for screens greater than the responsiveBreakpoint', function() {
it('displays tabs in a simple tab container', function() {
expect(`.hidden-xs.${tabType} nav li.active`).toContainText('Tab2');
expect(`.hidden-xs.${tabType} .tab-content`).toContainText('Content2');
});
});

describe('for screens smaller than the responsiveBreakpoint', function() {
it('renders an accordion', function() {
expect('.visible-xs-block.panel-group').toExist();
});

it('renders headers for each tab', function() {
expect('.visible-xs-block.panel-group .panel-title:eq(0)').toContainText('Tab1');
expect('.visible-xs-block.panel-group .panel-title:eq(1)').toContainText('Tab2');
expect('.visible-xs-block.panel-group .panel-title a:eq(1)').toHaveAttr('aria-expanded', 'true');
});

it('renders content for each tab', function() {
expect('.visible-xs-block.panel-group .panel-collapse:eq(0)').toContainText('Content1');
expect('.visible-xs-block.panel-group .panel-collapse:eq(1)').toContainText('Content2');
expect('.visible-xs-block.panel-group .panel-collapse:eq(1)').toHaveClass('in');
});
});

describe('when switching tabs', function() {
it('switches tabs in both small-screen and large-screen tabs', function() {
$('.hidden-xs li:eq(0) a').simulate('click');
expect('.hidden-xs li.active').toContainText('Tab1');
expect('.visible-xs-block .panel-title a[aria-expanded=true]').toContainText('Tab1');
$('.visible-xs-block .panel-title:eq(1) a').simulate('click');
expect('.hidden-xs li.active').toContainText('Tab2');
expect('.visible-xs-block .panel-title a[aria-expanded=true]').toContainText('Tab2');
});
});

describe('changing the defaultActiveKey props', function() {
beforeEach(function() {
emitter.emit('changeActiveKey', 1);
});

it('updates the current open tab', function() {
expect('.hidden-xs li.active').toContainText('Tab1');
expect('.visible-xs-block .panel-title a[aria-expanded=true]').toContainText('Tab1');
});
});

describe('when an id is not given to the Tabs component', function() {
it('sets up the correct aria-controls relationship', function() {
let pane1 = $(root).find(`.${tabType} .tab-pane:first`);
expect(pane1.length).toEqual(1);
expect(pane1.attr('id')).toBeTruthy();
expect(`.${tabType} nav ul.nav.nav-tabs li:first a`).toHaveAttr('aria-controls', pane1.attr('id'));
});
});
});
});
}

describe('SimpleTabs', function() {
itBehavesLikeTabs(SimpleTabs, 'tab-simple');
});

describe('SimpleAltTabs', function() {
itBehavesLikeTabs(SimpleAltTabs, 'tab-simple-alt');
});
});
Loading

0 comments on commit 721cdbf

Please sign in to comment.