Skip to content

Commit

Permalink
Merge pull request #106 from vaadin/feature/checkbox-group-validation
Browse files Browse the repository at this point in the history
Feature/checkbox group validation
  • Loading branch information
platosha authored Jul 10, 2018
2 parents e1fb8f6 + fad5e6f commit 21dd7f6
Show file tree
Hide file tree
Showing 15 changed files with 507 additions and 40 deletions.
239 changes: 202 additions & 37 deletions analysis.json

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions demo/checkbox-group-validation-demos.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<dom-module id="checkbox-group-validation-demos">
<template>
<style include="vaadin-component-demo-shared-styles">
:host {
display: block;
}
</style>

<h3>Checkbox Group with validation message</h3>
<vaadin-demo-snippet id="checkbox-group-demos-checkbox-group-with-validation-message">
<template preserve-content>
<vaadin-checkbox-group required label="Preferred language of contact:" error-message="Please choose a language.">
<vaadin-checkbox name="language" value="en">English</vaadin-checkbox>
<vaadin-checkbox name="language" value="fr">Français</vaadin-checkbox>
<vaadin-checkbox name="language" value="de">Deutsch</vaadin-checkbox>
</vaadin-checkbox-group>
<vaadin-button id="submit-button-validation-message">Validate</vaadin-button>
<script>
window.addDemoReadyListener('#checkbox-group-demos-checkbox-group-with-validation-message', function(document) {
const checkboxGroup = document.querySelector('vaadin-checkbox-group');
const submitButton = document.getElementById('submit-button-validation-message');

submitButton.addEventListener('click', function(event) {
checkboxGroup.validate();
});
});
</script>
</template>
</vaadin-demo-snippet>

<h3>Checkbox Group with Iron Form validation</h3>
<vaadin-demo-snippet id="checkbox-group-demos-checkbox-group-with-iron-form-validation">
<template preserve-content>
<iron-form id="test-form-validation">
<form>
<vaadin-checkbox-group required label="Preferred language of contact:">
<vaadin-checkbox name="language" value="en">English</vaadin-checkbox>
<vaadin-checkbox name="language" value="fr">Français</vaadin-checkbox>
<vaadin-checkbox name="language" value="de">Deutsch</vaadin-checkbox>
</vaadin-checkbox-group>
<vaadin-button id="submit-button-validation">Submit</vaadin-button>
</form>
<div id="iron-form-output-validation"></div>
</iron-form>
<script>
window.addDemoReadyListener('#checkbox-group-demos-checkbox-group-with-iron-form-validation', function(document) {
const testForm = document.getElementById('test-form-validation');
const submitButton = document.getElementById('submit-button-validation');

submitButton.addEventListener('click', function(event) {
testForm.submit();
});

testForm.addEventListener('iron-form-submit', function(event) {
this.querySelector('#iron-form-output-validation').innerHTML = JSON.stringify(event.detail);
});
});
</script>
</template>
</vaadin-demo-snippet>

</template>
<script>
class CheckboxGroupValidationDemos extends DemoReadyEventEmitter(CheckboxDemo(Polymer.Element)) {
static get is() {
return 'checkbox-group-validation-demos';
}
}
customElements.define(CheckboxGroupValidationDemos.is, CheckboxGroupValidationDemos);
</script>
</dom-module>
13 changes: 12 additions & 1 deletion demo/demos.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
}
,
{
"name": "Vaadin Checkbox Group",
"name": "Checkbox Group",
"url": "checkbox-group-demos",
"src": "checkbox-group-demos.html",
"meta": {
Expand All @@ -44,5 +44,16 @@
"image": ""
}
}
,
{
"name": "Checkbox Group Validation",
"url": "checkbox-group-validation-demos",
"src": "checkbox-group-validation-demos.html",
"meta": {
"title": "vaadin-checkbox-group Validation Examples",
"description": "",
"image": ""
}
}
]
}
58 changes: 57 additions & 1 deletion src/vaadin-checkbox-group.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<slot id="slot"></slot>
</div>

<div part="error-message"
aria-live="assertive"
aria-hidden$="[[_getErrorMessageAriaHidden(invalid, errorMessage)]]"
>[[errorMessage]]</div>

</div>

</template>
Expand All @@ -68,13 +73,16 @@
* ----------------|----------------
* `label` | The label element
* `group-field` | The element that wraps checkboxes
* `error-message` | The error message element
*
* The following state attributes are available for styling:
*
* Attribute | Description | Part name
* -----------|-------------|------------
* `disabled` | Set when the checkbox group and its children are disabled. | :host
* `has-label` | Set when the element has a label | :host
* `required` | Set when the element is required | :host
* `invalid` | Set when the element is invalid | :host
*
* See [ThemableMixin – how to apply styles for shadow parts](https://github.com/vaadin/vaadin-themable-mixin/wiki)
*
Expand Down Expand Up @@ -115,7 +123,34 @@
type: Array,
value: () => [],
notify: true
}
},

/**
* Error to show when the input value is invalid.
*/
errorMessage: {
type: String,
value: ''
},

/**
* Specifies that the user must fill in a value.
*/
required: {
type: Boolean,
reflectToAttribute: true
},

/**
* This property is set to true when the control value is invalid.
*/
invalid: {
type: Boolean,
reflectToAttribute: true,
notify: true,
value: false
},

};
}

Expand Down Expand Up @@ -168,6 +203,17 @@
this.setAttribute('role', 'checkboxgroup');
}

/**
* Returns true if `value` is valid.
* `<iron-form>` uses this to check the validity or all its elements.
*
* @return {boolean} True if the value is valid.
*/
validate() {
this.invalid = this.required && this.value.length === 0;
return !this.invalid;
}

get _checkboxes() {
return this._filterCheckboxes(this.querySelectorAll('*'));
}
Expand Down Expand Up @@ -206,13 +252,19 @@
}

_valueChanged(newV, oldV) {
// setting initial value to empty string, skip validation
if (newV.length === 0 && oldV === undefined) {
return;
}
// set a flag to avoid updating loop
this._updatingValue = true;
// reflect the value array to checkboxes
this._checkboxes.forEach(checkbox => {
checkbox.checked = newV.indexOf(checkbox.value) > -1;
});
this._updatingValue = false;

this.validate();
}

_labelChanged(label) {
Expand All @@ -223,6 +275,10 @@
}
}

_getErrorMessageAriaHidden(invalid, errorMessage) {
return (!errorMessage || !invalid).toString();
}

}

customElements.define(CheckboxGroupElement.is, CheckboxGroupElement);
Expand Down
91 changes: 91 additions & 0 deletions test/vaadin-checkbox-group_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<script src="../../web-component-tester/browser.js"></script>
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="../../iron-test-helpers/mock-interactions.html">
<link rel="import" href="../../iron-form/iron-form.html">
<link rel="import" href="../../test-fixture/test-fixture.html">
<link rel="import" href="../vaadin-checkbox.html">
<link rel="import" href="../vaadin-checkbox-group.html">
Expand All @@ -22,6 +23,20 @@
</template>
</test-fixture>

<test-fixture id="ironForm">
<template>
<iron-form>
<form>
<vaadin-checkbox-group>
<vaadin-checkbox name="language" value="en">English</vaadin-checkbox>
<vaadin-checkbox name="language" value="fr">Français</vaadin-checkbox>
<vaadin-checkbox name="language" value="de">Deutsch</vaadin-checkbox>
</vaadin-checkbox-group>
</form>
</iron-form>
</template>
</test-fixture>

<script>
describe('vaadin-checkbox-group', () => {

Expand Down Expand Up @@ -138,5 +153,81 @@
});

});

describe('vaadin-checkbox-group validation', () => {

let vaadinCheckboxGroup, vaadinCheckboxList, ironForm;

beforeEach(() => {
ironForm = fixture('ironForm');
vaadinCheckboxGroup = ironForm.querySelector('vaadin-checkbox-group');
vaadinCheckboxList = vaadinCheckboxGroup.querySelectorAll('vaadin-checkbox');
vaadinCheckboxGroup._observer.flush();
});

it('should not have invalid attribute initially', () => {
expect(vaadinCheckboxGroup.hasAttribute('invalid')).to.be.false;
});

it('should not add invalid attribute if required attribute is not present', () => {
vaadinCheckboxList[0].checked = true;
vaadinCheckboxList[0].checked = false;

expect(vaadinCheckboxGroup.hasAttribute('invalid')).to.be.false;
});

it('should add invalid attribute if required attribute is present and checkbox group value is empty', () => {
vaadinCheckboxGroup.required = true;
vaadinCheckboxList[0].checked = true;
vaadinCheckboxList[0].checked = false;

expect(vaadinCheckboxGroup.hasAttribute('invalid')).to.be.true;
});

it('should remove invalid attribute if checkbox group value is not empty', () => {
vaadinCheckboxGroup.required = true;
vaadinCheckboxList[0].checked = true;
vaadinCheckboxList[0].checked = false;

expect(vaadinCheckboxGroup.hasAttribute('invalid')).to.be.true;

vaadinCheckboxList[0].checked = true;

expect(vaadinCheckboxGroup.hasAttribute('invalid')).to.be.false;

});

it('should prevent submitting form when value of required checkbox group is empty', () => {
vaadinCheckboxGroup.required = true;

expect(ironForm.validate()).to.be.false;

});

it('should not prevent submitting form when checkbox group value is not empty or does not have required attribute', () => {
expect(ironForm.validate()).to.be.true;

vaadinCheckboxGroup.required = true;
vaadinCheckboxGroup.value = ['en'];

expect(ironForm.validate()).to.be.true;

});

it('should not show the error message initially', () => {
vaadinCheckboxGroup.errorMessage = 'Error message';

expect(vaadinCheckboxGroup.shadowRoot.querySelector('[part="error-message"]').getAttribute('aria-hidden')).to.be.equal('true');
});

it('should show the error message if validation status is invalid', () => {
vaadinCheckboxGroup.errorMessage = 'Error message';
vaadinCheckboxGroup.invalid = true;

expect(vaadinCheckboxGroup.shadowRoot.querySelector('[part="error-message"]').getAttribute('aria-hidden')).to.be.equal('false');
});

});

</script>
</body>
14 changes: 14 additions & 0 deletions test/visual/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,19 @@
<vaadin-checkbox value="2">2</vaadin-checkbox>
</vaadin-checkbox-group>
</div>

<div id="validation-tests">
<vaadin-checkbox-group required error-message="Please choose a number" label="Choose a number" id="validation-checkbox-group">
<vaadin-checkbox value="1">1</vaadin-checkbox>
<vaadin-checkbox value="2">2</vaadin-checkbox>
</vaadin-checkbox-group>
</div>

<script>
window.addEventListener('WebComponentsReady', () => {
const checkboxGroup = document.getElementById('validation-checkbox-group');
checkboxGroup.validate();
});
</script>

</body>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions test/visual/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,11 @@ gemini.suite('vaadin-checkbox', function(rootSuite) {
.capture('default');
});

gemini.suite('validation-tests', function(suite) {
suite
.setUrl('default.html')
.setCaptureElements('#validation-tests')
.capture('error');
});

});
Loading

0 comments on commit 21dd7f6

Please sign in to comment.