├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .template-lintrc.js ├── .watchmanconfig ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── RELEASE.md ├── addon ├── .gitkeep └── components │ ├── bs-form.js │ └── bs-form │ └── element.js ├── app ├── .gitkeep └── components │ ├── bs-form.js │ └── bs-form │ └── element.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── renovate.json ├── testem.js ├── tests ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── application.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ └── application.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ └── robots.txt ├── helpers │ └── .gitkeep ├── index.html ├── integration │ ├── .gitkeep │ └── components │ │ └── bs-form-element-test.js ├── test-helper.js └── unit │ └── .gitkeep ├── vendor └── .gitkeep └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .*/ 17 | .eslintcache 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /npm-shrinkwrap.json.ember-try 23 | /package.json.ember-try 24 | /package-lock.json.ember-try 25 | /yarn.lock.ember-try 26 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true, 11 | }, 12 | }, 13 | plugins: ['ember'], 14 | extends: [ 15 | 'eslint:recommended', 16 | 'plugin:ember/recommended', 17 | 'plugin:prettier/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | }, 22 | rules: {}, 23 | overrides: [ 24 | // node files 25 | { 26 | files: [ 27 | './.eslintrc.js', 28 | './.prettierrc.js', 29 | './.template-lintrc.js', 30 | './ember-cli-build.js', 31 | './index.js', 32 | './testem.js', 33 | './blueprints/*/index.js', 34 | './config/**/*.js', 35 | './tests/dummy/config/**/*.js', 36 | ], 37 | parserOptions: { 38 | sourceType: 'script', 39 | }, 40 | env: { 41 | browser: false, 42 | node: true, 43 | }, 44 | plugins: ['node'], 45 | extends: ['plugin:node/recommended'], 46 | }, 47 | { 48 | // test files 49 | files: ['tests/**/*-test.{js,ts}'], 50 | extends: ['plugin:qunit/recommended'], 51 | }, 52 | ], 53 | }; 54 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | - renovate/* 9 | pull_request: {} 10 | 11 | concurrency: 12 | group: ci-${{ github.head_ref || github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | test: 17 | name: "Tests" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Install Node 23 | uses: actions/setup-node@v2 24 | with: 25 | node-version: 12.x 26 | cache: yarn 27 | - name: Install Dependencies 28 | run: yarn install --frozen-lockfile 29 | - name: Lint 30 | run: yarn lint 31 | - name: Run Tests 32 | run: yarn test:ember 33 | 34 | floating: 35 | name: "Floating Dependencies" 36 | runs-on: ubuntu-latest 37 | 38 | steps: 39 | - uses: actions/checkout@v3 40 | - uses: actions/setup-node@v2 41 | with: 42 | node-version: 12.x 43 | cache: yarn 44 | - name: Install Dependencies 45 | run: yarn install --no-lockfile 46 | - name: Run Tests 47 | run: yarn test:ember 48 | 49 | try-scenarios: 50 | name: ${{ matrix.try-scenario }} 51 | runs-on: ubuntu-latest 52 | needs: 'test' 53 | 54 | strategy: 55 | fail-fast: false 56 | matrix: 57 | try-scenario: 58 | - ember-lts-3.24 59 | - ember-lts-3.28 60 | - ember-release 61 | - ember-beta 62 | - ember-canary 63 | - ember-classic 64 | - embroider-safe 65 | - embroider-optimized 66 | 67 | steps: 68 | - uses: actions/checkout@v3 69 | - name: Install Node 70 | uses: actions/setup-node@v2 71 | with: 72 | node-version: 12.x 73 | cache: yarn 74 | - name: Install Dependencies 75 | run: yarn install --frozen-lockfile 76 | - name: Run Tests 77 | run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }} 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /.eslintcache 16 | /connect.lock 17 | /coverage/ 18 | /libpeerconnection.log 19 | /npm-debug.log* 20 | /testem.log 21 | /yarn-error.log 22 | 23 | # ember-try 24 | /.node_modules.ember-try/ 25 | /bower.json.ember-try 26 | /npm-shrinkwrap.json.ember-try 27 | /package.json.ember-try 28 | /package-lock.json.ember-try 29 | /yarn.lock.ember-try 30 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintcache 14 | /.eslintignore 15 | /.eslintrc.js 16 | /.git/ 17 | /.github/ 18 | /.gitignore 19 | /.prettierignore 20 | /.prettierrc.js 21 | /.template-lintrc.js 22 | /.travis.yml 23 | /.watchmanconfig 24 | /bower.json 25 | /config/ember-try.js 26 | /CONTRIBUTING.md 27 | /ember-cli-build.js 28 | /testem.js 29 | /tests/ 30 | /yarn-error.log 31 | /yarn.lock 32 | .gitkeep 33 | 34 | # ember-try 35 | /.node_modules.ember-try/ 36 | /bower.json.ember-try 37 | /npm-shrinkwrap.json.ember-try 38 | /package.json.ember-try 39 | /package-lock.json.ember-try 40 | /yarn.lock.ember-try 41 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .eslintcache 17 | .lint-todo/ 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /npm-shrinkwrap.json.ember-try 23 | /package.json.ember-try 24 | /package-lock.json.ember-try 25 | /yarn.lock.ember-try 26 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v5.0.0 (2022-04-29) 2 | 3 | #### :boom: Breaking Change 4 | * [#43](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/43) Drop support for ember-bootstrap v4 ([@simonihmig](https://github.com/simonihmig)) 5 | * [#41](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/41) Update to Ember 4.2, drop support for Ember < 3.24, node 10, ember-changeset < 4 ([@simonihmig](https://github.com/simonihmig)) 6 | 7 | #### :rocket: Enhancement 8 | * [#42](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/42) Convert extended Form to native class, providing compatibility with ember-bootstrap 5.1+ ([@simonihmig](https://github.com/simonihmig)) 9 | * [#41](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/41) Update to Ember 4.2, drop support for Ember < 3.24, node 10, ember-changeset < 4 ([@simonihmig](https://github.com/simonihmig)) 10 | 11 | #### Committers: 1 12 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig)) 13 | 14 | ## v4.0.0 (2020-12-04) 15 | 16 | #### :boom: Breaking Change 17 | * [#34](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/34) drop support for Ember Bootstrap <= v4.4 ([@jelhan](https://github.com/jelhan)) 18 | 19 | #### :rocket: Enhancement 20 | * [#34](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/34) support Ember Bootstrap v4.5 and above ([@jelhan](https://github.com/jelhan)) 21 | 22 | #### :memo: Documentation 23 | * [#31](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/31) use angle-bracket invocation in README and some other improvements ([@simonihmig](https://github.com/simonihmig)) 24 | 25 | #### :house: Internal 26 | * [#35](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/35) migrate CI from TravisCI to GitHub actions ([@jelhan](https://github.com/jelhan)) 27 | 28 | #### Committers: 2 29 | - Jeldrik Hanschke ([@jelhan](https://github.com/jelhan)) 30 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig)) 31 | 32 | 33 | ## v3.1.2 (2020-08-13) 34 | 35 | #### :bug: Bug Fix 36 | * [#29](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/29) support multiple validation errors ([@basz](https://github.com/basz)) 37 | 38 | #### Committers: 1 39 | - Bas Kamer ([@basz](https://github.com/basz)) 40 | 41 | ## v3.1.1 (2020-08-02) 42 | 43 | #### :bug: Bug Fix 44 | * [#28](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/28) fix validation for nested properties ([@basz](https://github.com/basz)) 45 | 46 | #### Committers: 1 47 | - Bas Kamer ([@basz](https://github.com/basz)) 48 | 49 | ## v3.1.0 (2020-07-20) 50 | 51 | #### :bug: Bug Fix 52 | * [#27](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/27) support latest version of ember-changeset / ember-changeset-validations ([@jelhan](https://github.com/jelhan)) 53 | * [#25](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/25) hide deprecation notice when subclassing ember bootstrap classes ([@basz](https://github.com/basz)) 54 | 55 | #### Committers: 2 56 | - Bas Kamer ([@basz](https://github.com/basz)) 57 | - Jeldrik Hanschke ([@jelhan](https://github.com/jelhan)) 58 | 59 | ## v3.0.0 (2020-03-31) 60 | 61 | #### :boom: Breaking Change 62 | * [#19](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/19) upgrade ember changeset to v3 ([@jelhan](https://github.com/jelhan)) 63 | * [#18](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/18) upgrade dependencies except ember-changeset ([@jelhan](https://github.com/jelhan)) 64 | 65 | #### :house: Internal 66 | * [#20](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/20) automate releases with release-it and lerna-changelog ([@jelhan](https://github.com/jelhan)) 67 | 68 | #### Committers: 1 69 | - Jeldrik Hanschke ([@jelhan](https://github.com/jelhan)) 70 | 71 | 72 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` 6 | * `cd my-addon` 7 | * `npm install` 8 | 9 | ## Linting 10 | 11 | * `yarn lint` 12 | * `yarn lint:fix` 13 | 14 | ## Running tests 15 | 16 | * `ember test` – Runs the test suite on the current Ember version 17 | * `ember test --server` – Runs the test suite in "watch mode" 18 | * `ember try:each` – Runs the test suite against multiple Ember versions 19 | 20 | ## Running the dummy application 21 | 22 | * `ember serve` 23 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 24 | 25 | For more information on using ember-cli, visit [https://cli.emberjs.com/release/](https://cli.emberjs.com/release/). 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ember-bootstrap-changeset-validations 2 | 3 | [![Build Status](https://travis-ci.org/kaliber5/ember-bootstrap-changeset-validations.svg?branch=master)](https://travis-ci.org/kaliber5/ember-bootstrap-changeset-validations) 4 | 5 | This Ember addon adds support for validations based on [ember-changeset](https://github.com/poteto/ember-changeset) to [ember-bootstrap](https://www.ember-bootstrap.com/) forms. 6 | This way your forms are only submitted when the underlying data is valid, otherwise the appropriate bootstrap error 7 | markup will be applied. See the [FormElement documentation](https://www.ember-bootstrap.com/api/classes/Components.FormElement.html) for 8 | further details. 9 | 10 | Compatibility 11 | ------------------------------------------------------------------------------ 12 | 13 | * Ember Bootstrap v5 or above 14 | * Ember Changeset and Ember Changeset Validations v4 15 | * Ember.js v3.24 or above 16 | * Ember CLI v3.24 or above 17 | * Node.js v12 or above 18 | 19 | 20 | Installation 21 | ------------------------------------------------------------------------------ 22 | 23 | ember install ember-bootstrap-changeset-validations 24 | 25 | You should have installed the ember-bootstrap and ember-changeset addons already. If not install them: 26 | 27 | ``` 28 | ember install ember-bootstrap 29 | ember install ember-changeset 30 | ``` 31 | 32 | You probably also want to install [ember-changeset-validations](https://github.com/poteto/ember-changeset-validations/) 33 | if you do not have a custom validation implementation: 34 | 35 | ``` 36 | ember install ember-changeset-validations 37 | ``` 38 | 39 | ## Usage 40 | 41 | Define your model and its validations as described in [ember-changeset-validations](https://github.com/poteto/ember-changeset-validations/). 42 | Then assign the changeset based on that to your form: 43 | 44 | ```hbs 45 | 46 | 47 | 48 | 49 | Submit 50 | 51 | ``` 52 | 53 | ## Authors 54 | 55 | * [Simon Ihmig](https://github.com/simonihmig) @ [kaliber5](http://www.kaliber5.de) 56 | * [Jeldrik Hanschke](https://github.com/jelhan) 57 | 58 | 59 | Contributing 60 | ------------------------------------------------------------------------------ 61 | 62 | See the [Contributing](CONTRIBUTING.md) guide for details. 63 | 64 | ## Copyright and license 65 | 66 | Code and documentation copyright 2017 kaliber5 GmbH and contributors. Code released under [the MIT license](LICENSE.md). 67 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | Releases are mostly automated using 4 | [release-it](https://github.com/release-it/release-it/) and 5 | [lerna-changelog](https://github.com/lerna/lerna-changelog/). 6 | 7 | 8 | ## Preparation 9 | 10 | Since the majority of the actual release process is automated, the primary 11 | remaining task prior to releasing is confirming that all pull requests that 12 | have been merged since the last release have been labeled with the appropriate 13 | `lerna-changelog` labels and the titles have been updated to ensure they 14 | represent something that would make sense to our users. Some great information 15 | on why this is important can be found at 16 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall 17 | guiding principle here is that changelogs are for humans, not machines. 18 | 19 | When reviewing merged PR's the labels to be used are: 20 | 21 | * breaking - Used when the PR is considered a breaking change. 22 | * enhancement - Used when the PR adds a new feature or enhancement. 23 | * bug - Used when the PR fixes a bug included in a previous release. 24 | * documentation - Used when the PR adds or updates documentation. 25 | * internal - Used for internal changes that still require a mention in the 26 | changelog/release notes. 27 | 28 | 29 | ## Release 30 | 31 | Once the prep work is completed, the actual release is straight forward: 32 | 33 | * First ensure that you have `release-it` installed globally, generally done by 34 | using one of the following commands: 35 | 36 | ``` 37 | # using https://volta.sh 38 | volta install release-it 39 | 40 | # using Yarn 41 | yarn global add release-it 42 | 43 | # using npm 44 | npm install --global release-it 45 | ``` 46 | 47 | * Second, ensure that you have installed your projects dependencies: 48 | 49 | ``` 50 | yarn install 51 | ``` 52 | 53 | * And last (but not least 😁) do your release. It requires a 54 | [GitHub personal access token](https://github.com/settings/tokens) as 55 | `$GITHUB_AUTH` environment variable. Only "repo" access is needed; no "admin" 56 | or other scopes are required. 57 | 58 | ``` 59 | export GITHUB_AUTH="f941e0..." 60 | release-it 61 | ``` 62 | 63 | [release-it](https://github.com/release-it/release-it/) manages the actual 64 | release process. It will prompt you to to choose the version number after which 65 | you will have the chance to hand tweak the changelog to be used (for the 66 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging, 67 | pushing the tag and commits, etc. 68 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/addon/.gitkeep -------------------------------------------------------------------------------- /addon/components/bs-form.js: -------------------------------------------------------------------------------- 1 | import { assert } from '@ember/debug'; 2 | import BsForm from 'ember-bootstrap/components/bs-form'; 3 | 4 | export default class BsFormWithChangesetValidationsSupport extends BsForm { 5 | '__ember-bootstrap_subclass' = true; 6 | 7 | get hasValidator() { 8 | return typeof this.model?.validate === 'function'; 9 | } 10 | 11 | async validate(model) { 12 | let m = model; 13 | 14 | assert( 15 | 'Model must be a Changeset instance', 16 | m && typeof m.validate === 'function' 17 | ); 18 | 19 | await m.validate(); 20 | if (!model.get('isValid')) { 21 | throw new Error(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /addon/components/bs-form/element.js: -------------------------------------------------------------------------------- 1 | import BsFormElement from 'ember-bootstrap/components/bs-form/element'; 2 | import { action, get } from '@ember/object'; 3 | import { isNone, typeOf } from '@ember/utils'; 4 | 5 | export default class BsFormElementWithChangesetValidationsSupport extends BsFormElement { 6 | '__ember-bootstrap_subclass' = true; 7 | 8 | get errors() { 9 | let { model, property } = this.args; 10 | 11 | // must use `get` method to support nested properties 12 | let errors = get(model, `error.${property}.validation`); 13 | 14 | // no messages 15 | if (isNone(errors)) { 16 | return []; 17 | } 18 | 19 | // a single messages 20 | if (typeOf(errors) === 'string') { 21 | return [errors]; 22 | } 23 | 24 | // assume it's an array of messages 25 | return errors; 26 | } 27 | 28 | get hasValidator() { 29 | return typeof this.args.model?.validate === 'function'; 30 | } 31 | 32 | // Ember Changeset does not validate the initial state. Properties are not 33 | // validated until they are set the first time. But Ember Bootstrap may show 34 | // validation results before the property was changed. We need to make sure 35 | // that changeset is validated at that time. 36 | // Ember Bootstrap may show the validation in three cases: 37 | // 1. User triggered one of the events that should cause validation errors to 38 | // be shown (e.g. focus out) by interacting with the form element. 39 | // Ember Bootstrap stores these state in `showOwnValidation` property of 40 | // the form element. 41 | // 2. User submits the form. Ember Bootstrap will show validation errors 42 | // for all form elements in that case. That state is handled by 43 | // `showAllValidations` arguments passed to the form element. 44 | // 3. User passes in a validation error or warning explicilty using 45 | // `customError` or `customWarning` arguments of the form element. 46 | // Ember Bootstrap ensures that the model is valided as part of its submit 47 | // handler. So we can assume that validations are run in second case. Ember 48 | // Bootstrap does not show the validation errors of the model but only the 49 | // custom error and warning if present. So it does not matter if initial 50 | // state is validated or not. That means we only have to handle the first 51 | // case. 52 | // Ember Bootstrap does not provide any API for validation plugins to support 53 | // these needs. We have to override a private method to run the validate 54 | // logic for now. 55 | @action 56 | async showValidationOnHandler(event) { 57 | let validationShowBefore = this.showOwnValidation; 58 | 59 | // run original implementation provided by Ember Bootstrap 60 | super.showValidationOnHandler(event); 61 | 62 | // run initial validation if 63 | // - visibility of validations changed 64 | let canValidate = this.hasValidator && this.args.property; 65 | let validationVisibilityChanged = 66 | !validationShowBefore && this.showOwnValidation; 67 | if (canValidate && validationVisibilityChanged) { 68 | await this.args.model.validate(this.args.property); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/app/.gitkeep -------------------------------------------------------------------------------- /app/components/bs-form.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-bootstrap-changeset-validations/components/bs-form'; 2 | -------------------------------------------------------------------------------- /app/components/bs-form/element.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-bootstrap-changeset-validations/components/bs-form/element'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | useYarn: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-3.24', 12 | npm: { 13 | devDependencies: { 14 | 'ember-source': '~3.24.3', 15 | }, 16 | }, 17 | }, 18 | { 19 | name: 'ember-lts-3.28', 20 | npm: { 21 | devDependencies: { 22 | 'ember-source': '~3.28.0', 23 | }, 24 | }, 25 | }, 26 | { 27 | name: 'ember-release', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': await getChannelURL('release'), 31 | }, 32 | }, 33 | }, 34 | { 35 | name: 'ember-beta', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': await getChannelURL('beta'), 39 | }, 40 | }, 41 | }, 42 | { 43 | name: 'ember-canary', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': await getChannelURL('canary'), 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'ember-classic', 52 | env: { 53 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 54 | 'application-template-wrapper': true, 55 | 'default-async-observers': false, 56 | 'template-only-glimmer-components': false, 57 | }), 58 | }, 59 | npm: { 60 | devDependencies: { 61 | 'ember-source': '~3.28.0', 62 | }, 63 | ember: { 64 | edition: 'classic', 65 | }, 66 | }, 67 | }, 68 | embroiderSafe(), 69 | embroiderOptimized(), 70 | ], 71 | }; 72 | }; 73 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (/* environment, appConfig */) { 4 | return {}; 5 | }; 6 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function (defaults) { 6 | let app = new EmberAddon(defaults, { 7 | // Add options here 8 | 'ember-bootstrap': { 9 | bootstrapVersion: 4, 10 | importBootstrapFont: false, 11 | importBootstrapCSS: true, 12 | }, 13 | }); 14 | 15 | /* 16 | This build file specifies the options for the dummy test app of this 17 | addon, located in `/tests/dummy` 18 | This build file does *not* influence how the addon or the app using it 19 | behave. You most likely want to be modifying `./index.js` or app's build file 20 | */ 21 | 22 | const { maybeEmbroider } = require('@embroider/test-setup'); 23 | return maybeEmbroider(app, { 24 | skipBabel: [ 25 | { 26 | package: 'qunit', 27 | }, 28 | ], 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name, 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-bootstrap-changeset-validations", 3 | "version": "5.0.0", 4 | "description": "This Ember addon adds support for validations based on ember-changeset to ember-bootstrap", 5 | "keywords": [ 6 | "ember-addon", 7 | "ember-bootstrap", 8 | "ember-changeset" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "http://github.com/ember-bootstrap/ember-bootstrap-changeset-validations.git" 13 | }, 14 | "license": "MIT", 15 | "author": "Simon Ihmig ", 16 | "directories": { 17 | "doc": "doc", 18 | "test": "tests" 19 | }, 20 | "scripts": { 21 | "build": "ember build --environment=production", 22 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", 23 | "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", 24 | "lint:hbs": "ember-template-lint .", 25 | "lint:hbs:fix": "ember-template-lint . --fix", 26 | "lint:js": "eslint . --cache", 27 | "lint:js:fix": "eslint . --fix", 28 | "start": "ember serve", 29 | "test": "npm-run-all lint test:*", 30 | "test:ember": "ember test", 31 | "test:ember-compatibility": "ember try:each" 32 | }, 33 | "dependencies": { 34 | "ember-cli-babel": "^7.26.11", 35 | "ember-cli-htmlbars": "^6.0.1" 36 | }, 37 | "devDependencies": { 38 | "@ember/optional-features": "2.0.0", 39 | "@ember/test-helpers": "2.8.1", 40 | "@embroider/test-setup": "1.8.3", 41 | "@glimmer/component": "1.1.2", 42 | "@glimmer/tracking": "1.1.2", 43 | "babel-eslint": "10.1.0", 44 | "bootstrap": "4.6.2", 45 | "broccoli-asset-rev": "3.0.0", 46 | "ember-auto-import": "2.4.2", 47 | "ember-bootstrap": "5.1.1", 48 | "ember-changeset": "4.0.0", 49 | "ember-changeset-validations": "4.0.0", 50 | "ember-cli": "4.5.0", 51 | "ember-cli-dependency-checker": "3.3.1", 52 | "ember-cli-inject-live-reload": "2.1.0", 53 | "ember-cli-sri": "2.1.1", 54 | "ember-cli-terser": "4.0.2", 55 | "ember-disable-prototype-extensions": "1.1.3", 56 | "ember-export-application-global": "2.0.1", 57 | "ember-focus-trap": "1.0.1", 58 | "ember-load-initializers": "2.1.2", 59 | "ember-page-title": "7.0.0", 60 | "ember-qunit": "5.1.5", 61 | "ember-resolver": "8.0.3", 62 | "ember-source": "4.7.0", 63 | "ember-source-channel-url": "3.0.0", 64 | "ember-template-lint": "4.14.0", 65 | "ember-try": "2.0.0", 66 | "eslint": "7.32.0", 67 | "eslint-config-prettier": "8.5.0", 68 | "eslint-plugin-ember": "10.6.1", 69 | "eslint-plugin-node": "11.1.0", 70 | "eslint-plugin-prettier": "4.2.1", 71 | "eslint-plugin-qunit": "7.3.1", 72 | "loader.js": "4.7.0", 73 | "npm-run-all": "4.1.5", 74 | "prettier": "2.7.1", 75 | "qunit": "2.19.1", 76 | "qunit-dom": "2.0.0", 77 | "release-it": "14.14.3", 78 | "release-it-lerna-changelog": "3.1.0", 79 | "webpack": "5.74.0" 80 | }, 81 | "engines": { 82 | "node": "12.* || 14.* || >= 16" 83 | }, 84 | "publishConfig": { 85 | "registry": "https://registry.npmjs.org" 86 | }, 87 | "ember": { 88 | "edition": "octane" 89 | }, 90 | "ember-addon": { 91 | "configPath": "tests/dummy/config", 92 | "after": "ember-bootstrap" 93 | }, 94 | "release-it": { 95 | "plugins": { 96 | "release-it-lerna-changelog": { 97 | "infile": "CHANGELOG.md", 98 | "launchEditor": true 99 | } 100 | }, 101 | "git": { 102 | "tagName": "v${version}" 103 | }, 104 | "github": { 105 | "release": true, 106 | "tokenRef": "GITHUB_AUTH" 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>kaliber5/renovate-config:ember-addon" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import { action } from '@ember/object'; 3 | import Changeset from 'ember-changeset'; 4 | import { 5 | validatePresence, 6 | validateLength, 7 | } from 'ember-changeset-validations/validators'; 8 | import lookupValidator from 'ember-changeset-validations'; 9 | 10 | class Model { 11 | name = ''; 12 | } 13 | 14 | const Validation = { 15 | name: [validatePresence(true), validateLength({ min: 4 })], 16 | }; 17 | 18 | export default class ApplicationController extends Controller { 19 | changeset; 20 | model = new Model(); 21 | 22 | constructor() { 23 | super(...arguments); 24 | 25 | this.changeset = new Changeset( 26 | this.model, 27 | lookupValidator(Validation), 28 | Validation 29 | ); 30 | } 31 | 32 | @action 33 | submit() { 34 | window.alert('Submitted!'); 35 | } 36 | 37 | @action 38 | invalid() { 39 | window.alert('Invalid!'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () {}); 10 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/dummy/app/styles/app.css -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "4.2.0", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--yarn", 15 | "--no-welcome" 16 | ] 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'history', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false, 17 | }, 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | }, 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | module.exports = { 10 | browsers, 11 | }; 12 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{content-for "body-footer"}} 38 | {{content-for "test-body-footer"}} 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/integration/components/bs-form-element-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { 4 | render, 5 | triggerEvent, 6 | fillIn, 7 | focus, 8 | blur, 9 | findAll, 10 | } from '@ember/test-helpers'; 11 | import hbs from 'htmlbars-inline-precompile'; 12 | import { 13 | validatePresence, 14 | validateLength, 15 | } from 'ember-changeset-validations/validators'; 16 | 17 | module('Integration | Component | bs form element', function (hooks) { 18 | setupRenderingTest(hooks); 19 | 20 | const validation = { 21 | name: [validatePresence(true), validateLength({ min: 4 })], 22 | }; 23 | 24 | const nestedValidation = { 25 | nested: { 26 | name: [validatePresence(true), validateLength({ min: 4 })], 27 | }, 28 | }; 29 | 30 | test('form is submitted if valid and validation success shown', async function (assert) { 31 | assert.expect(2); 32 | 33 | let model = { 34 | name: '1234', 35 | }; 36 | 37 | this.set('model', model); 38 | this.set('validation', validation); 39 | this.submitAction = function () { 40 | assert.step('submit action has been called.'); 41 | }; 42 | this.invalidAction = function () { 43 | assert.ok(false, 'Invalid action must not been called.'); 44 | }; 45 | 46 | await render(hbs` 47 | 48 | 49 | 50 | `); 51 | 52 | await triggerEvent('form', 'submit'); 53 | assert.verifySteps(['submit action has been called.']); 54 | }); 55 | 56 | test('validation errors are shown on submit', async function (assert) { 57 | assert.expect(3); 58 | 59 | let model = { 60 | name: '', 61 | }; 62 | 63 | this.set('model', model); 64 | this.set('validation', validation); 65 | this.submitAction = function () { 66 | assert.ok(false, 'submit action must not been called.'); 67 | }; 68 | this.invalidAction = function () { 69 | assert.step('Invalid action has been called.'); 70 | }; 71 | 72 | await render(hbs` 73 | 74 | 75 | 76 | `); 77 | 78 | await triggerEvent('form', 'submit'); 79 | assert.dom('input').hasClass('is-invalid', 'input has error class'); 80 | assert.verifySteps(['Invalid action has been called.']); 81 | }); 82 | 83 | test('validation nested errors are shown on submit', async function (assert) { 84 | assert.expect(3); 85 | 86 | let model = { 87 | nested: { name: '' }, 88 | }; 89 | 90 | this.set('model', model); 91 | this.set('validation', nestedValidation); 92 | this.submitAction = function () { 93 | assert.ok(false, 'submit action must not been called.'); 94 | }; 95 | this.invalidAction = function () { 96 | assert.step('Invalid action has been called.'); 97 | }; 98 | 99 | await render(hbs` 100 | 101 | 102 | 103 | `); 104 | 105 | await triggerEvent('form', 'submit'); 106 | assert.dom('input').hasClass('is-invalid', 'input has error class'); 107 | assert.verifySteps(['Invalid action has been called.']); 108 | }); 109 | 110 | test('validation errors are shown after blur', async function (assert) { 111 | this.set('model', { name: '' }); 112 | this.set('validation', validation); 113 | 114 | await render(hbs` 115 | 116 | 117 | 118 | `); 119 | assert.dom('input').doesNotHaveClass('is-invalid'); 120 | 121 | await focus('input'); 122 | await blur('input'); 123 | assert.dom('input').hasClass('is-invalid'); 124 | }); 125 | 126 | test('validation success is shown after blur', async function (assert) { 127 | this.set('model', { name: 'Clara' }); 128 | this.set('validation', validation); 129 | 130 | await render(hbs` 131 | 132 | 133 | 134 | `); 135 | assert.dom('input').doesNotHaveClass('is-valid'); 136 | 137 | await focus('input'); 138 | await blur('input'); 139 | assert.dom('input').hasClass('is-valid'); 140 | }); 141 | 142 | test('validation errors are shown after user input', async function (assert) { 143 | this.set('model', { name: '' }); 144 | this.set('validation', validation); 145 | 146 | await render(hbs` 147 | 148 | 149 | 150 | `); 151 | assert.dom('input').doesNotHaveClass('is-invalid'); 152 | 153 | await fillIn('input', 'R'); 154 | assert 155 | .dom('input') 156 | .doesNotHaveClass( 157 | 'is-invalid', 158 | 'validation is not shown while user is typing' 159 | ); 160 | 161 | await blur('input'); 162 | assert 163 | .dom('input') 164 | .hasClass('is-invalid', 'validation error is shown after focus out'); 165 | }); 166 | 167 | test('validation success is shown after user input', async function (assert) { 168 | this.set('model', { name: '' }); 169 | this.set('validation', validation); 170 | 171 | await render(hbs` 172 | 173 | 174 | 175 | `); 176 | assert.dom('input').doesNotHaveClass('is-valid'); 177 | 178 | await fillIn('input', 'Rosa'); 179 | assert 180 | .dom('input') 181 | .doesNotHaveClass( 182 | 'is-valid', 183 | 'validation is not shown while user is typing' 184 | ); 185 | 186 | await blur('input'); 187 | assert 188 | .dom('input') 189 | .hasClass('is-valid', 'validation error is shown after focus out'); 190 | }); 191 | 192 | test('does not break forms which are not using a changeset as model', async function (assert) { 193 | this.set('model', { name: '' }); 194 | this.set('submitAction', () => { 195 | assert.step('submit action has been called'); 196 | }); 197 | 198 | await render(hbs` 199 | 200 | 201 | 202 | `); 203 | assert.dom('input').doesNotHaveClass('is-valid'); 204 | assert.dom('input').doesNotHaveClass('is-invalid'); 205 | 206 | await fillIn('input', 'Rosa'); 207 | await blur('input'); 208 | assert.dom('input').doesNotHaveClass('is-valid'); 209 | assert.dom('input').doesNotHaveClass('is-invalid'); 210 | 211 | await triggerEvent('form', 'submit'); 212 | assert.dom('input').doesNotHaveClass('is-valid'); 213 | assert.dom('input').doesNotHaveClass('is-invalid'); 214 | assert.verifySteps(['submit action has been called']); 215 | }); 216 | 217 | test('does not break for forms which are not having a model at all', async function (assert) { 218 | this.set('submitAction', () => { 219 | assert.step('submit action has been called'); 220 | }); 221 | this.set('noop', () => {}); 222 | 223 | await render(hbs` 224 | 225 | 226 | 227 | `); 228 | assert.dom('input').doesNotHaveClass('is-valid'); 229 | assert.dom('input').doesNotHaveClass('is-invalid'); 230 | 231 | await fillIn('input', 'Rosa'); 232 | await blur('input'); 233 | assert.dom('input').doesNotHaveClass('is-valid'); 234 | assert.dom('input').doesNotHaveClass('is-invalid'); 235 | 236 | await triggerEvent('form', 'submit'); 237 | assert.dom('input').doesNotHaveClass('is-valid'); 238 | assert.dom('input').doesNotHaveClass('is-invalid'); 239 | assert.verifySteps(['submit action has been called']); 240 | }); 241 | 242 | test('invalid-feedback is shown from single validation', async function (assert) { 243 | let model = { 244 | name: '', 245 | }; 246 | 247 | this.set('model', model); 248 | this.set('validation', { 249 | name: validatePresence(true), 250 | }); 251 | 252 | await render(hbs` 253 | 254 | 255 | 256 | `); 257 | 258 | await triggerEvent('form', 'submit'); 259 | assert.dom('.invalid-feedback').hasText("Name can't be blank"); 260 | }); 261 | 262 | test('invalid-feedback is shown in order from multiple validations', async function (assert) { 263 | let model = { 264 | name: '', 265 | }; 266 | 267 | this.set('model', model); 268 | this.set('validation', { 269 | name: [validatePresence(true), validateLength({ min: 4 })], 270 | }); 271 | 272 | await render(hbs` 273 | 274 | 275 | 276 | `); 277 | 278 | await triggerEvent('form', 'submit'); 279 | assert.dom('.invalid-feedback').hasText("Name can't be blank"); 280 | 281 | await fillIn('input', 'R'); 282 | await triggerEvent('form', 'submit'); 283 | assert 284 | .dom('.invalid-feedback') 285 | .hasText('Name is too short (minimum is 4 characters)'); 286 | }); 287 | 288 | test('invalid-feedback is shown (multiple messages) in order from multiple validations', async function (assert) { 289 | assert.expect(2); 290 | 291 | let model = { 292 | name: '', 293 | }; 294 | 295 | this.set('model', model); 296 | this.set('validation', { 297 | name: [validatePresence(true), validateLength({ min: 4 })], 298 | }); 299 | 300 | await render(hbs` 301 | 302 | 303 | 304 | `); 305 | 306 | await triggerEvent('form', 'submit'); 307 | 308 | let feedbackElements = findAll('.invalid-feedback'); 309 | let results = Array.from(feedbackElements, (element) => 310 | element.textContent.trim() 311 | ); 312 | let expected = [ 313 | "Name can't be blank", 314 | 'Name is too short (minimum is 4 characters)', 315 | ]; 316 | 317 | expected.forEach((message) => { 318 | assert.ok(results.includes(message)); 319 | }); 320 | }); 321 | 322 | test('no feedback is shown for nonexistant validations', async function (assert) { 323 | let model = { 324 | name: '', 325 | }; 326 | 327 | this.set('model', model); 328 | this.set('validation', { 329 | nombre: validatePresence(true), 330 | }); 331 | 332 | await render(hbs` 333 | 334 | 335 | 336 | `); 337 | 338 | await triggerEvent('form', 'submit'); 339 | assert.dom('.invalid-feedback').doesNotExist(); 340 | }); 341 | }); 342 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | 8 | setApplication(Application.create(config.APP)); 9 | 10 | setup(QUnit.assert); 11 | 12 | start(); 13 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/tests/unit/.gitkeep -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-bootstrap/ember-bootstrap-changeset-validations/3ccdf4c16c4508889169feca270878d0a2541fc2/vendor/.gitkeep --------------------------------------------------------------------------------