From 38899a32d69cd5c62ade7341a83708d4a8e1e726 Mon Sep 17 00:00:00 2001 From: jadrake75 Date: Sat, 15 Jan 2022 17:49:49 -0500 Subject: [PATCH] Fix/Rework Multi-select removal * The bitwiseToArray was not firing because the array reference was not getting tickled and it was being completely removed instead of removing one entry. * Fixed version displayed on splash screen * Added some tests for select-picker Fixes #181 --- README.md | 6 +- index.html | 2 +- .../elements/ownerships/ownership-cert.scss | 2 + .../elements/ownerships/ownership-editor.html | 4 +- .../elements/ownerships/ownership-notes.scss | 2 + .../elements/select-picker/select-picker.js | 89 +++++----- .../elements/select-picker/select-picker.scss | 2 +- .../select-picker/select-picker.spec.js | 152 ++++++++++++++++++ 8 files changed, 214 insertions(+), 45 deletions(-) create mode 100644 test/unit/resources/elements/select-picker/select-picker.spec.js diff --git a/README.md b/README.md index 0ae05ce..9bb78d5 100644 --- a/README.md +++ b/README.md @@ -93,9 +93,9 @@ Webdriver for NodeJS is used for the integration tests. This project has been mo The following is a list of test statistics for the project for date | Date | Commit | Number of Tests | Code Coverage | -| --- | --- | --- | --- | -| 2022-01-15 | [8ac447f](https://github.com/stamp-web/stamp-web-aurelia/commit/8ac447f580f29d1f0f8dd23e284c6f25448cf1d7) | 83 | 12% | - +| --- | --- | --- |---------------| +| 2022-01-15 | [8ac447f](https://github.com/stamp-web/stamp-web-aurelia/commit/8ac447f580f29d1f0f8dd23e284c6f25448cf1d7) | 83 | 12.05% | +| 2022-01-15 | [081fe3f](https://github.com/stamp-web/stamp-web-aurelia/commit/081fe3f31d5962c10777f4017e2c7a5dbe26e12e) | 86 | 12.20% | ## Optimizing for Browsers diff --git a/index.html b/index.html index c85ee63..ee98b94 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@
Stamp Web
-
Version 6.0.0
+
Version 6.2.0
diff --git a/src/resources/elements/ownerships/ownership-cert.scss b/src/resources/elements/ownerships/ownership-cert.scss index 6244bce..eaf8225 100644 --- a/src/resources/elements/ownerships/ownership-cert.scss +++ b/src/resources/elements/ownerships/ownership-cert.scss @@ -1,6 +1,8 @@ @import "../../../theme/_semantic.scss"; ownership-cert { + z-index: 10; + .sw-icon-ribbon { color: $theme-success-color; } diff --git a/src/resources/elements/ownerships/ownership-editor.html b/src/resources/elements/ownerships/ownership-editor.html index 1b022f2..685a38d 100644 --- a/src/resources/elements/ownerships/ownership-editor.html +++ b/src/resources/elements/ownerships/ownership-editor.html @@ -39,7 +39,7 @@
-
@@ -48,7 +48,7 @@
diff --git a/src/resources/elements/ownerships/ownership-notes.scss b/src/resources/elements/ownerships/ownership-notes.scss index 8c0c2cc..545e53a 100644 --- a/src/resources/elements/ownerships/ownership-notes.scss +++ b/src/resources/elements/ownerships/ownership-notes.scss @@ -1,6 +1,8 @@ @import "../../../theme/_semantic.scss"; ownership-notes { + z-index: 10; + .sw-icon-info { color: $theme-link-color; } diff --git a/src/resources/elements/select-picker/select-picker.js b/src/resources/elements/select-picker/select-picker.js index 8421b2c..7b2a62e 100644 --- a/src/resources/elements/select-picker/select-picker.js +++ b/src/resources/elements/select-picker/select-picker.js @@ -24,7 +24,7 @@ import select2 from 'select2'; //eslint-disable-line no-unused-vars @customElement('select-picker') export class Select2Picker { - @bindable({defaultBindingMode : bindingMode.twoWay}) value; + @bindable({defaultBindingMode: bindingMode.twoWay}) value; @bindable valueType = 'Number'; @bindable valueProperty = 'id'; @bindable labelProperty = 'name'; @@ -51,37 +51,37 @@ export class Select2Picker { let options = { placeholder: caption, - allowClear: true - }; + allowClear: true + }; - if( typeof this.config.filterSearch !== 'undefined' && this.config.filterSearch === false ) { + if (typeof this.config.filterSearch !== 'undefined' && this.config.filterSearch === false) { options.minimumResultsForSearch = Infinity; } - if( this.config.valueProperty ) { + if (this.config.valueProperty) { this.valueProperty = this.config.valueProperty; } - if( this.config.labelProperty ) { + if (this.config.labelProperty) { this.labelProperty = this.config.labelProperty; } - if( this.config.valueType ) { + if (this.config.valueType) { this.valueType = this.config.valueType; } - if( typeof this.config.allowClear !== 'undefined') { + if (typeof this.config.allowClear !== 'undefined') { options.allowClear = this.config.allowClear; } - if( typeof this.config.noSearch !== 'undefined') { + if (typeof this.config.noSearch !== 'undefined') { options.minimumResultsForSearch = Infinity; } - if( this.config.id ) { + if (this.config.id) { this.id = this.config.id; } else { - this.id = 'select-' + parseInt(Math.random() * 16384, 10); + this.id = `select-${parseInt(Math.random() * 16384, 10)}`; } - if( this.config.multiple === true || this.multiple === 'true' ) { + if (_.get(this, 'config.multiple') === true || _.get(this, 'multiple') === 'true') { options.multiple = true; this.multiple = true; } @@ -96,7 +96,7 @@ export class Select2Picker { this.select2 = $select.select2(options); let tabIndex = this.config.tabIndex; - if( typeof tabIndex === 'undefined' ) { + if (typeof tabIndex === 'undefined') { tabIndex = -1; } @@ -109,9 +109,9 @@ export class Select2Picker { unbind() { let select = this.element.firstElementChild; - if( select ) { + if (select) { const $select = $(select); - if( $select.data('select2')) { // depending on the destory state need to check whether it still thinks it is a select2 + if ($select.data('select2')) { // depending on the destroyed state need to check whether it still thinks it is a select2 $select.select2('destroy'); } } @@ -122,28 +122,29 @@ export class Select2Picker { * @param e selection event. */ onSelect(e) { - if( e.params && e.params.data ) { - let data = e.params.data.id; - if( this.multiple === true ) { - let val = data; - data = (this.value) ? _.clone(this.value) : this._getClearedValue(); - data.push(val); - this.value = _.uniq(data); // prevent duplicates - } else if ( this.valueType === 'Number') { - this.value = data ? Number.parseInt(data) : this._getClearedValue(); + if (e.params && e.params.data) { + let data = _.get(e, 'params.data.id'); + let originValue = this.value; + if (this.multiple === true) { + originValue.push(data); + this.value = this._getClearedValue(); + } else if (this.valueType === 'Number') { + originValue = data ? Number.parseInt(data) : this._getClearedValue(); } else { - this.value = data; + originValue = data; } - let changeEvent = EventHelper.changeEvent(data); - this.element.dispatchEvent(changeEvent); + _.defer(() => { // need to tickle the array + this.value = originValue; + this.element.dispatchEvent(EventHelper.changeEvent(this.value)); + }); } } _getClearedValue() { let value = ''; - if( this.multiple === true ) { + if (this.multiple === true) { value = []; - } else if ( this.valueType === 'Number') { + } else if (this.valueType === 'Number') { value = -1; } return value; @@ -157,15 +158,27 @@ export class Select2Picker { return item[this.labelProperty]; } - onUnselect(e) { + onUnselect() { _.defer(() => { $(this.select2).off('select2:opening'); }); } onUnselecting(e) { - this.value = this._getClearedValue(); - this.element.dispatchEvent(EventHelper.changeEvent(this.value)); + let removalValue = _.get(e, 'params.args.data'); + let origValue = this.value; + if (removalValue) { + if (this.multiple) { + this.value = this._getClearedValue(); + _.pull(origValue, _.get(removalValue, 'id')); + } else { + origValue = this._getClearedValue(); + } + } + _.defer(() => { + this.value = origValue; + this.element.dispatchEvent(EventHelper.changeEvent(origValue)); + }); $(this.select2).on('select2:opening', e => { e.preventDefault(); @@ -198,11 +211,11 @@ export class Select2Picker { * Handle the conversion of the type from the modeled value to the String equivalence of the value used by select2 */ _convertToInternal(val) { - if( typeof val === 'undefined' || _.isNil(val)) { + if (typeof val === 'undefined' || _.isNil(val)) { return undefined; } let retValue = val; - switch(this.valueType) { + switch (this.valueType) { case 'Number': retValue = val.toString(); break; @@ -215,9 +228,9 @@ export class Select2Picker { */ attached() { _.debounce(() => { - if( this.items && this.items.length > 0 ) { // cached items are loaded - this.valueChanged(this.value); - } + if (this.items && this.items.length > 0) { // cached items are loaded + this.valueChanged(this.value); + } }, 125)(); } @@ -229,7 +242,7 @@ export class Select2Picker { * @param oldValue */ itemsChanged(newValue, oldValue) { //eslint-disable-line no-unused-vars - if( newValue && newValue.length > 0 ) { + if (newValue && newValue.length > 0) { _.defer(() => { this.valueChanged(this.value); }); diff --git a/src/resources/elements/select-picker/select-picker.scss b/src/resources/elements/select-picker/select-picker.scss index d4b59a5..362bf30 100644 --- a/src/resources/elements/select-picker/select-picker.scss +++ b/src/resources/elements/select-picker/select-picker.scss @@ -79,7 +79,7 @@ select-picker { .select2-selection__clear { color: $theme-clear-color; margin-left: $theme-margin-thinner; - margin-right: 0; + margin-right: $theme-margin-thinner; margin-top: 0; transform: scale(1.5) translateY(0.5rem); } diff --git a/test/unit/resources/elements/select-picker/select-picker.spec.js b/test/unit/resources/elements/select-picker/select-picker.spec.js new file mode 100644 index 0000000..d83959f --- /dev/null +++ b/test/unit/resources/elements/select-picker/select-picker.spec.js @@ -0,0 +1,152 @@ +/** + Copyright 2022 Jason Drake + + Licensed 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 CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import {Select2Picker} from 'resources/elements/select-picker/select-picker'; +import {createSpyObj} from 'jest-createspyobj'; +import _ from 'lodash'; + +describe('SelectPicker test suite', () => { + + describe('onSelect', () => { + let selectPicker; + + beforeEach(() => { + let elm = { + dispatchEvent: jest.fn() + } + selectPicker = new Select2Picker(elm); + }); + + it('single selected for String', () => { + let evt = {}; + _.defer = jest.fn().mockImplementation(f => f()); + _.set(evt, 'params.data.id', '32'); + selectPicker.value = '16'; + selectPicker.valueType = 'String'; + selectPicker.onSelect(evt); + expect(selectPicker.value).toBe('32'); + _.delay(() => { + expect(selectPicker.value).toBe('32'); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + }); + + it('single selected for Number', () => { + let evt = {}; + _.defer = jest.fn().mockImplementation(f => f()); + _.set(evt, 'params.data.id', 32); + selectPicker.value = 16; + selectPicker.onSelect(evt); + expect(selectPicker.value).toBe(32); + _.delay(() => { + expect(selectPicker.value).toBe(32); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + }); + + it('multiple selected from empty', () => { + let evt = {}; + _.set(evt, 'params.data.id', '32'); + selectPicker.value = [] + selectPicker.multiple = true; + selectPicker.onSelect(evt); + expect(selectPicker.value.length).toBe(0); + _.delay(() => { + expect(selectPicker.value.length).toBe(1); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + }); + + it('multiple selected with existing', () => { + let evt = {}; + _.set(evt, 'params.data.id', '32'); + selectPicker.value = ['4'] + selectPicker.multiple = true; + selectPicker.onSelect(evt); + expect(selectPicker.value.length).toBe(0); + _.delay(() => { + expect(selectPicker.value.length).toBe(2); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + }); + }); + + describe('onUnselecting', () => { + let selectPicker; + + beforeEach(() => { + let elm = { + dispatchEvent: jest.fn() + } + selectPicker = new Select2Picker(elm); + }); + + it('single removed', () => { + let evt = {}; + _.defer = jest.fn().mockImplementation(f => f()); + _.set(evt, 'params.args.data', '32'); + selectPicker.value = '32'; + selectPicker.valueType = 'String'; + selectPicker.onUnselecting(evt); + expect(selectPicker.value).toBe(''); + _.delay(() => { + expect(selectPicker.value).toBe(''); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + }); + + it('single removed as numeric', () => { + let evt = {}; + _.set(evt, 'params.args.data', 64); + selectPicker.value = 64; + selectPicker.valueType = 'Number'; + selectPicker.onUnselecting(evt); + expect(selectPicker.value).toBe(64); + _.delay(() => { + expect(selectPicker.value).toBe(-1); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + }); + + it('multiple removed one from list', () => { + let evt = {}; + _.set(evt, 'params.args.data', '32'); + selectPicker.multiple = true; + selectPicker.value = ['2', '32', '64']; + selectPicker.onUnselecting(evt); + expect(selectPicker.value.length).toBe(0); + _.delay(() => { + expect(selectPicker.value.length).toBe(2); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + }); + + it('multiple removed last from list', () => { + let evt = {}; + _.set(evt, 'params.args.data', '32'); + selectPicker.multiple = true; + selectPicker.value = ['32']; + selectPicker.onUnselecting(evt); + expect(selectPicker.value.length).toBe(0); + _.delay(() => { + expect(selectPicker.value.length).toBe(0); + expect(selectPicker.element.dispatchEvent).toHaveBeenCalled(); + }, 125); + + }); + }); + + +});