├── app
├── .gitkeep
└── components
│ ├── bs-form.js
│ └── bs-form
│ └── element.js
├── addon
├── .gitkeep
└── components
│ ├── bs-form.js
│ └── bs-form
│ └── element.js
├── vendor
└── .gitkeep
├── tests
├── unit
│ └── .gitkeep
├── integration
│ ├── .gitkeep
│ └── components
│ │ └── bs-form-element-test.js
├── dummy
│ ├── app
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── routes
│ │ │ └── .gitkeep
│ │ ├── components
│ │ │ └── .gitkeep
│ │ ├── controllers
│ │ │ ├── .gitkeep
│ │ │ └── application.js
│ │ ├── styles
│ │ │ └── app.css
│ │ ├── router.js
│ │ ├── templates
│ │ │ └── application.hbs
│ │ ├── app.js
│ │ ├── index.html
│ │ └── deprecation-workflow.js
│ ├── public
│ │ └── robots.txt
│ └── config
│ │ ├── optional-features.json
│ │ ├── targets.js
│ │ ├── ember-cli-update.json
│ │ ├── environment.js
│ │ └── ember-try.js
├── test-helper.js
├── index.html
└── helpers
│ └── index.js
├── .watchmanconfig
├── .template-lintrc.js
├── index.js
├── .stylelintignore
├── .stylelintrc.js
├── config
└── environment.js
├── renovate.json
├── .prettierignore
├── .ember-cli
├── .gitignore
├── .prettierrc.js
├── .editorconfig
├── .npmignore
├── testem.js
├── CONTRIBUTING.md
├── ember-cli-build.js
├── LICENSE.md
├── .github
└── workflows
│ └── ci.yml
├── RELEASE.md
├── README.md
├── eslint.config.mjs
├── package.json
└── CHANGELOG.md
/app/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/addon/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/integration/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/routes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["dist"]
3 | }
4 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/.template-lintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | extends: 'recommended',
5 | };
6 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | name: require('./package').name,
5 | };
6 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | # unconventional files
2 | /blueprints/*/files/
3 |
4 | # compiled output
5 | /dist/
6 |
--------------------------------------------------------------------------------
/app/components/bs-form.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-bootstrap-changeset-validations/components/bs-form';
2 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | extends: ['stylelint-config-standard'],
5 | };
6 |
--------------------------------------------------------------------------------
/app/components/bs-form/element.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-bootstrap-changeset-validations/components/bs-form/element';
2 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (/* environment, appConfig */) {
4 | return {};
5 | };
6 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
1 | /* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */
2 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["local>kaliber5/renovate-config:ember-addon"]
4 | }
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # unconventional js
2 | /blueprints/*/files/
3 |
4 | # compiled output
5 | /dist/
6 |
7 | # misc
8 | /coverage/
9 | !.*
10 | .*/
11 | /pnpm-lock.yaml
12 | ember-cli-update.json
13 | *.html
14 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
4 | rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
5 | */
6 | "isTypeScriptProject": false
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist/
3 | /declarations/
4 |
5 | # dependencies
6 | /node_modules/
7 |
8 | # misc
9 | /.env*
10 | /.pnp*
11 | /.eslintcache
12 | /coverage/
13 | /npm-debug.log*
14 | /testem.log
15 | /yarn-error.log
16 |
17 | # broccoli-debug
18 | /DEBUG/
19 |
--------------------------------------------------------------------------------
/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/templates/application.hbs:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | plugins: ['prettier-plugin-ember-template-tag'],
5 | overrides: [
6 | {
7 | files: '*.{js,gjs,ts,gts,mjs,mts,cjs,cts}',
8 | options: {
9 | singleQuote: true,
10 | templateSingleQuote: false,
11 | },
12 | },
13 | ],
14 | };
15 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist/
3 | /tmp/
4 |
5 | # misc
6 | /.editorconfig
7 | /.ember-cli
8 | /.env*
9 | /.eslintcache
10 | /.git/
11 | /.github/
12 | /.gitignore
13 | /.prettierignore
14 | /.prettierrc.js
15 | /.stylelintignore
16 | /.stylelintrc.js
17 | /.template-lintrc.js
18 | /.watchmanconfig
19 | /CONTRIBUTING.md
20 | /ember-cli-build.js
21 | /eslint.config.mjs
22 | /testem.js
23 | /tests/
24 | /tsconfig.declarations.json
25 | /tsconfig.json
26 | /yarn-error.log
27 | /yarn.lock
28 | .gitkeep
29 |
--------------------------------------------------------------------------------
/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 { loadTests } from 'ember-qunit/test-loader';
7 | import { start, setupEmberOnerrorValidation } from 'ember-qunit';
8 |
9 | setApplication(Application.create(config.APP));
10 |
11 | setup(QUnit.assert);
12 | setupEmberOnerrorValidation();
13 | loadTests();
14 | start();
15 |
--------------------------------------------------------------------------------
/tests/dummy/config/ember-cli-update.json:
--------------------------------------------------------------------------------
1 | {
2 | "schemaVersion": "1.0.0",
3 | "packages": [
4 | {
5 | "name": "ember-cli",
6 | "version": "6.4.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 | "--pnpm",
15 | "--no-welcome"
16 | ]
17 | }
18 | ]
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/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 | import { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';
6 |
7 | if (macroCondition(isDevelopingApp())) {
8 | importSync('./deprecation-workflow');
9 | }
10 |
11 | export default class App extends Application {
12 | modulePrefix = config.modulePrefix;
13 | podModulePrefix = config.podModulePrefix;
14 | Resolver = Resolver;
15 | }
16 |
17 | loadInitializers(App, config.modulePrefix);
18 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Dummy
6 |
7 |
8 |
9 | {{content-for "head"}}
10 |
11 |
12 |
13 |
14 | {{content-for "head-footer"}}
15 |
16 |
17 | {{content-for "body"}}
18 |
19 |
20 |
21 |
22 | {{content-for "body-footer"}}
23 |
24 |
25 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How To Contribute
2 |
3 | ## Installation
4 |
5 | - `git clone `
6 | - `cd my-addon`
7 | - `pnpm install`
8 |
9 | ## Linting
10 |
11 | - `pnpm lint`
12 | - `pnpm lint:fix`
13 |
14 | ## Running tests
15 |
16 | - `pnpm test` – Runs the test suite on the current Ember version
17 | - `pnpm test:ember --server` – Runs the test suite in "watch mode"
18 | - `pnpm test:ember-compatibility` – Runs the test suite against multiple Ember versions
19 |
20 | ## Running the dummy application
21 |
22 | - `pnpm start`
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 |
--------------------------------------------------------------------------------
/tests/dummy/app/deprecation-workflow.js:
--------------------------------------------------------------------------------
1 | import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow';
2 |
3 | /**
4 | * Docs: https://github.com/ember-cli/ember-cli-deprecation-workflow
5 | */
6 | setupDeprecationWorkflow({
7 | /**
8 | false by default, but if a developer / team wants to be more aggressive about being proactive with
9 | handling their deprecations, this should be set to "true"
10 | */
11 | throwOnUnhandled: false,
12 | workflow: [
13 | /* ... handlers ... */
14 | /* to generate this list, run your app for a while (or run the test suite),
15 | * and then run in the browser console:
16 | *
17 | * deprecationWorkflow.flushDeprecations()
18 | *
19 | * And copy the handlers here
20 | */
21 | /* example: */
22 | /* { handler: 'silence', matchId: 'template-action' }, */
23 | ],
24 | });
25 |
--------------------------------------------------------------------------------
/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 | const app = new EmberAddon(defaults, {
7 | // Add options here
8 | 'ember-bootstrap': {
9 | bootstrapVersion: 5,
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Dummy Tests
6 |
7 |
8 |
9 | {{content-for "head"}}
10 | {{content-for "test-head"}}
11 |
12 |
13 |
14 |
15 |
16 | {{content-for "head-footer"}}
17 | {{content-for "test-head-footer"}}
18 |
19 |
20 | {{content-for "body"}}
21 | {{content-for "test-body"}}
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | {{content-for "body-footer"}}
37 | {{content-for "test-body-footer"}}
38 |
39 |
40 |
--------------------------------------------------------------------------------
/tests/dummy/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (environment) {
4 | const ENV = {
5 | modulePrefix: 'dummy',
6 | environment,
7 | rootURL: '/',
8 | locationType: 'history',
9 | EmberENV: {
10 | EXTEND_PROTOTYPES: false,
11 | FEATURES: {
12 | // Here you can enable experimental features on an ember canary build
13 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
14 | },
15 | },
16 |
17 | APP: {
18 | // Here you can pass flags/options to your application instance
19 | // when it is created
20 | },
21 | };
22 |
23 | if (environment === 'development') {
24 | // ENV.APP.LOG_RESOLVER = true;
25 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
26 | // ENV.APP.LOG_TRANSITIONS = true;
27 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
28 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
29 | }
30 |
31 | if (environment === 'test') {
32 | // Testem prefers this...
33 | ENV.locationType = 'none';
34 |
35 | // keep test console output quieter
36 | ENV.APP.LOG_ACTIVE_GENERATION = false;
37 | ENV.APP.LOG_VIEW_LOOKUPS = false;
38 |
39 | ENV.APP.rootElement = '#ember-testing';
40 | ENV.APP.autoboot = false;
41 | }
42 |
43 | if (environment === 'production') {
44 | // here you can enable a production-specific feature
45 | }
46 |
47 | return ENV;
48 | };
49 |
--------------------------------------------------------------------------------
/tests/helpers/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | setupApplicationTest as upstreamSetupApplicationTest,
3 | setupRenderingTest as upstreamSetupRenderingTest,
4 | setupTest as upstreamSetupTest,
5 | } from 'ember-qunit';
6 |
7 | // This file exists to provide wrappers around ember-qunit's
8 | // test setup functions. This way, you can easily extend the setup that is
9 | // needed per test type.
10 |
11 | function setupApplicationTest(hooks, options) {
12 | upstreamSetupApplicationTest(hooks, options);
13 |
14 | // Additional setup for application tests can be done here.
15 | //
16 | // For example, if you need an authenticated session for each
17 | // application test, you could do:
18 | //
19 | // hooks.beforeEach(async function () {
20 | // await authenticateSession(); // ember-simple-auth
21 | // });
22 | //
23 | // This is also a good place to call test setup functions coming
24 | // from other addons:
25 | //
26 | // setupIntl(hooks, 'en-us'); // ember-intl
27 | // setupMirage(hooks); // ember-cli-mirage
28 | }
29 |
30 | function setupRenderingTest(hooks, options) {
31 | upstreamSetupRenderingTest(hooks, options);
32 |
33 | // Additional setup for rendering tests can be done here.
34 | }
35 |
36 | function setupTest(hooks, options) {
37 | upstreamSetupTest(hooks, options);
38 |
39 | // Additional setup for unit tests can be done here.
40 | }
41 |
42 | export { setupApplicationTest, setupRenderingTest, setupTest };
43 |
--------------------------------------------------------------------------------
/tests/dummy/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 | packageManager: 'pnpm',
9 | scenarios: [
10 | {
11 | name: 'ember-lts-3.28',
12 | npm: {
13 | devDependencies: {
14 | '@glimmer/component': '^1.1.2',
15 | '@ember/test-waiters': '^3.1.0',
16 | 'ember-bootstrap': '^5.0.0',
17 | 'ember-cli': '~4.12.0',
18 | 'ember-resolver': '^11.0.0',
19 | 'ember-source': '~3.28.0',
20 | },
21 | },
22 | },
23 | {
24 | name: 'ember-lts-4.12',
25 | npm: {
26 | devDependencies: {
27 | 'ember-source': '~4.12.0',
28 | },
29 | },
30 | },
31 | {
32 | name: 'ember-lts-5.12',
33 | npm: {
34 | devDependencies: {
35 | 'ember-source': '~5.12.0',
36 | },
37 | },
38 | },
39 | {
40 | name: 'ember-release',
41 | npm: {
42 | devDependencies: {
43 | 'ember-source': await getChannelURL('release'),
44 | },
45 | },
46 | },
47 | {
48 | name: 'ember-beta',
49 | npm: {
50 | devDependencies: {
51 | 'ember-source': await getChannelURL('beta'),
52 | },
53 | },
54 | },
55 | {
56 | name: 'ember-canary',
57 | npm: {
58 | devDependencies: {
59 | 'ember-source': await getChannelURL('canary'),
60 | },
61 | },
62 | },
63 | {
64 | name: 'ember-changeset-v4',
65 | npm: {
66 | devDependencies: {
67 | 'ember-changeset': '^4.2.0',
68 | 'ember-changeset-validations': '^4.2.0',
69 | },
70 | },
71 | },
72 | embroiderSafe(),
73 | embroiderOptimized(),
74 | ],
75 | };
76 | };
77 |
--------------------------------------------------------------------------------
/.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 | timeout-minutes: 10
20 |
21 | steps:
22 | - uses: actions/checkout@v5
23 | - uses: pnpm/action-setup@v4
24 | with:
25 | version: 10
26 | - name: Install Node
27 | uses: actions/setup-node@v6
28 | with:
29 | node-version: 20.x
30 | cache: pnpm
31 | - name: Install Dependencies
32 | run: pnpm install --frozen-lockfile
33 | - name: Lint
34 | run: pnpm lint
35 | - name: Run Tests
36 | run: pnpm test:ember
37 |
38 | floating:
39 | name: "Floating Dependencies"
40 | runs-on: ubuntu-latest
41 | timeout-minutes: 10
42 |
43 | steps:
44 | - uses: actions/checkout@v5
45 | - uses: pnpm/action-setup@v4
46 | with:
47 | version: 10
48 | - uses: actions/setup-node@v6
49 | with:
50 | node-version: 20.x
51 | cache: pnpm
52 | - name: Install Dependencies
53 | run: pnpm install --no-lockfile
54 | - name: Run Tests
55 | run: pnpm test:ember
56 |
57 | try-scenarios:
58 | name: ${{ matrix.try-scenario }}
59 | runs-on: ubuntu-latest
60 | needs: "test"
61 | timeout-minutes: 10
62 |
63 | strategy:
64 | fail-fast: false
65 | matrix:
66 | try-scenario:
67 | - ember-lts-3.28
68 | - ember-lts-4.12
69 | - ember-lts-5.12
70 | - ember-release
71 | - ember-beta
72 | - ember-canary
73 | - ember-changeset-v4
74 | - embroider-safe
75 | - embroider-optimized
76 |
77 | steps:
78 | - uses: actions/checkout@v5
79 | - uses: pnpm/action-setup@v4
80 | with:
81 | version: 10
82 | - name: Install Node
83 | uses: actions/setup-node@v6
84 | with:
85 | node-version: 20.x
86 | cache: pnpm
87 | - name: Install Dependencies
88 | run: pnpm install --frozen-lockfile
89 | - name: Run Tests
90 | run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }}
91 |
--------------------------------------------------------------------------------
/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 | ## Preparation
8 |
9 | Since the majority of the actual release process is automated, the primary
10 | remaining task prior to releasing is confirming that all pull requests that
11 | have been merged since the last release have been labeled with the appropriate
12 | `lerna-changelog` labels and the titles have been updated to ensure they
13 | represent something that would make sense to our users. Some great information
14 | on why this is important can be found at
15 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall
16 | guiding principle here is that changelogs are for humans, not machines.
17 |
18 | When reviewing merged PR's the labels to be used are:
19 |
20 | - breaking - Used when the PR is considered a breaking change.
21 | - enhancement - Used when the PR adds a new feature or enhancement.
22 | - bug - Used when the PR fixes a bug included in a previous release.
23 | - documentation - Used when the PR adds or updates documentation.
24 | - internal - Used for internal changes that still require a mention in the
25 | changelog/release notes.
26 |
27 | ## Release
28 |
29 | Once the prep work is completed, the actual release is straight forward:
30 |
31 | - First ensure that you have `release-it` installed globally, generally done by
32 | using one of the following commands:
33 |
34 | ```
35 | # using https://volta.sh
36 | volta install release-it
37 |
38 | # using Yarn
39 | yarn global add release-it
40 |
41 | # using npm
42 | npm install --global release-it
43 | ```
44 |
45 | - Second, ensure that you have installed your projects dependencies:
46 |
47 | ```
48 | yarn install
49 | ```
50 |
51 | - And last (but not least 😁) do your release. It requires a
52 | [GitHub personal access token](https://github.com/settings/tokens) as
53 | `$GITHUB_AUTH` environment variable. Only "repo" access is needed; no "admin"
54 | or other scopes are required.
55 |
56 | ```
57 | export GITHUB_AUTH="f941e0..."
58 | release-it
59 | ```
60 |
61 | [release-it](https://github.com/release-it/release-it/) manages the actual
62 | release process. It will prompt you to to choose the version number after which
63 | you will have the chance to hand tweak the changelog to be used (for the
64 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging,
65 | pushing the tag and commits, etc.
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ember Bootstrap Changeset Validations
2 |
3 | [](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 | - Ember Bootstrap v5 or above
13 | - Ember Changeset and Ember Changeset Validations v4
14 | - Ember.js v3.28 or above
15 | - Ember CLI v3.28 or above
16 | - Node.js v20 or above
17 |
18 | ## Installation
19 |
20 | ember install ember-bootstrap-changeset-validations
21 |
22 | You should have installed the ember-bootstrap and ember-changeset addons already. If not install them:
23 |
24 | ```
25 | ember install ember-bootstrap
26 | ember install ember-changeset
27 | ```
28 |
29 | You probably also want to install [ember-changeset-validations](https://github.com/poteto/ember-changeset-validations/)
30 | if you do not have a custom validation implementation:
31 |
32 | ```
33 | ember install ember-changeset-validations
34 | ```
35 |
36 | ## Usage
37 |
38 | Define your model and its validations as described in [ember-changeset-validations](https://github.com/poteto/ember-changeset-validations/).
39 | Then assign the changeset based on that to your form:
40 |
41 | ```hbs
42 |
43 |
44 |
45 |
50 | Submit
51 |
52 | ```
53 |
54 | ## Authors
55 |
56 | - [Simon Ihmig](https://github.com/simonihmig) @ [kaliber5](http://www.kaliber5.de)
57 | - [Jeldrik Hanschke](https://github.com/jelhan)
58 |
59 | ## Contributing
60 |
61 | See the [Contributing](CONTRIBUTING.md) guide for details.
62 |
63 | ## Copyright and license
64 |
65 | Code and documentation copyright 2017 kaliber5 GmbH and contributors. Code released under [the MIT license](LICENSE.md).
66 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Debugging:
3 | * https://eslint.org/docs/latest/use/configure/debug
4 | * ----------------------------------------------------
5 | *
6 | * Print a file's calculated configuration
7 | *
8 | * npx eslint --print-config path/to/file.js
9 | *
10 | * Inspecting the config
11 | *
12 | * npx eslint --inspect-config
13 | *
14 | */
15 | import globals from 'globals';
16 | import js from '@eslint/js';
17 |
18 | import ember from 'eslint-plugin-ember/recommended';
19 | import eslintConfigPrettier from 'eslint-config-prettier';
20 | import qunit from 'eslint-plugin-qunit';
21 | import n from 'eslint-plugin-n';
22 |
23 | import babelParser from '@babel/eslint-parser';
24 |
25 | const esmParserOptions = {
26 | ecmaFeatures: { modules: true },
27 | ecmaVersion: 'latest',
28 | requireConfigFile: false,
29 | babelOptions: {
30 | plugins: [
31 | ['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
32 | ],
33 | },
34 | };
35 |
36 | export default [
37 | js.configs.recommended,
38 | eslintConfigPrettier,
39 | ember.configs.base,
40 | ember.configs.gjs,
41 | /**
42 | * Ignores must be in their own object
43 | * https://eslint.org/docs/latest/use/configure/ignore
44 | */
45 | {
46 | ignores: ['dist/', 'node_modules/', 'coverage/', '!**/.*'],
47 | },
48 | /**
49 | * https://eslint.org/docs/latest/use/configure/configuration-files#configuring-linter-options
50 | */
51 | {
52 | linterOptions: {
53 | reportUnusedDisableDirectives: 'error',
54 | },
55 | },
56 | {
57 | files: ['**/*.js'],
58 | languageOptions: {
59 | parser: babelParser,
60 | },
61 | },
62 | {
63 | files: ['**/*.{js,gjs}'],
64 | languageOptions: {
65 | parserOptions: esmParserOptions,
66 | globals: {
67 | ...globals.browser,
68 | },
69 | },
70 | },
71 | {
72 | files: ['tests/**/*-test.{js,gjs}'],
73 | plugins: {
74 | qunit,
75 | },
76 | },
77 | /**
78 | * CJS node files
79 | */
80 | {
81 | files: [
82 | '**/*.cjs',
83 | 'config/**/*.js',
84 | 'tests/dummy/config/**/*.js',
85 | 'testem.js',
86 | 'testem*.js',
87 | 'index.js',
88 | '.prettierrc.js',
89 | '.stylelintrc.js',
90 | '.template-lintrc.js',
91 | 'ember-cli-build.js',
92 | ],
93 | plugins: {
94 | n,
95 | },
96 |
97 | languageOptions: {
98 | sourceType: 'script',
99 | ecmaVersion: 'latest',
100 | globals: {
101 | ...globals.node,
102 | },
103 | },
104 | },
105 | /**
106 | * ESM node files
107 | */
108 | {
109 | files: ['**/*.mjs'],
110 | plugins: {
111 | n,
112 | },
113 |
114 | languageOptions: {
115 | sourceType: 'module',
116 | ecmaVersion: 'latest',
117 | parserOptions: esmParserOptions,
118 | globals: {
119 | ...globals.node,
120 | },
121 | },
122 | },
123 | ];
124 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-bootstrap-changeset-validations",
3 | "version": "6.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 | "format": "prettier . --cache --write",
23 | "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto",
24 | "lint:css": "stylelint \"**/*.css\"",
25 | "lint:css:fix": "concurrently \"pnpm:lint:css -- --fix\"",
26 | "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm format",
27 | "lint:format": "prettier . --cache --check",
28 | "lint:hbs": "ember-template-lint .",
29 | "lint:hbs:fix": "ember-template-lint . --fix",
30 | "lint:js": "eslint . --cache",
31 | "lint:js:fix": "eslint . --fix",
32 | "start": "ember serve",
33 | "test": "concurrently \"pnpm:lint\" \"pnpm:test:*\" --names \"lint,test:\" --prefixColors auto",
34 | "test:ember": "ember test",
35 | "test:ember-compatibility": "ember try:each"
36 | },
37 | "dependencies": {
38 | "@babel/core": "^7.27.1",
39 | "ember-cli-babel": "^8.2.0",
40 | "ember-cli-htmlbars": "^6.3.0",
41 | "ember-template-imports": "^4.3.0"
42 | },
43 | "devDependencies": {
44 | "@babel/eslint-parser": "7.28.5",
45 | "@babel/plugin-proposal-decorators": "7.28.0",
46 | "@ember/optional-features": "2.3.0",
47 | "@ember/string": "4.0.1",
48 | "@ember/test-helpers": "5.4.1",
49 | "@embroider/macros": "1.19.5",
50 | "@embroider/test-setup": "4.0.0",
51 | "@eslint/js": "9.39.2",
52 | "@glimmer/component": "2.0.0",
53 | "@glimmer/tracking": "1.1.2",
54 | "@release-it-plugins/lerna-changelog": "8.0.1",
55 | "bootstrap": "5.3.8",
56 | "broccoli-asset-rev": "3.0.0",
57 | "concurrently": "9.2.1",
58 | "ember-auto-import": "2.12.0",
59 | "ember-bootstrap": "6.7.0",
60 | "ember-changeset": "5.0.0",
61 | "ember-changeset-validations": "5.0.0",
62 | "ember-cli": "6.9.1",
63 | "ember-cli-clean-css": "3.0.0",
64 | "ember-cli-dependency-checker": "3.3.3",
65 | "ember-cli-deprecation-workflow": "3.4.0",
66 | "ember-cli-inject-live-reload": "2.1.0",
67 | "ember-cli-sri": "2.1.1",
68 | "ember-cli-terser": "4.0.2",
69 | "ember-focus-trap": "1.1.1",
70 | "ember-load-initializers": "3.0.1",
71 | "ember-page-title": "9.0.3",
72 | "ember-qunit": "9.0.4",
73 | "ember-resolver": "13.1.1",
74 | "ember-source": "6.9.0",
75 | "ember-source-channel-url": "3.0.0",
76 | "ember-template-lint": "7.9.3",
77 | "ember-try": "4.0.0",
78 | "eslint": "9.39.2",
79 | "eslint-config-prettier": "10.1.8",
80 | "eslint-plugin-ember": "12.7.5",
81 | "eslint-plugin-n": "17.23.1",
82 | "eslint-plugin-qunit": "8.2.5",
83 | "globals": "16.5.0",
84 | "loader.js": "4.7.0",
85 | "prettier": "3.7.4",
86 | "prettier-plugin-ember-template-tag": "2.1.2",
87 | "qunit": "2.24.3",
88 | "qunit-dom": "3.5.0",
89 | "release-it": "19.1.0",
90 | "stylelint": "16.26.1",
91 | "stylelint-config-standard": "39.0.1",
92 | "webpack": "5.104.1"
93 | },
94 | "peerDependencies": {
95 | "ember-bootstrap": ">=5.0.0",
96 | "ember-changeset-validations": ">=4.0.0",
97 | "ember-source": ">=3.28.0"
98 | },
99 | "engines": {
100 | "node": "20.* || 22.* || >= 24"
101 | },
102 | "publishConfig": {
103 | "registry": "https://registry.npmjs.org"
104 | },
105 | "ember": {
106 | "edition": "octane"
107 | },
108 | "ember-addon": {
109 | "configPath": "tests/dummy/config",
110 | "after": "ember-bootstrap"
111 | },
112 | "release-it": {
113 | "plugins": {
114 | "@release-it-plugins/lerna-changelog": {
115 | "infile": "CHANGELOG.md",
116 | "launchEditor": true
117 | }
118 | },
119 | "git": {
120 | "tagName": "v${version}"
121 | },
122 | "github": {
123 | "release": true,
124 | "tokenRef": "GITHUB_AUTH"
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v6.0.0 (2025-09-30)
2 |
3 | #### :boom: Breaking Change
4 |
5 | - [#75](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/75) drop support for Ember < 3.28 and Ember classic ([@jelhan](https://github.com/jelhan))
6 | - [#74](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/74) drop support for Node < 20 ([@jelhan](https://github.com/jelhan))
7 |
8 | #### :rocket: Enhancement
9 |
10 | - [#109](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/109) declare Ember Bootstrap and Ember Changeset Validations compatbility per peer dependencies ([@jelhan](https://github.com/jelhan))
11 |
12 | #### :house: Internal
13 |
14 | - [#106](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/106) Upgrade with Ember CLI v6.4 blueprints ([@jelhan](https://github.com/jelhan))
15 | - [#91](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/91) Upgrade with Ember CLI v5.12 blueprints ([@jelhan](https://github.com/jelhan))
16 | - [#89](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/89) ensure test coverage with ember-changeset v4 ([@jelhan](https://github.com/jelhan))
17 | - [#86](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/86) Upgrade with Ember CLI 4.12 blueprints ([@jelhan](https://github.com/jelhan))
18 | - [#76](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/76) ensure test coverage with Ember 4.12 ([@jelhan](https://github.com/jelhan))
19 | - [#77](https://github.com/ember-bootstrap/ember-bootstrap-changeset-validations/pull/77) migrate to pnpm ([@jelhan](https://github.com/jelhan))
20 |
21 | #### Committers: 1
22 |
23 | - Jeldrik Hanschke ([@jelhan](https://github.com/jelhan))
24 |
25 | ## v5.0.0 (2022-04-29)
26 |
27 | #### :boom: Breaking Change
28 |
29 | - [#43](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/43) Drop support for ember-bootstrap v4 ([@simonihmig](https://github.com/simonihmig))
30 | - [#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))
31 |
32 | #### :rocket: Enhancement
33 |
34 | - [#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))
35 | - [#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))
36 |
37 | #### Committers: 1
38 |
39 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig))
40 |
41 | ## v4.0.0 (2020-12-04)
42 |
43 | #### :boom: Breaking Change
44 |
45 | - [#34](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/34) drop support for Ember Bootstrap <= v4.4 ([@jelhan](https://github.com/jelhan))
46 |
47 | #### :rocket: Enhancement
48 |
49 | - [#34](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/34) support Ember Bootstrap v4.5 and above ([@jelhan](https://github.com/jelhan))
50 |
51 | #### :memo: Documentation
52 |
53 | - [#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))
54 |
55 | #### :house: Internal
56 |
57 | - [#35](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/35) migrate CI from TravisCI to GitHub actions ([@jelhan](https://github.com/jelhan))
58 |
59 | #### Committers: 2
60 |
61 | - Jeldrik Hanschke ([@jelhan](https://github.com/jelhan))
62 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig))
63 |
64 | ## v3.1.2 (2020-08-13)
65 |
66 | #### :bug: Bug Fix
67 |
68 | - [#29](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/29) support multiple validation errors ([@basz](https://github.com/basz))
69 |
70 | #### Committers: 1
71 |
72 | - Bas Kamer ([@basz](https://github.com/basz))
73 |
74 | ## v3.1.1 (2020-08-02)
75 |
76 | #### :bug: Bug Fix
77 |
78 | - [#28](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/28) fix validation for nested properties ([@basz](https://github.com/basz))
79 |
80 | #### Committers: 1
81 |
82 | - Bas Kamer ([@basz](https://github.com/basz))
83 |
84 | ## v3.1.0 (2020-07-20)
85 |
86 | #### :bug: Bug Fix
87 |
88 | - [#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))
89 | - [#25](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/25) hide deprecation notice when subclassing ember bootstrap classes ([@basz](https://github.com/basz))
90 |
91 | #### Committers: 2
92 |
93 | - Bas Kamer ([@basz](https://github.com/basz))
94 | - Jeldrik Hanschke ([@jelhan](https://github.com/jelhan))
95 |
96 | ## v3.0.0 (2020-03-31)
97 |
98 | #### :boom: Breaking Change
99 |
100 | - [#19](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/19) upgrade ember changeset to v3 ([@jelhan](https://github.com/jelhan))
101 | - [#18](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/18) upgrade dependencies except ember-changeset ([@jelhan](https://github.com/jelhan))
102 |
103 | #### :house: Internal
104 |
105 | - [#20](https://github.com/kaliber5/ember-bootstrap-changeset-validations/pull/20) automate releases with release-it and lerna-changelog ([@jelhan](https://github.com/jelhan))
106 |
107 | #### Committers: 1
108 |
109 | - Jeldrik Hanschke ([@jelhan](https://github.com/jelhan))
110 |
--------------------------------------------------------------------------------
/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 | let model = {
32 | name: '1234',
33 | };
34 |
35 | this.set('model', model);
36 | this.set('validation', validation);
37 | this.submitAction = function () {
38 | assert.step('submit action has been called.');
39 | };
40 | this.invalidAction = function () {
41 | assert.ok(false, 'Invalid action must not been called.');
42 | };
43 |
44 | await render(hbs`
45 |
46 |
47 |
48 | `);
49 |
50 | await triggerEvent('form', 'submit');
51 | assert.verifySteps(['submit action has been called.']);
52 | });
53 |
54 | test('validation errors are shown on submit', async function (assert) {
55 | let model = {
56 | name: '',
57 | };
58 |
59 | this.set('model', model);
60 | this.set('validation', validation);
61 | this.submitAction = function () {
62 | assert.ok(false, 'submit action must not been called.');
63 | };
64 | this.invalidAction = function () {
65 | assert.step('Invalid action has been called.');
66 | };
67 |
68 | await render(hbs`
69 |
70 |
71 |
72 | `);
73 |
74 | await triggerEvent('form', 'submit');
75 | assert.dom('input').hasClass('is-invalid', 'input has error class');
76 | assert.verifySteps(['Invalid action has been called.']);
77 | });
78 |
79 | test('validation nested errors are shown on submit', async function (assert) {
80 | let model = {
81 | nested: { name: '' },
82 | };
83 |
84 | this.set('model', model);
85 | this.set('validation', nestedValidation);
86 | this.submitAction = function () {
87 | assert.ok(false, 'submit action must not been called.');
88 | };
89 | this.invalidAction = function () {
90 | assert.step('Invalid action has been called.');
91 | };
92 |
93 | await render(hbs`
94 |
95 |
96 |
97 | `);
98 |
99 | await triggerEvent('form', 'submit');
100 | assert.dom('input').hasClass('is-invalid', 'input has error class');
101 | assert.verifySteps(['Invalid action has been called.']);
102 | });
103 |
104 | test('validation errors are shown after blur', async function (assert) {
105 | this.set('model', { name: '' });
106 | this.set('validation', validation);
107 |
108 | await render(hbs`
109 |
110 |
111 |
112 | `);
113 | assert.dom('input').doesNotHaveClass('is-invalid');
114 |
115 | await focus('input');
116 | await blur('input');
117 | assert.dom('input').hasClass('is-invalid');
118 | });
119 |
120 | test('validation success is shown after blur', async function (assert) {
121 | this.set('model', { name: 'Clara' });
122 | this.set('validation', validation);
123 |
124 | await render(hbs`
125 |
126 |
127 |
128 | `);
129 | assert.dom('input').doesNotHaveClass('is-valid');
130 |
131 | await focus('input');
132 | await blur('input');
133 | assert.dom('input').hasClass('is-valid');
134 | });
135 |
136 | test('validation errors are shown after user input', async function (assert) {
137 | this.set('model', { name: '' });
138 | this.set('validation', validation);
139 |
140 | await render(hbs`
141 |
142 |
143 |
144 | `);
145 | assert.dom('input').doesNotHaveClass('is-invalid');
146 |
147 | await fillIn('input', 'R');
148 | assert
149 | .dom('input')
150 | .doesNotHaveClass(
151 | 'is-invalid',
152 | 'validation is not shown while user is typing',
153 | );
154 |
155 | await blur('input');
156 | assert
157 | .dom('input')
158 | .hasClass('is-invalid', 'validation error is shown after focus out');
159 | });
160 |
161 | test('validation success is shown after user input', async function (assert) {
162 | this.set('model', { name: '' });
163 | this.set('validation', validation);
164 |
165 | await render(hbs`
166 |
167 |
168 |
169 | `);
170 | assert.dom('input').doesNotHaveClass('is-valid');
171 |
172 | await fillIn('input', 'Rosa');
173 | assert
174 | .dom('input')
175 | .doesNotHaveClass(
176 | 'is-valid',
177 | 'validation is not shown while user is typing',
178 | );
179 |
180 | await blur('input');
181 | assert
182 | .dom('input')
183 | .hasClass('is-valid', 'validation error is shown after focus out');
184 | });
185 |
186 | test('does not break forms which are not using a changeset as model', async function (assert) {
187 | this.set('model', { name: '' });
188 | this.set('submitAction', () => {
189 | assert.step('submit action has been called');
190 | });
191 |
192 | await render(hbs`
193 |
194 |
195 |
196 | `);
197 | assert.dom('input').doesNotHaveClass('is-valid');
198 | assert.dom('input').doesNotHaveClass('is-invalid');
199 |
200 | await fillIn('input', 'Rosa');
201 | await blur('input');
202 | assert.dom('input').doesNotHaveClass('is-valid');
203 | assert.dom('input').doesNotHaveClass('is-invalid');
204 |
205 | await triggerEvent('form', 'submit');
206 | assert.dom('input').doesNotHaveClass('is-valid');
207 | assert.dom('input').doesNotHaveClass('is-invalid');
208 | assert.verifySteps(['submit action has been called']);
209 | });
210 |
211 | test('does not break for forms which are not having a model at all', async function (assert) {
212 | this.set('submitAction', () => {
213 | assert.step('submit action has been called');
214 | });
215 | this.set('noop', () => {});
216 |
217 | await render(hbs`
218 |
219 |
220 |
221 | `);
222 | assert.dom('input').doesNotHaveClass('is-valid');
223 | assert.dom('input').doesNotHaveClass('is-invalid');
224 |
225 | await fillIn('input', 'Rosa');
226 | await blur('input');
227 | assert.dom('input').doesNotHaveClass('is-valid');
228 | assert.dom('input').doesNotHaveClass('is-invalid');
229 |
230 | await triggerEvent('form', 'submit');
231 | assert.dom('input').doesNotHaveClass('is-valid');
232 | assert.dom('input').doesNotHaveClass('is-invalid');
233 | assert.verifySteps(['submit action has been called']);
234 | });
235 |
236 | test('invalid-feedback is shown from single validation', async function (assert) {
237 | let model = {
238 | name: '',
239 | };
240 |
241 | this.set('model', model);
242 | this.set('validation', {
243 | name: validatePresence(true),
244 | });
245 |
246 | await render(hbs`
247 |
248 |
249 |
250 | `);
251 |
252 | await triggerEvent('form', 'submit');
253 | assert.dom('.invalid-feedback').hasText("Name can't be blank");
254 | });
255 |
256 | test('invalid-feedback is shown in order from multiple validations', async function (assert) {
257 | let model = {
258 | name: '',
259 | };
260 |
261 | this.set('model', model);
262 | this.set('validation', {
263 | name: [validatePresence(true), validateLength({ min: 4 })],
264 | });
265 |
266 | await render(hbs`
267 |
268 |
269 |
270 | `);
271 |
272 | await triggerEvent('form', 'submit');
273 | assert.dom('.invalid-feedback').hasText("Name can't be blank");
274 |
275 | await fillIn('input', 'R');
276 | await triggerEvent('form', 'submit');
277 | assert
278 | .dom('.invalid-feedback')
279 | .hasText('Name is too short (minimum is 4 characters)');
280 | });
281 |
282 | test('invalid-feedback is shown (multiple messages) in order from multiple validations', async function (assert) {
283 | let model = {
284 | name: '',
285 | };
286 |
287 | this.set('model', model);
288 | this.set('validation', {
289 | name: [validatePresence(true), validateLength({ min: 4 })],
290 | });
291 |
292 | await render(hbs`
293 |
294 |
295 |
296 | `);
297 |
298 | await triggerEvent('form', 'submit');
299 |
300 | let feedbackElements = findAll('.invalid-feedback');
301 | let results = Array.from(feedbackElements, (element) =>
302 | element.textContent.trim(),
303 | );
304 | let expected = [
305 | "Name can't be blank",
306 | 'Name is too short (minimum is 4 characters)',
307 | ];
308 |
309 | expected.forEach((message) => {
310 | assert.ok(results.includes(message));
311 | });
312 | });
313 |
314 | test('no feedback is shown for nonexistant validations', async function (assert) {
315 | let model = {
316 | name: '',
317 | };
318 |
319 | this.set('model', model);
320 | this.set('validation', {
321 | nombre: validatePresence(true),
322 | });
323 |
324 | await render(hbs`
325 |
326 |
327 |
328 | `);
329 |
330 | await triggerEvent('form', 'submit');
331 | assert.dom('.invalid-feedback').doesNotExist();
332 | });
333 | });
334 |
--------------------------------------------------------------------------------