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

feat(validated-button): yield customizable button #705

Merged
merged 6 commits into from
Jan 5, 2022
Merged
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
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