-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#CX-12946 : Explore and fix how multi select and grouping of selected…
… records using new wc listbox component (#1855) * feat(cx-12946): update list and example * updating component * feat(cx-12946): added the border bottom logic * update * feat(cx-12946): with meny overlay * updating parent with overlay * updating overlay parent * updating grouping logic * feat(cx-12946): fixed multiselect logic * adding select all * feat(cx-12946): select all grouping and reset filter done * feat(cx-12946): fix of UI in sandbox * adding select all and unselect all * adding chevron to example * feat(cx-12946): pre-select issue resolved * updating css * updating select all logic * updating UI * updating UI * updating label and uts * adding UTs for multi select * fix(cx-12946): a11y fix for multiselect * fix(cx-12946): separator issue fix * fix(cx-12946): arrows, tab and space keyboard navi fixed * fix(cx-12946): updated unit test * fix(cx-12946): addressed review comment * fix(cx-12946): fix for sandbox issue * fix(cx-12946): made enter and space key for selection and deselection * fix(cx-12946): version update --------- Co-authored-by: Naman Goel <[email protected]> Co-authored-by: nithishkumar98 <[email protected]> Co-authored-by: nithishkumar98 <[email protected]>
- Loading branch information
1 parent
b0bf911
commit dc95105
Showing
8 changed files
with
486 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
252 changes: 252 additions & 0 deletions
252
web-components/src/[sandbox]/examples/AdvanceList/components/ParentComponentWithMdOverlay.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
import { html, internalProperty, LitElement, property, PropertyValues } from "lit-element"; | ||
import "@/components/advance-list/AdvanceList"; | ||
import "@/components/list/List"; | ||
import "@/components/list/ListItem"; | ||
import "@/components/menu-overlay/MenuOverlay"; | ||
import { customElementWithCheck } from "@/mixins/CustomElementCheck"; | ||
|
||
export namespace ParentComponentWithMdOverlay { | ||
@customElementWithCheck("parent-component-with-overlay") | ||
export class ELEMENT extends LitElement { | ||
@property({ type: Array }) items: any = []; | ||
@internalProperty() page = 1; | ||
@property({ type: Boolean }) isLoading = false; | ||
@property({ type: Array }) value: string[] = []; | ||
@property({ type: Boolean }) isError = false; | ||
@property({ type: Boolean }) groupOnMultiSelect = true; | ||
@internalProperty() totalRecords = 0; | ||
@internalProperty() loadedRecords = 0; | ||
@internalProperty() lastSelectedIdByOrder = ""; | ||
@internalProperty() selectAllItems = false; | ||
@internalProperty() isMenuOverlayOpen = false; | ||
@internalProperty() disabledItems: string[] = []; | ||
@internalProperty() inputIcon = "arrow-down-bold"; | ||
@internalProperty() overlayTriggerPlaceholder = "Search field with tabs"; | ||
|
||
private counter = 0; | ||
constructor() { | ||
super(); | ||
this.items = []; | ||
this.page = 1; | ||
this.isLoading = false; | ||
this.isError = false; | ||
this.loadMoreItems(); | ||
} | ||
|
||
updated(changedProperties: PropertyValues) { | ||
console.log("changedProperties", changedProperties); | ||
if (changedProperties.has("value")) { | ||
this.updatePlaceholder(); | ||
} | ||
} | ||
|
||
generateUUID() { | ||
const baseUUID = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { | ||
const r = (Math.random() * 16) | 0; | ||
const v = c === "x" ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
|
||
// Increment the counter and pad with zeros to ensure 3 characters | ||
this.counter++; | ||
const counterString = this.counter.toString().padStart(3, "0"); | ||
|
||
// Replace the last 3 characters of the UUID with the counter | ||
return baseUUID.slice(0, -3) + counterString; | ||
} | ||
|
||
async loadMoreItems() { | ||
try { | ||
this.isLoading = true; | ||
const newItems = await this.fetchItems(this.page); | ||
this.items = [...this.items, ...newItems]; | ||
this.totalRecords = 60000; | ||
this.page += 1; | ||
this.value.push(this.items[1].id); | ||
this.loadedRecords = this.items.length; | ||
} catch (err) { | ||
this.isError = true; | ||
} finally { | ||
this.isLoading = false; | ||
} | ||
} | ||
|
||
async fetchItems(page: number) { | ||
const newItems = this.generateItems(page); | ||
return newItems; | ||
} | ||
|
||
private generateItems(page: number) { | ||
const itemsPerPage = 2000; | ||
return Array.from({ length: itemsPerPage }, (_, i) => { | ||
const id = this.generateUUID(); // Generate a unique ID for each item | ||
const itemName = `Item ${(page - 1) * itemsPerPage + i + 1}`; | ||
const disabled = i % 2 === 0 ? "true" : ""; | ||
if (disabled) { | ||
this.disabledItems.push(id); | ||
} | ||
return { | ||
name: itemName, | ||
id, | ||
index: i, | ||
ariaLabel: itemName, | ||
template: (data: any, index: number) => | ||
html`<div | ||
style="position:relative;min-height:1.25rem;box-sizing: border-box;display:flex;flex-flow:row nowrap;justify-content:flex-start;align-items:center;line-height:26px;" | ||
?disabled=${disabled} | ||
aria-hidden="true" | ||
indexing="${index}" | ||
> | ||
<md-checkbox .disabled=${disabled} >${data.name}</md-checkbox> | ||
</div>` | ||
}; | ||
}); | ||
} | ||
|
||
getOrderedItems() { | ||
if (this.groupOnMultiSelect) { | ||
const selectedItems = this.items.filter((item: any) => this.value.includes(item.id)); | ||
const unselectedItems = this.items.filter((item: any) => !this.value.includes(item.id)); | ||
|
||
selectedItems.sort((a: any, b: any) => this.naturalSort(a.name, b.name)); | ||
unselectedItems.sort((a: any, b: any) => this.naturalSort(a.name, b.name)); | ||
|
||
if (selectedItems.length > 0) { | ||
this.lastSelectedIdByOrder = selectedItems[selectedItems.length - 1].id; | ||
} else { | ||
this.lastSelectedIdByOrder = ""; | ||
} | ||
return [...selectedItems, ...unselectedItems]; | ||
} else { | ||
return [...this.items].sort((a, b) => this.naturalSort(a.name, b.name)); | ||
} | ||
} | ||
|
||
private naturalSort(a: string, b: string): number { | ||
return a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }); | ||
} | ||
|
||
updatePlaceholder() { | ||
const selectedItemsCount = this.value.length; | ||
this.overlayTriggerPlaceholder = selectedItemsCount ? `${selectedItemsCount} selected` : "Search field with tabs"; | ||
} | ||
|
||
private handleListItemChange(event: CustomEvent) { | ||
this.value = event.detail.selected; | ||
// Filter and sort the selected items based on the `name` property | ||
const selectedItems = this.items | ||
.filter((item: any) => this.value.includes(item.id)) | ||
.sort((a: any, b: any) => this.naturalSort(a.name, b.name)); | ||
|
||
// Update `this.value` with the sorted IDs | ||
this.value = selectedItems.map((item: any) => item.id); | ||
|
||
this.updatePlaceholder(); | ||
if (this.value.length === this.items.length - this.disabledItems.length) { | ||
this.selectAllItems = true; | ||
} else { | ||
this.selectAllItems = false; | ||
} | ||
document.dispatchEvent(new CustomEvent("on-widget-update")); | ||
} | ||
|
||
updateSelectAllCheckboxOnClick() { | ||
if (this.selectAllItems) { | ||
this.resetFilter(); | ||
} else { | ||
this.selectAllItems = true; | ||
} | ||
} | ||
|
||
resetFilter() { | ||
this.selectAllItems = false; | ||
this.value = []; | ||
} | ||
|
||
getSelectAllLabel() { | ||
return this.items.length < 100 | ||
? "Select all" | ||
: `Select ${this.loadedRecords - this.disabledItems.length} of ${this.totalRecords}`; | ||
} | ||
|
||
render() { | ||
return html` | ||
<h2>With overlay</h2> | ||
<md-menu-overlay | ||
class="queueDropdown test-overlay" | ||
custom-width="300px" | ||
@menu-overlay-open=${() => { | ||
this.items = this.getOrderedItems(); | ||
this.inputIcon = "arrow-up-bold"; | ||
this.isMenuOverlayOpen = true; | ||
document.dispatchEvent(new CustomEvent("on-widget-update")); | ||
}} | ||
@menu-overlay-close=${() => { | ||
this.inputIcon = "arrow-down-bold"; | ||
this.isMenuOverlayOpen = false; | ||
}} | ||
> | ||
<md-input | ||
value=${this.overlayTriggerPlaceholder} | ||
slot="menu-trigger" | ||
autoFocus | ||
shape="pill" | ||
readonly | ||
newMomentum | ||
> | ||
<md-icon slot="input-section-right" name=${this.inputIcon} iconset="momentumDesign"></md-icon> | ||
</md-input> | ||
<div style="margin:0.5rem; width: 100%"> | ||
<md-input placeholder="Search..." shape="pill" clear autoFocus></md-input> | ||
<div style="margin-top:0.5rem; width: 100%"> | ||
<div | ||
style="padding-left:15px; padding-bottom:10px; border-bottom: 1px solid var(--md-gray-20);" | ||
> | ||
<md-checkbox | ||
id="select-all-one" | ||
class="selectAll" | ||
@checkbox-change=${this.updateSelectAllCheckboxOnClick} | ||
.checked=${this.selectAllItems} | ||
>${this.getSelectAllLabel()}</md-checkbox | ||
> | ||
</div> | ||
${this.isMenuOverlayOpen | ||
? html` | ||
<md-advance-list | ||
.items=${this.items} | ||
.isLoading=${this.isLoading} | ||
.isError=${this.isError} | ||
.value=${this.value} | ||
ariaRoleList="listbox" | ||
ariaLabelList="state selector" | ||
.totalRecords=${this.totalRecords} | ||
.lastSelectedIdByOrder=${this.lastSelectedIdByOrder} | ||
is-multi | ||
.groupOnMultiSelect=${this.groupOnMultiSelect} | ||
.selectAllItems=${this.selectAllItems} | ||
.disabledItems=${this.disabledItems} | ||
@list-item-change=${this.handleListItemChange} | ||
@load-more=${this.loadMoreItems} | ||
> | ||
<md-spinner size="24" slot="spin-loader"></md-spinner> | ||
</md-advance-list>` | ||
: html`` | ||
} | ||
</div> | ||
<div> | ||
<md-button @button-click=${this.resetFilter} variant="primary">Reset the filter</md-button> | ||
</div> | ||
</div> | ||
</md-menu-overlay> | ||
`; | ||
} | ||
} | ||
} | ||
|
||
declare global { | ||
interface HTMLElementTagNameMap { | ||
"parent-component-with-overlay": ParentComponentWithMdOverlay.ELEMENT; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.