@heroku/ember-hk-components 0.21.2 → 1.21.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +12 -0
- package/.github/workflows/sync_component_metadata.yml +25 -0
- package/.heroku/components-inventory/ember-hk-component.json +33 -0
- package/BABEL_TRAVERSE_VULNERABILITY_GUIDE.md +245 -0
- package/CLAUDE.md +122 -0
- package/CODEOWNERS +5 -0
- package/MIGRATION_GUIDE.md +287 -0
- package/Procfile +1 -0
- package/README.md +71 -14
- package/addon/components/hk-field-validations.js +116 -56
- package/addon/components/hk-validation-errors-list.js +4 -0
- package/addon/templates/components/hk-field-validations.hbs +7 -7
- package/addon/templates/components/hk-validation-errors-list.hbs +4 -4
- package/app.json +3 -3
- package/config/nginx.conf.erb +104 -0
- package/package.json +30 -23
- package/pnpm-workspace.yaml +4 -0
- package/.eslintcache +0 -1
- package/yarn-error.log +0 -17123
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide helps you migrate from the previous version of `ember-hk-components` to the latest version. Please read through all sections carefully as there are several important changes that may affect your application.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This update includes significant improvements to validation handling, dependency upgrades, and package management changes. The most notable changes are:
|
|
8
|
+
|
|
9
|
+
- Enhanced validation state management in `hk-field-validations`
|
|
10
|
+
- Major dependency upgrades (`ember-changeset-validations` v2 → v4)
|
|
11
|
+
- Migration from Yarn to pnpm
|
|
12
|
+
- Improved accessibility and modern Ember template syntax
|
|
13
|
+
|
|
14
|
+
## Breaking Changes
|
|
15
|
+
|
|
16
|
+
### 1. Dependency Updates
|
|
17
|
+
|
|
18
|
+
#### ember-changeset-validations (v2.2.1 → v4.0.0)
|
|
19
|
+
|
|
20
|
+
**Impact**: Major version upgrade with potential breaking changes.
|
|
21
|
+
|
|
22
|
+
**Action Required**:
|
|
23
|
+
1. Review the [ember-changeset-validations v4 changelog](https://github.com/poteto/ember-changeset-validations/releases)
|
|
24
|
+
2. Update your validation schemas if needed
|
|
25
|
+
3. Test all form validation thoroughly
|
|
26
|
+
|
|
27
|
+
#### ember-a11y-testing (v4.4.0 → v5.2.1)
|
|
28
|
+
|
|
29
|
+
**Impact**: Major version upgrade affecting accessibility testing.
|
|
30
|
+
|
|
31
|
+
**Action Required**:
|
|
32
|
+
1. Review the [ember-a11y-testing v5 migration guide](https://github.com/ember-a11y/ember-a11y-testing/releases)
|
|
33
|
+
2. Update your test files if using this addon directly
|
|
34
|
+
|
|
35
|
+
### 2. Package Manager Migration (Yarn → pnpm)
|
|
36
|
+
|
|
37
|
+
**Impact**: The project now enforces pnpm usage and removes yarn.lock.
|
|
38
|
+
|
|
39
|
+
**Action Required**:
|
|
40
|
+
```bash
|
|
41
|
+
# Remove yarn.lock if present
|
|
42
|
+
rm yarn.lock
|
|
43
|
+
|
|
44
|
+
# Install pnpm if not already installed
|
|
45
|
+
npm install -g pnpm
|
|
46
|
+
|
|
47
|
+
# Install dependencies
|
|
48
|
+
pnpm install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Note**: The addon now includes a preinstall hook that enforces pnpm usage.
|
|
52
|
+
|
|
53
|
+
### 3. Node.js Engine Requirements
|
|
54
|
+
|
|
55
|
+
**Impact**: Removed explicit Node.js engine constraints (previously required `14.x || 16.x`).
|
|
56
|
+
|
|
57
|
+
**Action Required**: Ensure you're using a supported Node.js version for your Ember CLI version.
|
|
58
|
+
|
|
59
|
+
## Component Behavior Changes
|
|
60
|
+
|
|
61
|
+
### hk-field-validations Component
|
|
62
|
+
|
|
63
|
+
The validation logic has been significantly refactored to provide more reliable validation state management.
|
|
64
|
+
|
|
65
|
+
#### Key Changes
|
|
66
|
+
|
|
67
|
+
1. **Validation State Tracking**
|
|
68
|
+
- New internal `_hasValidatedFlag` for precise validation tracking
|
|
69
|
+
- `hasValidated` now considers both manual validation and changeset pristine state
|
|
70
|
+
- Improved handling of async validation promises
|
|
71
|
+
|
|
72
|
+
2. **Enhanced Validation Timing**
|
|
73
|
+
- Better debounced validation with improved error handling
|
|
74
|
+
- More robust cleanup of observers and validation state
|
|
75
|
+
- Improved handling of component destruction during validation
|
|
76
|
+
|
|
77
|
+
3. **Changeset Integration**
|
|
78
|
+
- `hasValidated` now returns `true` when changeset is no longer pristine
|
|
79
|
+
- This means validation errors may appear earlier than before
|
|
80
|
+
- Better integration with changeset lifecycle events
|
|
81
|
+
|
|
82
|
+
#### Migration Steps
|
|
83
|
+
|
|
84
|
+
1. **Test Validation Behavior**
|
|
85
|
+
```javascript
|
|
86
|
+
// Before: Validation only triggered after explicit user interaction
|
|
87
|
+
// After: Validation may trigger when changeset becomes non-pristine
|
|
88
|
+
|
|
89
|
+
// Test these scenarios:
|
|
90
|
+
// - Initial form load
|
|
91
|
+
// - First field interaction
|
|
92
|
+
// - Async validation timing
|
|
93
|
+
// - Form reset behavior
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
2. **Review Validation Timing**
|
|
97
|
+
If your application relies on specific validation timing, you may need to adjust:
|
|
98
|
+
```javascript
|
|
99
|
+
// If you need the old behavior, you can check the internal flag:
|
|
100
|
+
// component.get('_hasValidatedFlag') // true only after explicit validation
|
|
101
|
+
|
|
102
|
+
// New behavior considers changeset state:
|
|
103
|
+
// component.get('hasValidated') // true when validated OR changeset not pristine
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### hk-validation-errors-list Component
|
|
107
|
+
|
|
108
|
+
#### New Features
|
|
109
|
+
|
|
110
|
+
- Added `errorsListId` computed property for better accessibility
|
|
111
|
+
- Generates IDs like `validation-errors-${property}`
|
|
112
|
+
|
|
113
|
+
#### Migration Steps
|
|
114
|
+
|
|
115
|
+
**No action required** - this is a backward-compatible addition.
|
|
116
|
+
|
|
117
|
+
## Template Syntax Updates
|
|
118
|
+
|
|
119
|
+
All component templates now use modern Ember syntax with `this.` prefix.
|
|
120
|
+
|
|
121
|
+
**Before**:
|
|
122
|
+
```handlebars
|
|
123
|
+
{{yield (hash
|
|
124
|
+
value=value
|
|
125
|
+
isValidating=isValidating
|
|
126
|
+
isValid=isValid
|
|
127
|
+
isInvalid=isInvalid
|
|
128
|
+
errors=validationErrors
|
|
129
|
+
)}}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**After**:
|
|
133
|
+
```handlebars
|
|
134
|
+
{{yield (hash
|
|
135
|
+
value=this.value
|
|
136
|
+
isValidating=this.isValidating
|
|
137
|
+
isValid=this.isValid
|
|
138
|
+
isInvalid=this.isInvalid
|
|
139
|
+
errors=this.validationErrors
|
|
140
|
+
)}}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Impact**: No breaking changes for consumers, but improves consistency with modern Ember practices.
|
|
144
|
+
|
|
145
|
+
## Development Environment Changes
|
|
146
|
+
|
|
147
|
+
### ESLint Configuration
|
|
148
|
+
|
|
149
|
+
- Updated to use `@babel/eslint-parser`
|
|
150
|
+
- Removed `babel-eslint` dependency
|
|
151
|
+
|
|
152
|
+
### Removed Dependencies
|
|
153
|
+
|
|
154
|
+
- `ember-cli-chai` - removed from devDependencies
|
|
155
|
+
- `npm-run-all` - replaced with direct pnpm commands
|
|
156
|
+
|
|
157
|
+
### Security Updates
|
|
158
|
+
|
|
159
|
+
Added package resolutions for security vulnerabilities:
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"resolutions": {
|
|
163
|
+
"lodash.template": "npm:lodash@^4.17.21",
|
|
164
|
+
"braces": "npm:braces@^3.0.3",
|
|
165
|
+
"rollup": "^4.50.1",
|
|
166
|
+
"json5": "^2.2.3",
|
|
167
|
+
"ansi-html": "^0.0.8",
|
|
168
|
+
"consolidate": "npm:@ladjs/consolidate@^1.0.0",
|
|
169
|
+
"validated-changeset": "1.4.1"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Testing Recommendations
|
|
175
|
+
|
|
176
|
+
### 1. Validation Testing
|
|
177
|
+
|
|
178
|
+
Create comprehensive tests for validation behavior:
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
import { module, test } from 'qunit';
|
|
182
|
+
import { setupRenderingTest } from 'ember-qunit';
|
|
183
|
+
import { render, fillIn, blur } from '@ember/test-helpers';
|
|
184
|
+
import { hbs } from 'ember-cli-htmlbars';
|
|
185
|
+
|
|
186
|
+
module('Integration | Component | your-form', function(hooks) {
|
|
187
|
+
setupRenderingTest(hooks);
|
|
188
|
+
|
|
189
|
+
test('validation timing works correctly', async function(assert) {
|
|
190
|
+
// Test initial state
|
|
191
|
+
await render(hbs`{{your-form-component}}`);
|
|
192
|
+
|
|
193
|
+
// Test validation on changeset modification
|
|
194
|
+
await fillIn('input', 'invalid-value');
|
|
195
|
+
// Assert validation state
|
|
196
|
+
|
|
197
|
+
// Test validation on blur
|
|
198
|
+
await blur('input');
|
|
199
|
+
// Assert validation state
|
|
200
|
+
|
|
201
|
+
// Test async validation
|
|
202
|
+
await fillIn('input', 'async-validation-trigger');
|
|
203
|
+
// Wait for validation to complete
|
|
204
|
+
// Assert final state
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 2. Accessibility Testing
|
|
210
|
+
|
|
211
|
+
If using `ember-a11y-testing`, update your tests:
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
// Update import if needed
|
|
215
|
+
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
|
216
|
+
|
|
217
|
+
test('accessibility compliance', async function(assert) {
|
|
218
|
+
await render(hbs`{{your-component}}`);
|
|
219
|
+
await a11yAudit(this.element);
|
|
220
|
+
assert.ok(true, 'no a11y errors found');
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 3. Integration Testing
|
|
225
|
+
|
|
226
|
+
Test the complete validation flow:
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
test('complete validation flow', async function(assert) {
|
|
230
|
+
// Test initial pristine state
|
|
231
|
+
// Test first interaction
|
|
232
|
+
// Test validation errors display
|
|
233
|
+
// Test error clearing
|
|
234
|
+
// Test form submission with errors
|
|
235
|
+
// Test successful validation
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Troubleshooting
|
|
240
|
+
|
|
241
|
+
### Common Issues
|
|
242
|
+
|
|
243
|
+
1. **Validation errors appear too early**
|
|
244
|
+
- This is likely due to the new `hasValidated` logic
|
|
245
|
+
- Review your form initialization and changeset setup
|
|
246
|
+
- Consider if the new behavior is actually more user-friendly
|
|
247
|
+
|
|
248
|
+
2. **Async validation issues**
|
|
249
|
+
- The component now has better handling of async validation
|
|
250
|
+
- Check that your validation functions return promises correctly
|
|
251
|
+
- Ensure proper error handling in your validators
|
|
252
|
+
|
|
253
|
+
3. **Package installation issues**
|
|
254
|
+
- Make sure you're using pnpm, not npm or yarn
|
|
255
|
+
- Clear node_modules and reinstall if needed: `rm -rf node_modules && pnpm install`
|
|
256
|
+
|
|
257
|
+
4. **ESLint errors**
|
|
258
|
+
- Update your ESLint configuration if you have custom rules
|
|
259
|
+
- The addon now uses `@babel/eslint-parser`
|
|
260
|
+
|
|
261
|
+
### Getting Help
|
|
262
|
+
|
|
263
|
+
If you encounter issues during migration:
|
|
264
|
+
|
|
265
|
+
1. Check the component documentation
|
|
266
|
+
2. Review the test files for usage examples
|
|
267
|
+
3. Open an issue with a minimal reproduction case
|
|
268
|
+
|
|
269
|
+
## Rollback Plan
|
|
270
|
+
|
|
271
|
+
If you need to rollback:
|
|
272
|
+
|
|
273
|
+
1. Revert to the previous version in package.json
|
|
274
|
+
2. Run `pnpm install` (or your package manager)
|
|
275
|
+
3. Restore any custom validation logic you may have modified
|
|
276
|
+
|
|
277
|
+
## Summary
|
|
278
|
+
|
|
279
|
+
This update significantly improves the reliability and user experience of form validation while maintaining backward compatibility for most use cases. The main areas requiring attention are:
|
|
280
|
+
|
|
281
|
+
- Validation timing behavior (may show errors sooner)
|
|
282
|
+
- Package manager migration to pnpm
|
|
283
|
+
- Testing of async validation scenarios
|
|
284
|
+
- Dependency compatibility (especially ember-changeset-validations v4)
|
|
285
|
+
|
|
286
|
+
Take time to thoroughly test your forms after upgrading, particularly focusing on validation timing and error display behavior.
|
|
287
|
+
|
package/Procfile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
web: bin/start-nginx-static
|
package/README.md
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
# Ember HK Components
|
|
2
2
|
|
|
3
|
-
Reusable Ember components
|
|
3
|
+
Reusable Ember components for Heroku applications.
|
|
4
4
|
|
|
5
5
|
## Assumptions
|
|
6
6
|
|
|
7
7
|
Usage of these components assumes you are using the [Purple3 CSS framework](https://purple3.herokuapp.com/) and [Malibu](https://hk-malibu.herokuapp.com).
|
|
8
8
|
|
|
9
|
+
## Security
|
|
10
|
+
|
|
11
|
+
This project maintains high security standards and regularly addresses vulnerabilities through:
|
|
12
|
+
|
|
13
|
+
- **Automated vulnerability scanning** via `pnpm audit`
|
|
14
|
+
- **Strategic dependency resolutions** to address transitive vulnerabilities
|
|
15
|
+
- **Regular dependency updates** while maintaining compatibility
|
|
16
|
+
- **Comprehensive security documentation** (see `BABEL_TRAVERSE_VULNERABILITY_GUIDE.md`)
|
|
17
|
+
|
|
18
|
+
For security-related questions or to report vulnerabilities, please follow Heroku's security guidelines.
|
|
19
|
+
|
|
9
20
|
## Usage
|
|
10
21
|
|
|
11
22
|
### Installation
|
|
12
23
|
|
|
13
|
-
1. Install `ember-cli-eyeglass` if it's not installed already
|
|
14
|
-
|
|
24
|
+
1. Install `ember-cli-eyeglass` if it's not installed already:
|
|
25
|
+
```bash
|
|
26
|
+
ember install ember-cli-eyeglass
|
|
27
|
+
```
|
|
15
28
|
|
|
16
|
-
2. Install `@heroku/ember-hk-components
|
|
17
|
-
|
|
29
|
+
2. Install `@heroku/ember-hk-components`:
|
|
30
|
+
```bash
|
|
31
|
+
ember install @heroku/ember-hk-components
|
|
32
|
+
```
|
|
18
33
|
|
|
19
34
|
#### CSS
|
|
20
35
|
|
|
@@ -39,7 +54,7 @@ See [ember-hk-components.herokuapp.com](https://ember-hk-components.herokuapp.co
|
|
|
39
54
|
|
|
40
55
|
* `git clone https://github.com/heroku/ember-hk-components`
|
|
41
56
|
* `cd ember-hk-components`
|
|
42
|
-
* `
|
|
57
|
+
* `pnpm install`
|
|
43
58
|
|
|
44
59
|
### Running
|
|
45
60
|
|
|
@@ -48,27 +63,69 @@ See [ember-hk-components.herokuapp.com](https://ember-hk-components.herokuapp.co
|
|
|
48
63
|
|
|
49
64
|
### Running Tests
|
|
50
65
|
|
|
51
|
-
* `
|
|
66
|
+
* `pnpm test` (Runs `ember try:each` to test your addon against multiple Ember versions)
|
|
52
67
|
* `ember test`
|
|
53
68
|
* `ember test --server`
|
|
54
69
|
|
|
70
|
+
### Security Auditing
|
|
71
|
+
|
|
72
|
+
This project uses PNPM for enhanced security and performance:
|
|
73
|
+
|
|
74
|
+
* `pnpm audit` - Check for security vulnerabilities
|
|
75
|
+
* `pnpm audit --fix` - Automatically fix resolvable vulnerabilities
|
|
76
|
+
|
|
77
|
+
See `BABEL_TRAVERSE_VULNERABILITY_GUIDE.md` for detailed security resolution strategies.
|
|
78
|
+
|
|
55
79
|
### Local Usage in Another Application
|
|
56
80
|
|
|
57
|
-
The demo app is useful for developing this addon, but it can often be helpful to consume your version of this addon in another application either to more easily develop your changes or to validate that your changes work as you expect.
|
|
81
|
+
The demo app is useful for developing this addon, but it can often be helpful to consume your version of this addon in another application either to more easily develop your changes or to validate that your changes work as you expect. You can use your local version of `ember-hk-components` in another application that consumes it via PNPM's [link](https://pnpm.io/cli/link) command.
|
|
58
82
|
|
|
59
83
|
```sh
|
|
60
84
|
// in your ember-hk-components directory
|
|
61
|
-
>
|
|
85
|
+
> pnpm link --global
|
|
62
86
|
|
|
63
87
|
// in your consuming app directory
|
|
64
|
-
>
|
|
88
|
+
> pnpm link --global @heroku/ember-hk-components
|
|
65
89
|
|
|
66
90
|
// to put consuming app back on the release version
|
|
67
|
-
>
|
|
91
|
+
> pnpm unlink --global @heroku/ember-hk-components
|
|
92
|
+
> pnpm install @heroku/ember-hk-components
|
|
68
93
|
```
|
|
69
94
|
|
|
70
95
|
Now, when you make changes in your copy of `ember-hk-components` those changes will be reflected in the consuming application.
|
|
71
96
|
|
|
97
|
+
### Package Management
|
|
98
|
+
|
|
99
|
+
This project has migrated from Yarn to PNPM for:
|
|
100
|
+
|
|
101
|
+
- **Enhanced security** through stricter dependency resolution
|
|
102
|
+
- **Better performance** with content-addressable storage
|
|
103
|
+
- **Improved workspace support** for monorepo scenarios
|
|
104
|
+
- **Advanced resolution strategies** for vulnerability mitigation
|
|
105
|
+
|
|
106
|
+
All package management commands should use `pnpm` instead of `npm` or `yarn`.
|
|
107
|
+
|
|
108
|
+
### Recent Security Improvements
|
|
109
|
+
|
|
110
|
+
This project has recently undergone significant security hardening:
|
|
111
|
+
|
|
112
|
+
#### Vulnerability Resolution
|
|
113
|
+
- **Critical babel-traverse vulnerability** resolved via strategic package resolutions
|
|
114
|
+
- **High-severity vulnerabilities** in `rollup`, `json5`, `ansi-html` addressed
|
|
115
|
+
- **Transitive dependency vulnerabilities** mitigated through `@ladjs/consolidate` adoption
|
|
116
|
+
|
|
117
|
+
#### Dependency Updates
|
|
118
|
+
- **ember-a11y-testing** updated to `^5.2.1` for better compatibility
|
|
119
|
+
- **Package resolutions** strategically implemented for security without breaking changes
|
|
120
|
+
- **Comprehensive audit process** documented for future maintenance
|
|
121
|
+
|
|
122
|
+
#### Security Documentation
|
|
123
|
+
- `BABEL_TRAVERSE_VULNERABILITY_GUIDE.md` - Comprehensive guide for resolving babel-traverse vulnerabilities
|
|
124
|
+
- Detailed troubleshooting and implementation strategies
|
|
125
|
+
- Best practices for maintaining security in Ember CLI projects
|
|
126
|
+
|
|
127
|
+
For more details on security implementations, see the vulnerability guide and recent changelog entries.
|
|
128
|
+
|
|
72
129
|
### Releases
|
|
73
130
|
|
|
74
131
|
#### Notes
|
|
@@ -88,12 +145,12 @@ Once you are ready to make a new release follow these steps:
|
|
|
88
145
|
* Ensure all merged pull requests are labelled correctly as indicated in the Changelog section
|
|
89
146
|
* Create a new branch
|
|
90
147
|
* Update the version number in `package.json`
|
|
91
|
-
* Run `
|
|
92
|
-
* Copy the output of that command into
|
|
148
|
+
* Run `pnpm run changelog --from x.x.x` where `x.x.x` is the _last_ version of this library that was released. This should generate changelog of changes _since_ that last release.
|
|
149
|
+
* Copy the output of that command into `CHANGELOG.md`
|
|
93
150
|
* Commit your changes and open a PR
|
|
94
151
|
|
|
95
152
|
Once the PR is approved and merged you can then tag your new version by running `git tag x.x.x` where `x.x.x` is the new version number. Push your tag to GitHub using `git push origin --tags`.
|
|
96
153
|
|
|
97
|
-
Publish your new version to npm with the command `
|
|
154
|
+
Publish your new version to npm with the command `pnpm publish` 🎉
|
|
98
155
|
|
|
99
156
|
_Note that you must have publish access to the @heroku npm organization to successfully publish_
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Component from '@ember/component';
|
|
2
2
|
import { computed, get } from '@ember/object';
|
|
3
|
-
import { alias, and, notEmpty,
|
|
3
|
+
import { alias, and, not, notEmpty, or } from '@ember/object/computed';
|
|
4
4
|
import { debounce } from '@ember/runloop';
|
|
5
5
|
import layout from '@heroku/ember-hk-components/templates/components/hk-field-validations';
|
|
6
6
|
|
|
@@ -8,92 +8,120 @@ export default Component.extend({
|
|
|
8
8
|
layout,
|
|
9
9
|
classNameBindings: ['isInvalid:has-validation-errors'],
|
|
10
10
|
debounceValidation: 1000,
|
|
11
|
+
|
|
12
|
+
// Use a manual flag for validation state
|
|
11
13
|
isValidating: false,
|
|
12
14
|
isNotValidating: not('isValidating'),
|
|
13
|
-
|
|
15
|
+
|
|
16
|
+
_hasValidatedFlag: false,
|
|
17
|
+
|
|
18
|
+
// A field has been validated if it was triggered internally OR
|
|
19
|
+
// if the changeset is no longer in its initial pristine state.
|
|
20
|
+
hasValidated: or('_hasValidatedFlag', 'isChangesetAttempted'),
|
|
21
|
+
isChangesetAttempted: not('changeset.isPristine'),
|
|
22
|
+
|
|
14
23
|
hasValidationErrors: notEmpty('validationErrors'),
|
|
15
24
|
doesNotHaveValidationErrors: not('hasValidationErrors'),
|
|
16
25
|
isValid: and('isNotValidating', 'hasValidated', 'doesNotHaveValidationErrors'),
|
|
17
26
|
isInvalid: and('isNotValidating', 'hasValidated', 'hasValidationErrors'),
|
|
18
27
|
changesetErrors: alias('changeset.error'),
|
|
19
28
|
|
|
20
|
-
validationErrors: computed('
|
|
21
|
-
|
|
22
|
-
const property = this.get('property');
|
|
23
|
-
const errors = get(changesetErrors, property);
|
|
24
|
-
|
|
25
|
-
// filter out validations that are promises...
|
|
26
|
-
// ember changeset seems to put the validation promise as the value for `validation`
|
|
27
|
-
// before it resolves or rejects.
|
|
28
|
-
if (!errors || typeof errors.validation === 'object' && errors.validation !== null && typeof errors.validation.then === 'function') {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
return errors;
|
|
29
|
+
validationErrors: computed('changeset.error', 'property', function() {
|
|
30
|
+
return get(this.get('changeset.error'), this.get('property'));
|
|
32
31
|
}),
|
|
33
32
|
|
|
34
33
|
didReceiveAttrs() {
|
|
35
34
|
this._super(...arguments);
|
|
36
|
-
|
|
37
35
|
this.cleanUpObservers();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const changeset = this.get('changeset');
|
|
37
|
+
const property = this.get('property');
|
|
38
|
+
|
|
39
|
+
// Add observer for the property
|
|
42
40
|
changeset.addObserver(property, this, 'valueDidChange');
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
|
|
42
|
+
// Listen for validation state changes on the changeset
|
|
43
|
+
if (changeset.validate) {
|
|
44
|
+
const originalValidate = changeset.validate;
|
|
45
|
+
changeset.validate = (...args) => {
|
|
46
|
+
this.set('isValidating', true);
|
|
47
|
+
const result = originalValidate.apply(changeset, args);
|
|
48
|
+
|
|
49
|
+
if (result && typeof result.then === 'function') {
|
|
50
|
+
return result
|
|
51
|
+
.then((validationResult) => {
|
|
52
|
+
if (!this.isDestroyed) {
|
|
53
|
+
this.set('isValidating', false);
|
|
54
|
+
this.set('_hasValidatedFlag', true);
|
|
55
|
+
this.notifyPropertyChange('validationErrors');
|
|
56
|
+
}
|
|
57
|
+
return validationResult;
|
|
58
|
+
})
|
|
59
|
+
.catch((error) => {
|
|
60
|
+
if (!this.isDestroyed) {
|
|
61
|
+
this.set('isValidating', false);
|
|
62
|
+
this.set('_hasValidatedFlag', true);
|
|
63
|
+
this.notifyPropertyChange('validationErrors');
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
this.set('isValidating', false);
|
|
69
|
+
this.set('_hasValidatedFlag', true);
|
|
70
|
+
this.notifyPropertyChange('validationErrors');
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this._prev = {
|
|
77
|
+
changeset,
|
|
78
|
+
property,
|
|
79
|
+
originalValidate: changeset.validate
|
|
80
|
+
};
|
|
47
81
|
},
|
|
48
82
|
|
|
49
83
|
willDestroyElement() {
|
|
84
|
+
// Set a flag to prevent any pending validations from updating state
|
|
85
|
+
this._isDestroyed = true;
|
|
50
86
|
this.cleanUpObservers();
|
|
51
|
-
|
|
52
87
|
this._super(...arguments);
|
|
53
88
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
89
|
+
|
|
90
|
+
willDestroy() {
|
|
91
|
+
this._isDestroyed = true;
|
|
92
|
+
this.cleanUpObservers();
|
|
93
|
+
this._super(...arguments);
|
|
59
94
|
},
|
|
60
95
|
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
cleanUpObservers() {
|
|
97
|
+
if (!this._prev) { return; }
|
|
98
|
+
const { changeset, property, originalValidate } = this._prev;
|
|
99
|
+
changeset.removeObserver(property, this, 'valueDidChange');
|
|
100
|
+
|
|
101
|
+
// Restore original validate function if we patched it
|
|
102
|
+
if (originalValidate && changeset.validate) {
|
|
103
|
+
changeset.validate = originalValidate;
|
|
65
104
|
}
|
|
105
|
+
|
|
106
|
+
this._prev = null;
|
|
66
107
|
},
|
|
67
108
|
|
|
68
109
|
valueDidChange() {
|
|
110
|
+
if (this._isDestroyed) { return; }
|
|
69
111
|
const newValue = this.get('changeset').get(this.get('property'));
|
|
70
|
-
|
|
71
112
|
if (newValue === this._prevValue) { return; }
|
|
72
|
-
|
|
73
113
|
this._prevValue = newValue;
|
|
74
114
|
this.notifyPropertyChange('value');
|
|
75
115
|
this.scheduleValidation();
|
|
76
116
|
},
|
|
77
117
|
|
|
78
|
-
cleanUpObservers() {
|
|
79
|
-
if (!this._prev) { return; }
|
|
80
|
-
|
|
81
|
-
let { changeset, property } = this._prev;
|
|
82
|
-
|
|
83
|
-
changeset.removeObserver(property, this, 'valueDidChange');
|
|
84
|
-
changeset.off('beforeValidation', this, 'beforeValidation');
|
|
85
|
-
changeset.off('afterValidation', this, 'afterValidation');
|
|
86
|
-
|
|
87
|
-
this._prev = null;
|
|
88
|
-
},
|
|
89
|
-
|
|
90
118
|
value: computed('changeset', 'property', {
|
|
91
|
-
get(
|
|
92
|
-
|
|
119
|
+
get() {
|
|
120
|
+
const property = this.get('property');
|
|
121
|
+
// This getter now correctly establishes a dependency on the nested property
|
|
122
|
+
return this.get(`changeset.${property}`);
|
|
93
123
|
},
|
|
94
124
|
set(key, value) {
|
|
95
|
-
// The `valueDidChange` observer is responsible for invoking
|
|
96
|
-
// `scheduleValidation` in reaction to this change.
|
|
97
125
|
this.get('changeset').set(this.get('property'), value);
|
|
98
126
|
return value;
|
|
99
127
|
}
|
|
@@ -105,10 +133,42 @@ export default Component.extend({
|
|
|
105
133
|
},
|
|
106
134
|
|
|
107
135
|
validateProperty() {
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
136
|
+
if (this.isDestroyed || this._isDestroyed) { return; }
|
|
137
|
+
|
|
138
|
+
// Manually setting the flag here ensures it's true even if debounce is 0
|
|
139
|
+
this.set('isValidating', true);
|
|
140
|
+
const changeset = this.get('changeset');
|
|
141
|
+
const property = this.get('property');
|
|
142
|
+
const maybePromise = changeset.validate(property);
|
|
143
|
+
|
|
144
|
+
const onFinally = (error) => {
|
|
145
|
+
if (this.isDestroyed) { return; }
|
|
146
|
+
|
|
147
|
+
this.set('_hasValidatedFlag', true);
|
|
148
|
+
this.set('isValidating', false);
|
|
149
|
+
|
|
150
|
+
// If there was an error, ensure the error is set on the changeset
|
|
151
|
+
if (error) {
|
|
152
|
+
const errorObj = {};
|
|
153
|
+
errorObj[property] = error.message || error;
|
|
154
|
+
changeset.set('error', { [property]: [errorObj] });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.notifyPropertyChange('validationErrors');
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
161
|
+
return maybePromise
|
|
162
|
+
.then(() => onFinally())
|
|
163
|
+
.catch(error => onFinally(error))
|
|
164
|
+
.finally(() => {
|
|
165
|
+
if (!this.isDestroyed) {
|
|
166
|
+
this.notifyPropertyChange('validationErrors');
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
} else {
|
|
170
|
+
onFinally();
|
|
171
|
+
return maybePromise;
|
|
112
172
|
}
|
|
113
173
|
}
|
|
114
|
-
});
|
|
174
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Component from '@ember/component';
|
|
2
2
|
import { alias, bool } from '@ember/object/computed';
|
|
3
|
+
import { computed } from '@ember/object';
|
|
3
4
|
import layout from '@heroku/ember-hk-components/templates/components/hk-validation-errors-list';
|
|
4
5
|
|
|
5
6
|
export default Component.extend({
|
|
@@ -7,4 +8,7 @@ export default Component.extend({
|
|
|
7
8
|
tagName: '',
|
|
8
9
|
hasValidationErrors: bool('validationErrors'),
|
|
9
10
|
validationErrorMessages: alias('validationErrors.validation'),
|
|
11
|
+
errorsListId: computed('property', function() {
|
|
12
|
+
return `validation-errors-${this.get('property')}`;
|
|
13
|
+
}),
|
|
10
14
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{{yield (hash
|
|
2
|
-
value=value
|
|
3
|
-
isValidating=isValidating
|
|
4
|
-
isValid=isValid
|
|
5
|
-
isInvalid=isInvalid
|
|
6
|
-
errors=validationErrors
|
|
7
|
-
errorsList=(component 'hk-validation-errors-list' validationErrors=validationErrors property=property)
|
|
8
|
-
)}}
|
|
2
|
+
value=this.value
|
|
3
|
+
isValidating=this.isValidating
|
|
4
|
+
isValid=this.isValid
|
|
5
|
+
isInvalid=this.isInvalid
|
|
6
|
+
errors=this.validationErrors
|
|
7
|
+
errorsList=(component 'hk-validation-errors-list' validationErrors=this.validationErrors property=this.property)
|
|
8
|
+
)}}
|