diff --git a/packages/webawesome/docs/docs/components/breadcrumb-item.md b/packages/webawesome/docs/docs/components/breadcrumb-item.md index 7941b85cb..b7c642287 100644 --- a/packages/webawesome/docs/docs/components/breadcrumb-item.md +++ b/packages/webawesome/docs/docs/components/breadcrumb-item.md @@ -5,17 +5,4 @@ layout: component category: Navigation --- -```html {.example} - - - - Home - - Clothing - Shirts - -``` - -:::info -Additional demonstrations can be found in the [breadcrumb examples](/docs/components/breadcrumb). -::: +This component must be used as a child of ``. Please see the [Breadcrumb docs](/docs/components/breadcrumb) to see examples of this component in action. diff --git a/packages/webawesome/docs/docs/components/carousel-item.md b/packages/webawesome/docs/docs/components/carousel-item.md index 8217b5e8f..732640321 100644 --- a/packages/webawesome/docs/docs/components/carousel-item.md +++ b/packages/webawesome/docs/docs/components/carousel-item.md @@ -5,41 +5,4 @@ layout: component category: Imagery --- -```html {.example} - - - The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash - - - A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash - - - The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash - - - A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash - - - A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash - - -``` - -:::info -Additional demonstrations can be found in the [carousel examples](/docs/components/carousel). -::: +This component must be used as a child of ``. Please see the [Carousel docs](/docs/components/carousel) to see examples of this component in action. diff --git a/packages/webawesome/docs/docs/components/option.md b/packages/webawesome/docs/docs/components/option.md index 5fd0b90cb..4f8a2b8b0 100644 --- a/packages/webawesome/docs/docs/components/option.md +++ b/packages/webawesome/docs/docs/components/option.md @@ -5,50 +5,4 @@ layout: component category: Form Controls --- -```html {.example} - - Option 1 - Option 2 - Option 3 - -``` - -## Examples - -### Disabled - -Use the `disabled` attribute to disable an option and prevent it from being selected. - -```html {.example} - - Option 1 - Option 2 - Option 3 - -``` - -### Start & End Decorations - -Use the `start` and `end` slots to add presentational elements like `` next to the option label. - -```html {.example} - - - - Email - - - - - - Phone - - - - - - Chat - - - -``` +This component must be used as a child of ``. Please see the [Select docs](/docs/components/select) to see examples of this component in action. diff --git a/packages/webawesome/docs/docs/components/radio.md b/packages/webawesome/docs/docs/components/radio.md index b1c2e6680..7e6661ba0 100644 --- a/packages/webawesome/docs/docs/components/radio.md +++ b/packages/webawesome/docs/docs/components/radio.md @@ -5,70 +5,4 @@ layout: component category: Form Controls --- -Radios are designed to be used with [radio groups](/docs/components/radio-group). - -```html {.example} - - Option 1 - Option 2 - Option 3 - -``` - -:::info -This component works with standard `
` elements. Please refer to the section on [form controls](/docs/form-controls) to learn more about form submission and client-side validation. -::: - -## Examples - -### Initial Value - -To set the initial value and checked state, use the `value` attribute on the containing radio group. - -```html {.example} - - Option 1 - Option 2 - Option 3 - -``` - -### Disabled - -Use the `disabled` attribute to disable a radio. - -```html {.example} - - Option 1 - Option 2 - Option 3 - -``` - -### Sizes - -Add the `size` attribute to the [Radio Group](/docs/components/radio-group) to change the radios' size. - -```html {.example} - - Small 1 - Small 2 - Small 3 - - -
- - - Medium 1 - Medium 2 - Medium 3 - - -
- - - Large 1 - Large 2 - Large 3 - -``` +This component must be used as a child of ``. Please see the [Radio Group docs](/docs/components/radio-group) to see examples of this component in action. diff --git a/packages/webawesome/docs/docs/components/select.md b/packages/webawesome/docs/docs/components/select.md index d78236f09..c98c59b44 100644 --- a/packages/webawesome/docs/docs/components/select.md +++ b/packages/webawesome/docs/docs/components/select.md @@ -108,13 +108,13 @@ Use the `disabled` attribute to disable a select. ### Multiple -To allow multiple options to be selected, use the `multiple` attribute. It's a good practice to use `with-clear` when this option is enabled. To set multiple values at once, set `value` to a space-delimited list of values. +To allow multiple options to be selected, use the `multiple` attribute. It's a good practice to use `with-clear` when this option is enabled. You can select multiple options by adding the `selected` attribute to individual options. ```html {.example} - - Option 1 - Option 2 - Option 3 + + Option 1 + Option 2 + Option 3 Option 4 Option 5 Option 6 @@ -122,33 +122,37 @@ To allow multiple options to be selected, use the `multiple` attribute. It's a g ``` :::info -Note that multi-select options may wrap, causing the control to expand vertically. You can use the `max-options-visible` attribute to control the maximum number of selected options to show at once. +Selecting multiple options may result in wrapping, causing the control to expand vertically. You can use the `max-options-visible` attribute to control the maximum number of selected options to show at once. ::: ### Setting Initial Values -Use the `value` attribute to set the initial selection. +Use the `selected` attribute on individual options to set the initial selection, similar to native HTML. ```html {.example} - - Option 1 + + Option 1 Option 2 Option 3 Option 4 ``` -When using `multiple`, the `value` _attribute_ uses space-delimited values to select more than one option. Because of this, `` values cannot contain spaces. If you're accessing the `value` _property_ through Javascript, it will be an array. +For multiple selections, apply it to all selected options. ```html {.example} - - Option 1 - Option 2 + + Option 1 + Option 2 Option 3 Option 4 ``` +:::info +Framework users can bind directly to the `value` property for reactive data binding and form state management. +::: + ### Grouping Options Use `` to group listbox items visually. You can also use `` to provide labels, but they won't be announced by most assistive devices. @@ -239,17 +243,17 @@ Use the `start` and `end` slots to add presentational elements like `` ### Custom Tags -When multiple options can be selected, you can provide custom tags by passing a function to the `getTag` property. Your function can return a string of HTML, a Lit Template, or an [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement). The `getTag()` function will be called for each option. The first argument is an `` element and the second argument is the tag's index (its position in the tag list). +When multiple options can be selected, you can provide custom tags by passing a function to the `getTag` property. Your function can return a string of HTML, a [Lit Template](https://lit.dev/docs/templates/overview/), or an [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement). The `getTag()` function will be called for each option. The first argument is an `` element and the second argument is the tag's index (its position in the tag list). Remember that custom tags are rendered in a shadow root. To style them, you can use the `style` attribute in your template or you can add your own [parts](/docs/customizing/#css-parts) and target them with the [`::part()`](https://developer.mozilla.org/en-US/docs/Web/CSS/::part) selector. ```html {.example} - - + + Email - + Phone @@ -285,17 +289,15 @@ Be sure you trust the content you are outputting! Passing unsanitized user input ### Lazy loading options -Lazy loading options is very hard to get right. `` largely follows how a native `` elements. The select component handles various scenarios intelligently: -Here are the following conditions: +#### Basic lazy loading scenarios: -- If a `` 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 `` value, the value of the `` will equal that of the option. +- **Empty select with value**: If a `` is created without any options but given a `value` attribute, its value will be `""` initially. When options are added later, if any option has a value matching the select's value attribute, the select's value will update to match. -EX: `` will have a value of `""` until `Foo` connects, at which point its value will become `"foo"` when submitting. +- **Multiple select with partial options**: If a `` has an initial value with multiple options, but only some options are present in the DOM, it will respect only the available options. When additional selected options are loaded later (and the user hasn't changed the selection), those options will be automatically added to the selection. -- If a `` with an initial value has multiple values, but only some of the options are present, it will only respect the options that are present, and if a selected option is loaded in later, _AND_ the value of the select has not changed via user interaction or direct property assignment, it will add the selected option to the form value and to the `.value` of the select. - -This can be hard to conceptualize, so heres a fairly large example showing how lazy loaded options work with `` and `` when given initial value attributes. Feel free to play around with it in a codepen. +Here's a comprehensive example showing different lazy loading scenarios: ```html {.example} @@ -319,12 +321,12 @@ This can be hard to conceptualize, so heres a fairly large example showing how l
- - Bar - Baz + + Bar + Baz
- Add "foo" option + Add "foo" option (selected)

@@ -365,6 +367,12 @@ This can be hard to conceptualize, so heres a fairly large example showing how l const option = document.createElement('wa-option'); option.setAttribute('value', 'foo'); option.innerText = 'Foo'; + + // For the multiple select with existing selected options, make the new option selected + if (select.getAttribute('name') === 'select-3') { + option.selected = true; + } + select.append(option); } @@ -391,3 +399,7 @@ This can be hard to conceptualize, so heres a fairly large example showing how l container.addEventListener('submit', handleLazySubmit); ``` + +:::info +The key principle is that the select component prioritizes user interactions and explicit selections over programmatic changes, ensuring a predictable user experience even with dynamically loaded content. +::: diff --git a/packages/webawesome/docs/docs/components/tab-panel.md b/packages/webawesome/docs/docs/components/tab-panel.md index 93147d796..83cc0e1fd 100644 --- a/packages/webawesome/docs/docs/components/tab-panel.md +++ b/packages/webawesome/docs/docs/components/tab-panel.md @@ -5,20 +5,4 @@ layout: component category: Navigation --- -```html {.example} - - General - Custom - Advanced - Disabled - - This is the general tab panel. - This is the custom tab panel. - This is the advanced tab panel. - This is a disabled tab panel. - -``` - -:::info -Additional demonstrations can be found in the [tab group examples](/docs/components/tab-group). -::: +This component must be used as a child of ``. Please see the [Tab Group docs](/docs/components/tab-group) to see examples of this component in action. diff --git a/packages/webawesome/docs/docs/components/tab.md b/packages/webawesome/docs/docs/components/tab.md index 271ae235b..dc542f77a 100644 --- a/packages/webawesome/docs/docs/components/tab.md +++ b/packages/webawesome/docs/docs/components/tab.md @@ -5,6 +5,4 @@ layout: component category: Navigation --- -:::info -Additional demonstrations can be found in the [tab group examples](/docs/components/tab-group). -::: +This component must be used as a child of ``. Please see the [Tab Group docs](/docs/components/tab-group) to see examples of this component in action. diff --git a/packages/webawesome/docs/docs/components/tree-item.md b/packages/webawesome/docs/docs/components/tree-item.md index 456671c91..0ae3e2d9b 100644 --- a/packages/webawesome/docs/docs/components/tree-item.md +++ b/packages/webawesome/docs/docs/components/tree-item.md @@ -5,78 +5,4 @@ layout: component category: Navigation --- -```html {.example} - - - Item 1 - Item A - Item B - Item C - - Item 2 - Item 3 - -``` - -## Examples - -### Nested tree items - -A tree item can contain other tree items. This allows the node to be expanded or collapsed by the user. - -```html {.example} - - - Item 1 - - Item A - Item Z - Item Y - Item X - - Item B - Item C - - Item 2 - Item 3 - -``` - -### Selected - -Use the `selected` attribute to select a tree item initially. - -```html {.example} - - - Item 1 - Item A - Item B - Item C - - Item 2 - Item 3 - -``` - -### Expanded - -Use the `expanded` attribute to expand a tree item initially. - -```html {.example} - - - Item 1 - - Item A - Item Z - Item Y - Item X - - Item B - Item C - - Item 2 - Item 3 - -``` +This component must be used as a child of ``. Please see the [Tree docs](/docs/components/tree) to see examples of this component in action. diff --git a/packages/webawesome/docs/docs/resources/changelog.md b/packages/webawesome/docs/docs/resources/changelog.md index 6d3f967e4..5c44627f8 100644 --- a/packages/webawesome/docs/docs/resources/changelog.md +++ b/packages/webawesome/docs/docs/resources/changelog.md @@ -60,6 +60,7 @@ During the alpha period, things might break! We take breaking changes very serio - `` - `` - `` +- 🚨 BREAKING: reworked `` to use `` to set initially selected options, removing the "no spaces allowed" restrictions for option values - Added a new free component: `` (#2 of 14 per stretch goals) - Added a new free component: `` (#3 of 14 per stretch goals) - Added a `min-block-size` to `` to ensure the divider is visible regardless of container height [issue:675] diff --git a/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts b/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts index 610422755..274cf6f6e 100644 --- a/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts +++ b/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts @@ -6,7 +6,7 @@ import WebAwesomeElement from '../../internal/webawesome-element.js'; import styles from './breadcrumb-item.css'; /** - * @summary Breadcrumb Items are used inside [breadcrumbs](/docs/components/breadcrumb) to represent different links. + * @summary Breadcrumb Items are used inside breadcrumbs to represent different links. * @documentation https://backers.webawesome.com/docs/components/breadcrumb-item * @status stable * @since 2.0 diff --git a/packages/webawesome/src/components/carousel-item/carousel-item.ts b/packages/webawesome/src/components/carousel-item/carousel-item.ts index 779d5df08..a655b317c 100644 --- a/packages/webawesome/src/components/carousel-item/carousel-item.ts +++ b/packages/webawesome/src/components/carousel-item/carousel-item.ts @@ -4,7 +4,7 @@ import WebAwesomeElement from '../../internal/webawesome-element.js'; import styles from './carousel-item.css'; /** - * @summary A carousel item represent a slide within a [carousel](/docs/components/carousel). + * @summary A carousel item represent a slide within a carousel. * * @since 2.0 * @status experimental diff --git a/packages/webawesome/src/components/option/option.ts b/packages/webawesome/src/components/option/option.ts index 5972cec9a..b8b826fcb 100644 --- a/packages/webawesome/src/components/option/option.ts +++ b/packages/webawesome/src/components/option/option.ts @@ -8,7 +8,7 @@ import '../icon/icon.js'; import styles from './option.css'; /** - * @summary Options define the selectable items within various form controls such as [select](/docs/components/select). + * @summary Options define the selectable items within a select component. * @documentation https://backers.webawesome.com/docs/components/option * @status stable * @since 2.0 @@ -46,8 +46,6 @@ export default class WaOption extends WebAwesomeElement { // Set via the parent select @state() current = false; - @state() selected = false; - /** * The option's value. When selected, the containing form control will receive this value. The value must be unique * from other options in the same group. Values may not contain spaces, as spaces are used as delimiters when listing @@ -56,7 +54,13 @@ export default class WaOption extends WebAwesomeElement { @property({ reflect: true }) value = ''; /** Draws the option in a disabled state, preventing selection. */ - @property({ type: Boolean, reflect: true }) disabled = false; + @property({ type: Boolean }) disabled = false; + + /** @internal */ + @property({ type: Boolean, attribute: false }) selected = false; + + /** Selects an option initially. */ + @property({ type: Boolean, attribute: 'selected' }) defaultSelected = false; _label: string = ''; /** @@ -107,10 +111,6 @@ export default class WaOption extends WebAwesomeElement { private handleDefaultSlotChange() { // Tell the controller to update the label - if (customElements.get('wa-select')) { - this.closest('wa-select')?.selectionChanged(); - } - this.updateDefaultLabel(); if (this.isInitialized) { @@ -119,6 +119,7 @@ export default class WaOption extends WebAwesomeElement { const controller = this.closest('wa-select'); if (controller) { controller.handleDefaultSlotChange(); + controller.selectionChanged?.(); } }); } else { @@ -136,6 +137,17 @@ export default class WaOption extends WebAwesomeElement { } }; + protected willUpdate(changedProperties: PropertyValues): void { + if (changedProperties.has('defaultSelected')) { + if (!this.closest('wa-select')?.hasInteracted) { + const oldVal = this.selected; + this.selected = this.defaultSelected; + this.requestUpdate('selected', oldVal); + } + } + super.willUpdate(changedProperties); + } + updated(changedProperties: PropertyValues) { super.updated(changedProperties); @@ -146,6 +158,7 @@ export default class WaOption extends WebAwesomeElement { if (changedProperties.has('selected')) { this.setAttribute('aria-selected', this.selected ? 'true' : 'false'); this.customStates.set('selected', this.selected); + this.handleDefaultSlotChange(); } if (changedProperties.has('value')) { @@ -155,12 +168,6 @@ export default class WaOption extends WebAwesomeElement { this.value = String(this.value); } - if (this.value.includes(' ')) { - // eslint-disable-next-line no-console - console.error(`Option values cannot include a space. All spaces have been replaced with underscores.`, this); - this.value = this.value.replace(/ /g, '_'); - } - this.handleDefaultSlotChange(); } diff --git a/packages/webawesome/src/components/select/select.test.ts b/packages/webawesome/src/components/select/select.test.ts index 106f3a94f..375c42b45 100644 --- a/packages/webawesome/src/components/select/select.test.ts +++ b/packages/webawesome/src/components/select/select.test.ts @@ -223,7 +223,7 @@ describe('', () => { it('should not throw on incomplete events', async () => { const el = await fixture(html` - Option 1 + Option 1 `); @@ -416,10 +416,10 @@ describe('', () => { it('should serialize its name and value in FormData when multiple options are selected', async () => { const form = await fixture(html` - + Option 1 - Option 2 - Option 3 + Option 2 + Option 3 `); @@ -445,10 +445,10 @@ describe('', () => { it('should serialize its name and value in JSON when multiple options are selected', async () => { const form = await fixture(html`
- + Option 1 - Option 2 - Option 3 + Option 2 + Option 3 `); @@ -576,10 +576,10 @@ describe('', () => { it('should emit change and input when a tag is removed', async () => { const el = await fixture(html` - - Option 1 - Option 2 - Option 3 + + Option 1 + Option 2 + Option 3 `); const changeHandler = sinon.spy(); @@ -628,9 +628,9 @@ describe('', () => { it('should have rounded tags when using the pill attribute', async () => { const el = await fixture(html` - - Option 1 - Option 2 + + Option 1 + Option 2 Option 3 `); @@ -714,24 +714,17 @@ describe('', () => { it('Should not select the option if options already exists for multiple select', async () => { const form = await fixture( html`
- + Bar Baz + Foo `, ); const el = form.querySelector('wa-select')!; expect(el.value).to.be.an('array'); - expect(el.value!.length).to.equal(0); - - const option = document.createElement('wa-option'); - option.value = 'foo'; - option.innerText = 'Foo'; - el.append(option); - - await aTimeout(10); - await el.updateComplete; + expect(el.value!.length).to.equal(1); expect(el.value).to.have.members(['foo']); expect(new FormData(form).getAll('select')).to.have.members(['foo']); }); @@ -739,9 +732,9 @@ describe('', () => { it('Should only select the existing options if options already exists for multiple select', async () => { const form = await fixture( html`
- - Bar - Baz + + Bar + Baz `, ); @@ -756,12 +749,13 @@ describe('', () => { const option = document.createElement('wa-option'); option.value = 'foo'; option.innerText = 'Foo'; + option.selected = true; el.append(option); await aTimeout(10); await el.updateComplete; - expect(el.value).to.have.members(['foo', 'bar', 'baz']); - expect(new FormData(form).getAll('select')).to.have.members(['foo', 'bar', 'baz']); + expect(el.value).to.have.members(['bar', 'baz', 'foo']); + expect(new FormData(form).getAll('select')).to.have.members(['bar', 'baz', 'foo']); }); }); @@ -796,6 +790,74 @@ describe('', () => { }); }); + describe('with selected attribute', () => { + it('should select options using the selected attribute for single select', async () => { + const el = await fixture(html` + + Option 1 + Option 2 + Option 3 + + `); + + expect(el.value).to.equal('option-2'); + expect(el.displayInput.value).to.equal('Option 2'); + }); + + it('should select multiple options using the selected attribute', async () => { + const el = await fixture(html` + + Option 1 + Option 2 + Option 3 + + `); + + expect(el.value).to.have.members(['option-1', 'option-3']); + expect(el.value).to.have.length(2); + }); + + it('should handle options with spaces in values', async () => { + const el = await fixture(html` + + Option with spaces + Another option + + `); + + expect(el.value).to.equal('another option'); + expect(el.displayInput.value).to.equal('Another option'); + }); + + it('should handle multiple options with spaces in values', async () => { + const el = await fixture(html` + + Option with spaces + Another option + Third option + + `); + + expect(el.value).to.have.members(['option with spaces', 'third option']); + expect(el.value).to.have.length(2); + }); + + it('should serialize options with spaces correctly in FormData', async () => { + const form = await fixture(html` +
+ + Option with spaces + Another option + +
+ `); + + const formData = new FormData(form); + const values = formData.getAll('test'); + expect(values).to.have.members(['option with spaces', 'another option']); + }); + }); + // https://github.com/shoelace-style/webawesome-alpha/issues/263 it('should allow interaction after being disabled and re-enabled', async () => { const el = await fixture(html` diff --git a/packages/webawesome/src/components/select/select.ts b/packages/webawesome/src/components/select/select.ts index 18998413e..ba6d37209 100644 --- a/packages/webawesome/src/components/select/select.ts +++ b/packages/webawesome/src/components/select/select.ts @@ -127,21 +127,13 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement { private _defaultValue: string | string[] = ''; @property({ - attribute: 'value', - reflect: true, - converter: { - fromAttribute: (value: string) => value.split(' '), - toAttribute: (value: string | string[]) => (Array.isArray(value) ? value.join(' ') : value), - }, + attribute: false, }) set defaultValue(val: string | string[]) { this._defaultValue = this.convertDefaultValue(val); } get defaultValue() { - if (!this.hasUpdated) { - this._defaultValue = this.convertDefaultValue(this._defaultValue); - } return this._defaultValue; } @@ -154,28 +146,32 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement { const isMultiple = this.multiple || this.hasAttribute('multiple'); if (!isMultiple && Array.isArray(val)) { - val = val.join(' '); + val = val[0]; } return val; } private _value: string[] | undefined; - @property({ attribute: false }) + + /** The select's value. This will be a string for single select or an array for multi-select. */ + @property({ attribute: 'value', reflect: false }) set value(val: string | string[]) { let oldValue = this.value; - if (!Array.isArray(val)) { - val = val.split(' '); + if ((val as any) instanceof FormData) { + val = (val as unknown as FormData).getAll(this.name) as string[]; } - if (!this._value || this._value.join(' ') !== val.join(' ')) { - this._value = val; - let newValue = this.value; + if (!Array.isArray(val)) { + val = [val]; + } - if (newValue !== oldValue) { - this.requestUpdate('value', oldValue); - } + this._value = val; + let newValue = this.value; + + if (newValue !== oldValue) { + this.requestUpdate('value', oldValue); } } @@ -300,6 +296,17 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement { // Because this is a form control, it shouldn't be opened initially this.open = false; + + if (!this._defaultValue) { + const allOptions = this.getAllOptions(); + const selectedOptions = allOptions.filter(el => el.selected || el.defaultSelected); + if (selectedOptions.length > 0) { + const selectedValues = selectedOptions.map(el => el.value); + this._defaultValue = this.multiple ? selectedValues : selectedValues[0]; + } else if (this.hasAttribute('value')) { + this._defaultValue = this.getAttribute('value') || ''; + } + } } private addOpenListeners() { @@ -563,10 +570,19 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement { const allOptions = this.getAllOptions(); this.optionValues = undefined; // dirty the value so it gets recalculated + // Update defaultValue if it hasn't been explicitly set and we have selected options + if (!this._defaultValue && !this.hasUpdated) { + const selectedOptions = allOptions.filter(el => el.selected || el.defaultSelected); + if (selectedOptions.length > 0) { + const selectedValues = selectedOptions.map(el => el.value); + this._defaultValue = this.multiple ? selectedValues : selectedValues[0]; + } + } + const value = this.value; // Select only the options that match the new value - this.setSelectedOptions(allOptions.filter(el => value.includes(el.value))); + this.setSelectedOptions(allOptions.filter(el => value.includes(el.value) || el.selected)); } private handleTagRemove(event: WaRemoveEvent, directOption?: WaOption) { @@ -645,7 +661,12 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement { const newSelectedOptions = Array.isArray(option) ? option : [option]; // Clear existing selection - allOptions.forEach(el => (el.selected = false)); + allOptions.forEach(el => { + if (newSelectedOptions.includes(el)) { + return; + } + el.selected = false; + }); // Set the new selection if (newSelectedOptions.length) { @@ -673,7 +694,9 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement { const options = this.getAllOptions(); // Update selected options cache - this.selectedOptions = options.filter(el => el.selected); + this.selectedOptions = options.filter(el => { + return el.selected; + }); let selectedValues = new Set(this.selectedOptions.map(el => el.value)); // Toggle values present in the DOM from this.value, while preserving options NOT present in the DOM (for lazy loading) @@ -847,6 +870,11 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement { this.value = this.defaultValue; super.formResetCallback(); this.handleValueChange(); + + this.updateComplete.then(() => { + this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true })); + this.dispatchEvent(new Event('change', { bubbles: true, composed: true })); + }); } render() { diff --git a/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts b/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts index 68a6aae48..6f952a6e3 100644 --- a/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts +++ b/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts @@ -25,8 +25,6 @@ import styles from './zoomable-frame.css'; * @csspart controls - The container that surrounds zoom control buttons. * @csspart zoom-in-button - The zoom in button. * @csspart zoom-out-button - The zoom out button. - * - * @cssproperty [--aspect-ratio=16/9] - The aspect ratio of the frame. */ @customElement('wa-zoomable-frame') export default class WaZoomableFrame extends WebAwesomeElement {