Skip to content

Commit

Permalink
feat(validated-button): add triggerValidations flag (#721)
Browse files Browse the repository at this point in the history
  • Loading branch information
czosel committed Jan 6, 2022
1 parent 1520511 commit 765f5f4
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 44 deletions.
5 changes: 5 additions & 0 deletions addon/components/validated-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export default class ValidatedButtonComponent extends Component {
}

event.preventDefault();

if (this.args.triggerValidations) {
this.args.markAsDirty();
}

const model = this.args.model;

if (!model || !model.validate) {
Expand Down
1 change: 1 addition & 0 deletions addon/components/validated-form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
loading=this.loading
label="Action"
model=@model
markAsDirty=this.markAsDirty
)
)
}}
Expand Down
5 changes: 5 additions & 0 deletions addon/components/validated-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export default class ValidatedFormComponent extends Component {
model.validate();
}

@action
markAsDirty() {
this.submitted = true;
}

@action
async submit(event) {
event.preventDefault();
Expand Down
11 changes: 7 additions & 4 deletions tests/dummy/app/templates/docs/components/validated-button.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Validated button

`{{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.

Expand All @@ -14,7 +15,7 @@ The label of the form 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.
_Watch out:_ If you define `type=submit` then the `on-submit` handler of the form will be triggered.

**disabled `<Boolean>`**
Specifies if the button is disabled.
Expand All @@ -38,17 +39,19 @@ Specifies if the button is loading. Default: Automatic integration of `ember-con
{{/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:
Custom buttons support the following additional options:

**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.

**triggerValidations `<Boolean>`**
Trigger the form validations when the button is clicked (or, more precisely: show all error messages).

<!-- prettier-ignore-start -->
{{#docs-demo as |demo|}}
{{#demo.example name='button-advanced-template.hbs'}}
Expand All @@ -63,4 +66,4 @@ Passes a function which is triggered after clicking on the button and when the v

{{demo.snippet 'button-advanced-template.hbs'}}
{{/docs-demo}}
<!-- prettier-ignore-end -->
<!-- prettier-ignore-end -->
87 changes: 47 additions & 40 deletions tests/integration/components/validated-form-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import EmberObject from "@ember/object";
import { run } from "@ember/runloop";
import { render, click, blur, focus } from "@ember/test-helpers";
import { render, click, blur, fillIn, focus } from "@ember/test-helpers";
import UserValidations from "dummy/validations/user";
import { validateLength } from "ember-changeset-validations/validators";
import { setupRenderingTest } from "ember-qunit";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit";
Expand Down Expand Up @@ -144,6 +145,43 @@ module("Integration | Component | validated form", function (hooks) {
.hasText("First name must be between 3 and 40 characters");
});

test("it shows error message for custom buttons if (and only if) triggerValidations is passed", async function (assert) {
this.set("UserValidations", UserValidations);

run(() => {
this.set(
"model",
EmberObject.create({
firstName: "x",
})
);
});

this.set("triggerValidations", false);

await render(hbs`
<ValidatedForm
@model={{changeset this.model this.UserValidations}}
as |f|>
<f.input @label="First name" @name="firstName"/>
<f.button @label="Hello" @triggerValidations={{this.triggerValidations}} />
</ValidatedForm>
`);

assert.dom("span.invalid-feedback").doesNotExist();
await click("button");
assert.dom("span.invalid-feedback").doesNotExist();

this.set("triggerValidations", true);
await click("button");

assert.dom("input").hasValue("x");
assert.dom("span.invalid-feedback").exists({ count: 1 });
assert
.dom("span.invalid-feedback")
.hasText("First name must be between 3 and 40 characters");
});

test("it calls on-invalid-submit after submit if changeset is invalid", async function (assert) {
let invalidSubmitCalled;
this.set("invalidSubmit", function () {
Expand Down Expand Up @@ -206,43 +244,8 @@ module("Integration | Component | validated form", function (hooks) {
assert.true(submitCalled);
});

test("it performs validation and calls onClick function on custom buttons", async function (assert) {
assert.expect(3);

this.set("onClick", function (model) {
assert.step("onClick");
assert.strictEqual(model.firstName, "xenia");
});
this.set("onInvalidClick", function () {
assert.step("onInvalidClick");
});

run(() => {
this.set(
"model",
EmberObject.create({
firstName: "xenia",
})
);
});

await render(hbs`
<ValidatedForm
@model={{this.model}}
as |f|
>
<f.input @label="First name" @name="firstName"/>
<f.button @on-click={{this.onClick}} @on-invalid-click={{this.onInvalidClick}}/>
</ValidatedForm>
`);

await click("button");

assert.verifySteps(["onClick"]);
});

test("it performs validation and calls onInvalidClick function on custom buttons", async function (assert) {
assert.expect(3);
assert.expect(5);

this.set("onClick", function () {
assert.step("onClick");
Expand All @@ -251,7 +254,9 @@ module("Integration | Component | validated form", function (hooks) {
assert.step("onInvalidClick");
assert.strictEqual(model.firstName, "x");
});
this.set("UserValidations", UserValidations);
this.set("SimpleValidations", {
firstName: [validateLength({ min: 3, max: 40 })],
});

run(() => {
this.set(
Expand All @@ -264,7 +269,7 @@ module("Integration | Component | validated form", function (hooks) {

await render(hbs`
<ValidatedForm
@model={{changeset this.model this.UserValidations}}
@model={{changeset this.model this.SimpleValidations}}
as |f|
>
<f.input @label="First name" @name="firstName"/>
Expand All @@ -273,8 +278,10 @@ module("Integration | Component | validated form", function (hooks) {
`);

await click("button");

assert.verifySteps(["onInvalidClick"]);
await fillIn("input[name=firstName]", "Some name");
await click("button");
assert.verifySteps(["onClick"]);
});

test("it performs basic validations on focus out", async function (assert) {
Expand Down

0 comments on commit 765f5f4

Please sign in to comment.