ember-validated-form 5.0.0 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.husky/commit-msg CHANGED
@@ -1 +1,7 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ # skip in CI
5
+ [ -n "$CI" ] && exit 0
6
+
1
7
  yarn commitlint --edit $1
@@ -0,0 +1,8 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ # skip in CI
5
+ [ -n "$CI" ] && exit 0
6
+
7
+ # lint only staged files
8
+ yarn lint-staged
package/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [5.2.1](https://github.com/adfinis-sygroup/ember-validated-form/compare/v5.2.0...v5.2.1) (2022-02-09)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * respect scrollErrorIntoView for validated buttons as well ([#743](https://github.com/adfinis-sygroup/ember-validated-form/issues/743)) ([fd6be2a](https://github.com/adfinis-sygroup/ember-validated-form/commit/fd6be2a49c5f947bfcf5eb3f7ab61a23ac00064a))
7
+
8
+ # [5.2.0](https://github.com/adfinis-sygroup/ember-validated-form/compare/v5.1.1...v5.2.0) (2022-02-03)
9
+
10
+
11
+ ### Features
12
+
13
+ * scroll first invalid element into view ([#733](https://github.com/adfinis-sygroup/ember-validated-form/issues/733)) ([ae7c8b2](https://github.com/adfinis-sygroup/ember-validated-form/commit/ae7c8b2b160307646adf90cd09a091effa549238))
14
+ * **validated-button:** add `triggerValidations` flag ([#721](https://github.com/adfinis-sygroup/ember-validated-form/issues/721)) ([765f5f4](https://github.com/adfinis-sygroup/ember-validated-form/commit/765f5f40c9d2e5ccfca7129fb701ff3bb0ed661e))
15
+
1
16
  # [5.0.0](https://github.com/adfinis-sygroup/ember-validated-form/compare/v4.1.0...v5.0.0) (2021-10-08)
2
17
 
3
18
 
@@ -1,9 +1,9 @@
1
1
  {{#let
2
2
  (component
3
3
  this.buttonComponent
4
- onClick=@action
5
- loading=@loading
6
- disabled=@disabled
4
+ onClick=this.click
5
+ loading=this.loading
6
+ disabled=(or @disabled this.loading)
7
7
  label=@label
8
8
  type=@type
9
9
  )
@@ -1,7 +1,75 @@
1
+ import { getOwner } from "@ember/application";
2
+ import { action } from "@ember/object";
1
3
  import Component from "@glimmer/component";
4
+ import { tracked } from "@glimmer/tracking";
5
+ import { resolve } from "rsvp";
2
6
 
3
7
  import themedComponent from "../-private/themed-component";
4
8
 
9
+ const ON_CLICK = "on-click";
10
+ const ON_INVALID_CLICK = "on-invalid-click";
5
11
  export default class ValidatedButtonComponent extends Component {
6
12
  @themedComponent("validated-button/button") buttonComponent;
13
+
14
+ @tracked _loading;
15
+
16
+ constructor(...args) {
17
+ super(...args);
18
+ this.config =
19
+ getOwner(this).resolveRegistration("config:environment")[
20
+ "ember-validated-form"
21
+ ];
22
+ }
23
+
24
+ get loading() {
25
+ return this.args.loading || this._loading;
26
+ }
27
+
28
+ @action
29
+ async click(event) {
30
+ // handle only clicks for custom buttons
31
+ // everything else is handled by the validated form itself
32
+ if (this.args.type !== "button") {
33
+ return this.args.action(event);
34
+ }
35
+
36
+ event.preventDefault();
37
+
38
+ if (this.args.triggerValidations) {
39
+ this.args.markAsDirty();
40
+ }
41
+
42
+ const model = this.args.model;
43
+
44
+ if (!model || !model.validate) {
45
+ this.runCallback(ON_CLICK);
46
+ return;
47
+ }
48
+
49
+ await model.validate();
50
+
51
+ if (this.config?.features?.scrollErrorIntoView && model.errors[0]?.key) {
52
+ document
53
+ .querySelector(`[name=${model.errors[0].key}]`)
54
+ ?.scrollIntoView({ behavior: "smooth" });
55
+ }
56
+
57
+ if (model.get("isInvalid")) {
58
+ this.runCallback(ON_INVALID_CLICK);
59
+ } else {
60
+ this.runCallback(ON_CLICK);
61
+ }
62
+ }
63
+
64
+ runCallback(callbackProp) {
65
+ const callback = this.args[callbackProp];
66
+ if (typeof callback !== "function") {
67
+ return;
68
+ }
69
+
70
+ this._loading = true;
71
+ resolve(callback(this.args.model)).finally(() => {
72
+ this._loading = false;
73
+ });
74
+ }
7
75
  }
@@ -16,6 +16,14 @@
16
16
  label="Save"
17
17
  action=this.submit
18
18
  )
19
+ button=(component
20
+ "validated-button"
21
+ type="button"
22
+ loading=this.loading
23
+ label="Action"
24
+ model=@model
25
+ markAsDirty=this.markAsDirty
26
+ )
19
27
  )
20
28
  }}
21
29
  </form>
@@ -1,4 +1,6 @@
1
+ import { getOwner } from "@ember/application";
1
2
  import { action } from "@ember/object";
3
+ import { scheduleOnce } from "@ember/runloop";
2
4
  import Component from "@glimmer/component";
3
5
  import { tracked } from "@glimmer/tracking";
4
6
  import { resolve } from "rsvp";
@@ -13,12 +15,25 @@ export default class ValidatedFormComponent extends Component {
13
15
 
14
16
  constructor(...args) {
15
17
  super(...args);
18
+ this.config =
19
+ getOwner(this).resolveRegistration("config:environment")[
20
+ "ember-validated-form"
21
+ ];
16
22
 
17
23
  if (this.args.model && this.args.model.validate) {
18
- this.args.model.validate();
24
+ scheduleOnce("actions", this, "validateModel", this.args.model);
19
25
  }
20
26
  }
21
27
 
28
+ validateModel(model) {
29
+ model.validate();
30
+ }
31
+
32
+ @action
33
+ markAsDirty() {
34
+ this.submitted = true;
35
+ }
36
+
22
37
  @action
23
38
  async submit(event) {
24
39
  event.preventDefault();
@@ -34,6 +49,11 @@ export default class ValidatedFormComponent extends Component {
34
49
  await model.validate();
35
50
 
36
51
  if (model.get("isInvalid")) {
52
+ if (this.config?.features?.scrollErrorIntoView && model.errors[0]?.key) {
53
+ document
54
+ .querySelector(`[name=${model.errors[0].key}]`)
55
+ ?.scrollIntoView({ behavior: "smooth" });
56
+ }
37
57
  this.runCallback(PROP_ON_INVALID_SUBMIT);
38
58
  } else {
39
59
  this.runCallback(PROP_ON_SUBMIT);
@@ -3,8 +3,8 @@
3
3
  <input
4
4
  type="checkbox"
5
5
  class="custom-control-input
6
- {{if @isValid "is-valid"}}
7
- {{if @isInvalid "is-invalid"}}"
6
+ {{if @isValid 'is-valid'}}
7
+ {{if @isInvalid 'is-invalid'}}"
8
8
  checked={{includes option.key @value}}
9
9
  name={{@name}}
10
10
  id="{{@inputId}}-{{i}}"
@@ -1,8 +1,8 @@
1
1
  <div class="custom-control custom-checkbox">
2
2
  <input
3
3
  class="custom-control-input
4
- {{if @isValid "is-valid"}}
5
- {{if @isInvalid "is-invalid"}}"
4
+ {{if @isValid 'is-valid'}}
5
+ {{if @isInvalid 'is-invalid'}}"
6
6
  type="checkbox"
7
7
  name={{@name}}
8
8
  id={{@inputId}}
@@ -3,8 +3,8 @@
3
3
  <input
4
4
  type="radio"
5
5
  class="custom-control-input
6
- {{if @isValid "is-valid"}}
7
- {{if @isInvalid "is-invalid"}}"
6
+ {{if @isValid 'is-valid'}}
7
+ {{if @isInvalid 'is-invalid'}}"
8
8
  checked={{eq @value option.key}}
9
9
  value={{option.key}}
10
10
  name={{@name}}
@@ -2,8 +2,8 @@
2
2
  {{#if (not-eq i 0)}}<br />{{/if}}
3
3
  <label
4
4
  class="uk-form-label
5
- {{if @isValid "uk-text-success"}}
6
- {{if @isInvalid "uk-text-danger"}}"
5
+ {{if @isValid 'uk-text-success'}}
6
+ {{if @isInvalid 'uk-text-danger'}}"
7
7
  >
8
8
  <input
9
9
  type="checkbox"
@@ -1,6 +1,6 @@
1
1
  {{#let (component @labelComponent) as |Label|}}
2
2
  <Label
3
- class="{{if @isValid "uk-text-success"}} {{if @isInvalid "uk-text-danger"}}"
3
+ class="{{if @isValid 'uk-text-success'}} {{if @isInvalid 'uk-text-danger'}}"
4
4
  >
5
5
  <input
6
6
  class="uk-checkbox uk-margin-small-right"
@@ -2,8 +2,8 @@
2
2
  {{#if (not-eq i 0)}}<br />{{/if}}
3
3
  <label
4
4
  class="uk-form-label
5
- {{if @isValid "uk-text-success"}}
6
- {{if @isInvalid "uk-text-danger"}}"
5
+ {{if @isValid 'uk-text-success'}}
6
+ {{if @isInvalid 'uk-text-danger'}}"
7
7
  >
8
8
  <input
9
9
  type="radio"
@@ -3,7 +3,7 @@ import Component from "@glimmer/component";
3
3
 
4
4
  export default class CheckboxGroupComponent extends Component {
5
5
  @action
6
- onUpdate(event, key) {
6
+ onUpdate(key, event) {
7
7
  event.preventDefault();
8
8
 
9
9
  const value = this.value || [];
@@ -1,17 +1,72 @@
1
- {{! template-lint-disable no-passed-in-event-handlers }}
2
- <OneWaySelect
3
- @value={{@value}}
4
- @options={{@options}}
5
- @optionLabelPath={{@optionLabelPath}}
6
- @optionValuePath={{@optionValuePath}}
7
- @optionTargetPath={{@optionTargetPath}}
8
- @name={{@name}}
9
- @id={{@inputId}}
10
- @class={{this.class}}
11
- @update={{@update}}
12
- @focusOut={{@setDirty}}
13
- @includeBlank={{@includeBlank}}
14
- @disabled={{@disabled}}
15
- @multiple={{@multiple}}
16
- @promptIsSelectable={{or @promptIsSelectable false}}
17
- />
1
+ <select
2
+ class={{this.class}}
3
+ ...attributes
4
+ name={{@name}}
5
+ id={{@inpudId}}
6
+ disabled={{@disabled}}
7
+ multiple={{@multiple}}
8
+ {{on "change" this.onUpdate}}
9
+ {{on "blur" this.onBlur}}
10
+ >
11
+ {{#if (or @prompt @includeBlank)}}
12
+ <option
13
+ value=""
14
+ disabled={{not @promptIsSelectable}}
15
+ selected={{not @value}}
16
+ >{{or @prompt @includeBlank}}</option>
17
+ {{/if}}
18
+
19
+ {{#if this.hasGrouping}}
20
+ {{#each this.optionGroups as |optionGroup|}}
21
+ <optgroup label={{optionGroup.groupName}}>
22
+ {{#each optionGroup.options as |opt|}}
23
+ {{#let
24
+ (if
25
+ (or @optionValuePath @optionTargetPath)
26
+ (get opt (or @optionValuePath @optionTargetPath))
27
+ opt
28
+ )
29
+ as |optionValue|
30
+ }}
31
+ <option
32
+ selected={{eq optionValue @value}}
33
+ value={{optionValue}}
34
+ >{{#if @optionLabelPath}}
35
+ {{get opt @optionLabelPath}}
36
+ {{else if @optionValuePath}}
37
+ {{get opt @optionValuePath}}
38
+ {{else if @optionTargetPath}}
39
+ {{get opt @optionTargetPath}}
40
+ {{else}}
41
+ {{opt}}
42
+ {{/if}}
43
+ </option>
44
+ {{/let}}
45
+ {{/each}}
46
+ </optgroup>
47
+ {{/each}}
48
+ {{else}}
49
+ {{#each @options as |opt|}}
50
+ {{#let
51
+ (if
52
+ (or @optionValuePath @optionTargetPath)
53
+ (get opt (or @optionValuePath @optionTargetPath))
54
+ opt
55
+ )
56
+ as |optionValue|
57
+ }}
58
+ <option selected={{eq optionValue @value}} value={{optionValue}}>{{#if
59
+ @optionLabelPath
60
+ }}
61
+ {{get opt @optionLabelPath}}
62
+ {{else if @optionValuePath}}
63
+ {{get opt @optionValuePath}}
64
+ {{else if @optionTargetPath}}
65
+ {{get opt @optionTargetPath}}
66
+ {{else}}
67
+ {{opt}}
68
+ {{/if}}</option>
69
+ {{/let}}
70
+ {{/each}}
71
+ {{/if}}
72
+ </select>
@@ -1,4 +1,120 @@
1
+ import { A as emberArray, isArray } from "@ember/array";
2
+ import { deprecate } from "@ember/debug";
3
+ import EmberObject, { action, get } from "@ember/object";
1
4
  import Component from "@glimmer/component";
2
5
 
3
- // eslint-disable-next-line ember/no-empty-glimmer-component-classes
4
- export default class SelectComponent extends Component {}
6
+ /**
7
+ * This component is heavily inspired by the ember-one-way-select addon.
8
+ * https://github.com/DockYard/ember-one-way-select
9
+ * Our implementation is slightly simpler, since it does not support
10
+ * the block syntax for options.
11
+ */
12
+
13
+ export default class SelectComponent extends Component {
14
+ constructor(...args) {
15
+ super(...args);
16
+
17
+ if (this.args.includeBlank) {
18
+ deprecate(
19
+ "The `includeBlank` property is deprecated. Please use `prompt` instead.",
20
+ false,
21
+ {
22
+ id: "ember-validated-form:NoMoreincludeBlank",
23
+ until: "6.0.0",
24
+ since: "5.1.0",
25
+ url: "https://github.com/adfinis-sygroup/ember-validated-form/releases/tag/v5.1.0",
26
+ }
27
+ );
28
+ }
29
+ }
30
+
31
+ get hasPreGroupedOptions() {
32
+ return (
33
+ this.args.options[0]?.groupName && isArray(this.args.options[0]?.options)
34
+ );
35
+ }
36
+
37
+ get hasGrouping() {
38
+ return this.hasPreGroupedOptions || this.args.groupLabelPath;
39
+ }
40
+
41
+ get optionGroups() {
42
+ const groupLabelPath = this.args.groupLabelPath;
43
+ if (!groupLabelPath) {
44
+ return this.args.options;
45
+ }
46
+ const groups = emberArray();
47
+
48
+ this.args.options.forEach((item) => {
49
+ const label = get(item, groupLabelPath);
50
+
51
+ if (label) {
52
+ let group = groups.findBy("groupName", label);
53
+
54
+ if (!group) {
55
+ group = EmberObject.create({
56
+ groupName: label,
57
+ options: emberArray(),
58
+ });
59
+
60
+ groups.pushObject(group);
61
+ }
62
+
63
+ group.options.pushObject(item);
64
+ } else {
65
+ groups.pushObject(item);
66
+ }
67
+ });
68
+
69
+ return groups;
70
+ }
71
+
72
+ findOption(target) {
73
+ const targetPath = this.args.optionTargetPath;
74
+ const valuePath = this.args.optionValuePath || targetPath;
75
+ let options = this.args.options;
76
+
77
+ //flatten pre grouped options
78
+ if (this.hasPreGroupedOptions) {
79
+ options = options.flatMap((group) => group.options);
80
+ }
81
+
82
+ // multi select
83
+ if (this.args.multiple) {
84
+ const selectedValues = Array.prototype.filter
85
+ .call(target.options, (option) => option.selected)
86
+ .map((option) => option.value);
87
+
88
+ const foundOptions = options.filter((item) =>
89
+ selectedValues.includes(`${valuePath ? item[valuePath] : item}`)
90
+ );
91
+ if (targetPath) {
92
+ return foundOptions.map((item) => item[targetPath]);
93
+ }
94
+ return foundOptions;
95
+ }
96
+
97
+ //single select
98
+ const foundOption = options.find(
99
+ (item) => `${valuePath ? item[valuePath] : item.value}` === target.value
100
+ );
101
+ if (targetPath) {
102
+ return foundOption[targetPath];
103
+ }
104
+ return foundOption;
105
+ }
106
+
107
+ @action
108
+ onUpdate(event) {
109
+ if (this.args.update) {
110
+ this.args.update(this.findOption(event.target));
111
+ }
112
+ }
113
+
114
+ @action
115
+ onBlur(event) {
116
+ if (this.args.setDirty) {
117
+ this.args.setDirty(event.target.value);
118
+ }
119
+ }
120
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-validated-form",
3
- "version": "5.0.0",
3
+ "version": "5.2.1",
4
4
  "description": "Easily create forms with client-side validations",
5
5
  "keywords": [
6
6
  "ember-addon",
@@ -29,27 +29,26 @@
29
29
  "start": "ember serve",
30
30
  "test": "npm-run-all lint test:*",
31
31
  "test:ember": "ember test",
32
- "test:ember-compatibility": "ember try:each"
33
- },
34
- "lint-staged": {
35
- "*.{js,json,css,md}": [
36
- "prettier --write",
37
- "git add"
38
- ]
32
+ "test:ember-compatibility": "ember try:each",
33
+ "prepare": "husky install"
39
34
  },
40
35
  "dependencies": {
41
- "ember-auto-import": "^2.2.0",
36
+ "ember-auto-import": "^2.4.0",
42
37
  "ember-cli-babel": "^7.26.6",
43
38
  "ember-cli-htmlbars": "^5.7.1",
44
- "ember-one-way-select": "^4.0.1",
45
39
  "ember-truth-helpers": "^3.0.0"
46
40
  },
47
41
  "devDependencies": {
48
- "@adfinis-sygroup/eslint-config": "1.4.2",
49
- "@adfinis-sygroup/semantic-release-config": "3.2.0",
50
- "@babel/core": "7.15.8",
42
+ "@adfinis-sygroup/eslint-config": "1.5.0",
43
+ "@adfinis-sygroup/semantic-release-config": "3.4.0",
44
+ "@babel/core": "7.16.12",
45
+ "@babel/helper-create-regexp-features-plugin": "^7.16.7",
46
+ "@babel/helper-environment-visitor": "^7.16.7",
47
+ "@babel/plugin-proposal-decorators": "^7.16.7",
48
+ "@babel/plugin-transform-modules-amd": "^7.16.7",
49
+ "@babel/preset-env": "^7.16.11",
51
50
  "@ember/optional-features": "2.0.0",
52
- "@ember/test-helpers": "2.5.0",
51
+ "@ember/test-helpers": "2.6.0",
53
52
  "@embroider/test-setup": "0.45.0",
54
53
  "@fortawesome/ember-fontawesome": "0.2.3",
55
54
  "@fortawesome/free-solid-svg-icons": "5.15.4",
@@ -57,10 +56,10 @@
57
56
  "@glimmer/tracking": "1.0.4",
58
57
  "babel-eslint": "10.1.0",
59
58
  "broccoli-asset-rev": "3.0.0",
60
- "ember-changeset": "3.15.0",
61
- "ember-changeset-validations": "3.16.0",
59
+ "ember-changeset": "4.0.0-beta.2",
60
+ "ember-changeset-validations": "4.0.0-beta.2",
62
61
  "ember-cli": "3.28.1",
63
- "ember-cli-addon-docs": "4.0.3",
62
+ "ember-cli-addon-docs": "4.2.1",
64
63
  "ember-cli-dependency-checker": "3.2.0",
65
64
  "ember-cli-deploy": "1.0.2",
66
65
  "ember-cli-deploy-build": "2.0.0",
@@ -71,37 +70,40 @@
71
70
  "ember-cli-sri": "2.1.1",
72
71
  "ember-cli-terser": "4.0.2",
73
72
  "ember-cli-test-loader": "3.0.0",
74
- "ember-concurrency": "2.1.2",
73
+ "ember-concurrency": "2.2.0",
75
74
  "ember-data": "3.28.3",
76
75
  "ember-disable-prototype-extensions": "1.1.3",
77
76
  "ember-load-initializers": "2.1.2",
78
77
  "ember-maybe-import-regenerator": "1.0.0",
79
- "ember-power-select": "4.1.6",
78
+ "ember-power-select": "5.0.3",
80
79
  "ember-qunit": "5.1.5",
81
80
  "ember-resolver": "8.0.3",
82
81
  "ember-source": "3.28.1",
83
82
  "ember-source-channel-url": "3.0.0",
84
- "ember-template-lint": "3.9.0",
83
+ "ember-template-lint": "3.15.0",
85
84
  "ember-template-lint-plugin-prettier": "2.0.1",
86
85
  "ember-try": "1.4.0",
87
86
  "eslint": "7.32.0",
88
87
  "eslint-config-prettier": "8.3.0",
89
- "eslint-plugin-ember": "10.5.5",
90
- "eslint-plugin-import": "2.24.2",
88
+ "eslint-plugin-ember": "10.5.8",
89
+ "eslint-plugin-import": "2.25.4",
91
90
  "eslint-plugin-node": "11.1.0",
92
91
  "eslint-plugin-prettier": "4.0.0",
93
92
  "eslint-plugin-qunit": "6.2.0",
94
- "husky": "7.0.2",
93
+ "husky": "7.0.4",
95
94
  "lint-staged": "11.2.0",
96
95
  "loader.js": "4.7.0",
97
96
  "npm-run-all": "4.1.5",
98
- "prettier": "2.4.1",
97
+ "prettier": "2.5.1",
99
98
  "qunit": "2.17.2",
100
99
  "qunit-dom": "2.0.0",
101
- "webpack": "5.58.0"
100
+ "webpack": "5.67.0"
102
101
  },
103
102
  "resolutions": {
104
- "graceful-fs": ">=4.2.0"
103
+ "graceful-fs": ">=4.2.0",
104
+ "@embroider/macros": "^1.0.0",
105
+ "@embroider/core": "^1.0.0",
106
+ "@embroider/util": "^1.0.0"
105
107
  },
106
108
  "engines": {
107
109
  "node": "12.* || 14.* || >= 16"
@@ -116,6 +118,11 @@
116
118
  "release": {
117
119
  "extends": "@adfinis-sygroup/semantic-release-config"
118
120
  },
121
+ "lint-staged": {
122
+ "*.js": "eslint --cache --fix",
123
+ "*.hbs": "ember-template-lint --fix",
124
+ "*.{scss,graphql,json,md,yml}": "prettier --write"
125
+ },
119
126
  "commitlint": {
120
127
  "extends": [
121
128
  "@commitlint/config-conventional"
package/.husky/precommit DELETED
@@ -1 +0,0 @@
1
- yarn lint-staged