Skip to content

Commit

Permalink
feat(validated-button): yield customizable button (#705)
Browse files Browse the repository at this point in the history
* feat(validated-button): yield customizable button

* chore(deps): upgrade to fix CI issues with babel

* doc: delete legacy {{link-to}} ref

* test: rewrite spare tests to octane/glimmer syntax

* docs: glimmer syntax in form demo
  • Loading branch information
derrabauke authored Jan 5, 2022
1 parent a83c7bf commit 18d141e
Show file tree
Hide file tree
Showing 34 changed files with 2,668 additions and 1,104 deletions.
6 changes: 3 additions & 3 deletions addon/components/validated-button.hbs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{{#let
(component
this.buttonComponent
onClick=@action
loading=@loading
disabled=@disabled
onClick=this.click
loading=this.loading
disabled=(or @disabled this.loading)
label=@label
type=@type
)
Expand Down
48 changes: 48 additions & 0 deletions addon/components/validated-button.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,55 @@
import { action } from "@ember/object";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { resolve } from "rsvp";

import themedComponent from "../-private/themed-component";

const ON_CLICK = "on-click";
const ON_INVALID_CLICK = "on-invalid-click";
export default class ValidatedButtonComponent extends Component {
@themedComponent("validated-button/button") buttonComponent;

@tracked _loading;

get loading() {
return this.args.loading || this._loading;
}

@action
async click(event) {
// handle only clicks for custom buttons
// everything else is handled by the validated form itself
if (this.args.type !== "button") {
return this.args.action(event);
}

event.preventDefault();
const model = this.args.model;

if (!model || !model.validate) {
this.runCallback(ON_CLICK);
return;
}

await model.validate();

if (model.get("isInvalid")) {
this.runCallback(ON_INVALID_CLICK);
} else {
this.runCallback(ON_CLICK);
}
}

runCallback(callbackProp) {
const callback = this.args[callbackProp];
if (typeof callback !== "function") {
return;
}

this._loading = true;
resolve(callback(this.args.model)).finally(() => {
this._loading = false;
});
}
}
7 changes: 7 additions & 0 deletions addon/components/validated-form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
label="Save"
action=this.submit
)
button=(component
"validated-button"
type="button"
loading=this.loading
label="Action"
model=@model
)
)
}}
</form>
7 changes: 6 additions & 1 deletion addon/components/validated-form.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { action } from "@ember/object";
import { scheduleOnce } from "@ember/runloop";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { resolve } from "rsvp";
Expand All @@ -15,10 +16,14 @@ export default class ValidatedFormComponent extends Component {
super(...args);

if (this.args.model && this.args.model.validate) {
this.args.model.validate();
scheduleOnce("actions", this, "validateModel", this.args.model);
}
}

validateModel(model) {
model.validate();
}

@action
async submit(event) {
event.preventDefault();
Expand Down
2 changes: 1 addition & 1 deletion addon/components/validated-input/types/checkbox-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Component from "@glimmer/component";

export default class CheckboxGroupComponent extends Component {
@action
onUpdate(event, key) {
onUpdate(key, event) {
event.preventDefault();

const value = this.value || [];
Expand Down
25 changes: 15 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,16 @@
"ember-truth-helpers": "^3.0.0"
},
"devDependencies": {
"@adfinis-sygroup/eslint-config": "1.4.2",
"@adfinis-sygroup/semantic-release-config": "3.2.0",
"@babel/core": "7.15.8",
"@adfinis-sygroup/eslint-config": "1.5.0",
"@adfinis-sygroup/semantic-release-config": "3.2.1",
"@babel/core": "7.16.5",
"@babel/helper-create-regexp-features-plugin": "^7.16.7",
"@babel/helper-environment-visitor": "^7.16.5",
"@babel/plugin-proposal-decorators": "^7.16.7",
"@babel/plugin-transform-modules-amd": "^7.16.7",
"@babel/preset-env": "^7.16.7",
"@ember/optional-features": "2.0.0",
"@ember/test-helpers": "2.5.0",
"@ember/test-helpers": "2.6.0",
"@embroider/test-setup": "0.45.0",
"@fortawesome/ember-fontawesome": "0.2.3",
"@fortawesome/free-solid-svg-icons": "5.15.4",
Expand All @@ -60,7 +65,7 @@
"ember-changeset": "3.15.0",
"ember-changeset-validations": "3.16.0",
"ember-cli": "3.28.1",
"ember-cli-addon-docs": "4.0.3",
"ember-cli-addon-docs": "4.2.1",
"ember-cli-dependency-checker": "3.2.0",
"ember-cli-deploy": "1.0.2",
"ember-cli-deploy-build": "2.0.0",
Expand All @@ -71,7 +76,7 @@
"ember-cli-sri": "2.1.1",
"ember-cli-terser": "4.0.2",
"ember-cli-test-loader": "3.0.0",
"ember-concurrency": "2.1.2",
"ember-concurrency": "2.2.0",
"ember-data": "3.28.3",
"ember-disable-prototype-extensions": "1.1.3",
"ember-load-initializers": "2.1.2",
Expand All @@ -81,17 +86,17 @@
"ember-resolver": "8.0.3",
"ember-source": "3.28.1",
"ember-source-channel-url": "3.0.0",
"ember-template-lint": "3.9.0",
"ember-template-lint": "3.15.0",
"ember-template-lint-plugin-prettier": "2.0.1",
"ember-try": "1.4.0",
"eslint": "7.32.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-ember": "10.5.5",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-ember": "10.5.8",
"eslint-plugin-import": "2.25.3",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-qunit": "6.2.0",
"husky": "7.0.2",
"husky": "7.0.4",
"lint-staged": "11.2.0",
"loader.js": "4.7.0",
"npm-run-all": "4.1.5",
Expand Down
62 changes: 48 additions & 14 deletions tests/dummy/app/templates/docs/components/validated-button.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,66 @@
# Validated button

`{{validated-form}}` also yields a submit button component that can be
accessed with `{{f.submit}}`. You can also use it as a block style component
`{{#f.submit}}Test{{/f.submit}}` if you don't want to pass the label as a
property. It takes the following properties:
`{{validated-form}}` yields two kinds of button components:
- `{{f.submit}}`: a submit button for the form
- `{{f.button}}`: a customizable button without HTML-form specific functionality.

**label `<String>`**
You can use them as a block style component `{{#f.submit}}Test{{/f.submit}}` if you don't want to pass the label as a
property.

Both take the following properties:

**label `<String>`**
The label of the form button.

**type `<String>`**
Type of the button. Default: `button`.
**type `<String>`**
Type of the button. Default for submit: `submit` and for standard button: `button`.
*Watch out:* If you define `type=submit` then the `on-submit` handler of the form will be triggered.

**disabled `<Boolean>`**
**disabled `<Boolean>`**
Specifies if the button is disabled.

**loading `<Boolean>`**
**loading `<Boolean>`**
Specifies if the button is loading. Default: Automatic integration of `ember-concurrency`.

<!-- prettier-ignore-start -->
{{#docs-demo as |demo|}}
{{#demo.example name='button-template.hbs'}}
{{#validated-form on-submit=(action (mut saved) true) as |f|}}
{{f.submit label='Save'}}
{{#f.submit}}Still save but in block style...{{/f.submit}}
{{if saved 'Saved!'}}
{{/validated-form}}
<ValidatedForm @on-submit={{fn (mut saved) true}} as |f|>
{{#let f.submit as |Submit|}}
<Submit @label={{"Save"}} />
<Submit>Save button in block style...</Submit>
{{/let}}
{{if saved 'Saved!'}}
</ValidatedForm>
{{/demo.example}}

{{demo.snippet 'button-template.hbs'}}
{{/docs-demo}}
<!-- prettier-ignore-end -->


Further you can leverage the `{{f.button}}` component for custom actions. The model of the wrapping form component will get passed to the on-click handler as first argument.

Passing a custom on click function is possible on the `{{f.buttton}}` via:

**on-click `<Function>`**
Passes an on-click function to the button component.

**on-invalid-click `<Function>`**
Passes a function which is triggered after clicking on the button and when the validation proved the contents to be invalid.

<!-- prettier-ignore-start -->
{{#docs-demo as |demo|}}
{{#demo.example name='button-advanced-template.hbs'}}
<ValidatedForm as |f|>
{{#let f.button as |CustomButton|}}
<CustomButton @label="Real Custom" @on-click={{fn (mut triggered) true}}/>
<CustomButton @on-click={{fn (mut triggered) true}}>Custom action button in block style...</CustomButton>
{{/let}}
{{if triggered 'Action triggered!'}}
</ValidatedForm>
{{/demo.example}}

{{demo.snippet 'button-advanced-template.hbs'}}
{{/docs-demo}}
<!-- prettier-ignore-end -->
16 changes: 10 additions & 6 deletions tests/dummy/app/templates/docs/components/validated-form.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ the `loading` template parameter:
<!-- prettier-ignore-start -->
{{#docs-demo as |demo|}}
{{#demo.example name='validated-form-action-template.hbs'}}
{{#validated-form model=(changeset model) on-submit=(action 'submit') as |f|}}
{{f.submit label=(if f.loading 'Saving...' 'Save') disabled=f.loading}}
{{/validated-form}}
<ValidatedForm @model={{changeset model}} @on-submit={{this.submit}} as |f|>
{{#let f.submit as |Submit|}}
<Submit @label={{if f.loading 'Saving...' 'Save'}} @disabled={{f.loading}}/>
{{/let}}
</ValidatedForm>
{{/demo.example}}

{{demo.snippet 'validated-form-action-template.hbs'}}
Expand All @@ -47,9 +49,11 @@ It also works very well with [ember-concurrency](http://ember-concurrency.com/)
<!-- prettier-ignore-start -->
{{#docs-demo as |demo|}}
{{#demo.example name='validated-form-task-template.hbs'}}
{{#validated-form model=(changeset model) on-submit=(perform submit) as |f|}}
{{f.submit label=(if f.loading 'Saving...' 'Save') disabled=f.loading}}
{{/validated-form}}
<ValidatedForm @model={{changeset model}} @on-submit={{perform this.submit}} as |f|>
{{#let f.submit as |Submit|}}
<Submit @label={{if f.loading 'Saving...' 'Save'}} @disabled={{f.loading}}/>
{{/let}}
</ValidatedForm>
{{/demo.example}}

{{demo.snippet 'validated-form-task-template.hbs'}}
Expand Down
3 changes: 1 addition & 2 deletions tests/dummy/app/templates/docs/quickstart.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Quickstart

You'll find a basic example in [this
twiddle](https://ember-twiddle.com/95b040c96b7dc60dc4d0bb2dc5f5de26?openFiles=templates.application.hbs%2C) or
{{link-to 'here' 'index'}}.
twiddle](https://ember-twiddle.com/95b040c96b7dc60dc4d0bb2dc5f5de26?openFiles=templates.application.hbs%2C)
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module("Integration | Component | validated form defaults", function (hooks) {
`
);

this.model = { error: { test1: { validation: ["Error"] } } };
this.set("model", { error: { test1: { validation: ["Error"] } } });

await render(hbs`
<ValidatedForm as |f|>
Expand Down
Loading

0 comments on commit 18d141e

Please sign in to comment.