Skip to content

Commit a5132f4

Browse files
committed
Update Select To track value change
Correctly get and Set value
1 parent eddb9f4 commit a5132f4

File tree

1 file changed

+66
-24
lines changed

1 file changed

+66
-24
lines changed

src/components/select/select.component.ts

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import {animateTo, stopAnimations} from '../../internal/animate.js';
2-
import {property, query, state} from 'lit/decorators.js';
3-
import {type CSSResultGroup, html, TemplateResult, unsafeCSS} from 'lit';
4-
import {watch} from '../../internal/watch';
5-
import ZincElement, {ZincFormControl} from '../../internal/zinc-element';
62
import {classMap} from "lit/directives/class-map.js";
7-
import {FormControlController, validValidityState} from "../../internal/form";
8-
import {scrollIntoView} from "../../internal/scroll";
3+
import {type CSSResultGroup, html, type TemplateResult, unsafeCSS} from 'lit';
94
import {defaultValue} from "../../internal/default-value";
5+
import {FormControlController} from "../../internal/form";
6+
import {getAnimation, setDefaultAnimation} from "../../utilities/animation-registry";
107
import {HasSlotController} from "../../internal/slot";
118
import {LocalizeController} from "../../utilities/localize";
12-
import {ZnRemoveEvent} from "../../events/zn-remove";
9+
import {property, query, state} from 'lit/decorators.js';
10+
import {scrollIntoView} from "../../internal/scroll";
1311
import {unsafeHTML} from "lit/directives/unsafe-html.js";
14-
import {getAnimation, setDefaultAnimation} from "../../utilities/animation-registry";
1512
import {waitForEvent} from "../../internal/event";
13+
import {watch} from '../../internal/watch';
14+
import type {ZincFormControl} from '../../internal/zinc-element';
15+
import ZincElement from '../../internal/zinc-element';
1616
import ZnChip from "../chip";
1717
import ZnIcon from "../icon";
18-
import ZnOption from "../option";
1918
import ZnPopup from "../popup";
19+
import type {ZnRemoveEvent} from "../../events/zn-remove";
20+
import type ZnOption from "../option";
2021

2122
import styles from './select.scss';
2223

@@ -94,22 +95,38 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
9495
@state() displayLabel = '';
9596
@state() currentOption: ZnOption;
9697
@state() selectedOptions: ZnOption[] = [];
98+
@state() private valueHasChanged: boolean = false;
9799

98100
/** The name of the select, submitted as a name/value pair with form data. */
99101
@property() name = '';
100102

103+
private _value: string | string[] = '';
104+
105+
get value() {
106+
return this._value
107+
}
108+
109+
101110
/**
102111
* The current value of the select, submitted as a name/value pair with form data. When `multiple` is enabled, the
103112
* value attribute will be a space-delimited list of values based on the options selected, and the value property will
104113
* be an array. **For this reason, values must not contain spaces.**
105114
*/
106-
@property({
107-
converter: {
108-
fromAttribute: (value: string) => value.split(' '),
109-
toAttribute: (value: string[]) => value.join(' ')
115+
@state()
116+
set value(val: string | string[]) {
117+
if (this.multiple) {
118+
val = Array.isArray(val) ? val : val.split(' ');
119+
} else {
120+
val = Array.isArray(val) ? val.join(' ') : val;
121+
}
122+
123+
if (this._value === val) {
124+
return;
110125
}
111-
})
112-
value: string | string[] = '';
126+
127+
this.valueHasChanged = true;
128+
this._value = val;
129+
}
113130

114131
/** The default value of the form control. Primarily used for resetting the form control. */
115132
@defaultValue() defaultValue: string | string[] = '';
@@ -209,12 +226,12 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
209226

210227
/** Gets the validity state object */
211228
get validity() {
212-
return this.valueInput ? this.valueInput.validity : validValidityState
229+
return this.valueInput.validity;
213230
}
214231

215232
/** Gets the validation message */
216233
get validationMessage() {
217-
return this.valueInput?.validationMessage;
234+
return this.valueInput.validationMessage;
218235
}
219236

220237
connectedCallback() {
@@ -237,7 +254,15 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
237254

238255

239256
private addOpenListeners() {
240-
const root = this.getRootNode();
257+
document.addEventListener('focusin', this.handleDocumentFocusIn);
258+
document.addEventListener('keydown', this.handleDocumentKeyDown);
259+
document.addEventListener('mousedown', this.handleDocumentMouseDown);
260+
261+
// If the component is rendered in a shadow root, we need to attach the focusin listener there too
262+
if (this.getRootNode() !== document) {
263+
this.getRootNode().addEventListener('focusin', this.handleDocumentFocusIn);
264+
}
265+
241266
if ('CloseWatcher' in window) {
242267
this.closeWatcher?.destroy();
243268
this.closeWatcher = new CloseWatcher();
@@ -248,16 +273,18 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
248273
}
249274
};
250275
}
251-
root.addEventListener('focusin', this.handleDocumentFocusIn);
252-
root.addEventListener('keydown', this.handleDocumentKeyDown);
253-
root.addEventListener('mousedown', this.handleDocumentMouseDown);
254276
}
255277

256278
private removeOpenListeners() {
257279
const root = this.getRootNode();
258280
root.removeEventListener('focusin', this.handleDocumentFocusIn);
259281
root.removeEventListener('keydown', this.handleDocumentKeyDown);
260282
root.removeEventListener('mousedown', this.handleDocumentMouseDown);
283+
284+
if (this.getRootNode() !== document) {
285+
this.getRootNode().removeEventListener('focusin', this.handleDocumentFocusIn);
286+
}
287+
261288
this.closeWatcher?.destroy();
262289
}
263290

@@ -312,6 +339,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
312339

313340
// If it is open, update the value based on the current selection and close it
314341
if (this.currentOption && !this.currentOption.disabled) {
342+
this.valueHasChanged = true;
315343
if (this.multiple) {
316344
this.toggleOptionSelection(this.currentOption);
317345
} else {
@@ -472,6 +500,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
472500
const oldValue = this.value;
473501

474502
if (option && !option.disabled) {
503+
this.valueHasChanged = true
475504
if (this.multiple) {
476505
this.toggleOptionSelection(option);
477506
} else {
@@ -593,6 +622,9 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
593622
// Update selected options cache
594623
this.selectedOptions = this.getAllOptions().filter(el => el.selected);
595624

625+
// Keep a reference to the previous `valueHasChanged`. Changes made here don't count has changing the value.
626+
const cachedValueHasChanged: boolean = this.valueHasChanged;
627+
596628
// Update the value and display label
597629
if (this.multiple) {
598630
this.value = this.selectedOptions.map(el => el.value);
@@ -604,10 +636,13 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
604636
this.displayLabel = this.localize.term('numOptionsSelected', this.selectedOptions.length);
605637
}
606638
} else {
607-
this.value = this.selectedOptions[0]?.value ?? '';
608-
this.displayLabel = this.selectedOptions[0]?.getTextLabel() ?? '';
639+
const selectedOption = this.selectedOptions[0];
640+
this.value = selectedOption?.value ?? '';
641+
this.displayLabel = selectedOption?.getTextLabel() ?? '';
609642
}
610643

644+
this.valueHasChanged = cachedValueHasChanged;
645+
611646
// Update validity
612647
this.updateComplete.then(() => {
613648
this.formControlController.updateValidity();
@@ -647,6 +682,13 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
647682

648683
@watch('value', {waitUntilFirstUpdate: true})
649684
handleValueChange() {
685+
if (!this.valueHasChanged) {
686+
const cachedValueHasChanged = this.valueHasChanged;
687+
this.value = this.defaultValue;
688+
689+
// Set it back to false since this isn't an interaction
690+
this.valueHasChanged = cachedValueHasChanged;
691+
}
650692
const allOptions = this.getAllOptions();
651693
const value = Array.isArray(this.value) ? this.value : [this.value];
652694

@@ -860,7 +902,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
860902
type="text"
861903
?disabled=${this.disabled}
862904
?required=${this.required}
863-
.value=${Array.isArray(this.value) ? this.value.join(', ') : this.value}
905+
value=${Array.isArray(this.value) ? this.value.join(', ') : this.value}
864906
tabindex="-1"
865907
aria-hidden="true"
866908
@focus=${() => this.focus()}

0 commit comments

Comments
 (0)