Skip to content

Wizard redesign - Checkboxes and radio buttons with optional description and price (without design update) #310

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions components/Formsy/CheckboxGroup.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { Component, PropTypes } from 'react'
import { HOC as hoc } from 'formsy-react'
import cn from 'classnames'
import { numberWithCommas } from './format'

class CheckboxGroup extends Component {

Expand All @@ -21,7 +22,7 @@ class CheckboxGroup extends Component {
}

render() {
const { label, name, options, layout } = this.props
const { label, name, options, layout, wrapperClass } = this.props
const hasError = !this.props.isPristine() && !this.props.isValid()
const disabled = this.props.isFormDisabled() || this.props.disabled
const errorMessage = this.props.getErrorMessage() || this.props.validationError
Expand All @@ -30,7 +31,7 @@ class CheckboxGroup extends Component {
const curValue = this.props.getValue() || []
const checked = curValue.indexOf(cb.value) !== -1
const disabled = this.props.isFormDisabled() || cb.disabled || this.props.disabled
const rClass = cn('checkbox-group-item', { disabled })
const rClass = cn('checkbox-group-item', { disabled, selected: checked })
const id = name+'-opt-'+key
const setRef = (c) => this['element-' + key] = c
return (
Expand All @@ -48,10 +49,16 @@ class CheckboxGroup extends Component {
<label htmlFor={id}/>
</div>
<label className="tc-checkbox-label" htmlFor={id}>{cb.label}</label>
{
cb.quoteUp && !checked && <div className="checkbox-option-price"> {`+ $${numberWithCommas(cb.quoteUp)}`} </div>
}
{
cb.description && checked && <div className="checkbox-option-description"> {cb.description} </div>
}
</div>
)
}
const chkGrpClass = cn('checkbox-group', {
const chkGrpClass = cn('checkbox-group', wrapperClass, {
horizontal: layout === 'horizontal',
vertical: layout === 'vertical'
})
Expand Down
27 changes: 25 additions & 2 deletions components/Formsy/RadioGroup.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { Component, PropTypes } from 'react'
import { HOC as hoc } from 'formsy-react'
import cn from 'classnames'
import { find } from "lodash";
import { numberWithCommas } from './format'

class RadioGroup extends Component {

Expand All @@ -15,16 +17,28 @@ class RadioGroup extends Component {
this.props.onChange(this.props.name, value)
}

getSelectedOption() {
const {options = [], getValue} = this.props;
const value = getValue()
return find(options, o => value === o.value)
}

render() {
const { label, name, wrapperClass, options } = this.props
const hasError = !this.props.isPristine() && !this.props.isValid()
const disabled = this.props.isFormDisabled() || this.props.disabled
const errorMessage = this.props.getErrorMessage() || this.props.validationError
const selectedOption = this.getSelectedOption()
const hasPrice = find(options, o => o.quoteUp)

const renderOption = (radio, key) => {
const checked = (this.props.getValue() === radio.value)
const relativePrice = (selectedOption, radio) => {
const price = (radio.quoteUp || 0) - (selectedOption.quoteUp || 0)
return (price < 0 ? '-' : '+') + ' $' + numberWithCommas(Math.abs(price))
}
const checked = (selectedOption && selectedOption.value === radio.value)
const disabled = this.props.isFormDisabled() || radio.disabled || this.props.disabled
const rClass = cn('radio', { disabled })
const rClass = cn('radio', { disabled, selected: checked })
const id = name+'-opt-'+key
const setRef = (c) => this['element-' + key] = c
return (
Expand All @@ -39,6 +53,15 @@ class RadioGroup extends Component {
disabled={disabled}
/>
<label htmlFor={id}>{radio.label}</label>
{
hasPrice &&
!checked &&
(radio.quoteUp || selectedOption) &&
<div className="radio-option-price"> {selectedOption ? relativePrice(selectedOption, radio) : `$${numberWithCommas(radio.quoteUp)}`} </div>
}
{
radio.description && checked && <div className="radio-option-description"> {radio.description} </div>
}
</div>
)
}
Expand Down
139 changes: 139 additions & 0 deletions components/Formsy/TiledCheckboxGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
'use strict'
import React, { PropTypes, Component } from 'react'
import classNames from 'classnames'
import Tooltip from '../Tooltip/Tooltip'
import IconUICheckSimple from '../Icons/IconUICheckSimple'
import { HOC as hoc } from 'formsy-react'

class TiledCheckboxGroup extends Component {
constructor(props) {
super(props)
this.onChange = this.onChange.bind(this)
this.getCheckMarkIconActive = this.getCheckMarkIconActive.bind(this)
}

onChange(value) {
const curValue = this.props.getValue() || []
const index = curValue.indexOf(value)
let newValue = [...curValue]
if (index > -1) {
newValue.splice(index, 1)
} else {
newValue.push(value)
}
this.props.setValue(newValue)
this.props.onChange(this.props.name, newValue)
}

getCheckMarkIconActive() {
return (this.props.checkMarkActiveIcon ? this.props.checkMarkActiveIcon : (
<span className="check-mark">
<IconUICheckSimple fill="#fff" width={12} height={12}/>
</span>))
}

render() {
const { wrapperClass, options, theme, tabable } = this.props
const curValue = this.props.getValue() || []
const hasError = !this.props.isPristine() && !this.props.isValid()
const disabled = this.props.isFormDisabled() || this.props.disabled
const errorMessage = this.props.getErrorMessage() || this.props.validationError

const renderOption = (opt, idx) => {
const checked = curValue.indexOf(opt.value) > -1
const itemClassnames = classNames('tiled-group-item', theme, {
active: checked
}, {
disabled: opt.disabled
})
const handleClick = () => this.onChange(opt.value)
const handleFocus = (e) => {
e.target.parentNode.classList.add('focused')
}
const handleBlur = (e) => {
e.target.parentNode.classList.remove('focused')
}
const Icon = opt.icon
const setRef = (c) => this['element-' + idx] = c
const renderTile = () => (
<a onClick={ !disabled && !opt.disabled && handleClick } data-value={opt.value} className={itemClassnames} key={idx} >
{
!!tabable &&
<input
ref={setRef}
type="checkbox"
name={ this.props.name }
style={{ position : 'absolute', left : '-9999px'}}
onFocus={handleFocus}
onChange={this.onChange}
onBlur={handleBlur}
/>
}
{
this.props.showCheckMarkBeforeTitle
&& (checked
? this.getCheckMarkIconActive()
: this.props.checkMarkUnActiveIcon)
}
<span className="icon">{ opt.icon && <Icon {...opt.iconOptions} />}</span>
<span className="title">{opt.title}</span>
<small>{opt.desc}</small>
{
!this.props.showCheckMarkBeforeTitle
&& (checked
? this.getCheckMarkIconActive()
: this.props.checkMarkUnActiveIcon)
}
</a>
)
return (
<span key={ idx } className="tiled-group-item-container">
{
opt.disabled && opt.errorMessage ?
<Tooltip>
<div className="tooltip-target" id={'tooltip-' + idx}>
{renderTile()}
</div>
<div className="tooltip-body">
<p>{opt.errorMessage}</p>
</div>
</Tooltip> :
renderTile()
}
</span>
)
}

return (
<div className={`${wrapperClass} tiled-group-row`}>

{this.props.label && (
<label className="tc-label">
{this.props.label}
</label>)}
{options.map(renderOption)}
{ hasError ? (<p className="error-message">{errorMessage}</p>) : null}
</div>
)
}
}
TiledCheckboxGroup.propTypes = {
options: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
desc: PropTypes.string
// icon: PropTypes.
}).isRequired
).isRequired,
checkMarkActiveIcon: PropTypes.node,
checkMarkUnActiveIcon: PropTypes.node,
showCheckMarkBeforeTitle: PropTypes.bool
}

TiledCheckboxGroup.defaultProps = {
onChange: () => {},
showCheckMarkBeforeTitle: false
}

export default hoc(TiledCheckboxGroup)
12 changes: 12 additions & 0 deletions components/Formsy/format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Helper methods to format values
*/

/**
* Fomats number, separating every 3 digits with comma
*
* @param {Number} number
*/
export function numberWithCommas(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
6 changes: 5 additions & 1 deletion components/Formsy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import RadioGroup from './RadioGroup'
import CheckboxGroup from './CheckboxGroup'
import SliderRadioGroup from './SliderRadioGroup'
import TiledRadioGroup from './TiledRadioGroup'
import TiledCheckboxInput from './TiledCheckboxInput'
import TiledCheckboxGroup from './TiledCheckboxGroup'

require('./FormFields.scss')

Expand Down Expand Up @@ -37,6 +39,8 @@ export default {
Checkbox,
CheckboxGroup,
SliderRadioGroup,
TiledRadioGroup
TiledRadioGroup,
TiledCheckboxInput,
TiledCheckboxGroup
}
}