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

Checkbox plugin #80

Merged
merged 11 commits into from
Oct 26, 2015
3 changes: 0 additions & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"extends": "ta-webapp",
"env": {
"node": true
}
}
44 changes: 44 additions & 0 deletions docs/app/Component Guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Every component in Stardust must conform to these guidelines.
They ensure consistency and optimal development experience for Stardust users.

1. Semantic UI
- [Modules](#Modules)
1. [Classes](#Classes)
- [Extend React.Component](#Extend React.Component)
- [Identical class and component names](#Identical class and component names)
Expand All @@ -11,6 +13,43 @@ They ensure consistency and optimal development experience for Stardust users.
1. [Props](#Props)
- [className](#className)

## Semantic UI

### Modules

Semantic UI [Modules](http://semantic-ui.com/introduction/glossary.html) are components that define appearance and behavior.
These are things that have state, like a [Dropdown](http://semantic-ui.com/modules/dropdown.html).
They usually require a Semantic UI jQuery plugin.

All the appropriate lifecycle work is handled. You can just use the module.

**Settings**
Semantic UI's jQuery plugin settings are exposed as props:

```jsx
<Checkbox onChange={this.handleChange} />; // the Semantic UI change callback
```
>Settings are applied on componentDidMount.

**Plugin**
Stardust exposes the Semantic UI jQuery plugin as `plugin`. Use it to trigger behaviors:

```jsx
<Checkbox ref='checkbox' />;
// ...
this.refs.checkbox.plugin('toggle'); // toggles the checkbox
this.refs.checkbox.plugin('is checked'); // get the state
```

**Element**
Stardust components expose the jQuery element as `element`:

```jsx
<Checkbox ref='checkbox' ref='checkbox' />;
// then
this.refs.checkbox.element; // the jQuery element
```

## Classes

### Extend React.Component
Expand Down Expand Up @@ -137,6 +176,11 @@ Stardust components construct the `className` prop in this order.
1. `this.props.className`
1. `<component>` (Semantic UI class)

Where it makes sense, some optional classes are automatically applied.
For instance, the [`fitted`](http://semantic-ui.com/modules/checkbox.html#fitted)
class on checkboxes with no labels (as prescribed by the checkbox docs).
All magic is noted in the documentation examples.

#### Inherits `props.className` after `sd-<component>`

**Always**
Expand Down
32 changes: 29 additions & 3 deletions docs/app/Components/ComponentDoc/ComponentExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import exampleContext from 'docs/app/utils/ExampleContext';
*/
export default class ComponentExample extends Component {
static propTypes = {
children: PropTypes.node,
description: PropTypes.string,
examplePath: PropTypes.string.isRequired,
title: PropTypes.string,
Expand All @@ -17,11 +18,21 @@ export default class ComponentExample extends Component {
state = {showCode: false};
fileContents = require(`!raw!docs/app/Examples/${this.props.examplePath}`);
component = exampleContext(`./${this.props.examplePath}.js`);
// 'elements/Button/Types/Button' => #Button-Types-Button
anchor = this.props.examplePath.split('/').slice(1).join('-');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specific examples now have links on hover:

image


toggleShowCode = () => {
this.setState({showCode: !this.state.showCode});
};

handleMouseEnter = () => {
this.setState({showLink: true});
};

handleMouseLeave = () => {
this.setState({showLink: false});
};

render() {
const code = (
<Column>
Expand All @@ -31,13 +42,28 @@ export default class ComponentExample extends Component {
</Column>
);

const linkIconStyle = {
display: this.state.showLink ? 'inline-block' : 'none',
marginLeft: '0.25em',
};

const children = <Column>{this.props.children}</Column>;

return (
<Grid className='one column' style={{marginBottom: '4em'}}>
<Grid className='one column' style={{marginBottom: '4em'}} id={this.anchor}>
<Column>
<Grid>
<Column width={12}>
<h3 className='ui header' style={{marginBottom: 0}}>
<h3
className='ui header'
style={{marginBottom: 0}}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
{this.props.title}
<a href={`#${this.anchor}`}>
<i className='linkify icon' style={linkIconStyle} />
</a>
</h3>
<p>{this.props.description}</p>
</Column>
Expand All @@ -49,7 +75,7 @@ export default class ComponentExample extends Component {
</Column>
</Grid>
</Column>

{this.props.children && children}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now render children so we can add <Message />s to our examples.

<Column>
{createElement(this.component)}
</Column>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, {Component} from 'react';
import {Button, Checkbox} from 'stardust';

export default class CheckboxRemoteControlExample extends Component {
toggle = () => {
this.refs.checkbox.plugin('toggle');
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to include these methods inside the stardust docs? Or just refer them to the semantic docs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup!


render() {
return (
<div>
<Button onClick={this.toggle}>Toggle it</Button>
<Checkbox label='Check this box' ref='checkbox' />
</div>
);
}
}
22 changes: 6 additions & 16 deletions docs/app/Examples/modules/Checkbox/States/Checked.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
import React, {Component} from 'react';
import React from 'react';
import {Checkbox} from 'stardust';

export default class CheckboxCheckedExample extends Component {
state = {isChecked: true};

handleChange = e => {
this.setState({
isChecked: !this.state.isChecked
});
};

render() {
return (
<Checkbox label='This checkbox comes prechecked' checked={this.state.isChecked} onChange={this.handleChange} />
);
}
}
export default CheckboxCheckedExample => {
return (
<Checkbox defaultChecked label='This checkbox comes prechecked' />
);
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To check a checkbox, just set the React defaultChecked prop. This works because props are spread where they should be.

18 changes: 17 additions & 1 deletion docs/app/Examples/modules/Checkbox/States/States.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {Component} from 'react';
import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample';
import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection';
import {Message} from 'stardust';

export default class CheckboxStatesExamples extends Component {
render() {
Expand All @@ -10,7 +11,17 @@ export default class CheckboxStatesExamples extends Component {
title='Checked'
description='A checkbox can come pre-checked'
examplePath='modules/Checkbox/States/Checked'
/>
>
<Message>
Use
&nbsp;
<a href='https://facebook.github.io/react/docs/forms.html#default-value' target='_blank'>
<code>defaultChecked</code>
</a>
&nbsp;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing with &nbsp; here - should that be used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous.

as you normally would to set default form values.
</Message>
</ComponentExample>
<ComponentExample
title='Disabled'
description='Checkboxes can be disabled'
Expand All @@ -21,6 +32,11 @@ export default class CheckboxStatesExamples extends Component {
description='Make the checkbox unable to be edited by the user'
examplePath='modules/Checkbox/States/ReadOnly'
/>
<ComponentExample
title='Remote Control'
description='You can trigger events remotely.'
examplePath='modules/Checkbox/States/CheckboxRemoteControlExample'
/>
</ExampleSection>
);
}
Expand Down
2 changes: 1 addition & 1 deletion docs/app/Examples/modules/Checkbox/Types/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Checkbox} from 'stardust';
export default class CheckboxCheckboxExample extends Component {
render() {
return (
<Checkbox label='Make my profile visible'/>
<Checkbox label='Make my profile visible' />
);
}
}
6 changes: 3 additions & 3 deletions docs/app/Examples/modules/Checkbox/Variations/Fitted.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ export default class CheckboxFittedExample extends Component {
return (
<div>
<Segment className='compact'>
<Checkbox className='fitted' />
<Checkbox />
</Segment>
<Segment className='compact'>
<Checkbox className='fitted slider' />
<Checkbox className='slider' />
</Segment>
<Segment className='compact'>
<Checkbox className='fitted toggle' />
<Checkbox className='toggle' />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fitted class is automatically added. Note the addition of the message explaining so on the example:

image

</Segment>
</div>
);
Expand Down
11 changes: 10 additions & 1 deletion docs/app/Examples/modules/Checkbox/Variations/Variations.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {Component} from 'react';
import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample';
import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection';
import {Message} from 'stardust';

export default class CheckboxVariationsExamples extends Component {
render() {
Expand All @@ -10,7 +11,15 @@ export default class CheckboxVariationsExamples extends Component {
title='Fitted'
description='A fitted checkbox does not leave padding for a label'
examplePath='modules/Checkbox/Variations/Fitted'
/>
>
<Message>
The&nbsp;
<a href='http://semantic-ui.com/modules/checkbox.html#fitted' target='_blank'>
<code>fitted</code>
</a>
&nbsp;class is automatically applied if there is no <code>label</code> prop.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the &nbsp; normal to use here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, React removes all whitespace regardless of how many blank lines (unlike html). So, there is no space between words/components without this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Crazy! Good to know

</Message>
</ComponentExample>
</ExampleSection>
);
}
Expand Down
4 changes: 3 additions & 1 deletion gulp/tasks/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ gulp.task('generate-docs-json', cb => {
paths.srcViews + '/**/*.js',
'!' + paths.src + '/**/Style.js'
])
.pipe(g.plumber(err => {
// do not remove the function keyword
// we need 'this' scope here
.pipe(g.plumber(function(err) {
g.util.log(err);
this.emit('end');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was why watchers we dying on jsx parse error again. this.emit was not the right this. Arrow functions inherit this from the surrounding context, but we need a new scope here.

}))
Expand Down
1 change: 0 additions & 1 deletion src/elements/Input/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export default class Input extends Component {
const actionChildren = [];

Children.forEach(this.props.children, child => {
// TODO: use child._meta here, once merged, to determine component type
const isButton = child.type.name === 'Button';
const isDropdown = child.type.name === 'Dropdown';
// TODO: use child.type.name === 'Label' once Label component is merged.
Expand Down
42 changes: 18 additions & 24 deletions src/modules/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import _ from 'lodash';
import META from 'src/utils/Meta';
import getUnhandledProps from 'src/utils/getUnhandledProps';
import React, {Component, PropTypes} from 'react';
import classNames from 'classnames';
import $ from 'jquery';
Expand All @@ -10,14 +11,8 @@ export default class Checkbox extends Component {
beforeDeterminate: PropTypes.func,
beforeIndeterminate: PropTypes.func,
beforeUnchecked: PropTypes.func,
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
PropTypes.object,
]),
defaultChecked: PropTypes.bool,
className: PropTypes.string,
label: PropTypes.string,
name: PropTypes.string,
onChange: PropTypes.func,
onChecked: PropTypes.func,
onDeterminate: PropTypes.func,
Expand All @@ -28,11 +23,13 @@ export default class Checkbox extends Component {
type: PropTypes.string,
};

componentDidMount() {
this.container = $(this.refs.container);
this.input = $(this.refs.input);
static defaultProps = {
type: 'checkbox'
};

this.container.checkbox({
componentDidMount() {
this.element = $(this.refs.element);
this.element.checkbox({
onChange: this.props.onChange,
onChecked: this.props.onChecked,
onIndeterminate: this.props.onIndeterminate,
Expand All @@ -48,7 +45,7 @@ export default class Checkbox extends Component {
}

componentWillUnmount() {
this.container.off();
this.element.off();
}

static _meta = {
Expand All @@ -57,16 +54,15 @@ export default class Checkbox extends Component {
type: META.type.module,
};

plugin() {
return this.element.checkbox(...arguments);
}

render() {
let type = this.props.type;

if (!type) {
type = 'checkbox';
if (_.includes(this.props.className, 'radio')) {
type = 'radio';
}
if (_.includes(this.props.className, 'radio')) {
type = 'radio';
}

const classes = classNames(
'sd-checkbox',
'ui',
Expand All @@ -76,12 +72,10 @@ export default class Checkbox extends Component {
{fitted: !this.props.label},
'checkbox'
);

const checkboxProps = _.clone(this.props);
delete checkboxProps.className;
const props = getUnhandledProps(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't seen this utility before! NEATO!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a newer util. Gets all props not defined in propTypes nor defaultProps.

return (
<div className={classes} ref='container'>
<input {...checkboxProps} type={type} ref='checkbox' />
<div className={classes} ref='element'>
<input {...props} type={type} />
<label>{this.props.label}</label>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions src/modules/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Modal extends Component {
children: PropTypes.any,
className: PropTypes.string,
ref: PropTypes.string,
settings: PropTypes.object,
};

static defaultProps = {
Expand Down
Loading