mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 12:09:26 +00:00
working on dynamic options
This commit is contained in:
@@ -299,4 +299,16 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
|
||||
|
||||
:::warning
|
||||
Be sure you trust the content you are outputting! Passing unsanitized user input to `getTag()` can result in XSS vulnerabilities.
|
||||
:::
|
||||
:::
|
||||
|
||||
## Lazy loading options
|
||||
|
||||
Lazy loading options is very hard to get right. `<wa-select>` largely follows how a native `<select>` works.
|
||||
|
||||
Here are the following conditions:
|
||||
|
||||
- If a `<wa-select>` is created without any options, but is given a `value` attribute, its `value` will be `""`, and then when options are added, if any of the options have a value equal to the `<wa-select>` value, the value of the `<wa-select>` will be that of the option.
|
||||
|
||||
- If a `<wa-select>` with an initial value has multiple values, but only some of the options are present, it will only respect the loaded options, and if a selected option is loaded in later, *AND* the value of the select has not changed via user interaction, it will add the selected option.
|
||||
|
||||
This can be hard to conceptualize, so heres a fairly large example showing how lazy loaded options work with `<wa-select>`
|
||||
|
||||
@@ -614,6 +614,88 @@ describe('<wa-select>', () => {
|
||||
|
||||
expect(tag.hasAttribute('pill')).to.be.true;
|
||||
});
|
||||
|
||||
describe.only("With lazily loaded options", () => {
|
||||
describe("With no existing options", () => {
|
||||
it("Should wait to select the option when the option exists for multiple select", async () => {
|
||||
const form = await fixture<HTMLFormElement>(html`<form><wa-select value="option-1"></wa-select></form>`)
|
||||
const el = form.querySelector<WaSelect>("wa-select")!
|
||||
|
||||
expect(el.value).to.equal("")
|
||||
|
||||
const option = document.createElement("wa-option")
|
||||
option.value = "option-1"
|
||||
option.innerText = "Option 1"
|
||||
el.append(option)
|
||||
|
||||
await el.updateComplete
|
||||
expect(el.value).to.equal("option-1")
|
||||
})
|
||||
|
||||
it("Should wait to select the option when the option exists for multiple select", async () => {
|
||||
const form = await fixture<HTMLFormElement>(html`<form><wa-select value="option-1" multiple></wa-select></form>`)
|
||||
|
||||
const el = form.querySelector<WaSelect>("wa-select")!
|
||||
expect(el.value).to.equal("")
|
||||
|
||||
const option = document.createElement("wa-option")
|
||||
option.value = "option-1"
|
||||
option.innerText = "Option 1"
|
||||
el.append(option)
|
||||
|
||||
await el.updateComplete
|
||||
expect(el.value).to.equal(["option-1"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("With existing options", () => {
|
||||
it("Should not select the option if options already exist for single select", async () => {
|
||||
const form = await fixture<HTMLFormElement>(html`
|
||||
<form>
|
||||
<wa-select value="option-1">
|
||||
<wa-option value="bar">Bar</wa-option>
|
||||
<wa-option value="baz">Baz</wa-option>
|
||||
</wa-select>
|
||||
</form>`
|
||||
)
|
||||
|
||||
const el = form.querySelector<WaSelect>("wa-select")!
|
||||
expect(el.value).to.equal("")
|
||||
|
||||
const option = document.createElement("wa-option")
|
||||
option.value = "option-1"
|
||||
option.innerText = "Option 1"
|
||||
el.append(option)
|
||||
|
||||
await aTimeout(10)
|
||||
await el.updateComplete
|
||||
expect(el.value).to.equal("option-1")
|
||||
})
|
||||
|
||||
it("Should not select the option if options already exists for multiple select", async () => {
|
||||
const form = await fixture<HTMLFormElement>(html`
|
||||
<form>
|
||||
<wa-select value="option-1" multiple>
|
||||
<wa-option value="bar">Bar</wa-option>
|
||||
<wa-option value="baz">Baz</wa-option>
|
||||
</wa-select>
|
||||
</form>`
|
||||
)
|
||||
|
||||
const el = form.querySelector<WaSelect>("wa-select")!
|
||||
expect(el.value).to.equal("")
|
||||
|
||||
const option = document.createElement("wa-option")
|
||||
option.value = "option-1"
|
||||
option.innerText = "Option 1"
|
||||
el.append(option)
|
||||
|
||||
await aTimeout(10)
|
||||
await el.updateComplete
|
||||
expect(el.value).to.equal([""])
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -183,11 +183,6 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
@property({ attribute: false })
|
||||
set value(val: string | string[] | null) {
|
||||
if (this._value === val) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.valueHasChanged = true;
|
||||
this._value = val;
|
||||
}
|
||||
|
||||
@@ -290,11 +285,6 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.updateComplete.then(() => {
|
||||
if (!this.hasInteracted) {
|
||||
this.value = this.defaultValue;
|
||||
}
|
||||
});
|
||||
// Because this is a form control, it shouldn't be opened initially
|
||||
this.open = false;
|
||||
}
|
||||
@@ -545,6 +535,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
const oldValue = this.value;
|
||||
|
||||
if (option && !option.disabled) {
|
||||
this.valueHasChanged = true
|
||||
if (this.multiple) {
|
||||
this.toggleOptionSelection(option);
|
||||
} else {
|
||||
@@ -570,20 +561,21 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
}
|
||||
|
||||
private handleDefaultSlotChange() {
|
||||
if (!customElements.get("wa-option")) {
|
||||
customElements.whenDefined('wa-option').then(() => this.handleDefaultSlotChange());
|
||||
return
|
||||
}
|
||||
|
||||
const allOptions = this.getAllOptions();
|
||||
const value = Array.isArray(this.value) ? this.value : [this.value];
|
||||
const val = this.valueHasChanged ? this.value : this.defaultValue
|
||||
const value = Array.isArray(val) ? val : [val];
|
||||
const values: string[] = [];
|
||||
|
||||
// Check for duplicate values in menu items
|
||||
if (customElements.get('wa-option')) {
|
||||
allOptions.forEach(option => values.push(option.value));
|
||||
allOptions.forEach(option => values.push(option.value));
|
||||
|
||||
// Select only the options that match the new value
|
||||
this.setSelectedOptions(allOptions.filter(el => value.includes(el.value)));
|
||||
} else {
|
||||
// Rerun this handler when `<wa-option>` is registered
|
||||
customElements.whenDefined('wa-option').then(() => this.handleDefaultSlotChange());
|
||||
}
|
||||
// Select only the options that match the new value
|
||||
this.setSelectedOptions(allOptions.filter(el => value.includes(el.value)));
|
||||
}
|
||||
|
||||
private handleTagRemove(event: WaRemoveEvent, option: WaOption) {
|
||||
@@ -661,13 +653,10 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
// This method must be called whenever the selection changes. It will update the selected options cache, the current
|
||||
// value, and the display value
|
||||
private selectionChanged() {
|
||||
// if (!customElements.get('wa-option')) {
|
||||
// customElements.whenDefined('wa-option').then(() => this.selectionChanged());
|
||||
// return;
|
||||
// }
|
||||
const options = this.getAllOptions()
|
||||
|
||||
// Update selected options cache
|
||||
this.selectedOptions = this.getAllOptions().filter(el => el.selected);
|
||||
this.selectedOptions = options.filter(el => el.selected);
|
||||
|
||||
// Update the value and display label
|
||||
if (this.multiple) {
|
||||
@@ -680,8 +669,9 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
this.displayLabel = this.localize.term('numOptionsSelected', this.selectedOptions.length);
|
||||
}
|
||||
} else {
|
||||
this.value = this.selectedOptions[0]?.value ?? '';
|
||||
this.displayLabel = this.selectedOptions[0]?.getTextLabel?.() ?? '';
|
||||
const selectedOption = this.selectedOptions[0]
|
||||
this.value = selectedOption?.value ?? '';
|
||||
this.displayLabel = selectedOption?.getTextLabel?.() ?? '';
|
||||
}
|
||||
|
||||
// Update validity
|
||||
|
||||
Reference in New Issue
Block a user