From cdcc78986d7a576deb1c7ac03593f1ee03a62fe3 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Mon, 16 Dec 2024 14:31:03 +0100 Subject: [PATCH 01/14] fix(combobox): emit events correctly, set options' initial attributes --- .../src/components/combobox/combobox.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/components/src/components/combobox/combobox.ts b/packages/components/src/components/combobox/combobox.ts index 7904c03ee..650d85ad7 100644 --- a/packages/components/src/components/combobox/combobox.ts +++ b/packages/components/src/components/combobox/combobox.ts @@ -285,7 +285,6 @@ export default class SdCombobox extends SolidElement implements SolidFormControl this.selectedTextLabel = option?.getTextLabel() || ''; } this.formControlController.updateValidity(); - this.applySizeToOptions(); } /** Gets the validity state object */ @@ -459,16 +458,16 @@ export default class SdCombobox extends SolidElement implements SolidFormControl this.setSelectedOptions(currentOption); } - // Set focus after updating so the value is announced by screen readers - this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true })); + this.updateComplete.then(() => { + // Set focus after updating so the value is announced by screen readers + this.displayInput.focus({ preventScroll: true }); - if (this.value !== oldValue) { // Emit after updating - this.updateComplete.then(() => { + if (this.value !== oldValue) { this.emit('sd-input'); this.emit('sd-change'); - }); - } + } + }); } this.displayInput.focus({ preventScroll: true }); @@ -636,16 +635,16 @@ export default class SdCombobox extends SolidElement implements SolidFormControl this.setOrderedSelectedOptions(option); this.setSelectedOptions(option); } - // Set focus after updating so the value is announced by screen readers - this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true })); - if (this.value !== oldValue) { + this.updateComplete.then(() => { + // Set focus after updating so the value is announced by screen readers + this.displayInput.focus({ preventScroll: true }); // Emit after updating - this.updateComplete.then(() => { + if (this.value !== oldValue) { this.emit('sd-input'); this.emit('sd-change'); - }); - } + } + }); if (!this.multiple) { this.hide(); this.displayInput.focus({ preventScroll: true }); @@ -850,6 +849,8 @@ export default class SdCombobox extends SolidElement implements SolidFormControl clonedOption.current = clonedOption.value === this.lastOption?.value; clonedOption.selected = option.selected; + clonedOption.checkbox = option.checkbox; + clonedOption.size = option.size; // Check if the option has a sd-optgroup as parent const hasOptgroup = option.parentElement?.tagName.toLowerCase() === 'sd-optgroup'; @@ -1094,6 +1095,8 @@ export default class SdCombobox extends SolidElement implements SolidFormControl const slottedOptions = this.getSlottedOptions(); const slottedOptgroups = this.getSlottedOptGroups(); + this.applySizeToOptions(); + slottedOptions.forEach((option, index) => { if (this.multiple) { option.checkbox = true; From a3e868ea54cef87566a8f680bd7544fb3185e548 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Mon, 16 Dec 2024 14:50:47 +0100 Subject: [PATCH 02/14] docs(combobox): fix disabled optgroup story --- packages/docs/src/stories/components/optgroup.stories.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/docs/src/stories/components/optgroup.stories.ts b/packages/docs/src/stories/components/optgroup.stories.ts index d23a41eb5..f5ad6c039 100644 --- a/packages/docs/src/stories/components/optgroup.stories.ts +++ b/packages/docs/src/stories/components/optgroup.stories.ts @@ -78,8 +78,7 @@ export const Disabled = { render: () => html`
- - Section 1 + Option Option Option From 928bcd8631932ffd0b19d76bcbf8989254626a26 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Mon, 16 Dec 2024 15:06:15 +0100 Subject: [PATCH 03/14] feat(combobox): add max-options-tag-label attribute --- packages/components/src/components/combobox/combobox.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/combobox/combobox.ts b/packages/components/src/components/combobox/combobox.ts index 650d85ad7..26bdac878 100644 --- a/packages/components/src/components/combobox/combobox.ts +++ b/packages/components/src/components/combobox/combobox.ts @@ -157,6 +157,9 @@ export default class SdCombobox extends SolidElement implements SolidFormControl /** Placeholder text to show as a hint when the combobox is empty. */ @property() placeholder = this.localize.term('comboboxDefaultPlaceholder'); + /** Label text shown on tag if max-options-visible is reached. */ + @property({ attribute: 'max-options-tag-label' }) maxOptionsTagLabel = this.localize.term('tagsSelected'); + /** Disables the combobox control. */ @property({ reflect: true, type: Boolean }) disabled = false; @@ -345,7 +348,7 @@ export default class SdCombobox extends SolidElement implements SolidFormControl removable @keydown=${(event: KeyboardEvent) => this.handleTagMaxOptionsKeyDown(event)} @sd-remove=${(event: CustomEvent) => this.handleTagRemove(event)} - >${this.selectedOptions.length} ${this.localize.term('tagsSelected')}${this.selectedOptions.length} ${this.maxOptionsTagLabel} ` ]; From d65738d95f4639e319d4b45d8a369a3353267f0a Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Mon, 16 Dec 2024 15:10:37 +0100 Subject: [PATCH 04/14] feat(select): add --tag-max-width and ellipsis --- packages/components/src/components/select/select.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/components/src/components/select/select.ts b/packages/components/src/components/select/select.ts index 1184441a2..7a26e68f5 100644 --- a/packages/components/src/components/select/select.ts +++ b/packages/components/src/components/select/select.ts @@ -1151,6 +1151,14 @@ export default class SdSelect extends SolidElement implements SolidFormControl { @apply rounded-default px-1; } + sd-tag::part(content) { + max-width: var(--tag-max-width, 15ch); + overflow: hidden; + white-space: nowrap; + display: inline-block; + text-overflow: ellipsis; + } + sd-tag[size='lg']::part(base) { @apply px-2; } From 5a8d03e1265b343192e0066a0e1e3ed0b5f42917 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Mon, 16 Dec 2024 15:14:43 +0100 Subject: [PATCH 05/14] feat(select): add max-options-tag-label attribute --- packages/components/src/components/select/select.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/select/select.ts b/packages/components/src/components/select/select.ts index 7a26e68f5..4dda8d48c 100644 --- a/packages/components/src/components/select/select.ts +++ b/packages/components/src/components/select/select.ts @@ -136,6 +136,9 @@ export default class SdSelect extends SolidElement implements SolidFormControl { /** Placeholder text to show as a hint when the select is empty. */ @property() placeholder = this.localize.term('selectDefaultPlaceholder'); + /** Label text shown on tag if max-options-visible is reached. */ + @property({ attribute: 'max-options-tag-label' }) maxOptionsTagLabel = this.localize.term('tagsSelected'); + /** Disables the select control. */ @property({ type: Boolean, reflect: true }) disabled = false; @@ -692,7 +695,7 @@ export default class SdSelect extends SolidElement implements SolidFormControl { removable @keydown=${(event: KeyboardEvent) => this.handleTagMaxOptionsKeyDown(event)} @sd-remove=${(event: CustomEvent) => this.handleTagRemove(event)} - >${this.selectedOptions.length} ${this.localize.term('tagsSelected')}${this.selectedOptions.length} ${this.maxOptionsTagLabel} ` ]; From 87244e8b5a78c5fbb5c2379aa2b720252b4030c3 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Mon, 16 Dec 2024 15:17:20 +0100 Subject: [PATCH 06/14] docs(select): add --tag-max-width info --- packages/docs/src/stories/components/select.stories.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/docs/src/stories/components/select.stories.ts b/packages/docs/src/stories/components/select.stories.ts index ffb4d6d78..1f8502ace 100644 --- a/packages/docs/src/stories/components/select.stories.ts +++ b/packages/docs/src/stories/components/select.stories.ts @@ -238,6 +238,7 @@ export const Clearable = { /** * Use the `multiple` attribute to allow multiple options to be selected. + * Use `--tag-max-width` to set the maximum width of the tags and to show an ellipsis, e.g. ``. The default value is `15ch` */ export const Multiple = { From 520b1d6d12a7b582972dfcb94d196237a8f09e25 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Tue, 17 Dec 2024 10:11:28 +0100 Subject: [PATCH 07/14] test(combobox): test events --- .../src/components/combobox/combobox.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/components/src/components/combobox/combobox.test.ts b/packages/components/src/components/combobox/combobox.test.ts index 342cc9c24..1add4ca01 100644 --- a/packages/components/src/components/combobox/combobox.test.ts +++ b/packages/components/src/components/combobox/combobox.test.ts @@ -627,6 +627,33 @@ describe('', () => { expect(el.value).to.deep.equal(['Option 2', 'Option 3']); }); + + it('should emit sd-change and sd-input when the value changes', async () => { + const el = await fixture(html` + + Option 1 + Option 2 + Option 3 + + `); + const inputHandler = sinon.spy(); + const changeHandler = sinon.spy(); + + el.addEventListener('sd-input', inputHandler); + el.addEventListener('sd-change', changeHandler); + await el.show(); + await el.updateComplete; + const filteredListbox = el.shadowRoot!.querySelector('[part="filtered-listbox"]')!; + const secondOption = filteredListbox.querySelectorAll('sd-option')[1]; + await clickOnElement(secondOption); + await el.updateComplete; + const thirdOption = filteredListbox.querySelectorAll('sd-option')[2]; + await clickOnElement(thirdOption); + await el.updateComplete; + + expect(inputHandler).to.have.been.calledTwice; + expect(changeHandler).to.have.been.calledTwice; + }); }); describe('when using constraint validation', () => { From fd1ee3d0483d72b51ec47ba7562a372041c95e78 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Tue, 17 Dec 2024 10:12:22 +0100 Subject: [PATCH 08/14] docs(select): add --tag-max-width comment --- packages/components/src/components/select/select.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/components/src/components/select/select.ts b/packages/components/src/components/select/select.ts index 4dda8d48c..ac762e4c7 100644 --- a/packages/components/src/components/select/select.ts +++ b/packages/components/src/components/select/select.ts @@ -64,6 +64,8 @@ import type SdOption from '../option/option'; * @csspart tag__removable-indicator - The tag's remove button. * @csspart clear-button - The clear button. * @csspart expand-icon - The container that wraps the expand icon. + * + * @cssproperty --tag-max-width - Set the maximum width of the tags and to show an ellipsis. Defaults to "15ch" */ @customElement('sd-select') export default class SdSelect extends SolidElement implements SolidFormControl { From f12e3be2e7bbf9d33f713536bb4f36780780a9eb Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Tue, 17 Dec 2024 10:24:13 +0100 Subject: [PATCH 09/14] docs(select): add changeset --- .changeset/red-drinks-travel.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changeset/red-drinks-travel.md diff --git a/.changeset/red-drinks-travel.md b/.changeset/red-drinks-travel.md new file mode 100644 index 000000000..9f2d2ecf2 --- /dev/null +++ b/.changeset/red-drinks-travel.md @@ -0,0 +1,11 @@ +--- +'@solid-design-system/components': patch +'@solid-design-system/docs': patch +--- + +Bugfixes and minor non-breaking changes to the sd-select and sd-combobox components + +- sd-combobox: emit events correctly +- sd-combobox: set options' initial attributes +- sd-select and sd-combobox: add max-options-tag-label attribute +- sd-select: add --tag-max-width and ellipsis From b5ec44dcf438b143e10f08b88ca24fe1ba3ff093 Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Tue, 17 Dec 2024 15:19:40 +0100 Subject: [PATCH 10/14] fix(select): add preventDefault to Backspace key event --- packages/components/src/components/combobox/combobox.test.ts | 2 +- packages/components/src/components/combobox/combobox.ts | 2 ++ packages/components/src/components/select/select.ts | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/combobox/combobox.test.ts b/packages/components/src/components/combobox/combobox.test.ts index 1add4ca01..e3aceb426 100644 --- a/packages/components/src/components/combobox/combobox.test.ts +++ b/packages/components/src/components/combobox/combobox.test.ts @@ -573,7 +573,7 @@ describe('', () => { it('should remove tag and option when tag is focused and backspace is pressed', async () => { const el = await fixture(html` - + Option 1 Option 2 Option 3 diff --git a/packages/components/src/components/combobox/combobox.ts b/packages/components/src/components/combobox/combobox.ts index 26bdac878..190eb4b50 100644 --- a/packages/components/src/components/combobox/combobox.ts +++ b/packages/components/src/components/combobox/combobox.ts @@ -521,6 +521,7 @@ export default class SdCombobox extends SolidElement implements SolidFormControl private handleTagKeyDown(event: KeyboardEvent, option: SdOption) { if (event.key === 'Backspace' && this.multiple) { + event.preventDefault(); event.stopPropagation(); this.handleTagRemove(new CustomEvent('sd-remove'), option); this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true })); @@ -529,6 +530,7 @@ export default class SdCombobox extends SolidElement implements SolidFormControl private handleTagMaxOptionsKeyDown(event: KeyboardEvent) { if (event.key === 'Backspace' && this.multiple) { + event.preventDefault(); event.stopPropagation(); this.handleTagRemove(new CustomEvent('sd-remove'), this.selectedOptions[this.selectedOptions.length - 1]); this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true })); diff --git a/packages/components/src/components/select/select.ts b/packages/components/src/components/select/select.ts index ac762e4c7..867188ccd 100644 --- a/packages/components/src/components/select/select.ts +++ b/packages/components/src/components/select/select.ts @@ -419,6 +419,7 @@ export default class SdSelect extends SolidElement implements SolidFormControl { private handleTagKeyDown(event: KeyboardEvent, option: SdOption) { if (event.key === 'Backspace' && this.multiple) { + event.preventDefault(); event.stopPropagation(); const tagParent = (event.currentTarget as HTMLElement)?.parentElement; const previousTag = tagParent?.previousElementSibling?.querySelector('sd-tag'); @@ -439,6 +440,7 @@ export default class SdSelect extends SolidElement implements SolidFormControl { private handleTagMaxOptionsKeyDown(event: KeyboardEvent) { if (event.key === 'Backspace' && this.multiple) { + event.preventDefault(); event.stopPropagation(); this.handleTagRemove(new CustomEvent('sd-remove'), this.selectedOptions[this.selectedOptions.length - 1]); this.updateComplete.then(() => { From 5e444235ef4fa59994efdf1ae57b8e0ffb769e9a Mon Sep 17 00:00:00 2001 From: Daniel Hargesheimer Date: Wed, 18 Dec 2024 14:36:27 +0100 Subject: [PATCH 11/14] fix(select): refactor css, adjust visibility of clearIcon --- packages/components/src/components/select/select.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/components/src/components/select/select.ts b/packages/components/src/components/select/select.ts index 867188ccd..68ee0efcb 100644 --- a/packages/components/src/components/select/select.ts +++ b/packages/components/src/components/select/select.ts @@ -877,7 +877,7 @@ export default class SdSelect extends SolidElement implements SolidFormControl { const hasLabel = this.label ? true : !!slots['label']; const hasHelpText = this.helpText ? true : !!slots['helpText']; - const hasClearIcon = this.clearable && !this.disabled && this.value.length > 0; + const hasClearIcon = this.clearable && !this.disabled; // Hierarchy of input states: const selectState = this.disabled @@ -1043,7 +1043,11 @@ export default class SdSelect extends SolidElement implements SolidFormControl { ? html`