Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Checkbox): onFocus and onBlur events #1361

Merged
merged 9 commits into from
Mar 24, 2017
12 changes: 10 additions & 2 deletions src/modules/Checkbox/Checkbox.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,23 @@ export interface CheckboxProps {
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props and proposed checked/indeterminate state.
*/
onChange?: (event: React.FormEvent<HTMLInputElement>, data: this) => void;
onChange?: (event: React.FormEvent<HTMLInputElement>, data: CheckboxProps) => void;

/**
* Called when the checkbox or label is clicked.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props and current checked/indeterminate state.
*/
onClick?: (event: React.MouseEvent<HTMLInputElement>, data: this) => void;
onClick?: (event: React.MouseEvent<HTMLInputElement>, data: CheckboxProps) => void;

/**
* Called when the user presses down on the mouse.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props and current checked/indeterminate state.
*/
onMouseDown?: (event: React.MouseEvent<HTMLInputElement>, data: CheckboxProps) => void;

/** Format as a radio element. This means it is an exclusive option. */
radio?: boolean;
Expand Down
44 changes: 36 additions & 8 deletions src/modules/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ export default class Checkbox extends Component {
*/
onClick: PropTypes.func,

/**
* Called when the user presses down on the mouse.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props and current checked/indeterminate state.
*/
onMouseDown: PropTypes.func,

/** Format as a radio element. This means it is an exclusive option. */
radio: customPropTypes.every([
PropTypes.bool,
Expand Down Expand Up @@ -132,10 +140,18 @@ export default class Checkbox extends Component {
return !disabled && !readOnly && !(radio && checked)
}

computeTabIndex = () => {
const { disabled, tabIndex } = this.props

if (!_.isNil(tabIndex)) return tabIndex
return disabled ? -1 : 0
}

handleInputRef = c => (this.inputRef = c)

handleClick = (e) => {
handleClick = e => {
debug('handleClick()')

const { onChange, onClick } = this.props
const { checked, indeterminate } = this.state

Expand All @@ -147,11 +163,22 @@ export default class Checkbox extends Component {
}
}

handleMouseDown = e => {
debug('handleMouseDown()')

const { onMouseDown } = this.props
const { checked, indeterminate } = this.state

_.invoke('focus', this.inputRef)
if (onMouseDown) onMouseDown(e, { ...this.props, checked: !!checked, indeterminate: !!indeterminate })
}

// Note: You can't directly set the indeterminate prop on the input, so we
// need to maintain a ref to the input and set it manually whenever the
// component updates.
setIndeterminate = () => {
const { indeterminate } = this.state

if (this.inputRef) this.inputRef.indeterminate = !!indeterminate
}

Expand All @@ -164,7 +191,6 @@ export default class Checkbox extends Component {
radio,
readOnly,
slider,
tabIndex,
toggle,
type,
value,
Expand All @@ -189,19 +215,21 @@ export default class Checkbox extends Component {
const rest = getUnhandledProps(Checkbox, this.props)
const ElementType = getElementType(Checkbox, this.props)

let computedTabIndex
if (!_.isNil(tabIndex)) computedTabIndex = tabIndex
else computedTabIndex = disabled ? -1 : 0

return (
<ElementType {...rest} className={classes} onClick={this.handleClick} onChange={this.handleClick}>
<ElementType
{...rest}
className={classes}
onChange={this.handleClick}
onClick={this.handleClick}
onMouseDown={this.handleMouseDown}
>
<input
checked={checked}
className='hidden'
name={name}
readOnly
ref={this.handleInputRef}
tabIndex={computedTabIndex}
tabIndex={this.computeTabIndex()}
type={type}
value={value}
/>
Expand Down
16 changes: 16 additions & 0 deletions test/specs/modules/Checkbox/Checkbox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,22 @@ describe('Checkbox', () => {
})
})

describe('onMouseDown', () => {
it('sets focus to container', () => {
const mountNode = document.createElement('div')
document.body.appendChild(mountNode)

const wrapper = mount(<Checkbox />, { attachTo: mountNode })
const input = document.querySelector('.ui.checkbox input')

wrapper.simulate('mousedown')
document.activeElement.should.equal(input)

wrapper.detach()
document.body.removeChild(mountNode)
})
})

describe('readOnly', () => {
it('cannot be checked', () => {
shallow(<Checkbox readOnly />)
Expand Down