Skip to content
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

feat(validations): Run and display warnings without submitting form #78

Merged
merged 13 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions apps/ember-test-app/app/components/f/form.gts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,33 @@ const Validators = {
console.log('Validating: ' + value);
return value !== 'foo';
},
isWarning: true,
}),
],
radio: [validator('exclusion', { in: ['A', 'B'], isWarning: true })],
phone: [
validator('length', {
min: 10,
message: 'Phone number must contain a 3-digit area code',
disabled() {
return !this.requirePhoneLength;
},
}),
validator('length', {
max: 10,
isWarning: true,
message: 'Phone number cannot contain a country code',
disabled() {
return !this.requirePhoneLength;
},
}),
],
};

class Model {
@tracked
requirePhoneLength = true;

@tracked
textField = '';

Expand All @@ -40,6 +62,9 @@ class Model {
@tracked
select = '';

@tracked
phone = '';

@tracked
radio = '';

Expand Down Expand Up @@ -86,6 +111,11 @@ export default class extends Component {
this.required = !this.required;
}

@action
toggleRequirePhoneLength() {
this.model.requirePhoneLength = !this.model.requirePhoneLength;
}

@action
onSubmit() {
alert('Form submitted');
Expand Down Expand Up @@ -138,12 +168,29 @@ export default class extends Component {
</Field.RadioGroup>
</Form.Field>
</div>
<div class="mb-3">
<Form.Field
@label="Phone Number"
@required={{this.required}}
as |Field|
>
<Field.Phone @binding={{bind this.model "phone"}} />
</Form.Field>
</div>
<Form.SubmitButton class="btn-primary mt-3" />
<Button
class="btn{{unless this.required '-outline'}}-secondary mt-3"
@text="Toggle Required"
@onClick={{this.toggleRequired}}
/>
<Button
class="btn{{unless
this.model.requirePhoneLength
'-outline'
}}-secondary mt-3"
@text="Toggle Phone Length"
@onClick={{this.toggleRequirePhoneLength}}
/>
</Form>
<hr />
<div class="grid">
Expand Down
2 changes: 1 addition & 1 deletion apps/ember-test-app/config/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = function (environment) {
rootURL: '/',
locationType: 'history',
EmberENV: {
EXTEND_PROTOTYPES: true,
EXTEND_PROTOTYPES: false,
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { setOwner } from '@ember/application';
import { array } from '@ember/helper';
import { click, fillIn, render } from '@ember/test-helpers';
import { tracked } from '@glimmer/tracking';
import Form from '@nrg-ui/ember/components/form';
import bind from '@nrg-ui/ember/helpers/bind';
import { validator } from '@nrg-ui/ember/validation';
import { setupIntl } from 'ember-intl/test-support';
import { setupRenderingTest } from 'ember-qunit';
import { module, test } from 'qunit';
Expand All @@ -15,6 +17,13 @@ class Model {
textArea: string = '';
}

const Validators = {
selectByAnotherProperty: validator('presence', {
presence: false,
isWarning: true,
}),
};

module('Integration | Component | form', function (hooks) {
setupRenderingTest(hooks);
setupIntl(hooks, 'en-us');
Expand Down Expand Up @@ -101,7 +110,7 @@ module('Integration | Component | form', function (hooks) {
});

test('validations work', async function (assert) {
assert.expect(12);
assert.expect(20);

const model = this.model;

Expand All @@ -111,7 +120,7 @@ module('Integration | Component | form', function (hooks) {
};

await render(<template>
<Form @onSubmit={{actionHandler}} as |Form|>
<Form @validators={{Validators}} @onSubmit={{actionHandler}} as |Form|>
<Form.Field @label="Text Field" @required={{true}} as |Field|>
<Field.TextField @binding={{bind model "textField"}} />
</Form.Field>
Expand All @@ -125,11 +134,43 @@ module('Integration | Component | form', function (hooks) {
Here's some extra context for this field
</Field.Text>
</Form.Field>
<Form.Field
@label="Select"
@validatorKey="selectByAnotherProperty"
as |Field|
>
<Field.Select
@binding={{bind model "select"}}
@options={{array "A" "B" "C"}}
/>
</Form.Field>
<Form.SubmitButton />
</Form>
</template>);

await click('button');
// Select
const select = this.element.querySelector('label + button.dropdown');

assert
.dom(select)
.exists()
.hasAttribute('role', 'combobox')
.hasClass('form-control');

await click(select);
await click('button > .dropdown-menu > li:first-child');

const ariaId = select.getAttribute('aria-describedby');

assert.dom('button.dropdown').doesNotHaveClass('is-invalid');
assert
.dom('button.dropdown + div')
.exists()
.hasAttribute('id', ariaId)
.hasClass('warning-feedback')
.containsText('This field must be blank');

await click('button[type="submit"]');

assert.false(didSubmit, 'Form should not submit when validations fail');

Expand Down Expand Up @@ -159,7 +200,7 @@ module('Integration | Component | form', function (hooks) {
assert.ok(true, 'Form should submit');
};

await click('button');
await click('button[type="submit"]');

assert.true(didSubmit, 'Form should submit when validations pass');
});
Expand Down
Loading
Loading