Skip to content

Commit

Permalink
fix(GRANITE-42603): Multifield component does not provide support for…
Browse files Browse the repository at this point in the history
… readonly (#257)
  • Loading branch information
Pareesh committed Nov 29, 2022
1 parent d7682ad commit 952936f
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 2,014 deletions.
15 changes: 15 additions & 0 deletions coral-component-multifield/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ <h2 class="coral--Heading--S">With Min specified</h2>
</coral-multifield>
</div>

<h2 class="coral--Heading--S">Readonly</h2>
<div class="markup">
<coral-multifield readOnly aria-label="Readonly multifield">
<coral-multifield-item>
<input is="coral-textfield" aria-label="input" type="text" value="Hello"/>
</coral-multifield-item>
<coral-multifield-item>
<input is="coral-textfield" aria-label="input" type="text" value="World"/>
</coral-multifield-item>
<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>
42 changes: 40 additions & 2 deletions coral-component-multifield/src/scripts/Multifield.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,33 @@ const Multifield = Decorator(class extends BaseComponent(HTMLElement) {
});
}

/**
Whether this multifield is readOnly or not. Indicating that the user cannot modify the value of the multifield fields.
@type {Boolean}
@default false
@htmlattribute readonly
@htmlattributereflected
*/
get readOnly() {
return this._readOnly || false;
}

set readOnly(value) {
value = transform.booleanAttr(value);
this._readOnly = value;
this._reflectAttribute('readonly', value);

this.items.getAll().forEach((item) => {
item[value ? 'setAttribute' : 'removeAttribute']('_readonly', '');
});

let addBtn = this.querySelector('[coral-multifield-add]');
if (addBtn) {
addBtn.disabled = value;
}

}

/**
Specifies the minimum number of items multifield should render.
If component contains less items, remaining items will be added.
Expand Down Expand Up @@ -193,8 +220,11 @@ const Multifield = Decorator(class extends BaseComponent(HTMLElement) {
let itemsToBeAdded = currentMin - currentLength;

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

deletable = !deletable;
}

Expand Down Expand Up @@ -649,10 +679,18 @@ const Multifield = Decorator(class extends BaseComponent(HTMLElement) {
/** @ignore */
static get observedAttributes() {
return super.observedAttributes.concat([
'min'
'min',
'readonly'
]);
}

/** @ignore */
static get _attributePropertyMap() {
return commons.extend(super._attributePropertyMap, {
readonly: 'readOnly',
});
}

/** @ignore */
render() {
super.render();
Expand Down
46 changes: 45 additions & 1 deletion coral-component-multifield/src/scripts/MultifieldItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ const MultifieldItem = Decorator(class extends BaseComponent(HTMLElement) {
set _deletable(value) {
value = transform.boolean(value);
this.__deletable = value;
this._elements.remove.disabled = !value;

if(!this._readOnly) {
this._elements.remove.disabled = !value;
}
}

/**
Expand Down Expand Up @@ -109,10 +112,51 @@ const MultifieldItem = Decorator(class extends BaseComponent(HTMLElement) {
this._elements.move.selected = this.__dragging;
}

/**
Whether this multifieldItem is readOnly or not. Indicating that the user cannot modify the value of the multifieldItem fields.
@type {Boolean}
@default false
@private
*/
get _readOnly() {
return this.__readOnly || false;
}

set _readOnly(value) {
value = transform.booleanAttr(value);
this.__readOnly = value;
this._reflectAttribute('_readonly', value);

// get all fields and set readonly to those whose has this property
let allFields = this.querySelectorAll("*");
Array.prototype.forEach.call(allFields, (field) => {
if(typeof field.readOnly === "boolean") {
field.readOnly = value;
}
});

this._elements.move.disabled = value;
this._elements.remove.disabled = value;
}

get _contentZones() {
return {'coral-multifield-item-content': 'content'};
}

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

/** @ignore */
static get _attributePropertyMap() {
return commons.extend(super._attributePropertyMap, {
_readonly: '_readOnly',
});
}

/** @ignore */
render() {
super.render();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<coral-multifield readOnly>
<coral-multifield-item>
<input type="text" value="Hello"/>
</coral-multifield-item>
<coral-multifield-item>
<input type="text" value="World"/>
</coral-multifield-item>
<button coral-multifield-add type="button" is="coral-button">Add a field</button>
<template coral-multifield-template>
<input type="text"/>
</template>
</coral-multifield>
107 changes: 98 additions & 9 deletions coral-component-multifield/src/tests/test.Multifield.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,18 @@ describe('Multifield', 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']);
var el = helpers.build(window.__html__['Multifield.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']);
var el = helpers.build(window.__html__['Multifield.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 el = helpers.build(window.__html__['Multifield.min.html']);
var initialCount = el.items.length;
el.min = initialCount - 1;
expect(initialCount).to.be.equal(3);
Expand All @@ -93,8 +93,8 @@ describe('Multifield', function () {
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']);
it('items should not be deletable when item count less than or equal to min', function (done) {
var el = helpers.build(window.__html__['Multifield.min.html']);
var initialCount = el.items.length;

expect(initialCount).to.be.equal(el.min);
Expand All @@ -117,8 +117,8 @@ describe('Multifield', function () {
});
});

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

// initial min validation takes 1 frame
helpers.next(function() {
Expand All @@ -131,7 +131,7 @@ describe('Multifield', function () {
});

it('toggle items deletable state when items added from button click', function (done) {
var el = helpers.build(window.__html__['Multifield.base.min.html']);
var el = helpers.build(window.__html__['Multifield.min.html']);
el.querySelector('[coral-multifield-add]').click();
// MO execution
helpers.next(function() {
Expand All @@ -143,7 +143,7 @@ describe('Multifield', function () {
});

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']);
var el = helpers.build(window.__html__['Multifield.min.html']);

el.min = 2;

Expand All @@ -158,6 +158,95 @@ describe('Multifield', function () {
});
});

describe("#readonly", function() {
it("readOnly property and attribute should be true when readonly is set", function() {
var el = helpers.build(window.__html__['Multifield.readonly.html']);

expect(el.readOnly).to.be.true;
expect(el.hasAttribute('readonly')).to.be.true;
});

it("should disable add, remove and move buttons when readonly is set", function() {
var el = helpers.build(window.__html__['Multifield.readonly.html']);

expect(el.querySelector('[coral-multifield-add]').disabled).to.be.true;

el.items.getAll().forEach(function(item) {
expect(item.querySelector('[coral-multifield-move]').disabled).to.be.true;
expect(item.querySelector('[coral-multifield-remove]').disabled).to.be.true;
});
});

it("should enable add, remove and move buttons when readonly is unset", function() {
var el = helpers.build(window.__html__['Multifield.readonly.html']);

el.readOnly = false;
expect(el.querySelector('[coral-multifield-add]').disabled).to.be.false;

el.items.getAll().forEach(function(item) {
expect(item.querySelector('[coral-multifield-move]').disabled).to.be.false;
expect(item.querySelector('[coral-multifield-remove]').disabled).to.be.false;
});
});

it("editable fields in multifield should be readable when reaadonly is set", function(done) {
var el = helpers.build(window.__html__['Multifield.readonly.html']);

helpers.next(() => {
el.items.getAll().forEach((item) => {
var allFields = item.content.querySelectorAll("*");

Array.prototype.forEach.call(allFields, (field) => {
if(typeof field.readOnly === "boolean") {
console.log(field);
expect(field.readOnly).to.be.true;
}
});
});
done();
});
});

it("multifield template fields should not be affected when readonly is set", function() {
var el = helpers.build(window.__html__['Multifield.readonly.html']);

var template = el.querySelector('template');

var allFields = template.querySelectorAll("*");

Array.prototype.forEach.call(allFields, (field) => {
if(typeof field.readOnly === "boolean") {
expect(field.readOnly).to.be.false;
}
});
});

it("should add item with disabled move and remove button when readonly is set and item count less than min", function() {
var el = helpers.build(`
<coral-multifield readOnly min="3">
<coral-multifield-item>
<input type="text" value="Hello"/>
</coral-multifield-item>
<coral-multifield-item>
<input type="text" value="World"/>
</coral-multifield-item>
<button coral-multifield-add type="button" is="coral-button">Add a field</button>
<template coral-multifield-template>
<input type="text"/>
</template>
</coral-multifield>
`);

expect(el.querySelector('[coral-multifield-add]').disabled).to.be.true;
expect(el.items.getAll().length).to.be.equal(3);

el.items.getAll().forEach(function(item) {
expect(item.querySelector('[coral-multifield-move]').disabled).to.be.true;
expect(item.querySelector('[coral-multifield-remove]').disabled).to.be.true;
});
});
})

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

0 comments on commit 952936f

Please sign in to comment.