Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

Commit

Permalink
Merge 1497103 into c608393
Browse files Browse the repository at this point in the history
  • Loading branch information
tinkertrain committed Jul 29, 2020
2 parents c608393 + 1497103 commit 8fbb68a
Show file tree
Hide file tree
Showing 14 changed files with 232 additions and 199 deletions.
10 changes: 4 additions & 6 deletions src/components/Button/Button.jsx
Expand Up @@ -192,22 +192,20 @@ Button.propTypes = {
isSuffix: PropTypes.bool,
/** Applies the specified style to the button.
* 'primary': Blue button. Used for primary actions.
* 'primaryAlt': Purple button. Used for primary actions.
* 'secondary': White button with a border. Used for secondary actions.
* 'secondaryAlt': White button with a green border. Used for secondary actions.
* 'tertiary': White button with a green border. Used for secondary actions.
* 'default': Borderless button. Used for subtle/tertiary actions.
* 'link': Button that looks like a `Link`. Used for subtle/tertiary actions.
*/
kind: PropTypes.oneOf([
'primary',
'primaryAlt',
'secondary',
'secondaryAlt',
'tertiary',
'default',
'link',
]),
/** Sets the size of the button. Can be one of "sm", "md" or "lg". */
size: PropTypes.string,
/** Sets the size of the button. */
size: PropTypes.oneOf(['sm', 'md', 'lg', 'xl', 'xs', 'xss']),
/** A special property that... spins the button if `isLoading`. */
spinButtonOnLoading: PropTypes.bool,
/** Applies state styles to the button.
Expand Down
15 changes: 14 additions & 1 deletion src/components/Checkbox/Checkbox.stories.mdx
@@ -1,4 +1,5 @@
import { Meta, Story, Props, Preview } from '@storybook/addon-docs/blocks'
import { boolean, select } from '@storybook/addon-knobs'
import Checkbox from './'
import { ChoiceGroup } from '../'

Expand All @@ -20,7 +21,19 @@ A Checkbox component is an enhanced version of the default HTML `<input>` `check

<Preview>
<Story name="default">
<ChoiceGroup>
<ChoiceGroup
multiSelect={boolean('multiSelect', true)}
value={select(
'Value from props',
{
zero: [],
one: ['hansel'],
two: ['hansel', 'mugatu'],
three: ['hansel', 'mugatu', 'derek'],
},
[]
)}
>
<Checkbox label="Derek" value="derek" />
<Checkbox label="Hansel" value="hansel" />
<Checkbox label="Mugatu" value="mugatu" />
Expand Down
19 changes: 16 additions & 3 deletions src/components/Choice/Choice.Input.jsx
@@ -1,5 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { key } from '../../constants/Keys'
import InputBackdropV2 from '../Input/Input.BackdropV2'
import Icon from '../Icon'
import { classNames } from '../../utilities/classNames'
Expand Down Expand Up @@ -32,6 +33,15 @@ class ChoiceInput extends React.PureComponent {
event.stopPropagation()
}

handleOnKeyDown = event => {
const isEnter = event.key === key.ENTER

if (isEnter) {
const { id, onEnter, value } = this.props
onEnter(value, !event.target.checked, id)
}
}

handleOnFocus = event => {
this.setState({
isFocused: true,
Expand Down Expand Up @@ -92,7 +102,6 @@ class ChoiceInput extends React.PureComponent {
value,
'data-cy': dataCy,
} = this.props

const { isFocused } = this.state

const componentClassName = classNames(
Expand Down Expand Up @@ -129,6 +138,7 @@ class ChoiceInput extends React.PureComponent {
onBlur={this.handleOnBlur}
onChange={this.handleOnChange}
onFocus={this.handleOnFocus}
onKeyDown={this.handleOnKeyDown}
readOnly={readOnly}
type={type}
value={value}
Expand Down Expand Up @@ -161,6 +171,7 @@ ChoiceInput.defaultProps = {
onBlur: noop,
onChange: noop,
onFocus: noop,
onEnter: noop,
inputRef: noop,
innerRef: noop,
readOnly: false,
Expand All @@ -186,11 +197,13 @@ ChoiceInput.propTypes = {
onBlur: PropTypes.func,
onChange: PropTypes.func,
onFocus: PropTypes.func,
/** Callback when pressing enter whenthe input is focused. */
onEnter: PropTypes.func,
name: PropTypes.string,
readOnly: PropTypes.bool,
state: PropTypes.string,
type: PropTypes.string,
value: PropTypes.string,
type: PropTypes.oneOf(['checkbox', 'radio']),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}

export default ChoiceInput
22 changes: 21 additions & 1 deletion src/components/Choice/Choice.jsx
Expand Up @@ -38,6 +38,12 @@ class Choice extends React.PureComponent {
this.props.onChange(value, checked)
}

handleOnEnter = (value, checked) => {
this.setState({ checked })

this.props.onEnter(value, checked)
}

handleOnBlur = event => {
this.props.onBlur(event)
}
Expand All @@ -46,6 +52,16 @@ class Choice extends React.PureComponent {
this.props.onFocus(event)
}

handleOnEnterWithContext = contextProps => {
return (...args) => {
this.handleOnEnter.apply(null, args)

if (contextProps.onEnter) {
contextProps.onEnter.apply(null, args)
}
}
}

handleOnBlurWithContext = contextProps => {
return (...args) => {
this.handleOnBlur.apply(null, args)
Expand Down Expand Up @@ -169,6 +185,7 @@ class Choice extends React.PureComponent {
onBlur: this.handleOnBlurWithContext(contextProps),
onFocus: this.handleOnFocusWithContext(contextProps),
onChange: this.handleOnChangeWithContext(contextProps),
onEnter: this.handleOnEnterWithContext(contextProps),
readOnly,
state,
type,
Expand Down Expand Up @@ -274,6 +291,7 @@ Choice.defaultProps = {
onBlur: noop,
onChange: noop,
onFocus: noop,
onEnter: noop,
inputRef: noop,
innerRef: noop,
isBlock: false,
Expand Down Expand Up @@ -306,7 +324,7 @@ Choice.propTypes = {
/** Retrieves the `input` DOM node. */
inputRef: PropTypes.func,
/** Label for the input. */
label: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
/** Name for the input. */
name: PropTypes.string,
/** Callback when the input is blurred. */
Expand All @@ -315,6 +333,8 @@ Choice.propTypes = {
onChange: PropTypes.func,
/** Callback when the input is focused. */
onFocus: PropTypes.func,
/** Callback when pressing enter whenthe input is focused. */
onEnter: PropTypes.func,
/** Disable editing of the input. */
readOnly: PropTypes.bool,
/** Stacks the input above the label. */
Expand Down
16 changes: 16 additions & 0 deletions src/components/Choice/Choice.test.js
Expand Up @@ -203,6 +203,22 @@ describe('Events', () => {
expect(spy).toHaveBeenCalledWith('Value', false)
wrapper.unmount()
})

test('onEnter changes value and triggers callback', () => {
const spy = jest.fn()
const wrapper = mount(<Choice onEnter={spy} value="Value" checked />)
const input = wrapper.find('input')

input.simulate('keydown', { key: 'Enter' })

expect(spy).toHaveBeenCalledWith('Value', false)

wrapper.setProps({ checked: false })
input.simulate('keydown', { key: 'Enter' })

expect(spy).toHaveBeenCalledWith('Value', true)
wrapper.unmount()
})
})

describe('States', () => {
Expand Down
10 changes: 10 additions & 0 deletions src/components/Choice/ChoiceInput.test.js
Expand Up @@ -72,6 +72,16 @@ describe('Events', () => {

expect(spy).toHaveBeenCalled()
})

test('Can trigger onEnter callback', () => {
const spy = jest.fn()
const wrapper = mount(<Input onEnter={spy} checked value="Value" />)
const input = wrapper.find('input')

input.simulate('keydown', { key: 'Enter' })

expect(spy).toHaveBeenCalled()
})
})

describe('States', () => {
Expand Down
73 changes: 36 additions & 37 deletions src/components/ChoiceGroup/ChoiceGroup.jsx
@@ -1,32 +1,39 @@
import React from 'react'
import PropTypes from 'prop-types'
import getValidProps from '@helpscout/react-utils/dist/getValidProps'
import Context from './ChoiceGroup.Context'
import equal from 'fast-deep-equal'
import ChoiceGroupContext from './ChoiceGroup.Context'
import FormGroup from '../FormGroup'
import FormLabelContext from '../FormLabel/Context'
import get from '../../utilities/get'
import { classNames } from '../../utilities/classNames'
import { createUniqueIDFactory } from '../../utilities/id'
import { noop } from '../../utilities/other'
import { ChoiceGroupUI } from './ChoiceGroup.css'
import Radio from '../Radio'
import RadioCard from '../RadioCard'

const uniqueID = createUniqueIDFactory('ChoiceGroup')

class ChoiceGroup extends React.PureComponent {
multiSelect = true

class ChoiceGroup extends React.Component {
constructor(props) {
super(props)

this.state = {
id: uniqueID(),
multiSelect: false,
selectedValue: props.value ? [].concat(props.value) : [],
}
}

shouldComponentUpdate(nextProps, nextState) {
if (
nextProps.value === this.props.value &&
equal(nextState.selectedValue, this.state.selectedValue)
) {
return false
}

return true
}

UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.value !== this.props.value) {
this.setState({
Expand All @@ -35,25 +42,6 @@ class ChoiceGroup extends React.PureComponent {
}
}

UNSAFE_componentWillMount() {
const child = this.props.children ? this.props.children[0] : false
let multiSelect

if (child) {
multiSelect = child.type !== Radio && child.type !== RadioCard
}
// Override auto-setting based on children
multiSelect =
this.props.multiSelect !== undefined
? this.props.multiSelect
: multiSelect

multiSelect = !!multiSelect

this.setState({ multiSelect })
this.multiSelect = multiSelect
}

getMultiSelectValue(value, checked) {
const { selectedValue } = this.state
const valueIndex = selectedValue.indexOf(value)
Expand All @@ -66,23 +54,33 @@ class ChoiceGroup extends React.PureComponent {
}

handleOnChange = (value, checked) => {
const { multiSelect } = this.state
const { multiSelect, onChange } = this.props
const selectedValue = multiSelect
? this.getMultiSelectValue(value, checked)
: value
: [value]

this.setState({ selectedValue })
this.props.onChange(selectedValue)
onChange(selectedValue)
}

handleOnEnter = (value, checked) => {
const { multiSelect, onEnter } = this.props
const selectedValue = multiSelect
? this.getMultiSelectValue(value, checked)
: [value]

this.setState({ selectedValue })
onEnter(selectedValue)
}

getContextProps = () => {
const { onBlur, onFocus, name } = this.props

const { selectedValue } = this.state

return {
onBlur,
onChange: this.handleOnChange,
onEnter: this.handleOnEnter,
onFocus,
name,
selectedValue,
Expand All @@ -91,7 +89,6 @@ class ChoiceGroup extends React.PureComponent {

getChildrenMarkup = () => {
const { isResponsive, choiceMaxWidth, children } = this.props

const { id } = this.state

return (
Expand Down Expand Up @@ -123,16 +120,15 @@ class ChoiceGroup extends React.PureComponent {
onBlur,
onChange,
onFocus,
multiSelect: multiSelectSetting,
onEnter,
multiSelect,
name,
...rest
} = this.props
const { multiSelect } = this.state
const isMultiSelect = multiSelectSetting || multiSelect
const componentClassName = classNames(
'c-ChoiceGroup',
align && `is-align-${align}`,
isMultiSelect && 'is-multi-select',
multiSelect && 'is-multi-select',
isResponsive && 'is-responsive',
className
)
Expand All @@ -141,15 +137,15 @@ class ChoiceGroup extends React.PureComponent {
return (
<FormLabelContext.Consumer>
{props => (
<Context.Provider value={this.getContextProps()}>
<ChoiceGroupContext.Provider value={this.getContextProps()}>
<ChoiceGroupUI
{...getValidProps(rest)}
className={componentClassName}
id={this.getIdFromContextProps(props)}
>
{childrenMarkup}
</ChoiceGroupUI>
</Context.Provider>
</ChoiceGroupContext.Provider>
)}
</FormLabelContext.Consumer>
)
Expand All @@ -163,6 +159,8 @@ ChoiceGroup.defaultProps = {
onBlur: noop,
onChange: noop,
onFocus: noop,
onEnter: noop,
multiSelect: true,
}

ChoiceGroup.propTypes = {
Expand All @@ -183,6 +181,7 @@ ChoiceGroup.propTypes = {
onChange: PropTypes.func,
/** Callback when an input is focused. */
onFocus: PropTypes.func,
onEnter: PropTypes.func,
/** The default value of input group. */
value: PropTypes.any,
/** Allow multiple choice selection */
Expand Down

0 comments on commit 8fbb68a

Please sign in to comment.