From 34302cdbf101504135df55842bcb52b04e788feb Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Mon, 25 Dec 2023 10:09:40 +0300 Subject: [PATCH 01/10] feat(select): add search feature for select --- src/components/select/bl-select.css | 81 +++++++++ src/components/select/bl-select.stories.mdx | 24 +++ src/components/select/bl-select.test.ts | 126 ++++++++++++- src/components/select/bl-select.ts | 165 +++++++++++++++++- .../select/option/bl-select-option.css | 6 +- 5 files changed, 392 insertions(+), 10 deletions(-) diff --git a/src/components/select/bl-select.css b/src/components/select/bl-select.css index 6a7621fa..1cd261dc 100644 --- a/src/components/select/bl-select.css +++ b/src/components/select/bl-select.css @@ -219,6 +219,15 @@ left: var(--left); } +.popover-no-result { + display: flex; + flex-direction: column; + gap: var(--bl-size-2xs); + align-items: center; + justify-content: center; + height: var(--menu-height); +} + .select-open .popover { display: flex; border: solid 1px var(--border-focus-color); @@ -361,3 +370,75 @@ legend span { bottom: 0; border-bottom: 1px solid var(--bl-color-neutral-lighter); } + +.search-bar-input { + font: var(--bl-font-title-3-regular); + font-size: var(--font-size); + color: var(--text-color); + border: none; + outline: none; + background-color: transparent; + width: 100%; + padding: 0; + margin: 0; + box-sizing: border-box; + height: var(--height); +} + +.search-bar-input::placeholder { + color: var(--placeholder-color); +} + +.search-bar-input:focus-visible { + outline: none; +} + +.search-mag-icon { + animation: wiggle 2s linear infinite; +} + +.search-loading-icon { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +@keyframes wiggle { + 0%, + 7% { + transform: rotateZ(0); + } + + 15% { + transform: rotateZ(-15deg); + } + + 20% { + transform: rotateZ(10deg); + } + + 25% { + transform: rotateZ(-10deg); + } + + 30% { + transform: rotateZ(6deg); + } + + 35% { + transform: rotateZ(-4deg); + } + + 40%, + 100% { + transform: rotateZ(0); + } +} diff --git a/src/components/select/bl-select.stories.mdx b/src/components/select/bl-select.stories.mdx index efc1258b..6ec1b5fd 100644 --- a/src/components/select/bl-select.stories.mdx +++ b/src/components/select/bl-select.stories.mdx @@ -100,6 +100,9 @@ export const SelectTemplate = (args) => html` +## Searchable + +[ADR](https://github.com/Trendyol/baklava/issues/265#issuecomment-1845414216) +[Figma](https://www.figma.com/file/RrcLH0mWpIUy4vwuTlDeKN/Baklava-Design-Guide?node-id=13059%3A6927) + +Select component can be searchable by using `search-bar` attribute. + + + + {SelectTemplate.bind({})} + + + + {SelectTemplate.bind({})} + + + + {SelectTemplate.bind({})} + + + ## `bl-select` Event Select component fires `bl-select` event once selection changes. This event has a payload in the type of diff --git a/src/components/select/bl-select.test.ts b/src/components/select/bl-select.test.ts index 9d6bd469..2cc09bff 100644 --- a/src/components/select/bl-select.test.ts +++ b/src/components/select/bl-select.test.ts @@ -280,6 +280,106 @@ describe("bl-select", () => { expect(selectOption).is.not.exist; }); + it("should show search input if search-bar attribute is given", async () => { + const el = await fixture(html` + Turkey + United States of America + `); + + const searchInput = el.shadowRoot?.querySelector("fieldset input"); + + expect(searchInput).to.exist; + }); + + it("should search 'Turkey' when 'turkey' is typed", async () => { + const el = await fixture(html` + Turkey + United States of America + `); + + const searchInput = el.shadowRoot?.querySelector("fieldset input"); + + searchInput?.focus(); + + await sendKeys({ + type: "turkey", + }); + + el.options.forEach(option => { + if (option.innerText === "Turkey") { + expect(option.hidden).to.be.false; + } else { + expect(option.hidden).to.be.true; + } + }); + }); + + it("should show loading icon when the search loading state is true", async () => { + const el = await fixture(html` + Turkey + United States of America + `); + + const searchInput = el.shadowRoot?.querySelector("fieldset input"); + + searchInput?.focus(); + + const loadingIcon = el.shadowRoot?.querySelector("fieldset bl-icon"); + + await sendKeys({ + type: "turkey", + }); + + expect(loadingIcon).to.exist; + }); + + it("should be displayed a 'no result' message if the searched term does not match with any option", async () => { + const el = await fixture(html` + Turkey + United States of America + `); + + const searchInput = el.shadowRoot?.querySelector("fieldset input"); + + searchInput?.focus(); + + await sendKeys({ + type: "netherlands", + }); + + const noResultContainer = el.shadowRoot?.querySelector(".popover .popover-no-result"); + const noResultMessage = el.shadowRoot?.querySelector(".popover .popover-no-result span")?.innerText; + + + el.options.forEach(option => { + expect(option.hidden).to.be.true; + }); + + expect(noResultContainer).to.exist; + expect(noResultMessage).to.equal("No Data Found"); + }); + + it("should be cleared the search input if the user click on the clear search button", async () => { + const el = await fixture(html` + Turkey + United States of America + `); + + const searchInput = el.shadowRoot?.querySelector("fieldset input"); + + searchInput?.focus(); + + await sendKeys({ + type: "netherlands", + }); + + const clearSearchButton = el.shadowRoot?.querySelector(".popover .popover-no-result bl-button"); + + clearSearchButton?.click(); + + setTimeout(() => expect(searchInput?.value).to.equal("")); + }); + describe("additional selection counter", () => { let el: BlSelect; @@ -649,5 +749,29 @@ describe("bl-select", () => { expect(selectAll.indeterminate).to.be.false; expect(selectAll.checked).to.be.false; }); - }); + }); + + describe("events", () => { + it("should fire search event when 'turkey' is typed", async () => { + const el = await fixture(html` + Turkey + United States of America + `); + + const searchInput = el.shadowRoot?.querySelector("fieldset input"); + + if (searchInput) { + searchInput.focus(); + + searchInput.value = "turkey"; + } + + setTimeout(() => searchInput?.dispatchEvent(new Event("input"))); + + const event = await oneEvent(el, "bl-search"); + + expect(event).to.exist; + expect(event.detail).to.equal("turkey"); + }); + }); }); diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index dbff8254..f945dd07 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -156,6 +156,36 @@ export default class BlSelect extends Form @property({ type: String, attribute: "select-all-text" }) selectAllText = "Select All"; + /** + * Enable search functionality for the options within the list + */ + @property({ type: Boolean, attribute: "search-bar", reflect: true }) + searchBar = false; + + /** + * Search for text variations such as "search," "searching," "search by country," and so on + */ + @property({ type: String, attribute: "search-bar-placeholder", reflect: true }) + searchBarPlaceholder?: string; + + /** + * Display a loading icon in place of the search icon. + */ + @property({ type: Boolean, attribute: "search-bar-loading-state", reflect: true }) + searchBarLoadingState = false; + + /** + * Text to display when no search results are found. + */ + @property({ type: String, attribute: "search-not-found-text", reflect: true }) + searchNotFoundText = "No Data Found"; + + /** + * Text to display on the clear search button. + */ + @property({ type: String, attribute: "popover-clear-search-text", reflect: true }) + popoverClearSearchText = "Clear Search"; + /* Declare internal reactive properties */ @state() private _isPopoverOpen = false; @@ -163,6 +193,9 @@ export default class BlSelect extends Form @state() private _additionalSelectedOptionCount = 0; + @state() + private _searchText = ""; + @query(".selected-options") private selectedOptionsContainer!: HTMLElement; @@ -180,6 +213,11 @@ export default class BlSelect extends Form */ @event("bl-select") private _onBlSelect: EventDispatcher[]>; + /** + * Fires when search text changes + */ + @event("bl-search") private _onBlSearch: EventDispatcher; + private _connectedOptions: BlSelectOption[] = []; private _cleanUpPopover: CleanUpFunction | null = null; @@ -203,6 +241,10 @@ export default class BlSelect extends Form return this._isPopoverOpen; } + get noResultFound() { + return this._searchText !== "" && this._connectedOptions.every(option => option.hidden); + } + @state() private _selectedOptions: BlSelectOption[] = []; @@ -252,6 +294,10 @@ export default class BlSelect extends Form } close() { + this._handleSearchOptions({ target: { value: "" } } as InputEvent & { + target: HTMLInputElement; + }); + this._isPopoverOpen = false; this.focusedOptionIndex = -1; this._cleanUpPopover && this._cleanUpPopover(); @@ -325,31 +371,82 @@ export default class BlSelect extends Form >` : ""; - return html`
`; + + const searchLoadingIcon = html``; + + const search = html`
0, })} tabindex="${this.disabled ? "-1" : 0}" - ?autofocus=${this.autofocus} - @click=${this.togglePopover} role="button" aria-haspopup="listbox" aria-expanded="${this.opened}" aria-labelledby="label" + @click=${this.open} > ${this.label} - ${this.placeholder} - ${this.label} - ${inputSelectedOptions} - +${this._additionalSelectedOptionCount} -
+ + ${this._selectedOptions.length > 0 && !this.opened + ? inputSelectedOptions + : html``} + ${!this.opened + ? html`+${this._additionalSelectedOptionCount}` + : ""} + +
+ ${this.opened ? (this.searchBarLoadingState ? searchLoadingIcon : searchMagIcon) : ""} ${removeButton}
`; + + return this.searchBar + ? search + : html`
0, + })} + tabindex="${this.disabled ? "-1" : 0}" + ?autofocus=${this.autofocus} + @click=${this.togglePopover} + role="button" + aria-haspopup="listbox" + aria-expanded="${this.opened}" + aria-labelledby="label" + > + ${this.label} + ${this.placeholder} + ${this.label} + ${inputSelectedOptions} + +${this._additionalSelectedOptionCount} +
+ ${removeButton} + + + +
+
`; } selectAllTemplate() { @@ -403,6 +500,20 @@ export default class BlSelect extends Form > ${this.selectAllTemplate()} + ${this.searchBar && this.noResultFound + ? html`
+ ${this.searchNotFoundText} + { + this._handleSearchOptions({ target: { value: "" } } as InputEvent & { + target: HTMLInputElement; + }); + }} + >${this.popoverClearSearchText} +
` + : ""}
${invalidMessage} ${helpMessage}
`; @@ -453,6 +564,44 @@ export default class BlSelect extends Form ); } + private _handleSearchEvent() { + this._onBlSearch(this._searchText); + } + + private _handleSearchOptions(e: InputEvent): void { + if (!this.searchBar) return; + + this._searchText = (e.target as HTMLInputElement).value.toLowerCase(); + + this._handleSearchEvent(); + + this._connectedOptions.forEach(option => { + const isVisible = option.textContent?.toLowerCase().includes(this._searchText); + + option.hidden = !isVisible; + }); + + this._selectedOptions = this.options.filter(option => option.selected); + + this._handleLastVisibleSearchedOption(); + + this.requestUpdate(); + } + + private _handleLastVisibleSearchedOption() { + const lastVisibleOption = [...this.options].reverse().find(option => !option.hidden); + + if (lastVisibleOption) { + lastVisibleOption?.shadowRoot?.querySelector("div")?.classList.add("no-border-bottom"); + } + + this.options.map(option => { + if (!option.hidden && option !== lastVisibleOption) { + option.shadowRoot?.querySelector("div")?.classList.remove("no-border-bottom"); + } + }); + } + private _handleSingleSelect(optionItem: BlSelectOption) { this.value = optionItem.value; diff --git a/src/components/select/option/bl-select-option.css b/src/components/select/option/bl-select-option.css index 83d55bf5..fe557bfd 100644 --- a/src/components/select/option/bl-select-option.css +++ b/src/components/select/option/bl-select-option.css @@ -22,6 +22,10 @@ border-bottom: var(--option-separator); } +.no-border-bottom::after { + border-bottom: none; +} + :host(:last-of-type) .option-container::after { border-bottom: none; } @@ -70,4 +74,4 @@ display: block; color: var(--option-color); padding: var(--option-spacing); -} +} \ No newline at end of file From 237138b29654a6c94cee977e1d2ccd908e94211f Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Mon, 25 Dec 2023 10:20:51 +0300 Subject: [PATCH 02/10] fix(select/select-option): fix prettier issue --- src/components/select/bl-select.css | 2 +- src/components/select/option/bl-select-option.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/select/bl-select.css b/src/components/select/bl-select.css index 1cd261dc..51fceb93 100644 --- a/src/components/select/bl-select.css +++ b/src/components/select/bl-select.css @@ -412,7 +412,7 @@ legend span { } @keyframes wiggle { - 0%, + 0%, 7% { transform: rotateZ(0); } diff --git a/src/components/select/option/bl-select-option.css b/src/components/select/option/bl-select-option.css index fe557bfd..8739cdf1 100644 --- a/src/components/select/option/bl-select-option.css +++ b/src/components/select/option/bl-select-option.css @@ -74,4 +74,4 @@ display: block; color: var(--option-color); padding: var(--option-spacing); -} \ No newline at end of file +} From b686d3c90d4c71965a8f3eb7334f9b1cf4165534 Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Mon, 25 Dec 2023 11:07:39 +0300 Subject: [PATCH 03/10] chore(select): remove search mag animation --- src/components/select/bl-select.css | 36 ----------------------------- src/components/select/bl-select.ts | 1 - 2 files changed, 37 deletions(-) diff --git a/src/components/select/bl-select.css b/src/components/select/bl-select.css index 51fceb93..56d23405 100644 --- a/src/components/select/bl-select.css +++ b/src/components/select/bl-select.css @@ -393,10 +393,6 @@ legend span { outline: none; } -.search-mag-icon { - animation: wiggle 2s linear infinite; -} - .search-loading-icon { animation: spin 1s linear infinite; } @@ -410,35 +406,3 @@ legend span { transform: rotate(360deg); } } - -@keyframes wiggle { - 0%, - 7% { - transform: rotateZ(0); - } - - 15% { - transform: rotateZ(-15deg); - } - - 20% { - transform: rotateZ(10deg); - } - - 25% { - transform: rotateZ(-10deg); - } - - 30% { - transform: rotateZ(6deg); - } - - 35% { - transform: rotateZ(-4deg); - } - - 40%, - 100% { - transform: rotateZ(0); - } -} diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index f945dd07..58d6606d 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -372,7 +372,6 @@ export default class BlSelect extends Form : ""; const searchMagIcon = html``; From 1049f61c0ca862738bfffe9d2a0b45dece90b341 Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Mon, 25 Dec 2023 12:08:44 +0300 Subject: [PATCH 04/10] fix(select): fix styling issues, show divider after search and loading icon, unshow remove button when searching, show label if the placeholder is missing --- src/components/select/bl-select.css | 8 +++++++- src/components/select/bl-select.ts | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/components/select/bl-select.css b/src/components/select/bl-select.css index 56d23405..53b91670 100644 --- a/src/components/select/bl-select.css +++ b/src/components/select/bl-select.css @@ -379,10 +379,10 @@ legend span { outline: none; background-color: transparent; width: 100%; + height: 100%; padding: 0; margin: 0; box-sizing: border-box; - height: var(--height); } .search-bar-input::placeholder { @@ -397,6 +397,12 @@ legend span { animation: spin 1s linear infinite; } +.search-bar-divider { + height: 1rem; + width: 0.5px; + background-color: var(--bl-color-neutral-lighter); +} + @keyframes spin { from { transform: rotate(0deg); diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index 58d6606d..df718a98 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -372,6 +372,7 @@ export default class BlSelect extends Form : ""; const searchMagIcon = html``; @@ -382,6 +383,8 @@ export default class BlSelect extends Form style="color: var(--bl-color-primary)" >`; + const searchActionDivider = html`
`; + const search = html`
extends Form ${this._selectedOptions.length > 0 && !this.opened ? inputSelectedOptions - : html``} + : html` + + `} ${!this.opened ? html`+${this._additionalSelectedOptionCount} extends Form
${this.opened ? (this.searchBarLoadingState ? searchLoadingIcon : searchMagIcon) : ""} - ${removeButton} + ${this.opened ? searchActionDivider : removeButton} From fdda4efeedc4792af4e55c45bca287dd23715dfa Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Mon, 25 Dec 2023 16:18:16 +0300 Subject: [PATCH 05/10] feat(select): autofocus search input if the selected options more than 1 --- src/components/select/bl-select.css | 2 +- src/components/select/bl-select.test.ts | 23 ++++++++++++++++++----- src/components/select/bl-select.ts | 6 ++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/components/select/bl-select.css b/src/components/select/bl-select.css index 53b91670..fda935b4 100644 --- a/src/components/select/bl-select.css +++ b/src/components/select/bl-select.css @@ -399,7 +399,7 @@ legend span { .search-bar-divider { height: 1rem; - width: 0.5px; + width: 1px; background-color: var(--bl-color-neutral-lighter); } diff --git a/src/components/select/bl-select.test.ts b/src/components/select/bl-select.test.ts index 2cc09bff..a2304e4c 100644 --- a/src/components/select/bl-select.test.ts +++ b/src/components/select/bl-select.test.ts @@ -304,7 +304,7 @@ describe("bl-select", () => { await sendKeys({ type: "turkey", }); - + el.options.forEach(option => { if (option.innerText === "Turkey") { expect(option.hidden).to.be.false; @@ -350,7 +350,7 @@ describe("bl-select", () => { const noResultContainer = el.shadowRoot?.querySelector(".popover .popover-no-result"); const noResultMessage = el.shadowRoot?.querySelector(".popover .popover-no-result span")?.innerText; - + el.options.forEach(option => { expect(option.hidden).to.be.true; }); @@ -373,13 +373,26 @@ describe("bl-select", () => { type: "netherlands", }); - const clearSearchButton = el.shadowRoot?.querySelector(".popover .popover-no-result bl-button"); + const clearSearchButton = el.shadowRoot?.querySelector(".popover .popover-no-result bl-button"); clearSearchButton?.click(); setTimeout(() => expect(searchInput?.value).to.equal("")); }); + it("should focus if one or more option selected already", async () => { + const el = await fixture(html` + Turkey + United States of America + `); + + const dropdownIcon = el.shadowRoot?.querySelector(".dropdown-icon"); + + dropdownIcon?.click(); + + await(()=> expect(document.activeElement).to.equal(el)); + }); + describe("additional selection counter", () => { let el: BlSelect; @@ -750,7 +763,7 @@ describe("bl-select", () => { expect(selectAll.checked).to.be.false; }); }); - + describe("events", () => { it("should fire search event when 'turkey' is typed", async () => { const el = await fixture(html` @@ -762,7 +775,7 @@ describe("bl-select", () => { if (searchInput) { searchInput.focus(); - + searchInput.value = "turkey"; } diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index df718a98..ee82b506 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -287,6 +287,12 @@ export default class BlSelect extends Form validationTarget: HTMLElement; open() { + if (this.searchBar) { + setTimeout(() => { + document.activeElement?.shadowRoot?.querySelector("input")?.focus(); + }, 100); + } + this._isPopoverOpen = true; this._setupPopover(); document.addEventListener("click", this._interactOutsideHandler, true); From 96cf8f48e748bf324fd15284d8055408d58d7caa Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Mon, 25 Dec 2023 16:31:19 +0300 Subject: [PATCH 06/10] chore(select): change no result found popover height --- src/components/select/bl-select.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/select/bl-select.css b/src/components/select/bl-select.css index fda935b4..00f37022 100644 --- a/src/components/select/bl-select.css +++ b/src/components/select/bl-select.css @@ -225,7 +225,7 @@ gap: var(--bl-size-2xs); align-items: center; justify-content: center; - height: var(--menu-height); + height: 80px; } .select-open .popover { From 2fc98595b4045c17b27e3e2c1c291c921fbde8b8 Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Tue, 26 Dec 2023 14:33:07 +0300 Subject: [PATCH 07/10] feat(select): seperate divider from remove button, be smaller icons and give padding, improve storybook for select --- src/components/select/bl-select.css | 22 ++++++++++----------- src/components/select/bl-select.stories.mdx | 5 +++-- src/components/select/bl-select.ts | 15 +++++++++----- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/components/select/bl-select.css b/src/components/select/bl-select.css index 00f37022..ad68bf2c 100644 --- a/src/components/select/bl-select.css +++ b/src/components/select/bl-select.css @@ -103,15 +103,6 @@ display: none; } -.remove-all::after { - content: ""; - position: absolute; - left: 1.5rem; - bottom: 4px; - height: 1rem; - border-left: 1px solid var(--bl-color-neutral-lighter); -} - .selected .remove-all { display: block; } @@ -193,7 +184,7 @@ display: flex; align-items: center; justify-content: center; - gap: var(--bl-size-2xs); + gap: var(--bl-size-4xs); margin-left: var(--bl-size-2xs); } @@ -397,7 +388,12 @@ legend span { animation: spin 1s linear infinite; } -.search-bar-divider { +.action-divider { + display: none; +} + +.select-wrapper .action-divider { + display: block; height: 1rem; width: 1px; background-color: var(--bl-color-neutral-lighter); @@ -412,3 +408,7 @@ legend span { transform: rotate(360deg); } } + +.actions bl-icon { + padding: 4px; +} diff --git a/src/components/select/bl-select.stories.mdx b/src/components/select/bl-select.stories.mdx index 6ec1b5fd..9dcdc067 100644 --- a/src/components/select/bl-select.stories.mdx +++ b/src/components/select/bl-select.stories.mdx @@ -255,16 +255,17 @@ Select component can be disabled by using `disabled` attribute. ## Searchable [ADR](https://github.com/Trendyol/baklava/issues/265#issuecomment-1845414216) -[Figma](https://www.figma.com/file/RrcLH0mWpIUy4vwuTlDeKN/Baklava-Design-Guide?node-id=13059%3A6927) Select component can be searchable by using `search-bar` attribute. +Display a loading icon in place of the search icon with using `search-bar-loading-state` attribute, seamlessly integrating it with your API endpoint to provide a smooth user experience during data retrieval. + {SelectTemplate.bind({})} - + {SelectTemplate.bind({})} diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index ee82b506..6c21fa5f 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -377,19 +377,24 @@ export default class BlSelect extends Form >` : ""; + const isSearchBarVisible = this.searchBar && this.opened; + const isMultipleWithSelection = this.multiple && this._selectedOptions.length > 0; + + const isDividerShown = isSearchBarVisible || isMultipleWithSelection; + const searchMagIcon = html``; const searchLoadingIcon = html``; - const searchActionDivider = html`
`; + const actionDivider = isDividerShown ? html`
` : ""; const search = html`
extends Form
${this.opened ? (this.searchBarLoadingState ? searchLoadingIcon : searchMagIcon) : ""} - ${this.opened ? searchActionDivider : removeButton} + ${!this.opened ? removeButton : ""} ${actionDivider} @@ -451,7 +456,7 @@ export default class BlSelect extends Form ${inputSelectedOptions} +${this._additionalSelectedOptionCount}
- ${removeButton} + ${removeButton} ${actionDivider} From 6c9bf783d2273366486f48b00bb4833531a2e0ec Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Thu, 28 Dec 2023 10:11:19 +0300 Subject: [PATCH 08/10] fix(select): fix lowercase convert for search text --- src/components/select/bl-select.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index 6c21fa5f..8738fc78 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -586,12 +586,12 @@ export default class BlSelect extends Form private _handleSearchOptions(e: InputEvent): void { if (!this.searchBar) return; - this._searchText = (e.target as HTMLInputElement).value.toLowerCase(); + this._searchText = (e.target as HTMLInputElement).value; this._handleSearchEvent(); this._connectedOptions.forEach(option => { - const isVisible = option.textContent?.toLowerCase().includes(this._searchText); + const isVisible = option.textContent?.toLowerCase().includes(this._searchText.toLowerCase()); option.hidden = !isVisible; }); From ee495e8561b49cf2f5562ee13cc10d176becdc54 Mon Sep 17 00:00:00 2001 From: Erbil Nas Date: Thu, 28 Dec 2023 13:52:45 +0300 Subject: [PATCH 09/10] chore(select): remove select all if no result found --- src/components/select/bl-select.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index 8738fc78..9006ab82 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -465,7 +465,7 @@ export default class BlSelect extends Form } selectAllTemplate() { - if (!this.multiple || !this.viewSelectAll) { + if (!this.multiple || !this.viewSelectAll || this.noResultFound) { return null; } From 3fd66b5a747c3af0b82b3ccc803005fee0f59dbd Mon Sep 17 00:00:00 2001 From: Ogun Babacan Date: Thu, 28 Dec 2023 16:51:42 +0300 Subject: [PATCH 10/10] feat(select): select works on filtered results now --- src/components/select/bl-select.stories.mdx | 8 ++++++ src/components/select/bl-select.ts | 27 +++++++++------------ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/components/select/bl-select.stories.mdx b/src/components/select/bl-select.stories.mdx index 9dcdc067..bc0fa702 100644 --- a/src/components/select/bl-select.stories.mdx +++ b/src/components/select/bl-select.stories.mdx @@ -195,6 +195,14 @@ The Select component features a 'Select All' option in the Multiple Select, whic +Select all is designed to operate on filtered results as well, selecting only those options that meet the filter. + + + + {SelectTemplate.bind({})} + + + ## Clear Button The select component includes a clear button. Clear button can be displayed by passing `clearable` attribute to the Select component. diff --git a/src/components/select/bl-select.ts b/src/components/select/bl-select.ts index 9006ab82..b8fc4610 100644 --- a/src/components/select/bl-select.ts +++ b/src/components/select/bl-select.ts @@ -259,10 +259,6 @@ export default class BlSelect extends Form return this._additionalSelectedOptionCount; } - get isAllSelected() { - return this._selectedOptions.length === this._connectedOptions.length; - } - validityCallback(): string | void { if (this.customInvalidText) { return this.customInvalidText; @@ -469,14 +465,17 @@ export default class BlSelect extends Form return null; } - const isAnySelected = this._selectedOptions.length > 0; + const isAllRenderedOptionsSelected = this._connectedOptions + .filter(option => !option.hidden) + .every(option => option.selected); + const isAnySelected = this._selectedOptions.filter(option => !option.hidden).length > 0; return html` ${this.selectAllText} @@ -648,7 +647,9 @@ export default class BlSelect extends Form const selectAllEl = this.shadowRoot?.querySelector(".select-all") as BlCheckbox; const checked = e.detail; - const unselectedOptions = this._connectedOptions.filter(option => !option.selected); + const unselectedOptions = this._connectedOptions.filter( + option => !option.selected && !option.hidden + ); const isAllUnselectedDisabled = unselectedOptions.every(option => option.disabled); // If all available options are selected, instead of checking, uncheck all options @@ -662,7 +663,7 @@ export default class BlSelect extends Form } this._connectedOptions.forEach(option => { - if (option.disabled) { + if (option.disabled || option.hidden) { return; } @@ -670,12 +671,6 @@ export default class BlSelect extends Form }); this._handleMultipleSelect(); - - // Make sure the checkbox state is in sync with selected options - setTimeout(() => { - selectAllEl.checked = this.isAllSelected; - selectAllEl.indeterminate = this._selectedOptions.length > 0 && !this.isAllSelected; - }); } private _onClickRemove(e: MouseEvent) {