Skip to content

Commit 5c56749

Browse files
authored
refactor(Button,DropdownToggle): default to secondary style, remove cloning (#120)
* refactor(DropdownToggle): remove cloning, default to Button as tag * refactor(Button): default to secondary style * docs(Dropdown): update based on component changes BREAKING CHANGE: DropdownToggle no longer clones each element in props.children when it’s an array, instead it renders props.children inside a single component (Butt BREAKING CHANGE: Button color now defaults to “secondary”. This behavior aligns with DropdownToggle color default. Closes #98
1 parent f088833 commit 5c56749

9 files changed

Lines changed: 89 additions & 63 deletions

File tree

docs/lib/Components/ButtonDropdownPage.js

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ DropdownToggle.propTypes = {
6868
<h3>Single button dropdowns</h3>
6969
<div className="docs-example">
7070
<div>
71-
<Example color="secondary" text="Default" />
7271
<Example color="primary" text="Primary" />
72+
<Example color="secondary" text="Secondary" />
7373
<Example color="success" text="Success" />
7474
<Example color="info" text="Info" />
7575
<Example color="warning" text="Warning" />
@@ -95,8 +95,8 @@ DropdownToggle.propTypes = {
9595
<h3>Split button dropdowns</h3>
9696
<div className="docs-example">
9797
<div>
98-
<ExampleSplit color="secondary" text="Default" />
9998
<ExampleSplit color="primary" text="Primary" />
99+
<ExampleSplit color="secondary" text="Secondary" />
100100
<ExampleSplit color="success" text="Success" />
101101
<ExampleSplit color="info" text="Info" />
102102
<ExampleSplit color="warning" text="Warning" />
@@ -107,9 +107,7 @@ DropdownToggle.propTypes = {
107107
<PrismCode className="language-jsx">
108108
{`<ButtonDropdown isOpen={isOpen} toggle={toggle}>
109109
<Button id="caret" color="primary">{this.props.text}</Button>
110-
<DropdownToggle caret>
111-
<Button color="primary"><span className="sr-only">Toggle Dropdown</span></Button>
112-
</DropdownToggle>
110+
<DropdownToggle caret color="primary" />
113111
<DropdownMenu>
114112
<DropdownItem header>Header</DropdownItem>
115113
<DropdownItem disabled>Action</DropdownItem>
@@ -124,8 +122,8 @@ DropdownToggle.propTypes = {
124122
<div className="docs-example">
125123
<div>
126124
<ButtonDropdown isOpen={this.state.btnLg} toggle={() => { this.setState({ btnLg: !this.state.btnLg }); }}>
127-
<DropdownToggle caret>
128-
<Button size="lg">Large Button</Button>
125+
<DropdownToggle caret size="lg">
126+
Large Button
129127
</DropdownToggle>
130128
<DropdownMenu>
131129
<DropdownItem>Another Action</DropdownItem>
@@ -135,8 +133,8 @@ DropdownToggle.propTypes = {
135133
</div>
136134
<div className="m-t-1">
137135
<ButtonDropdown isOpen={this.state.btnSm} toggle={() => { this.setState({ btnSm: !this.state.btnSm }); }}>
138-
<DropdownToggle caret>
139-
<Button size="sm">Small Button</Button>
136+
<DropdownToggle caret size="sm">
137+
Small Button
140138
</DropdownToggle>
141139
<DropdownMenu>
142140
<DropdownItem>Another Action</DropdownItem>
@@ -148,8 +146,8 @@ DropdownToggle.propTypes = {
148146
<pre>
149147
<PrismCode className="language-jsx">
150148
{`<ButtonDropdown isOpen={isOpen} toggle={toggle}>
151-
<DropdownToggle caret>
152-
<Button size="lg">Large Button</Button>
149+
<DropdownToggle caret size="lg">
150+
Large Button
153151
</DropdownToggle>
154152
<DropdownMenu>
155153
<DropdownItem>Another Action</DropdownItem>
@@ -158,8 +156,8 @@ DropdownToggle.propTypes = {
158156
</ButtonDropdown>
159157
160158
<ButtonDropdown isOpen={isOpen} toggle={toggle}>
161-
<DropdownToggle caret>
162-
<Button size="sm">Small Button</Button>
159+
<DropdownToggle caret size="sm">
160+
Small Button
163161
</DropdownToggle>
164162
<DropdownMenu>
165163
<DropdownItem>Another Action</DropdownItem>
@@ -172,8 +170,8 @@ DropdownToggle.propTypes = {
172170
<div className="docs-example">
173171
<div>
174172
<ButtonDropdown dropup isOpen={this.state.btnDropup} toggle={() => { this.setState({ btnDropup: !this.state.btnDropup }); }}>
175-
<DropdownToggle caret>
176-
<Button size="lg">Dropup</Button>
173+
<DropdownToggle caret size="lg">
174+
Dropup
177175
</DropdownToggle>
178176
<DropdownMenu>
179177
<DropdownItem>Another Action</DropdownItem>
@@ -185,8 +183,8 @@ DropdownToggle.propTypes = {
185183
<pre>
186184
<PrismCode className="language-jsx">
187185
{`<ButtonDropdown isOpen={isOpen} toggle={toggle} dropup>
188-
<DropdownToggle caret>
189-
<Button>Dropup</Button>
186+
<DropdownToggle caret caret size="lg">
187+
Dropup
190188
</DropdownToggle>
191189
<DropdownMenu>
192190
<DropdownItem>Another Action</DropdownItem>

docs/lib/Components/ButtonGroupPage.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export default class ButtonGroupPage extends React.Component {
122122
<Button>2</Button>
123123
<ButtonDropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
124124
<DropdownToggle caret>
125-
<Button>Dropdown</Button>
125+
Dropdown
126126
</DropdownToggle>
127127
<DropdownMenu>
128128
<DropdownItem>Dropdown Link</DropdownItem>
@@ -139,7 +139,7 @@ export default class ButtonGroupPage extends React.Component {
139139
<Button>2</Button>
140140
<ButtonDropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
141141
<DropdownToggle caret>
142-
<Button>Dropdown</Button>
142+
Dropdown
143143
</DropdownToggle>
144144
<DropdownMenu>
145145
<DropdownItem>Dropdown Link</DropdownItem>
@@ -156,7 +156,7 @@ export default class ButtonGroupPage extends React.Component {
156156
<Button>2</Button>
157157
<ButtonDropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
158158
<DropdownToggle caret>
159-
<Button>Dropdown</Button>
159+
Dropdown
160160
</DropdownToggle>
161161
<DropdownMenu>
162162
<DropdownItem>Dropdown Link</DropdownItem>
@@ -172,7 +172,7 @@ export default class ButtonGroupPage extends React.Component {
172172
<Button>2</Button>
173173
<ButtonDropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
174174
<DropdownToggle caret>
175-
<Button>Dropdown</Button>
175+
Dropdown
176176
</DropdownToggle>
177177
<DropdownMenu>
178178
<DropdownItem>Dropdown Link</DropdownItem>

docs/lib/Components/ButtonsPage.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ export default class ButtonsPage extends React.Component {
2929
{`Button.propTypes = {
3030
active: PropTypes.bool,
3131
block: PropTypes.bool,
32-
color: PropTypes.string,
32+
color: PropTypes.string, // default: 'secondary'
3333
disabled: PropTypes.bool,
3434
3535
// Pass in a Component to override default button element
3636
// example: react-router Link
37+
// default: 'button'
3738
tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
3839
3940
onClick: PropTypes.func,
@@ -52,54 +53,54 @@ export default class ButtonsPage extends React.Component {
5253
</pre>
5354
<h3>Sizes</h3>
5455
<div className="docs-example">
55-
<Button size="lg">Large Button</Button>
56+
<Button color="primary" size="lg">Large Button</Button>
5657
<Button color="secondary" size="lg">Large Button</Button>
5758
</div>
5859
<pre>
5960
<PrismCode className="language-jsx">
60-
{`<Button size="lg">Large Button</Button>
61+
{`<Button color="primary" size="lg">Large Button</Button>
6162
<Button color="secondary" size="lg">Large Button</Button>`}
6263
</PrismCode>
6364
</pre>
6465
<div className="docs-example">
65-
<Button size="sm">Small Button</Button>
66+
<Button color="primary" size="sm">Small Button</Button>
6667
<Button color="secondary" size="sm">Small Button</Button>
6768
</div>
6869
<pre>
6970
<PrismCode className="language-jsx">
70-
{`<Button size="sm">Small Button</Button>
71+
{`<Button color="primary" size="sm">Small Button</Button>
7172
<Button color="secondary" size="sm">Small Button</Button>`}
7273
</PrismCode>
7374
</pre>
7475
<div className="docs-example">
75-
<Button size="lg" block>Block level button</Button>
76+
<Button color="primary" size="lg" block>Block level button</Button>
7677
<Button color="secondary" size="lg" block>Block level button</Button>
7778
</div>
7879
<pre>
7980
<PrismCode className="language-jsx">
80-
{`<Button size="lg" block>Block level button</Button>
81+
{`<Button color="primary" size="lg" block>Block level button</Button>
8182
<Button color="secondary" size="lg" block>Block level button</Button>`}
8283
</PrismCode>
8384
</pre>
8485
<h3>Active State</h3>
8586
<div className="docs-example">
86-
<Button size="lg" active>Primary link</Button>
87+
<Button color="primary" size="lg" active>Primary link</Button>
8788
<Button color="secondary" size="lg" active>Link</Button>
8889
</div>
8990
<pre>
9091
<PrismCode className="language-jsx">
91-
{`<Button size="lg" active>Primary link</Button>
92+
{`<Button color="primary" size="lg" active>Primary link</Button>
9293
<Button color="secondary" size="lg" active>Link</Button>`}
9394
</PrismCode>
9495
</pre>
9596
<h3>Disabled State</h3>
9697
<div className="docs-example">
97-
<Button size="lg" disabled>Primary button</Button>
98+
<Button color="primary" size="lg" disabled>Primary button</Button>
9899
<Button color="secondary" size="lg" disabled>Button</Button>
99100
</div>
100101
<pre>
101102
<PrismCode className="language-jsx">
102-
{`<Button size="lg" disabled>Primary button</Button>
103+
{`<Button color="primary" size="lg" disabled>Primary button</Button>
103104
<Button color="secondary" size="lg" disabled>Button</Button>`}
104105
</PrismCode>
105106
</pre>

docs/lib/Components/DropdownsPage.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint react/no-multi-comp: 0, react/prop-types: 0 */
22
import React from 'react';
3+
import { Link } from 'react-router';
34
import { PrismCode } from 'react-prism';
45
import Helmet from 'react-helmet';
56
import {
@@ -32,6 +33,13 @@ export default class DropdownPage extends React.Component {
3233
<div>
3334
<Helmet title="Dropdowns" />
3435
<h3>Dropdowns</h3>
36+
<p>
37+
The <code>Dropdown</code> component is used to pass the <code>isOpen</code> & <code>toggle</code> props via context to the following components: <code>DropdownToggle</code>, <code>DropdownMenu</code>. The <code>DropdownToggle</code> uses the <code>Button</code> component internally, meaning it also accepts all the props the <Link to="/components/buttons/">Button component</Link> accepts.
38+
</p>
39+
<h4>Advanced Positioning</h4>
40+
<p>
41+
The <code>DropdownMenu</code> can automatically be flipped (dropup vs dropdown) according to space available in the viewport by passing the <code>tether</code> prop to Dropdown <code>{`<Dropdown tether />`}</code>. For full customization, an object with <a href="http://tether.io/#options">Tether options</a> can be used instead.
42+
</p>
3543
<div className="docs-example">
3644
<DropdownExample />
3745
</div>
@@ -48,18 +56,22 @@ export default class DropdownPage extends React.Component {
4856
dropup: PropTypes.bool,
4957
group: PropTypes.bool,
5058
isOpen: PropTypes.bool,
51-
tag: PropTypes.string,
59+
tag: PropTypes.string, // default: 'div'
5260
tether: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
5361
toggle: PropTypes.func
5462
};
5563
5664
DropdownToggle.propTypes = {
5765
caret: PropTypes.bool,
5866
color: PropTypes.string,
67+
className: PropTypes.any,
5968
disabled: PropTypes.bool,
6069
onClick: PropTypes.func,
6170
'data-toggle': PropTypes.string,
62-
'aria-haspopup': PropTypes.bool
71+
'aria-haspopup': PropTypes.bool,
72+
73+
// Defaults to Button component
74+
tag: PropTypes.any
6375
};`}
6476
</PrismCode>
6577
</pre>

docs/lib/examples/ButtonDropdownMultiSplit.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ export default class Example extends React.Component {
2121
return (
2222
<ButtonDropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
2323
<Button id="caret" color={this.props.color}>{this.props.text}</Button>
24-
<DropdownToggle caret split>
25-
<Button color={this.props.color}><span className="sr-only">Toggle Dropdown</span></Button>
26-
</DropdownToggle>
24+
<DropdownToggle split color={this.props.color} />
2725
<DropdownMenu>
2826
<DropdownItem header>Header</DropdownItem>
2927
<DropdownItem disabled>Action</DropdownItem>

src/Button.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const propTypes = {
1515
};
1616

1717
const defaultProps = {
18-
color: 'primary',
18+
color: 'secondary',
1919
tag: 'button'
2020
};
2121

src/DropdownToggle.js

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, { PropTypes } from 'react';
22
import classNames from 'classnames';
3+
import Button from './Button';
34

45
const propTypes = {
56
caret: PropTypes.bool,
6-
children: PropTypes.node.isRequired,
7+
children: PropTypes.node,
78
className: PropTypes.any,
8-
color: PropTypes.string,
99
disabled: PropTypes.bool,
1010
onClick: PropTypes.func,
1111
'data-toggle': PropTypes.string,
@@ -18,7 +18,7 @@ const defaultProps = {
1818
'data-toggle': 'dropdown',
1919
'aria-haspopup': true,
2020
color: 'secondary',
21-
tag: 'button'
21+
tag: Button
2222
};
2323

2424
const contextTypes = {
@@ -47,39 +47,26 @@ class DropdownToggle extends React.Component {
4747
}
4848

4949
render() {
50-
const { className, children, caret, color, split, tag: Tag, ...props } = this.props;
50+
const { className, caret, split, tag: Tag, ...props } = this.props;
51+
const ariaLabel = props['aria-label'] || 'Toggle Dropdown';
5152
const classes = classNames(
5253
className,
5354
{
54-
'dropdown-toggle': caret,
55+
'dropdown-toggle': caret || split,
5556
'dropdown-toggle-split': split,
5657
active: this.context.isOpen,
5758
}
5859
);
59-
const buttonClasses = classNames(
60-
classes,
61-
'btn',
62-
'btn-' + color
63-
);
64-
65-
if (React.isValidElement(children)) {
66-
return React.cloneElement(React.Children.only(children), {
67-
...props,
68-
className: classes,
69-
onClick: this.onClick,
70-
'aria-haspopup': true,
71-
'aria-expanded': this.context.isOpen
72-
});
73-
}
60+
const children = props.children || <span className="sr-only">{ariaLabel}</span>;
7461

7562
return (
7663
<Tag
7764
{...props}
78-
children={children}
79-
className={buttonClasses}
65+
className={classes}
8066
onClick={this.onClick}
8167
aria-haspopup="true"
8268
aria-expanded={this.context.isOpen}
69+
children={children}
8370
/>
8471
);
8572
}

test/Button.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('Button', () => {
2828
it('should render buttons with default color', () => {
2929
const wrapper = shallow(<Button>Default Button</Button>);
3030

31-
expect(wrapper.hasClass('btn-primary')).toBe(true);
31+
expect(wrapper.hasClass('btn-secondary')).toBe(true);
3232
});
3333

3434
it('should render buttons with other colors', () => {
@@ -40,7 +40,7 @@ describe('Button', () => {
4040
it('should render buttons with outline variant', () => {
4141
const wrapper = shallow(<Button outline>Default Button</Button>);
4242

43-
expect(wrapper.hasClass('btn-outline-primary')).toBe(true);
43+
expect(wrapper.hasClass('btn-outline-secondary')).toBe(true);
4444
});
4545

4646
it('should render buttons with outline variant with different colors', () => {

0 commit comments

Comments
 (0)