-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PropertyRequiredMixin #4272
Merged
Merged
PropertyRequiredMixin #4272
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
a2d6176
feat: attribute required mixin
dlockhart c495f04
feat: mixin for ensuring that string properties are given values
dlockhart 9c98da6
alternate option for PropertyRequiredMixin (#4273)
dlockhart 610f215
include error code
dlockhart 6d26432
switch TagListItemMixin to use PropertyRequiredMixin
dlockhart c0d70b0
docs
dlockhart 85db810
use tag name
dlockhart 0985fcd
refactor using an error helper
dlockhart 0145a8a
fix test
dlockhart File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import '../input-color.js'; | ||
import { expect, fixture, html, oneEvent, runConstructor } from '@brightspace-ui/testing'; | ||
import { createDefaultMessage } from '../../../mixins/property-required/property-required-mixin.js'; | ||
|
||
describe('d2l-input-color', () => { | ||
|
||
|
@@ -128,4 +129,35 @@ describe('d2l-input-color', () => { | |
|
||
}); | ||
|
||
describe('validation', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Example of how a component can now test that the validation is set up properly. |
||
|
||
['foreground', 'background'].forEach(type => { | ||
it(`should not require a label when type is "${type}"`, async() => { | ||
const elem = await fixture(html`<d2l-input-color type="${type}"></d2l-input-color>`); | ||
expect(() => elem.flushRequiredPropertyErrors()).to.not.throw(); | ||
}); | ||
}); | ||
|
||
it('should throw when type is "custom" and no label', async() => { | ||
const elem = await fixture(html`<d2l-input-color type="custom"></d2l-input-color>`); | ||
expect(() => elem.flushRequiredPropertyErrors()) | ||
.to.throw(TypeError, createDefaultMessage('d2l-input-color', 'label')); | ||
}); | ||
|
||
it('should not throw when type is "custom" and label is provided', async() => { | ||
const elem = await fixture(html`<d2l-input-color label="value" type="custom"></d2l-input-color>`); | ||
expect(() => elem.flushRequiredPropertyErrors()).to.not.throw(); | ||
}); | ||
|
||
it('should require a label when type changes to "custom"', async() => { | ||
const elem = await fixture(html`<d2l-input-color type="foreground"></d2l-input-color>`); | ||
expect(() => elem.flushRequiredPropertyErrors()).to.not.throw(); | ||
elem.setAttribute('type', 'custom'); | ||
await elem.updateComplete; | ||
expect(() => elem.flushRequiredPropertyErrors()) | ||
.to.throw(TypeError, createDefaultMessage('d2l-input-color', 'label')); | ||
}); | ||
|
||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# PropertyRequiredMixin | ||
|
||
This mixin will make a component's property "required". If a value is not provided for a required property, an exception will be thrown asynchronously. | ||
|
||
Required properties are useful in situations where a missing value would put the component into an invalid state, such as an accessibility violation. | ||
|
||
Only `String` properties can be required. | ||
|
||
## Making a Property Required | ||
|
||
To require a property, simply set `required` to `true` in the reactive Lit property definition: | ||
|
||
```javascript | ||
import { PropertyRequiredMixin } from '@brightspace-ui/core/mixins/property-required/property-required-mixin.js'; | ||
|
||
class MyElem extends PropertyRequiredMixin(LitElement) { | ||
static properties = { | ||
prop: { type: String, required: true } | ||
}; | ||
} | ||
``` | ||
|
||
If a non-empty `String` value for `prop` is not provided, an exception will be thrown after a few seconds. | ||
|
||
## Custom Validation | ||
|
||
By default, validating a required property involves ensuring that it's a non-empty `String`. To customize this logic, set `required` to an object and provide a `validator` delegate. | ||
|
||
```javascript | ||
static properties = { | ||
mustBeFoo: { | ||
required: { | ||
validator: (value, elem, hasValue) => { | ||
return value === 'foo'; | ||
} | ||
}, | ||
type: String | ||
} | ||
}; | ||
``` | ||
|
||
The `validator` will be passed the current property `value`, a reference to the element and the result of the default validation. | ||
|
||
## Custom Messages | ||
|
||
To customize the exception message that gets thrown when validation fails, set `required` to an object and provide a `message` delegate. | ||
|
||
```javascript | ||
static properties = { | ||
prop: { | ||
required: { | ||
message: (value, elem, defaultMessage) => { | ||
return `"prop" on "${elem.tagName}" is required.`; | ||
} | ||
}, | ||
type: String | ||
} | ||
}; | ||
``` | ||
|
||
The `message` delegate will be passed the current property `value`, a reference to the element and the default message. | ||
|
||
## Dependent Properties | ||
|
||
The mixin will automatically listen for updates to a required property and re-run the `validator` when the value changes. If custom validation logic relies on multiple properties, list them in `dependentProps`. | ||
|
||
```javascript | ||
static properties = { | ||
prop: { | ||
required: { | ||
dependentProps: ['isReallyRequired'] | ||
validator: (value, elem, hasValue) => { | ||
return elem.isReallyRequired && hasValue; | ||
} | ||
}, | ||
type: String | ||
}, | ||
isReallyRequired: { type: Boolean } | ||
}; | ||
``` | ||
|
||
## Unit Testing | ||
|
||
If no custom `validator` is used, tests that assert the correct required property errors are thrown are unnecessary -- `@brightspace-ui/core` already covers those tests. | ||
|
||
Required property exceptions are thrown asynchronously multiple seconds after the component has rendered. For unit tests, this makes catching them challenging. | ||
|
||
To help, use the `flushRequiredPropertyErrors()` method. | ||
|
||
```javascript | ||
const tag = defineCE( | ||
class extends PropertyRequiredMixin(LitElement) { | ||
static properties = { | ||
prop: { | ||
type: String, | ||
required: { | ||
validator: (value) => value === 'valid' | ||
} | ||
} | ||
}; | ||
} | ||
); | ||
const elem = await fixture(`<${tag} prop="invalid"></${tag}>`); | ||
expect(() => elem.flushRequiredPropertyErrors()).to.throw(); | ||
``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This use case forced me to add support for "dependent properties" -- essentially other properties to watch and re-validate when they change.