Skip to content

Commit 94e5684

Browse files
authored
feat(Button): add role prop (Semantic-Org#2752)
1 parent cc7d9ee commit 94e5684

File tree

3 files changed

+91
-93
lines changed

3 files changed

+91
-93
lines changed

src/elements/Button/Button.d.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import * as React from 'react';
22

3-
import {
4-
SemanticCOLORS,
5-
SemanticFLOATS,
6-
SemanticShorthandContent,
7-
SemanticShorthandItem,
8-
SemanticSIZES
9-
} from '../..';
3+
import { SemanticCOLORS, SemanticFLOATS, SemanticShorthandContent, SemanticShorthandItem, SemanticSIZES } from '../..';
104
import { LabelProps } from '../Label';
115
import { default as ButtonContent } from './ButtonContent';
126
import { default as ButtonGroup } from './ButtonGroup';
@@ -88,6 +82,9 @@ export interface ButtonProps {
8882
/** A button can be formatted to show different levels of emphasis. */
8983
primary?: boolean;
9084

85+
/** The role of the HTML element. */
86+
role?: string;
87+
9188
/** A button can be formatted to show different levels of emphasis. */
9289
secondary?: boolean;
9390

src/elements/Button/Button.js

+26-36
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,10 @@ class Button extends Component {
3636
active: PropTypes.bool,
3737

3838
/** A button can animate to show hidden content. */
39-
animated: PropTypes.oneOfType([
40-
PropTypes.bool,
41-
PropTypes.oneOf(['fade', 'vertical']),
42-
]),
39+
animated: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['fade', 'vertical'])]),
4340

4441
/** A button can be attached to other content. */
45-
attached: PropTypes.oneOfType([
46-
PropTypes.bool,
47-
PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
48-
]),
42+
attached: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['left', 'right', 'top', 'bottom'])]),
4943

5044
/** A basic button is less pronounced. */
5145
basic: PropTypes.bool,
@@ -75,7 +69,13 @@ class Button extends Component {
7569
/** A button can have different colors */
7670
color: PropTypes.oneOf([
7771
...SUI.COLORS,
78-
'facebook', 'google plus', 'instagram', 'linkedin', 'twitter', 'vk', 'youtube',
72+
'facebook',
73+
'google plus',
74+
'instagram',
75+
'linkedin',
76+
'twitter',
77+
'vk',
78+
'youtube',
7979
]),
8080

8181
/** A button can reduce its padding to fit into tighter spaces. */
@@ -94,22 +94,13 @@ class Button extends Component {
9494
fluid: PropTypes.bool,
9595

9696
/** Add an Icon by name, props object, or pass an <Icon />. */
97-
icon: customPropTypes.some([
98-
PropTypes.bool,
99-
PropTypes.string,
100-
PropTypes.object,
101-
PropTypes.element,
102-
]),
97+
icon: customPropTypes.some([PropTypes.bool, PropTypes.string, PropTypes.object, PropTypes.element]),
10398

10499
/** A button can be formatted to appear on dark backgrounds. */
105100
inverted: PropTypes.bool,
106101

107102
/** Add a Label by text, props object, or pass a <Label />. */
108-
label: customPropTypes.some([
109-
PropTypes.string,
110-
PropTypes.object,
111-
PropTypes.element,
112-
]),
103+
label: customPropTypes.some([PropTypes.string, PropTypes.object, PropTypes.element]),
113104

114105
/** A labeled button can format a Label or Icon to appear on the left or right. */
115106
labelPosition: PropTypes.oneOf(['right', 'left']),
@@ -133,24 +124,25 @@ class Button extends Component {
133124
/** A button can be formatted to show different levels of emphasis. */
134125
primary: PropTypes.bool,
135126

127+
/** The role of the HTML element. */
128+
role: PropTypes.string,
129+
136130
/** A button can be formatted to show different levels of emphasis. */
137131
secondary: PropTypes.bool,
138132

139133
/** A button can have different sizes. */
140134
size: PropTypes.oneOf(SUI.SIZES),
141135

142136
/** A button can receive focus. */
143-
tabIndex: PropTypes.oneOfType([
144-
PropTypes.number,
145-
PropTypes.string,
146-
]),
137+
tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
147138

148139
/** A button can be formatted to toggle on and off. */
149140
toggle: PropTypes.bool,
150141
}
151142

152143
static defaultProps = {
153144
as: 'button',
145+
role: 'button',
154146
}
155147

156148
static _meta = {
@@ -222,6 +214,7 @@ class Button extends Component {
222214
positive,
223215
primary,
224216
secondary,
217+
role,
225218
size,
226219
toggle,
227220
} = this.props
@@ -245,13 +238,8 @@ class Button extends Component {
245238
useKeyOrValueAndKey(animated, 'animated'),
246239
useKeyOrValueAndKey(attached, 'attached'),
247240
)
248-
const labeledClasses = cx(
249-
useKeyOrValueAndKey(labelPosition || !!label, 'labeled'),
250-
)
251-
const wrapperClasses = cx(
252-
useKeyOnly(disabled, 'disabled'),
253-
useValueAndKey(floated, 'floated'),
254-
)
241+
const labeledClasses = cx(useKeyOrValueAndKey(labelPosition || !!label, 'labeled'))
242+
const wrapperClasses = cx(useKeyOnly(disabled, 'disabled'), useValueAndKey(floated, 'floated'))
255243

256244
const rest = getUnhandledProps(Button, this.props)
257245
const ElementType = getElementType(Button, this.props, this.computeElementType)
@@ -260,10 +248,12 @@ class Button extends Component {
260248
if (!_.isNil(label)) {
261249
const buttonClasses = cx('ui', baseClasses, 'button', className)
262250
const containerClasses = cx('ui', labeledClasses, 'button', className, wrapperClasses)
263-
const labelElement = Label.create(label, { defaultProps: {
264-
basic: true,
265-
pointing: labelPosition === 'left' ? 'right' : 'left',
266-
} })
251+
const labelElement = Label.create(label, {
252+
defaultProps: {
253+
basic: true,
254+
pointing: labelPosition === 'left' ? 'right' : 'left',
255+
},
256+
})
267257

268258
return (
269259
<ElementType {...rest} className={containerClasses} onClick={this.handleClick}>
@@ -286,7 +276,7 @@ class Button extends Component {
286276
disabled={(disabled && ElementType === 'button') || undefined}
287277
onClick={this.handleClick}
288278
ref={this.handleRef}
289-
role='button'
279+
role={role}
290280
tabIndex={tabIndex}
291281
>
292282
{hasChildren && children}

test/specs/elements/Button/Button-test.js

+61-50
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,21 @@ describe('Button', () => {
6666

6767
describe('attached', () => {
6868
it('renders a div', () => {
69-
shallow(<Button attached />)
70-
.should.have.tagName('div')
69+
shallow(<Button attached />).should.have.tagName('div')
7170
})
7271
})
7372

7473
describe('disabled', () => {
7574
it('is not set by default', () => {
76-
shallow(<Button />)
77-
.should.not.have.prop('disabled')
75+
shallow(<Button />).should.not.have.prop('disabled')
7876
})
7977

8078
it('applied when defined', () => {
81-
shallow(<Button disabled />)
82-
.should.have.prop('disabled', true)
79+
shallow(<Button disabled />).should.have.prop('disabled', true)
8380
})
8481

8582
it("don't apply when the element's type isn't button", () => {
86-
shallow(<Button as='div' disabled />)
87-
.should.not.have.prop('disabled')
83+
shallow(<Button as='div' disabled />).should.not.have.prop('disabled')
8884
})
8985

9086
it('is not set by default when has a label', () => {
@@ -118,34 +114,27 @@ describe('Button', () => {
118114

119115
describe('icon', () => {
120116
it('adds className icon', () => {
121-
shallow(<Button icon='user' />)
122-
.should.have.className('icon')
117+
shallow(<Button icon='user' />).should.have.className('icon')
123118
})
124119

125120
it('adds className icon when true', () => {
126-
shallow(<Button icon />)
127-
.should.have.className('icon')
121+
shallow(<Button icon />).should.have.className('icon')
128122
})
129123

130124
it('does not add className icon when there is content', () => {
131-
shallow(<Button icon='user' content={0} />)
132-
.should.not.have.className('icon')
133-
shallow(<Button icon='user' content='Yo' />)
134-
.should.not.have.className('icon')
125+
shallow(<Button icon='user' content={0} />).should.not.have.className('icon')
126+
shallow(<Button icon='user' content='Yo' />).should.not.have.className('icon')
135127
})
136128

137129
it('adds className icon given labelPosition and content', () => {
138-
shallow(<Button labelPosition='left' icon='user' content='My Account' />)
139-
.should.have.className('icon')
140-
shallow(<Button labelPosition='right' icon='user' content='My Account' />)
141-
.should.have.className('icon')
130+
shallow(<Button labelPosition='left' icon='user' content='My Account' />).should.have.className('icon')
131+
shallow(<Button labelPosition='right' icon='user' content='My Account' />).should.have.className('icon')
142132
})
143133
})
144134

145135
describe('label', () => {
146136
it('renders as a div', () => {
147-
shallow(<Button label='http' />)
148-
.should.have.tagName('div')
137+
shallow(<Button label='http' />).should.have.tagName('div')
149138
})
150139
it('renders a div with a button and Label child', () => {
151140
const wrapper = shallow(<Button label='hi' />)
@@ -155,8 +144,7 @@ describe('Button', () => {
155144
wrapper.should.have.exactly(1).descendants('Label')
156145
})
157146
it('adds the labeled className to the root element', () => {
158-
shallow(<Button label='hi' />)
159-
.should.have.className('labeled')
147+
shallow(<Button label='hi' />).should.have.className('labeled')
160148
})
161149
it('contains children without disabled class when disabled attribute is set', () => {
162150
const wrapper = shallow(<Button label='hi' disabled />)
@@ -174,80 +162,103 @@ describe('Button', () => {
174162
})
175163
it('creates a basic pointing label', () => {
176164
shallow(<Button label='foo' />)
177-
.should.have.exactly(1).descendants('Label[basic][pointing]')
165+
.should.have.exactly(1)
166+
.descendants('Label[basic][pointing]')
178167
})
179168
it('is before the button and pointing="right" when labelPosition="left"', () => {
180169
const wrapper = shallow(<Button labelPosition='left' label='foo' />)
181170
wrapper.should.have.exactly(1).descendants('Label[pointing="right"]')
182171

183-
wrapper.children().at(0).shallow().should.match('.ui.label')
184-
wrapper.children().at(1).should.match('button')
172+
wrapper
173+
.children()
174+
.at(0)
175+
.shallow()
176+
.should.match('.ui.label')
177+
wrapper
178+
.children()
179+
.at(1)
180+
.should.match('button')
185181
})
186182
it('is after the button and pointing="left" when labelPosition="right"', () => {
187183
const wrapper = shallow(<Button labelPosition='right' label='foo' />)
188184
wrapper.should.have.exactly(1).descendants('Label[pointing="left"]')
189185

190-
wrapper.children().at(0).should.match('button')
191-
wrapper.children().at(1).shallow().should.match('.ui.label')
186+
wrapper
187+
.children()
188+
.at(0)
189+
.should.match('button')
190+
wrapper
191+
.children()
192+
.at(1)
193+
.shallow()
194+
.should.match('.ui.label')
192195
})
193196
it('is after the button and pointing="left" by default', () => {
194197
const wrapper = shallow(<Button label='foo' />)
195198
wrapper.should.have.exactly(1).descendants('Label[pointing="left"]')
196199

197-
wrapper.children().at(0).should.match('button')
198-
wrapper.children().at(1).shallow().should.match('.ui.label')
200+
wrapper
201+
.children()
202+
.at(0)
203+
.should.match('button')
204+
wrapper
205+
.children()
206+
.at(1)
207+
.shallow()
208+
.should.match('.ui.label')
199209
})
200210
})
201211

202212
describe('labelPosition', () => {
203213
it('renders as a button when given an icon', () => {
204-
shallow(<Button labelPosition='left' icon='user' />)
205-
.should.have.tagName('button')
206-
shallow(<Button labelPosition='right' icon='user' />)
207-
.should.have.tagName('button')
214+
shallow(<Button labelPosition='left' icon='user' />).should.have.tagName('button')
215+
shallow(<Button labelPosition='right' icon='user' />).should.have.tagName('button')
208216
})
209217
})
210218

211219
describe('onClick', () => {
212220
it('is called with (e, data) when clicked', () => {
213221
const onClick = sandbox.spy()
214222

215-
shallow(<Button onClick={onClick} />)
216-
.simulate('click', syntheticEvent)
223+
shallow(<Button onClick={onClick} />).simulate('click', syntheticEvent)
217224

218225
onClick.should.have.been.calledOnce()
219-
onClick.should.have.been.calledWithExactly(syntheticEvent, { onClick, as: 'button' })
226+
onClick.should.have.been.calledWithExactly(syntheticEvent, { onClick, ...Button.defaultProps })
220227
})
221228

222229
it('is not called when is disabled', () => {
223230
const onClick = sandbox.spy()
224231

225-
shallow(<Button disabled onClick={onClick} />)
226-
.simulate('click', syntheticEvent)
232+
shallow(<Button disabled onClick={onClick} />).simulate('click', syntheticEvent)
227233
onClick.should.have.callCount(0)
228234
})
229235
})
230236

237+
describe('role', () => {
238+
it('defaults to a button', () => {
239+
Button.defaultProps.role.should.equal('button')
240+
shallow(<Button />).should.have.prop('role', 'button')
241+
})
242+
it('is configurable', () => {
243+
shallow(<Button role='link' />).should.have.prop('role', 'link')
244+
})
245+
})
246+
231247
describe('tabIndex', () => {
232248
it('is not set by default', () => {
233-
shallow(<Button />)
234-
.should.not.have.prop('tabIndex')
249+
shallow(<Button />).should.not.have.prop('tabIndex')
235250
})
236251
it('defaults to 0 as div', () => {
237-
shallow(<Button as='div' />)
238-
.should.have.prop('tabIndex', 0)
252+
shallow(<Button as='div' />).should.have.prop('tabIndex', 0)
239253
})
240254
it('defaults to -1 when disabled', () => {
241-
shallow(<Button disabled />)
242-
.should.have.prop('tabIndex', -1)
255+
shallow(<Button disabled />).should.have.prop('tabIndex', -1)
243256
})
244257
it('can be set explicitly', () => {
245-
shallow(<Button tabIndex={123} />)
246-
.should.have.prop('tabIndex', 123)
258+
shallow(<Button tabIndex={123} />).should.have.prop('tabIndex', 123)
247259
})
248260
it('can be set explicitly when disabled', () => {
249-
shallow(<Button tabIndex={123} disabled />)
250-
.should.have.prop('tabIndex', 123)
261+
shallow(<Button tabIndex={123} disabled />).should.have.prop('tabIndex', 123)
251262
})
252263
})
253264
})

0 commit comments

Comments
 (0)