Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Allow children in Checklist options #105

Closed
wants to merge 3 commits into from
Closed
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
50 changes: 49 additions & 1 deletion demo/Demo.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ const ChecklistExample = `
const properties = {
id: 'my checklist',
labelStyle: {'display': 'block'},
disabled: false,
options: [
{'label': 'Melons', 'value': 'melons', 'disabled': false},
{'label': 'Apples', 'value': 'apples'},
Expand Down Expand Up @@ -299,6 +298,54 @@ class Controller extends Component {
ReactDOM.render(<Controller/>, mountNode);`


const ChecklistContainerExample = `
const applesDescription = (<div style={{marginLeft: '20px', marginBottom: '20px'}}>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Apples.jpg/97px-Apples.jpg" alt="Apples.jpg" /></p>
<p>This option selects the best and most fancy apples in the world</p>
</div>);

const orangesDescription = (<p style={{marginLeft: '15px'}}>There is no oranges available. Please come back in oranges season.</p>);

const properties = {
id: 'my container checklist',
labelStyle: {'display': 'block'},
options: [
{'label': 'Melons', 'value': 'melons', 'disabled': false},
{'label': 'Apples', 'value': 'apples',
'children': applesDescription,
'collapseChildrenButton': true,
'initiallyExpanded': false
},
{'label': 'Oranges', 'value': 'oranges', 'disabled': true,
'children': orangesDescription,
'collapseChildrenButton': true,
'initiallyExpanded': true}
]
};

class Controller extends Component {
constructor() {
super();
this.state = {
values: ['melons', 'apples']
};
}

render() {
return (<Checklist
setProps={(props) => {
this.setState(props);
}}
fireEvent={event => console.warn(event)}
values={this.state.values}
{...properties}
/>);
}
}

ReactDOM.render(<Controller/>, mountNode);`



const examples = [
{name: 'Upload', code: UploadExample},
Expand All @@ -308,6 +355,7 @@ const examples = [
{name: 'SyntaxHighlighter', code: SyntaxHighlighterExample},
{name: 'Radio', code: RadioExample},
{name: 'Checklist', code: ChecklistExample},
{name: 'Checklist with containers options', code: ChecklistContainerExample},
{name: 'Dropdown', code: DropdownExample},
{name: 'Slider', code: SliderExample},
{name: 'RangeSlider', code: RangeSliderExample},
Expand Down
253 changes: 200 additions & 53 deletions src/components/Checklist.react.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,135 @@
import {append, contains, without} from 'ramda';
import {append, contains, without} from 'ramda';
import React, {Component, PropTypes} from 'react';

class Checkbox extends Component {

constructor(props) {
super(props);

if (props.collapsable) {
this.state = {
collapsed: props.initiallyExpanded
};
this.handleCollapseClick = this.handleCollapseClick.bind(this);
}
}

handleCollapseClick() {
this.setState({ collapsed: !this.state.collapsed });
}

render() {

const {
children,
collapsable,
disabled,
inputClassName,
inputStyle,
isChecked,
label,
labelClassName,
labelStyle,
value
} = this.props;


let arrow;
let CollapsableChildren;

if (collapsable) {
const collapsed = this.state.collapsed;
arrow = (
<div
onClick={this.handleCollapseClick}
style={{cursor: 'pointer', marginLeft: '12px'}}
>
{collapsed? '▾' : '▴'}
</div>
);

CollapsableChildren = (
<div>
{collapsed ? null : children}
</div>
);
}

return (
<div>
<div style={{display: 'flex'}}>
<label
style={labelStyle}
className={labelClassName}
>
<input
checked={isChecked}
className={inputClassName}
disabled={Boolean(disabled)}
style={inputStyle}
type="checkbox"
onChange={() => this.props.handleOnChange(value)}
/>
{label}
</label>
{collapsable? arrow : null}
</div>

{collapsable? CollapsableChildren : children}
</div>
);
}
}

Checkbox.propTypes = {
/**
* The checkbox's label
*/
label: PropTypes.string,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `values` property.
*/
value: PropTypes.string,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,


/**
* Optinal wrapped components within this option
*/
children: PropTypes.node,

/**
* Show a button to show/hide children components of this option
*/
collapsable: PropTypes.bool,

/**
* Change default for the initiallyExpanded to be initially collapsed (hidden)
*/
initiallyExpanded: PropTypes.bool,

/**
* Mark if the input is checked or not
*/
checked: PropTypes.bool,

/**
* Callback to the parent to add or remove this option from the Checklist
*/
handleOnChange: PropTypes.func
}

Checkbox.defaultProps = {
initiallyExpanded: true
}

/**
* Checklist is a component that encapsulates several checkboxes.
* The values and labels of the checklist is specified in the `options`
Expand All @@ -11,56 +140,56 @@ export default class Checklist extends Component {
constructor(props) {
super(props);
this.state = {values: props.values};

this.handleOnChange = this.handleOnChange.bind(this);
}

componentWillReceiveProps(newProps) {
this.setState({values: newProps.values});
}

handleOnChange(value) {
const values = this.state.values;
let newValues;
if (contains(value, values)) {
newValues = without([value], values);
} else {
newValues = append(value, values);
}
this.setState({values: newValues});

const {fireEvent, setProps} = this.props;
if (setProps) setProps({values: newValues});
if (fireEvent) fireEvent({event: 'change'});
}

render() {
const {
className,
fireEvent,
id,
inputClassName,
inputStyle,
labelClassName,
labelStyle,
options,
setProps,
style
style,
values,
...rest
} = this.props;
const {values} = this.state;

return (
<div id={id} style={style} className={className}>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={contains(option.value, values)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValues;
if (contains(option.value, values)) {
newValues = without([option.value], values);
} else {
newValues = append(option.value, values);
}
this.setState({values: newValues});
if (setProps) setProps({values: newValues});
if (fireEvent) fireEvent({event: 'change'});
}}
/>
{option.label}
</label>
))}
<Checkbox
handleOnChange = {this.handleOnChange}
checked = {contains(option.value, values)}
collapsable = {option.children && option.collapseChildrenButton}
children = {option.children}
label = {option.label}
value = {option.value}
disabled = {option.disabled}
initiallyExpanded = {option.initiallyExpanded}
key = {option.value}
{...rest}
/>
)
)}
</div>
);
}
Expand All @@ -72,24 +201,42 @@ Checklist.propTypes = {
/**
* An array of options
*/
options: PropTypes.shape({
/**
* The checkbox's label
*/
label: PropTypes.string,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `values` property.
*/
value: PropTypes.string,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool
}),
options: PropTypes.arrayOf(
PropTypes.shape({
/**
* The checkbox's label
*/
label: PropTypes.string,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `values` property.
*/
value: PropTypes.string,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,

/**
* Optinal wrapped components within this option
*/
children: PropTypes.node,

/**
* Show a button to show/hide children components of this option
*/
collapseChildrenButton: PropTypes.bool,

/**
* Change default for the collapseChildrenButton to be initially hidden
*/
initiallyExpanded: PropTypes.bool

})
),

/**
* The currently selected value
Expand Down
15 changes: 15 additions & 0 deletions src/components/__tests__/Checklist.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import {shallow} from 'enzyme';
import CheckListComponent from '../Checklist.react';

describe('Checklist component', () => {

it('renders', () => {
const component = shallow(<CheckListComponent id="my-checklist"/>);
expect(component).to.be.ok;
});

describe('options', () => {
it('renders passed-in options');
});
});