chore: 🤖 add form component for simpler form building

This commit is contained in:
Randall Morey committed 2020-05-27 17:28:29 -04:00
1 parent 61d37037bc
commit 16f08744d3
22 files changed
+389 -168

No files matched your search

+27
View File
@@ -0,0 +1,27 @@
import Component from '@ember/component';
import layout from '../../templates/components/rose/form';
import { action } from '@ember/object';
/**
* A component for building accessible forms. Yields contextual fields
* and actions.
*/
export default Component.extend({
layout,
tagName: '',
// =actions
/**
* Calls the passed `onSubmit` function and disabled the default form
* submit behavior.
* @param {Event} e
*/
@action
handleSubmit(e) {
e.preventDefault();
e.stopPropagation();
this.onSubmit();
return false;
},
});
@@ -0,0 +1,126 @@
import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
import { hbs } from 'ember-cli-htmlbars';
import { action } from '@storybook/addon-actions';
import { text, boolean, select } from '@storybook/addon-knobs';
<Meta title="Rose|Form" component="Form" />
# Form
A component for building accessible forms. Yields contextual fields
and actions.
```htmlbars
<Rose::Form
@onSubmit={{this.submit}}
@cancel={{this.cancel}}
@disabled={{false}}
as |form|>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.select
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |select|
>
<select.option>Choose an option</select.option>
<select.option @value="value-1">Value 1</select.option>
<select.option @value="value-2">Value 2</select.option>
<select.option @value="value-3">Value 3</select.option>
</form.select>
<form.radioGroup @name="color" @selectedValue="green" as |radioGroup|>
<radioGroup.radio
@id="red-radio"
@label="Red"
@value="red"
/>
<radioGroup.radio
@id="green-radio"
@label="Green"
@value="green"
/>
<radioGroup.radio
@id="blue-radio"
@label="Blue"
@value="blue"
/>
</form.radioGroup>
<form.checkbox
@id="checkbox-1"
@label="Label"
/>
<form.actions @submitText="Save" @cancelText="Cancel" @showCancel={{true}} />
</Rose::Form>
```
<Preview>
<Story name="Form">{{
template: hbs`
<Rose::Form
@onSubmit={{submit}}
@cancel={{cancel}}
@disabled={{disabled}}
as |form|>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.select
@label="Label"
@helperText="Helper text"
@onChange={{selectChange}}
as |select|
>
<select.option>Choose an option</select.option>
<select.option @value="value-1">Value 1</select.option>
<select.option @value="value-2">Value 2</select.option>
<select.option @value="value-3">Value 3</select.option>
</form.select>
<form.radioGroup @name="color" @selectedValue="green" as |radioGroup|>
<radioGroup.radio
@id="red-radio"
@label="Red"
@value="red"
/>
<radioGroup.radio
@id="green-radio"
@label="Green"
@value="green"
/>
<radioGroup.radio
@id="blue-radio"
@label="Blue"
@value="blue"
/>
</form.radioGroup>
<form.checkbox
@id="checkbox-1"
@label="Label"
@checked={{true}}
/>
<form.actions @submitText="Save" @cancelText="Cancel" @showCancel={{true}} />
</Rose::Form>
`,
context: {
disabled: boolean('disabled', false),
submit: action('submit'),
cancel: action('cancel'),
selectChange: action('selectChange'),
},
}}</Story>
</Preview>
@@ -16,7 +16,7 @@ Renders a form input checkbox.
@label="Yes?"
@checked={{false}}
@error={{false}}
disabled={{false}}
@disabled={{false}}
/>
```
@@ -29,7 +29,7 @@ Renders a form input checkbox.
@label={{label}}
@checked={{checked}}
@error={{error}}
disabled={{disabled}}
@disabled={{disabled}}
/>
`,
context: {
@@ -16,7 +16,7 @@ Renders a simple form input.
@label="Email"
@helperText="Enter your email"
@error={{false}}
disabled={{false}}
@disabled={{false}}
readonly={{false}}
/>
```
@@ -30,7 +30,7 @@ Renders a simple form input.
@label={{label}}
@helperText={{helperText}}
@error={{error}}
disabled={{disabled}}
@disabled={{disabled}}
readonly={{read-only}}
/>
`,
@@ -1,6 +1,6 @@
import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
import { hbs } from 'ember-cli-htmlbars';
import { text } from '@storybook/addon-knobs';
import { text, boolean } from '@storybook/addon-knobs';
<Meta title="Rose|Form/Radio/Group" component="Form/Radio/Group" />
@@ -9,7 +9,12 @@ import { text } from '@storybook/addon-knobs';
Renders a form radiofield group.
```htmlbars
<Rose::Form::Radio::Group @name={{name}} @selectedValue="disagree" as |radioGroup|>
<Rose::Form::Radio::Group
@disabled={{false}}
@name={{name}}
@selectedValue="disagree"
as |radioGroup|
>
<radioGroup.radio
@id="agree-radiofield"
@label="Agree"
@@ -30,7 +35,12 @@ Renders a form radiofield group.
<Preview>
<Story name="Basic" height="10rem">{{
template: hbs`
<Rose::Form::Radio::Group @name={{name}} @selectedValue={{selectedValue}} as |radioGroup|>
<Rose::Form::Radio::Group
@disabled={{disabled}}
@name={{name}}
@selectedValue={{selectedValue}}
as |radioGroup|
>
<radioGroup.radio
@id="firstField"
@label={{firstFieldLabel}}
@@ -50,6 +60,7 @@ Renders a form radiofield group.
`,
context: {
name: text('name', 'mustagree'),
disabled: boolean('disabled', false),
selectedValue: text('selectedValue', 'disagree'),
firstFieldLabel: text('first label', 'Agree'),
firstFieldValue: text('first value', 'agree'),
@@ -15,7 +15,7 @@ Renders a form select element.
@value="green"
@label="Color"
@helperText="Set your desired color"
disabled={{false}}
@disabled={{false}}
@error={{false}}
as |select|
>
@@ -34,7 +34,7 @@ Renders a form select element.
@value={{value}}
@label={{label}}
@helperText={{helperText}}
disabled={{disabled}}
@disabled={{disabled}}
@error={{error}}
@onChange={{fn onChange}}
as |select|
@@ -15,7 +15,7 @@ Renders a simple form textarea.
@label="Description"
@helperText="Enter a description"
@error={{false}}
disabled={{false}}
@disabled={{false}}
readonly={{false}}
/>
```
@@ -28,7 +28,7 @@ Renders a simple form textarea.
@label={{label}}
@helperText={{helperText}}
@error={{error}}
disabled={{disabled}}
@disabled={{disabled}}
readonly={{read-only}}
/>
`,
@@ -0,0 +1,16 @@
<form
...attributes
novalidate
method="post"
class="rose-form"
{{on "submit" this.handleSubmit}}
>
{{yield (hash
radioGroup=(component 'rose/form/radio/group' disabled=@disabled)
checkbox=(component 'rose/form/checkbox' disabled=@disabled)
input=(component 'rose/form/input' disabled=@disabled)
select=(component 'rose/form/select' disabled=@disabled)
textarea=(component 'rose/form/textarea' disabled=@disabled)
actions=(component 'rose/form/actions' disabled=@disabled cancel=@cancel)
)}}
</form>
@@ -4,6 +4,7 @@
...attributes
class="rose-form-checkbox-field"
id={{@id}}
disabled={{@disabled}}
@type="checkbox"
@checked={{if @checked true false}} />
@@ -12,6 +12,7 @@
...attributes
class="rose-form-input-field"
id={{@id}}
disabled={{@disabled}}
@type={{@type}}
@value={{@value}} />
@@ -1,3 +1,3 @@
{{yield (hash
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue)
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue disabled=@disabled)
)}}
@@ -5,6 +5,7 @@
@radioClass="rose-form-radio-field"
@radioId={{@id}}
type="radio"
disabled={{@disabled}}
@name={{@name}}
@value={{@value}}
@groupValue={{@selectedValue}}
@@ -12,6 +12,7 @@
...attributes
class="rose-form-select-field"
id={{@id}}
disabled={{@disabled}}
@value={{@value}}
@onChange={{@onChange}}
as |xs|
@@ -10,8 +10,9 @@
<Textarea
...attributes
id={{@id}}
class="rose-form-input-field"
id={{@id}}
disabled={{@disabled}}
@value={{@value}} />
{{yield}}
+1
View File
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/form';
@@ -2,9 +2,11 @@ import Controller from '@ember/controller';
import { action } from '@ember/object';
export default class IndexController extends Controller {
color = 'green';
@action
actionHandler(/*value*/) {
// eslint-disable-next-line no-console
//console.log(value);
//console.log('action handled');
}
}
+121 -149
View File
@@ -47,160 +47,132 @@ Dummy app index template
<br>
<h2>Form</h2>
<Rose::Form::Select
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |select|
<Rose::Form
@onSubmit={{this.actionHandler}}
@cancel={{this.actionHandler}}
@disabled={{false}}
as |form|
>
<select.option>Choose an option</select.option>
<select.option @value="value-1">Value 1</select.option>
<select.option @value="value-2">Value 2</select.option>
<select.option @value="value-3">Value 3</select.option>
</Rose::Form::Select>
<Rose::Form::Select
disabled={{true}}
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |select|
>
<select.option>Choose an option</select.option>
</Rose::Form::Select>
<h3>Inputs</h3>
<form.input
@label="Label"
@value="Read only"
readonly={{true}}
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<h3>Form Actions</h3>
<Rose::Form::Actions
@submitText="Save"
@cancelText="Cancel"
@cancel={{this.actionHandler}} />
<h3>Textareas</h3>
<form.textarea
@label="Label"
@value="Read only"
readonly={{true}}
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<Rose::Form::Input
@label="Label"
@value="Read only"
readonly={{true}}
/>
<Rose::Form::Input
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<Rose::Form::Input
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<Rose::Form::Input
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<h3>Selects</h3>
<form.select
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |select|
>
<select.option>Choose an option</select.option>
<select.option @value="value-1">Value 1</select.option>
<select.option @value="value-2">Value 2</select.option>
<select.option @value="value-3">Value 3</select.option>
</form.select>
<form.select
disabled={{true}}
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |select|
>
<select.option>Choose an option</select.option>
</form.select>
<Rose::Form::Textarea
@label="Label"
@value="Read only"
readonly={{true}}
/>
<h3>Radio Group</h3>
<form.radioGroup @name="color" @selectedValue={{this.color}} as |radioGroup|>
<radioGroup.radio
@id="red-radio"
@label="Red"
@value="red"
/>
<radioGroup.radio
@id="green-radio"
@label="Green"
@value="green"
/>
<radioGroup.radio
@id="blue-radio"
@label="Blue"
@value="blue"
@disabled={{true}}
/>
</form.radioGroup>
<Rose::Form::Textarea
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<h3>Checkboxes</h3>
<form.checkbox
@id="checkbox-1"
@label="Label"
/>
<form.checkbox
@id="checkbox-2"
@error={{true}}
@label="Error"
/>
<form.checkbox
@id="checkbox-3"
@checked={{true}}
@label="Checked"
/>
<form.checkbox
@id="checkbox-4"
@label="Disabled"
disabled={{true}}
/>
<form.checkbox
@id="checkbox-5"
@checked={{true}}
@label="Disabled + Checked"
disabled={{true}}
/>
<Rose::Form::Textarea
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<Rose::Form::Textarea
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<h2>Checkbox</h2>
<Rose::Form::Checkbox
@id="checkbox-1"
@label="Label"
/>
<Rose::Form::Checkbox
@id="checkbox-2"
@error={{true}}
@label="Error"
/>
<Rose::Form::Checkbox
@id="checkbox-3"
@checked={{true}}
@label="Checked"
/>
<Rose::Form::Checkbox
@id="checkbox-4"
@label="Disabled"
disabled={{true}}
/>
<Rose::Form::Checkbox
@id="checkbox-5"
@checked={{true}}
@label="Disabled + Checked"
disabled={{true}}
/>
<h2>Radio field</h2>
<Rose::Form::Radio::Radio
@id="radio-1"
@label="Label"
@value="radio-1"
/>
<Rose::Form::Radio::Radio
@id="radio-2"
@error={{true}}
@label="Error"
@value="radio-2"
/>
<Rose::Form::Radio::Radio
@id="radio-3"
@label="Checked"
/>
<Rose::Form::Radio::Radio
@id="radio-4"
@label="Disabled"
@disabled={{true}}
@value="radio-4"
/>
<Rose::Form::Radio::Radio
@id="radio-5"
@label="Disabled + Checked"
@value="radio-5"
@variable="radio-5"
@disabled={{true}}
/>
<h2>Radio field group</h2>
<Rose::Form::Radio::Group @name="color" @variable="green" as |radioGroup|>
<radioGroup.radio
@id="red-radio"
@label="Red"
@value="red"
/>
<radioGroup.radio
@id="green-radio"
@label="Green"
@value="green"
/>
<radioGroup.radio
@id="blue-radio"
@label="Blue"
@value="blue"
/>
</Rose::Form::Radio::Group>
<form.actions
@submitText="Save"
@cancelText="Cancel" />
</Rose::Form>
@@ -0,0 +1,61 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, find, click } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | rose/form', function (hooks) {
setupRenderingTest(hooks);
test('it renders and handles submit/cancel actions', async function (assert) {
assert.expect(3);
this.submit = () => assert.ok(true, 'submitted');
this.cancel = () => assert.ok(true, 'cancelled');
await render(hbs`
<Rose::Form
@onSubmit={{this.submit}}
@cancel={{this.cancel}}
@disabled={{false}}
as |form|
>
<form.actions
@submitText="Save"
@cancelText="Cancel" />
</Rose::Form>
`);
assert.ok(find('.rose-form'));
await click('[type="submit"]');
await click('button:not([type="submit"])');
});
test('it can render contextual form components', async function (assert) {
assert.expect(5);
this.submit = () => {};
this.cancel = () => {};
this.select = () => {};
await render(hbs`
<Rose::Form
@onSubmit={{this.submit}}
@cancel={{this.cancel}}
@disabled={{false}}
as |form|
>
<form.input @label="Input" name="input-field" />
<form.textarea @label="Textarea" name="textarea-field" />
<form.select @label="Select" name="select-field" @onChange={{this.select}} />
<form.checkbox @label="Checkbox" name="checkbox-field" />
<form.radioGroup @name="radio-group-field" @selectedValue="green" as |radioGroup|>
<radioGroup.radio
@id="red-radio"
@label="Red"
@value="red"
/>
</form.radioGroup>
</Rose::Form>
`);
assert.ok(find('[name="input-field"]'));
assert.ok(find('[name="textarea-field"]'));
assert.ok(find('[name="select-field"]'));
assert.ok(find('[name="checkbox-field"]'));
assert.ok(find('[name="radio-group-field"]'));
});
});
@@ -32,8 +32,8 @@ module('Integration | Component | rose/form/checkbox', function (hooks) {
assert.ok(await find('.error'));
});
test('it is disabled when disabled={{true}}', async function (assert) {
await render(hbs`<Rose::Form::Checkbox disabled={{true}} />`);
test('it is disabled when @disabled={{true}}', async function (assert) {
await render(hbs`<Rose::Form::Checkbox @disabled={{true}} />`);
assert.equal(await find('input').disabled, true);
});
});
@@ -31,7 +31,7 @@ module('Integration | Component | rose/form/input', function (hooks) {
});
test('it supports disabled attribute', async function (assert) {
await render(hbs`<Rose::Form::Input disabled={{true}} />`);
await render(hbs`<Rose::Form::Input @disabled={{true}} />`);
assert.ok(await find('[disabled]'));
});
@@ -48,7 +48,7 @@ module('Integration | Component | rose/form/select', function (hooks) {
await render(hbs`
<Rose::Form::Select
@onChange={{fn this.onChange}}
disabled={{true}}
@disabled={{true}}
as |select|
>
<select.option>Choose an option</select.option>
@@ -21,7 +21,7 @@ module('Integration | Component | rose/form/textarea', function (hooks) {
});
test('it supports disabled attribute', async function (assert) {
await render(hbs`<Rose::Form::Textarea disabled={{true}} />`);
await render(hbs`<Rose::Form::Textarea @disabled={{true}} />`);
assert.ok(await find('[disabled]'));
});