Skip to content

Commit

Permalink
fix(GRANITE-32443): Add min property in coral-multifield component (#…
Browse files Browse the repository at this point in the history
…162)

* fix(GRANITE-32443): Added multifield min property

* fix(GRANITE-32443): Added multifield min property

* fix(GRANITE-32443): Added multifield min property
@added testcases

* fix(GRANITE-32443): Added multifield min property
@Correcting build

* fix(GRANITE-32443): Added multifield min property
@remove unnecessary changes

* fix(GRANITE-32443): Added multifield min property
@updating testcases

* fix(GRANITE-32443): Added multifield min property
@add example

* fix(GRANITE-32443): Added multifield min property
@ReFRACtor code

* fix(GRANITE-32443): Add min property in coral-multifield component
@change forced to schedule

* Revert "fix(GRANITE-32443): Add min property in coral-multifield component"

This reverts commit dbb7f95.

* fix(GRANITE-32443): Add min property in coral-multifield component

* fix(GRANITE-32443): Add min property in coral-multifield component
@add empty code to trigger build

Co-authored-by: Pareesh Gupta <paregupt@adobe.com>
  • Loading branch information
Pareesh and Pareesh Gupta committed Apr 23, 2021
1 parent 7c8a716 commit 819a6b4
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 5 deletions.
11 changes: 11 additions & 0 deletions coral-component-multifield/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ <h2 class="coral--Heading--S">Rendered with JS</h2>
});
</script>
</div>

<h2 class="coral--Heading--S">With Min specified</h2>
<div class="markup">
<coral-multifield aria-label="Multifield with min specified" min="2">
<button coral-multifield-add type="button" is="coral-button">Add a field</button>
<template coral-multifield-template>
<input is="coral-textfield" aria-label="input" type="text"/>
</template>
</coral-multifield>
</div>

</main>
</body>
</html>
104 changes: 99 additions & 5 deletions coral-component-multifield/src/scripts/Multifield.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {BaseComponent} from '../../../coral-base-component';
import MultifieldCollection from './MultifieldCollection';
import '../../../coral-component-textfield';
import {commons, i18n} from '../../../coral-utils';
import {commons, i18n, validate, transform} from '../../../coral-utils';

const CLASSNAME = '_coral-Multifield';
const IS_DRAGGING_CLASS = 'is-dragging';
Expand Down Expand Up @@ -150,6 +150,79 @@ class Multifield extends BaseComponent(HTMLElement) {
});
}

/**
Specifies the minimum number of items multifield should render.
If component contains less items, remaining items will be added.
@type {Number}
@default 0
@htmlattribute min
@htmlattributereflected
*/
get min() {
return this._min || 0;
}

set min(value) {
const self = this;
value = transform.number(value);

if(value && validate.valueMustChange(self._min, value)) {
self._min = value;
self._reflectAttribute('min', value);
self._validateMinItems();
}
}

/**
* Validates minimum items required. will add items, if validation fails.
* @param schedule schedule validation in next frame
* @ignore
*/
_validateMinItems(schedule) {
// only validate when multifield is connected
if(this._disconnected === false) {
const self = this;
const items = self.items;
let currentLength = items.length;
let currentMin = self.min;
let deletable = true;

if(currentLength <= currentMin) {
let itemsToBeAdded = currentMin - currentLength;

for(let i = 0; i < itemsToBeAdded; i++) {
items.add(document.createElement('coral-multifield-item'));
}
deletable = !deletable;
}

if(!schedule) {
window.cancelAnimationFrame(self._updateItemsDeletableId);
delete self._updateItemsDeletableId;
self._updateItemsDeletable(items.getAll(), deletable);
} else if(!self._updateItemsDeletableId) {
self._updateItemsDeletableId = window.requestAnimationFrame(() => {
delete self._updateItemsDeletableId;
self._updateItemsDeletable(items.getAll(), deletable);
});
}
}
}

/**
* Change the deletable property of passed items to the specified deletable value
* @ignore
*/
_updateItemsDeletable(items, deletable) {
deletable = transform.boolean(deletable);
items = !Array.isArray(items) ? [items] : items;

items.forEach(function(item) {
item._deletable = deletable;
});
}

/** @ignore */
_handleTemplateSupport(template) {
// @polyfill IE
Expand Down Expand Up @@ -484,15 +557,27 @@ class Multifield extends BaseComponent(HTMLElement) {

/** @private */
_onItemAdded(item) {
const self = this;
// Update the item content with the template content
if (item.parentNode === this) {
this._renderTemplate(item);
this._updatePosInSet();
if (item.parentNode === self) {
self._renderTemplate(item);
self._updatePosInSet();
}

if(self.items.length === self.min + 1) {
self._validateMinItems();
}
}

/** @private */
_onItemRemoved() {
this._updatePosInSet();
const self = this;
self._updatePosInSet();

// only validate when required
if(self.items.length <= self.min) {
self._validateMinItems();
}
}

/**
Expand Down Expand Up @@ -541,6 +626,13 @@ class Multifield extends BaseComponent(HTMLElement) {
return {template: 'template'};
}

/** @ignore */
static get observedAttributes() {
return super.observedAttributes.concat([
'min'
]);
}

/** @ignore */
render() {
super.render();
Expand All @@ -560,6 +652,8 @@ class Multifield extends BaseComponent(HTMLElement) {

// update aria-posinset and aria-setsize for each item in the collection
this._updatePosInSet();

this._validateMinItems(true);
}

/**
Expand Down
17 changes: 17 additions & 0 deletions coral-component-multifield/src/scripts/MultifieldItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ class MultifieldItem extends BaseComponent(HTMLElement) {
});
}

/**
Specify whether the remove button is in disabled state or not.
@type {Boolean}
@default false
@private
*/
get _deletable() {
return typeof this.__deletable === 'boolean' ? this.__deletable : true;
}

set _deletable(value) {
value = transform.boolean(value);
this.__deletable = value;
this._elements.remove.disabled = !value;
}

/**
Whether the item is set to be reorder using the keyboard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<coral-multifield id="multifieldWithMin" min="3">
<coral-multifield-item>
<input type="text"/>
</coral-multifield-item>
<coral-multifield-item>
<input type="text"/>
</coral-multifield-item>
<template coral-multifield-template>
<input type="text"/>
</template>
<button coral-multifield-add type="button">Add a field</button>
</coral-multifield>
1 change: 1 addition & 0 deletions coral-component-multifield/src/tests/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
*/

import './test.Multifield';
import './test.Multifield.Item';
41 changes: 41 additions & 0 deletions coral-component-multifield/src/tests/test.Multifield.Item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {helpers} from '../../../coral-utils/src/tests/helpers';

describe('Multifield.Item', function () {
describe('API', function () {
describe('#_deletable', function () {
var el;
beforeEach(function() {
el = helpers.build(`<coral-multifield-item></coral-multifield-item`);
});
it('remove button should be enable and _deletable true by default', function () {
expect(el._elements.remove.disabled).to.be.equal(false);
expect(el._deletable).to.be.equal(true);
});

it('should disable remove button when _deletable false', function () {
el._deletable = false;
expect(el._elements.remove.disabled).to.be.equal(true);
});

it('should enable remove button when _deletable true', function () {
el._deletable = false;
expect(el._elements.remove.disabled).to.be.equal(true);

el._deletable = true;
expect(el._elements.remove.disabled).to.be.equal(false);
});
});
});
});
103 changes: 103 additions & 0 deletions coral-component-multifield/src/tests/test.Multifield.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,109 @@ describe('Multifield', function () {
describe('API', function () {
describe('#items', function () {
});

describe('#min', function () {
it('remaining items should be added when items count less than min value', function () {
var el = helpers.build(window.__html__['Multifield.base.min.html']);
expect(el.items.length).to.be.equal(el.min);
});

it('remaining items should be added when min value increased to more than items count', function () {
var el = helpers.build(window.__html__['Multifield.base.min.html']);
el.min = 5;
expect(el.items.length).to.be.equal(5);
});

it('no items should be added when min value decreased to less than items count', function () {
var el = helpers.build(window.__html__['Multifield.base.min.html']);
var initialCount = el.items.length;
el.min = initialCount - 1;
expect(initialCount).to.be.equal(3);
});

it('no items should be added when min value is greater than or equal to items count', function () {
var el = helpers.build(`
<coral-multifield id="multifieldWithMin" min="1">
<coral-multifield-item>
<input type="text"/>
</coral-multifield-item>
<coral-multifield-item>
<input type="text"/>
</coral-multifield-item>
<template coral-multifield-template>
<input type="text"/>
</template>
<button coral-multifield-add type="button">Add a field</button>
</coral-multifield>`
);
expect(el.items.length).to.be.equal(2);
});

it('items should not be deletable when count less than or equal to min', function (done) {
var el = helpers.build(window.__html__['Multifield.base.min.html']);
var initialCount = el.items.length;

expect(initialCount).to.be.equal(el.min);

//initial min validation takes 1 frame
helpers.next(function() {
el.items.getAll().forEach(function(item) {
expect(item._deletable).to.be.false;
});

el.min = el.min + 1;

expect(initialCount + 1).to.be.equal(el.min);

el.items.getAll().forEach(function(item) {
expect(item._deletable).to.be.false;
});

done();
});
});

it('items should be deletable when count greater than min', function (done) {
var el = helpers.build(window.__html__['Multifield.base.min.html']);

// initial min validation takes 1 frame
helpers.next(function() {
el.min = el.min - 1;
el.items.getAll().forEach(function(item) {
expect(item._deletable).to.be.true;
});
done();
});
});

it('toggle items deletable state when items added from button click', function (done) {
var el = helpers.build(window.__html__['Multifield.base.min.html']);
el.querySelector('[coral-multifield-add]').click();
// MO execution
helpers.next(function() {
el.items.getAll().forEach(function(item) {
expect(item._deletable).to.be.true;
});
done();
});
});

it('toggle items deletable state when items removed from button click and items count equal to min', function (done) {
var el = helpers.build(window.__html__['Multifield.base.min.html']);

el.min = 2;

el.items.getAll()[0].querySelector('._coral-Multifield-remove').click();
// MO execution
helpers.next(function() {
el.items.getAll().forEach(function(item) {
expect(item._deletable).to.be.false;
});
done();
});
});
});

describe('#template', function () {
});
describe('#coral-multifield-add', function () {
Expand Down

0 comments on commit 819a6b4

Please sign in to comment.