Skip to content

Gut internal intermediate state from checkbox #751

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
Show all changes
18 commits
Select commit Hold shift + click to select a range
78ccea0
Adds indeterminate prop for checkbox, story to show it off. Re-arrang…
chriscorwin Oct 11, 2016
6618f10
Adds required story. You know, for kids.
chriscorwin Oct 11, 2016
819325e
Adds assistive text story and adds explinations to checkbox story.
chriscorwin Oct 11, 2016
c2f9228
Merge remote-tracking branch 'origin/master' into GH-626---checkbox-n…
chriscorwin Oct 12, 2016
a17cb45
Misspelled figured. https://github.com/salesforce-ux/design-system-re…
chriscorwin Oct 12, 2016
1e71f76
Updates indeterminate story (not working, WIP), removes dangling unde…
chriscorwin Oct 12, 2016
5f0f3f4
WIP
chriscorwin Oct 12, 2016
8d6ca18
Got indeterminate working from outside.
chriscorwin Oct 12, 2016
f3f9cb7
Updated story actions
chriscorwin Oct 12, 2016
af8c22e
WIP
chriscorwin Oct 13, 2016
547ceeb
Merge remote-tracking branch 'origin/master' into GH-626---checkbox-n…
chriscorwin Oct 17, 2016
6ee93aa
Fixes regression in checkbox onChange handler to make it work with da…
chriscorwin Oct 17, 2016
8f241a1
WIP
chriscorwin Oct 18, 2016
5fca285
Merge remote-tracking branch 'origin/master' into GH-626---checkbox-n…
chriscorwin Oct 18, 2016
624a5fc
Gets indeterminate working with data table.
chriscorwin Oct 18, 2016
749540d
Updates data table and test to support indeterminate
chriscorwin Oct 18, 2016
a665957
Merge branch 'master' of github.com:salesforce-ux/design-system-react…
interactivellama Oct 31, 2016
da4393c
Gut internal intermediate state from checkbox
interactivellama Oct 31, 2016
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
1 change: 1 addition & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ _Salesforce Lightning Design System :: React Components :: design-system-react_

These are changes that have backwards-compatible solutions present and that compatibiity will be removed at a breaking change release in the future.

- `Checkbox`'s `onChange` now recieves `event, {checked: [boolean], indeterminate: [boolean] }`. Previously, `checked` was the first parameter.
- `TabsPane` has be replaced with `TabsPanel`.
- `Input`'s props: `iconPosition`, `iconAssistiveText`, `iconCategory`, `iconName`, `onIconClick` are deprecated. An `Icon` component should be used instead.
- `DataTable`'s' `collection`, `onSelect`, `onDeselect` are deprecated.
Expand Down
2 changes: 2 additions & 0 deletions components/data-table/head.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const DataTableHead = React.createClass({
// ### Prop Types
propTypes: {
allSelected: PropTypes.bool.isRequired,
indeterminateSelected: PropTypes.bool.isRequired,
canSelectRows: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(
PropTypes.shape({
Expand All @@ -56,6 +57,7 @@ const DataTableHead = React.createClass({
<Checkbox
assistiveText="Select All"
checked={this.props.allSelected}
indeterminate={this.props.indeterminateSelected}
id={`${this.props.id}-SelectAll`}
name="SelectAll"
onChange={this.props.onToggleAll}
Expand Down
2 changes: 2 additions & 0 deletions components/data-table/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const DataTable = React.createClass({
const numSelected = count(this.props.selection);
const canSelectRows = this.props.selectRows && numRows > 0;
const allSelected = canSelectRows && numRows === numSelected;
const indeterminateSelected = canSelectRows && numRows !== numSelected && numSelected !== 0;
const columns = [];
let RowActions = null;

Expand Down Expand Up @@ -220,6 +221,7 @@ const DataTable = React.createClass({
>
<DataTableHead
allSelected={allSelected}
indeterminateSelected={indeterminateSelected}
canSelectRows={canSelectRows}
columns={columns}
id={`${this.props.id}_${DATA_TABLE_HEAD}`}
Expand Down
79 changes: 52 additions & 27 deletions components/forms/checkbox/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const Checkbox = React.createClass({
*/
assistiveText: React.PropTypes.string,
/**
* The Checkbox is a controlled component, and will always be in this state.
* The Checkbox is a controlled component, and will always be in this state. If checked is not defined, the state of the uncontrolled native `input` component will be used.
*/
checked: React.PropTypes.bool,
/**
Expand All @@ -74,6 +74,10 @@ const Checkbox = React.createClass({
* A unique ID is needed in order to support keyboard navigation and ARIA support.
*/
id: PropTypes.string,
/**
* The Checkbox will be indeterminate if its state can not be figured out or is partially checked. Once a checkbox is indeterminate, a click should cause it to be checked. Since a user cannot put a checkbox into an indeterminate state, it is assumed you are controlling the value of `checked` with the parent, also, and that this is a controlled component.
*/
indeterminate: React.PropTypes.bool,
/**
* An optional label for the Checkbox.
*/
Expand All @@ -92,24 +96,24 @@ const Checkbox = React.createClass({
required: PropTypes.bool
},

getDefaultProps () {
return {
id: shortid.generate()
};
componentWillMount () {
this.generatedId = shortid.generate();
},

// ### Render
render () {
const {
assistiveText,
checked,
indeterminate,
className,
disabled,
errorText,
label,
name,
onChange, // eslint-disable-line no-unused-vars
required,
id,

// ### Additional properties
// Using [object destructuring](https://facebook.github.io/react/docs/transferring-props.html#transferring-with-...-in-jsx) to pass on any properties which are not explicitly defined.
Expand All @@ -126,46 +130,67 @@ const Checkbox = React.createClass({
onKeyDown={this.handleKeyDown}
>
<div className="slds-form-element__control">
<label className="slds-checkbox">
<span className="slds-checkbox">
{required ? <abbr className="slds-required" title="required">*</abbr> : null}
<input
{...props}
id={id || this.generatedId}
checked={checked}
name={name}
disabled={disabled}
onChange={this.handleChange}
type="checkbox"
ref={
(component) => {
if (component) {
component.indeterminate = indeterminate;
}
this.input = component;
}}
/>
<span className="slds-checkbox--faux"></span>
{label
? <span className="slds-form-element__label">
{label}
</span>
: null}
{assistiveText
? <span className="slds-assistive-text">
{assistiveText}
</span>
: null}
</label>
<label className="slds-checkbox__label" htmlFor={id || this.generatedId}>
<span className="slds-checkbox--faux"></span>
{label
? <span className="slds-form-element__label">
{label}
</span>
: null}
{assistiveText
? <span className="slds-assistive-text">
{assistiveText}
</span>
: null}
</label>
</span>
</div>
{errorText ? <div className="slds-form-element__help">{errorText}</div> : null}
</div>
);
},

handleChange (e) {
if (isFunction(this.props.onChange)) {
this.props.onChange(!this.props.checked, e);
handleChange (event) {
const value = event.target.checked;
const {
checked,
indeterminate,
onChange
} = this.props;

if (isFunction(onChange)) {
// `checked` is present twice to maintain backwards compatibility. Please remove first parameter `value` on the next breaking change.
onChange(value, event, {
checked: indeterminate ? true : !checked,
indeterminate: false
});
}
},

handleKeyDown (e) {
if (e.keyCode) {
if (e.keyCode === KEYS.ENTER ||
e.keyCode === KEYS.SPACE) {
EventUtil.trapImmediate(e);
this.handleChange(e);
handleKeyDown (event) {
if (event.keyCode) {
if (event.keyCode === KEYS.ENTER ||
event.keyCode === KEYS.SPACE) {
EventUtil.trapImmediate(event);
this.handleChange(event);
}
}
}
Expand Down
25 changes: 23 additions & 2 deletions stories/data-table/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,29 @@ const DemoDataTable = React.createClass({
name: 'Cloud City',
count: 101280,
lastModified: 'Today'
}, {
id: '2FSH2DP0LY',
name: 'IoT',
count: 976,
lastModified: 'Yesterday'
}, {
id: '8NE888QKV1',
name: 'IoT + Anypoint Connectors',
count: 54976,
lastModified: 'Today'
}, {
id: 'M4D37GW83H',
name: 'Salesforce Tower',
count: 101280,
lastModified: 'Today'
}
],
selection: []
selection: [{
id: 'M4D37GW83H',
name: 'Salesforce Tower',
count: 101280,
lastModified: 'Today'
}]
};
},

Expand Down Expand Up @@ -83,7 +103,8 @@ const DemoDataTable = React.createClass({

handleChange (selection, ...rest) {
action('change')(selection, ...rest);

console.log("selection", selection);
console.dir(...rest);
this.setState({ selection });
},

Expand Down
125 changes: 123 additions & 2 deletions stories/forms/checkbox/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,83 @@ import { storiesOf, action } from '@kadira/storybook';

import { FORMS_CHECKBOX } from '../../../utilities/constants';
import Checkbox from '../../../components/forms/checkbox';
import Button from '../../../components/button';

const CheckboxIndeterminate = React.createClass({
displayName: `${FORMS_CHECKBOX}_INDETERMINATE`,

getInitialState () {
return {
indeterminate: true,
checked: true,
currentStateHelper: 'Indeterminate'
};
},

handleChange (checked, event, data) {
const checkedLabel = data.checked ? 'Checked' : 'Unchecked';
this.setState({
checked: data.checked,
currentStateHelper: data.indeterminate ? 'Indeterminate' : checkedLabel,
indeterminate: data.indeterminate
});

action('handleChange')(
checked,
event,
`checked: ${data.checked},
indeterminate: ${data.indeterminate}`
);
},

changeToIndeterminate () {
this.setState({ currentStateHelper: 'Inderterminate', checked: true, indeterminate: true });
action('changeToIndeterminate')(event, 'checked: true, indeterminate: true');
},

changeToCheck () {
this.setState({ currentStateHelper: 'Checked', checked: true, indeterminate: false });
action('changeToCheck')(event, 'checked: true, indeterminate: false');
},

changeToUnChecked () {
this.setState({ currentStateHelper: 'Unchecked', checked: false, indeterminate: false });
action('changeToUnChecked')(event, 'checked: false, indeterminate: false');
},

render () {
return (
<div>
<Button onClick={this.changeToIndeterminate} label="Inderterminate" />
<Button onClick={this.changeToCheck} label="Check" />
<Button onClick={this.changeToUnChecked} label="Uncheck" />
<p>
<strong>Current State:</strong> {this.state.currentStateHelper}
</p>
<Checkbox
assistiveText="Checkbox (indeterminate)"
label="Checkbox Label"
name="checkbox-example-standard-indeterminate"
checked={this.state.checked}
indeterminate={this.state.indeterminate}
onChange={this.handleChange}
/>
<div className="slds-box slds-text-longform slds-m-top--large">
<p>
This example has an <em>indeterminate</em> checkbox.
</p>
<p>
It is set by providing the <code>indeterminate</code> prop as <code><strong>true</strong></code>.
</p>
<p>
Once it is clicked, there is no way to make it go <em>back</em> to the indeterminate state, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:indeterminate#Checkbox_radio_button">it must be done programatically, through JavaScript</a>.
</p>
</div>
</div>
);
}

});

storiesOf(FORMS_CHECKBOX, module)
.addDecorator(getStory => <div className="slds-p-around--medium">{getStory()}</div>)
Expand All @@ -11,14 +88,58 @@ storiesOf(FORMS_CHECKBOX, module)
assistiveText="Checkbox"
label="Checkbox Label"
name="checkbox-example-standard"
onChange={action('change')}
/>
))
.add('Checkbox (indeterminate)', () => (
<CheckboxIndeterminate />
))
.add('Checkbox (required)', () => (
<Checkbox
assistiveText="Checkbox (required)"
label="Checkbox Label"
name="checkbox-example-standard-required"
onChange={action('change')}
required
/>
))
.add('Checkbox (assistive text)', () => (
<div>
<Checkbox
assistiveText={`This is my checkbox.
There are many like it, but this one is mine.
My checkbox is my best friend.
It is my life.
I must master it as I must master my life.
Without me, my checkbox is useless. Without my checkbox, I am useless.
I must make my checkbox true.
I must make it truer than my radio button who is trying to... `}
label="Checkbox Label"
name="checkbox-example-standard-assistiveText"
onChange={action('change')}
/>
<div className="slds-box slds-text-longform slds-m-top--large">
<p>
This example has assistive text. In Safari on Mac you can
turn assistive text on by using the keyboard combination:
<strong>Command + F5</strong>.
</p>
<p>
Once you have enabled it, use your tab key to focus on the
checkbox input, and the system should read you what is
supplied to the checkbox as the <code>assistiveText</code>
property.
</p>
</div>
</div>
))
.add('Checkbox (checked)', () => (
<Checkbox
assistiveText="Checkbox (checked)"
checked
label="Checkbox Label"
name="checkbox-example-standard-checked"
checked
onChange={action('change')}
/>
))
;
;
Loading