Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ComboBox - New methods + bullet proofing #479

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 77 additions & 32 deletions src/plugins/combobox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
IComboBox,
IComboBoxOptions,
IComboBoxItemAttr,
QueryTransformer,
} from '../combobox/interfaces';

import HSBasePlugin from '../base-plugin';
Expand All @@ -34,6 +35,7 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
apiDataPart: string | null;
apiQuery: string | null;
apiSearchQuery: string | null;
apiSearchQueryTransformer: ((query: string) => string) | null;
apiHeaders: {};
apiGroupField: string | null;
outputItemTemplate: string | null;
Expand All @@ -50,6 +52,7 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
private readonly output: HTMLElement | null;
private readonly itemsWrapper: HTMLElement | null;
private items: HTMLElement[] | [];
private selectedItemElement: HTMLElement | null;
private tabs: HTMLElement[] | [];
private readonly toggle: HTMLElement | null;
private readonly toggleClose: HTMLElement | null;
Expand Down Expand Up @@ -87,6 +90,7 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
this.apiDataPart = concatOptions?.apiDataPart ?? null;
this.apiQuery = concatOptions?.apiQuery ?? null;
this.apiSearchQuery = concatOptions?.apiSearchQuery ?? null;
this.apiSearchQueryTransformer = this.parseApiQueryTransformer(concatOptions?.apiSearchQueryTransformer);
this.apiHeaders = concatOptions?.apiHeaders ?? {};
this.apiGroupField = concatOptions?.apiGroupField ?? null;
this.outputItemTemplate =
Expand Down Expand Up @@ -150,7 +154,20 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
this.init();
}

private parseApiQueryTransformer(query: QueryTransformer | string | null): QueryTransformer | null {

if (!query) return null;

if (typeof query === 'string') {
return eval(query) as QueryTransformer;
}

return query;
}

private init() {
// So that not to dpeendon preloading whole library.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo in the comment.

window.$hsComboBoxCollection = window.$hsComboBoxCollection ?? [];
this.createCollection(window.$hsComboBoxCollection, this);

this.build();
Expand Down Expand Up @@ -239,10 +256,10 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
const equality =
params?.group?.name && group
? group === params.group.name &&
elI.getAttribute('data-hs-combo-box-search-text') ===
obj[elI.getAttribute('data-hs-combo-box-output-item-field')]
elI.getAttribute('data-hs-combo-box-search-text') ===
obj[elI.getAttribute('data-hs-combo-box-output-item-field')]
: elI.getAttribute('data-hs-combo-box-search-text') ===
obj[elI.getAttribute('data-hs-combo-box-output-item-field')];
obj[elI.getAttribute('data-hs-combo-box-output-item-field')];

return equality;
});
Expand Down Expand Up @@ -319,7 +336,9 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {

try {
const query = `${this.apiQuery}`;
const searchQuery = `${this.apiSearchQuery}=${this.value.toLowerCase()}`;
const initialSearchQuery = `${this.apiSearchQuery}=${this.value.toLowerCase()}`;
const searchQuery = this.apiSearchQueryTransformer ? this.apiSearchQueryTransformer(this.value) : initialSearchQuery;

let url = this.apiUrl;
if (this.apiQuery && this.apiSearchQuery) {
url += `?${searchQuery}&${query}`;
Expand Down Expand Up @@ -376,6 +395,12 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
}

private jsonItemsRender(items: any) {

// Bullet proofing.
if (Array.isArray(items)) {
return
}

items.forEach((item: never, index: number) => {
// TODO:: test without checking below
// if (this.isItemExists(item)) return false;
Expand All @@ -401,6 +426,7 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
item[el.getAttribute('data-hs-combo-box-output-item-field')] ?? '',
);
});
// FIXME: Move to combobox options
newItem
.querySelectorAll('[data-hs-combo-box-output-item-attr]')
.forEach((el) => {
Expand All @@ -416,8 +442,7 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
if (this.groupingType === 'tabs' || this.groupingType === 'default') {
newItem.setAttribute(
'data-hs-combo-box-output-item',
`{"group": {"name": "${item[this.apiGroupField]}", "title": "${
item[this.apiGroupField]
`{"group": {"name": "${item[this.apiGroupField]}", "title": "${item[this.apiGroupField]
}"}}`,
);
}
Expand All @@ -426,13 +451,13 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {

if (!this.preventSelection) {
(newItem as HTMLElement).addEventListener('click', () => {
this.selectedItemElement = newItem;
this.setSelectedByValue(this.valuesBySelector(newItem));
this.close(
(newItem as HTMLElement)
.querySelector('[data-hs-combo-box-value]')
.getAttribute('data-hs-combo-box-search-text'),
);

this.setSelectedByValue(this.valuesBySelector(newItem));
});
}

Expand Down Expand Up @@ -645,6 +670,7 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
});
}

// FIXME: Does not go through the setSelectedByValue method.
private setValue(val: string) {
this.selected = val;
this.value = val;
Expand All @@ -666,12 +692,12 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
? this.selectedGroup === 'all'
? this.items
: this.items.filter((f: HTMLElement) => {
const { group } = JSON.parse(
f.getAttribute('data-hs-combo-box-output-item'),
);
const { group } = JSON.parse(
f.getAttribute('data-hs-combo-box-output-item'),
);

return group.name === this.selectedGroup;
})
return group.name === this.selectedGroup;
})
: this.items;

if (this.groupingType === 'tabs' && this.selectedGroup !== 'all') {
Expand Down Expand Up @@ -739,7 +765,6 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
this.setSelectedByValue([this.selected]);
}

// Public methods
private setValueAndOpen(val: string) {
this.value = val;

Expand All @@ -748,6 +773,15 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
}
}

private setValueAndClear(val: string | null) {
if (val) this.setValue(val);
else this.setValue(this.selected);

if (this.outputPlaceholder) this.destroyOutputPlaceholder();
}


// Public methods
public open(val?: string) {
if (this.animationInProcess) return false;

Expand All @@ -771,13 +805,6 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
this.isOpened = true;
}

private setValueAndClear(val: string | null) {
if (val) this.setValue(val);
else this.setValue(this.selected);

if (this.outputPlaceholder) this.destroyOutputPlaceholder();
}

public close(val?: string | null) {
if (this.animationInProcess) return false;

Expand All @@ -803,18 +830,36 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {

afterTransition(this.output, () => {
this.output.style.display = 'none';

this.setValueAndClear(val);

this.animationInProcess = false;
});

if (this.input.value !== '') this.el.classList.add('has-value');
else this.el.classList.remove('has-value');


this.isOpened = false;
}

setSearchQueryTransformer(transformer: (query: string) => string) {
this.apiSearchQueryTransformer = transformer;
}

public selectedItem(): HTMLElement | null {
return this.selectedItemElement;
}

selectedValue(): string | null {
return this.selected;
}

selectedAttr(attr: string): string | null {
return this.selectedItemElement
? this.selectedItemElement.querySelector(`[${attr}]`)?.getAttribute(attr) ?? null
: null;
}


public recalculateDirection() {
if (
isEnoughSpace(
Expand Down Expand Up @@ -918,13 +963,13 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {

const preparedItems = isReversed
? Array.from(
output.querySelectorAll(':scope > *:not(.--exclude-accessibility)'),
)
.filter((el) => (el as HTMLElement).style.display !== 'none')
.reverse()
output.querySelectorAll(':scope > *:not(.--exclude-accessibility)'),
)
.filter((el) => (el as HTMLElement).style.display !== 'none')
.reverse()
: Array.from(
output.querySelectorAll(':scope > *:not(.--exclude-accessibility)'),
).filter((el) => (el as HTMLElement).style.display !== 'none');
output.querySelectorAll(':scope > *:not(.--exclude-accessibility)'),
).filter((el) => (el as HTMLElement).style.display !== 'none');
const items = preparedItems.filter(
(el: any) => !el.classList.contains('disabled'),
);
Expand Down Expand Up @@ -1058,7 +1103,7 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
(el) =>
!isParentOrElementHidden(el.element.el) &&
(evt.target as HTMLElement).closest('[data-hs-combo-box]') ===
el.element.el,
el.element.el,
);

const link: HTMLAnchorElement = opened.element.el.querySelector(
Expand All @@ -1080,8 +1125,8 @@ class HSComboBox extends HSBasePlugin<IComboBoxOptions> implements IComboBox {
opened.element.close(
!opened.element.preventSelection
? (evt.target as HTMLElement)
.querySelector('[data-hs-combo-box-value]')
.getAttribute('data-hs-combo-box-search-text')
.querySelector('[data-hs-combo-box-value]')
.getAttribute('data-hs-combo-box-search-text')
: null,
);
}
Expand Down
8 changes: 7 additions & 1 deletion src/plugins/combobox/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface IComboBoxOptions {
apiDataPart?: string | null;
apiQuery?: string | null;
apiSearchQuery?: string | null;
apiSearchQueryTransformer?: QueryTransformer | string | null;
apiHeaders?: {};
apiGroupField?: string | null;
outputItemTemplate?: string | null;
Expand All @@ -24,10 +25,15 @@ export interface IComboBox {

open(): void;
close(): void;
selectedItem(): HTMLElement | null;
selectedValue(): string | null;
selectedAttr(attr: string): string | null;
recalculateDirection(): void;
}

export interface IComboBoxItemAttr {
valueFrom: string;
attr: string;
}
}

export type QueryTransformer = (query: string) => string;