@iamproperty/components 7.8.2--beta2 → 7.8.2--beta4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/css/components/actionbar.component.css +1 -1
- package/assets/css/components/actionbar.component.css.map +1 -1
- package/assets/css/components/address-lookup.component.css +1 -1
- package/assets/css/components/address-lookup.component.css.map +1 -1
- package/assets/css/components/advanced-select.component.css +1 -1
- package/assets/css/components/advanced-select.component.css.map +1 -1
- package/assets/css/components/applied-filters.css +1 -1
- package/assets/css/components/applied-filters.css.map +1 -1
- package/assets/css/components/banner.preload.css +1 -0
- package/assets/css/components/banner.preload.css.map +1 -0
- package/assets/css/components/calendar.component.css +1 -1
- package/assets/css/components/calendar.component.css.map +1 -1
- package/assets/css/components/card.component.css +1 -1
- package/assets/css/components/card.component.css.map +1 -1
- package/assets/css/components/card.module.css +1 -1
- package/assets/css/components/card.module.css.map +1 -1
- package/assets/css/components/card.preload.css +1 -0
- package/assets/css/components/card.preload.css.map +1 -0
- package/assets/css/components/carousel.component.css +1 -1
- package/assets/css/components/carousel.component.css.map +1 -1
- package/assets/css/components/carousel.config.css +1 -1
- package/assets/css/components/carousel.config.css.map +1 -1
- package/assets/css/components/config.component.css +1 -1
- package/assets/css/components/config.component.css.map +1 -1
- package/assets/css/components/content.component.css +1 -1
- package/assets/css/components/content.component.css.map +1 -1
- package/assets/css/components/fileupload.css +1 -1
- package/assets/css/components/fileupload.css.map +1 -1
- package/assets/css/components/filter-card.component.css +1 -1
- package/assets/css/components/filter-card.component.css.map +1 -1
- package/assets/css/components/multiselect.css +1 -1
- package/assets/css/components/multiselect.css.map +1 -1
- package/assets/css/components/nav.component.css +1 -1
- package/assets/css/components/nav.component.css.map +1 -1
- package/assets/css/components/pagination.css +1 -1
- package/assets/css/components/pagination.css.map +1 -1
- package/assets/css/components/record-card.component.css +1 -1
- package/assets/css/components/record-card.component.css.map +1 -1
- package/assets/css/components/search.component.css +1 -1
- package/assets/css/components/search.component.css.map +1 -1
- package/assets/css/components/skeleton.global.css +1 -1
- package/assets/css/components/skeleton.global.css.map +1 -1
- package/assets/css/components/slider.css +1 -1
- package/assets/css/components/slider.css.map +1 -1
- package/assets/css/components/split-button.component.css +1 -1
- package/assets/css/components/split-button.component.css.map +1 -1
- package/assets/css/components/std-nav-standalone.component.css +1 -1
- package/assets/css/components/std-nav-standalone.component.css.map +1 -1
- package/assets/css/components/tabs.component.css +1 -1
- package/assets/css/components/tabs.component.css.map +1 -1
- package/assets/css/components/tag.component.css +1 -1
- package/assets/css/components/tag.component.css.map +1 -1
- package/assets/css/components/video-card.component.css +1 -1
- package/assets/css/components/video-card.component.css.map +1 -1
- package/assets/css/core.min.css +1 -1
- package/assets/css/core.min.css.map +1 -1
- package/assets/css/elements/badge-tag.css +1 -1
- package/assets/css/elements/badge-tag.css.map +1 -1
- package/assets/css/elements/dropdown.css +1 -1
- package/assets/css/elements/dropdown.css.map +1 -1
- package/assets/css/elements/forms.css +1 -1
- package/assets/css/elements/forms.css.map +1 -1
- package/assets/css/elements/links--global.css +1 -1
- package/assets/css/elements/links--global.css.map +1 -1
- package/assets/css/elements/links.css +1 -1
- package/assets/css/elements/links.css.map +1 -1
- package/assets/css/style.min.css +1 -1
- package/assets/css/style.min.css.map +1 -1
- package/assets/js/components/accordion/accordion.component.min.js +1 -1
- package/assets/js/components/actionbar/actionbar.component.min.js +2 -2
- package/assets/js/components/address-lookup/address-lookup.component.min.js +4 -4
- package/assets/js/components/address-lookup/address-lookup.component.min.js.map +1 -1
- package/assets/js/components/advanced-select/advanced-select.component.min.js +2 -2
- package/assets/js/components/applied-filters/applied-filters.component.min.js +2 -2
- package/assets/js/components/banner/banner.component.min.js +1 -1
- package/assets/js/components/barchart/barchart.component.min.js +1 -1
- package/assets/js/components/bento-grid/bento-grid.component.min.js +1 -1
- package/assets/js/components/bone/bone.component.min.js +1 -1
- package/assets/js/components/button/button.component.min.js +1 -1
- package/assets/js/components/calendar/calendar.component.min.js +2 -2
- package/assets/js/components/card/card.component.js +114 -125
- package/assets/js/components/card/card.component.min.js +7 -7
- package/assets/js/components/card/card.component.min.js.map +1 -1
- package/assets/js/components/carousel/carousel.component.js +83 -29
- package/assets/js/components/carousel/carousel.component.min.js +16 -11
- package/assets/js/components/carousel/carousel.component.min.js.map +1 -1
- package/assets/js/components/collapsible-side/collapsible-side.component.min.js +1 -1
- package/assets/js/components/config/config.component.min.js +7 -7
- package/assets/js/components/config/config.component.min.js.map +1 -1
- package/assets/js/components/content/content.component.js +28 -69
- package/assets/js/components/content/content.component.min.js +4 -4
- package/assets/js/components/content/content.component.min.js.map +1 -1
- package/assets/js/components/darkmode/darkmode.component.min.js +1 -1
- package/assets/js/components/doughnutchart/doughnutchart.component.min.js +1 -1
- package/assets/js/components/fileupload/fileupload.component.min.js +2 -2
- package/assets/js/components/filter-card/filter-card.component.min.js +5 -5
- package/assets/js/components/filter-card/filter-card.component.min.js.map +1 -1
- package/assets/js/components/filterlist/filterlist.component.min.js +1 -1
- package/assets/js/components/form/form.component.min.js +1 -1
- package/assets/js/components/header/header.component.min.js +1 -1
- package/assets/js/components/inline-edit/inline-edit.component.min.js +1 -1
- package/assets/js/components/input/input.component.min.js +1 -1
- package/assets/js/components/input-range/input-range.component.min.js +1 -1
- package/assets/js/components/marketing/marketing.component.min.js +1 -1
- package/assets/js/components/menu/menu.component.min.js +1 -1
- package/assets/js/components/milestone/milestone.component.min.js +1 -1
- package/assets/js/components/milestone-group/milestone-group.component.min.js +1 -1
- package/assets/js/components/modal/modal.component.min.js +1 -1
- package/assets/js/components/multi-step/multi-step.component.min.js +1 -1
- package/assets/js/components/multi-step-modal/multi-step-modal.component.min.js +1 -1
- package/assets/js/components/multiselect/multiselect.component.min.js +4 -4
- package/assets/js/components/multiselect/multiselect.component.min.js.map +1 -1
- package/assets/js/components/nav/nav.component.min.js +2 -2
- package/assets/js/components/notification/notification.component.min.js +1 -1
- package/assets/js/components/pagination/pagination.component.min.js +5 -5
- package/assets/js/components/password/password.component.min.js +1 -1
- package/assets/js/components/popover/popover.component.min.js +1 -1
- package/assets/js/components/rank/rank.component.min.js +1 -1
- package/assets/js/components/rankings/rankings.component.min.js +1 -1
- package/assets/js/components/rating/rating.component.min.js +1 -1
- package/assets/js/components/record-card/record-card.component.min.js +6 -6
- package/assets/js/components/record-card/record-card.component.min.js.map +1 -1
- package/assets/js/components/search/search.component.js +234 -186
- package/assets/js/components/search/search.component.min.js +12 -7
- package/assets/js/components/search/search.component.min.js.map +1 -1
- package/assets/js/components/skeleton/skeleton.component.min.js +1 -1
- package/assets/js/components/slider/slider.component.min.js +2 -2
- package/assets/js/components/split-button/split-button.component.min.js +2 -2
- package/assets/js/components/std-address-lookup/std-address-lookup.component.min.js +5 -5
- package/assets/js/components/std-address-lookup/std-address-lookup.component.min.js.map +1 -1
- package/assets/js/components/std-nav/std-nav.component.js +10 -9
- package/assets/js/components/std-nav/std-nav.component.min.js +9 -12
- package/assets/js/components/std-nav/std-nav.component.min.js.map +1 -1
- package/assets/js/components/std-nav-standalone/std-nav-standalone.component.min.js +5 -5
- package/assets/js/components/std-nav-standalone/std-nav-standalone.component.min.js.map +1 -1
- package/assets/js/components/table/table.component.min.js +1 -1
- package/assets/js/components/table-ajax/table-ajax.component.min.js +1 -1
- package/assets/js/components/table-basic/table-basic.component.min.js +1 -1
- package/assets/js/components/table-no-submit/table-no-submit.component.min.js +1 -1
- package/assets/js/components/table-submit/table-submit.component.min.js +1 -1
- package/assets/js/components/tabs/tabs.component.min.js +4 -4
- package/assets/js/components/tag/tag.component.min.js +3 -3
- package/assets/js/components/tag/tag.component.min.js.map +1 -1
- package/assets/js/components/tooltip/tooltip.component.min.js +1 -1
- package/assets/js/components/video/video.component.min.js +1 -1
- package/assets/js/components/video-card/video-card.component.min.js +9 -9
- package/assets/js/components/video-card/video-card.component.min.js.map +1 -1
- package/assets/js/components/video-modal/video-modal.component.min.js +1 -1
- package/assets/js/components/word-count/word-count.component.min.js +1 -1
- package/assets/js/modules/card.module.js +12 -11
- package/assets/js/modules/content.js +40 -8
- package/assets/js/modules/content.test.js +62 -12
- package/assets/js/modules/data-layer.js +7 -6
- package/assets/js/modules/dropdown.js +0 -1
- package/assets/js/modules/nav.js +10 -3
- package/assets/js/modules/search.js +153 -0
- package/assets/js/modules/search.test.js +125 -0
- package/assets/js/modules/tabs.test.js +64 -12
- package/assets/js/modules/testimonial.test.js +44 -6
- package/assets/js/modules/videos.test.js +61 -13
- package/assets/js/scripts.bundle.js +3 -3
- package/assets/js/scripts.bundle.js.map +1 -1
- package/assets/js/scripts.bundle.min.js +2 -2
- package/assets/js/scripts.bundle.min.js.map +1 -1
- package/assets/sass/_components.scss +2 -63
- package/assets/sass/components/banner.preload.scss +26 -0
- package/assets/sass/components/card.component.scss +1 -7
- package/assets/sass/components/card.module.scss +6 -6
- package/assets/sass/components/card.preload.scss +80 -0
- package/assets/sass/components/carousel.component.scss +165 -0
- package/assets/sass/components/carousel.config.scss +85 -249
- package/assets/sass/components/content.component.scss +0 -7
- package/assets/sass/components/nav.component.scss +2 -1
- package/assets/sass/components/search.component.scss +89 -7
- package/assets/sass/components/skeleton.global.scss +4 -0
- package/assets/sass/elements/badge-tag.css +1 -1
- package/assets/sass/elements/dropdown.css +2 -0
- package/assets/sass/elements/forms.scss +0 -27
- package/assets/sass/elements/links--global.scss +40 -2
- package/assets/sass/foundations/root.scss +0 -1
- package/assets/sass/utilities/border.css +1 -1
- package/assets/sass/utilities/js-display.css +2 -3
- package/assets/ts/components/card/card.component.ts +72 -62
- package/assets/ts/components/carousel/carousel.component.ts +84 -19
- package/assets/ts/components/content/content.component.ts +36 -100
- package/assets/ts/components/search/search.component.ts +257 -185
- package/assets/ts/components/std-nav/std-nav.component.ts +17 -16
- package/assets/ts/html.d.ts +6 -0
- package/assets/ts/modules/card.module.ts +20 -12
- package/assets/ts/modules/content.test.ts +84 -12
- package/assets/ts/modules/content.ts +56 -9
- package/assets/ts/modules/data-layer.ts +7 -11
- package/assets/ts/modules/dropdown.ts +0 -2
- package/assets/ts/modules/nav.ts +12 -3
- package/assets/ts/modules/search.test.ts +142 -0
- package/assets/ts/modules/search.ts +206 -0
- package/assets/ts/modules/tabs.test.ts +79 -12
- package/assets/ts/modules/testimonial.test.ts +45 -6
- package/assets/ts/modules/videos.test.ts +74 -14
- package/dist/components.es.js +25 -25
- package/dist/components.umd.js +164 -157
- package/package.json +1 -1
- package/assets/js/modules/carousel.js +0 -214
- package/assets/js/modules/carousel.test.js +0 -18
- package/assets/ts/modules/carousel.test.ts +0 -27
- package/assets/ts/modules/carousel.ts +0 -301
|
@@ -16,34 +16,42 @@ export const cardHTML = `<div class="wrapper">
|
|
|
16
16
|
</div>
|
|
17
17
|
</div>`;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const getCardPart = <T extends Element>(cardComponent: HTMLElement, selector: string): T | null =>
|
|
20
|
+
cardComponent.shadowRoot?.querySelector<T>(selector) || null;
|
|
21
|
+
|
|
22
|
+
export const setupCard = (cardComponent: HTMLElement): void => {
|
|
20
23
|
cardComponent.classList.add('card');
|
|
21
|
-
const cardHead = cardComponent
|
|
22
|
-
const cardBody = cardComponent
|
|
24
|
+
const cardHead = getCardPart<HTMLDivElement>(cardComponent, '.card__head');
|
|
25
|
+
const cardBody = getCardPart<HTMLDivElement>(cardComponent, '.card__body');
|
|
26
|
+
const cardBadges = getCardPart<HTMLDivElement>(cardComponent, '.card__badges');
|
|
23
27
|
|
|
24
28
|
if (cardComponent.hasAttribute('data-image')) {
|
|
25
|
-
cardHead
|
|
29
|
+
cardHead?.insertAdjacentHTML(
|
|
30
|
+
'beforeend',
|
|
31
|
+
`<img src="${cardComponent.getAttribute('data-image') || ''}" alt="" loading="lazy" part="image" />`
|
|
32
|
+
);
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
// Inset the HTML for the data total or icon fallback
|
|
29
36
|
if (cardComponent.hasAttribute('data-total')) {
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
const cardTotal = cardBody?.querySelector<HTMLDivElement>('.card__total');
|
|
39
|
+
|
|
40
|
+
if(!cardTotal)
|
|
41
|
+
cardBody?.insertAdjacentHTML(
|
|
33
42
|
'beforeend',
|
|
34
|
-
`<div class="card__total">${cardComponent.getAttribute('data-total')}</div>`
|
|
43
|
+
`<div class="card__total">${cardComponent.getAttribute('data-total') || ''}</div>`
|
|
35
44
|
);
|
|
36
45
|
else {
|
|
37
|
-
|
|
38
|
-
if (cardTotal) cardTotal.innerHTML = cardComponent.getAttribute('data-total');
|
|
46
|
+
cardTotal.innerHTML = cardComponent.getAttribute('data-total') || '';
|
|
39
47
|
}
|
|
40
48
|
} else if (cardComponent.querySelector('[slot="total-icon"]')) {
|
|
41
|
-
cardBody
|
|
49
|
+
cardBody?.insertAdjacentHTML('beforeend', `<div class="card__total"><slot name="total-icon"></slot></div>`);
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
if (!cardComponent.querySelector('[slot="badges"]')) {
|
|
45
|
-
|
|
53
|
+
cardBadges?.classList.add('empty');
|
|
46
54
|
} else {
|
|
47
|
-
|
|
55
|
+
cardBadges?.classList.remove('empty');
|
|
48
56
|
}
|
|
49
57
|
};
|
|
@@ -1,24 +1,96 @@
|
|
|
1
1
|
import { describe, expect, it } from './test.ts';
|
|
2
2
|
import { createElement, installTestDom } from './test-dom.ts';
|
|
3
3
|
import { append } from './test-utils.ts';
|
|
4
|
-
import { transformButtons } from './content.ts';
|
|
4
|
+
import { createTitle, loadComponents, replaceShortcode, transformButtons, transformElement } from './content.ts';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
installTestDom();
|
|
7
|
+
|
|
8
|
+
type TestAttribute = {
|
|
9
|
+
name: string;
|
|
10
|
+
value: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type AttributeComponent = {
|
|
14
|
+
attributes: TestAttribute[];
|
|
15
|
+
getAttribute: (name: string) => string | null;
|
|
16
|
+
hasAttribute: (name: string) => boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const createAttributeComponent = (attributes: TestAttribute[]): AttributeComponent => ({
|
|
20
|
+
attributes,
|
|
21
|
+
getAttribute(name: string): string | null {
|
|
22
|
+
const attribute = attributes.find((attr) => attr.name === name);
|
|
23
|
+
return attribute ? attribute.value : null;
|
|
24
|
+
},
|
|
25
|
+
hasAttribute(name: string): boolean {
|
|
26
|
+
return attributes.some((attr) => attr.name === name);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
7
29
|
|
|
8
30
|
describe('Content module', () => {
|
|
31
|
+
it('replaces WordPress shortcode paragraphs with loading placeholders', () => {
|
|
32
|
+
const content = '<p>Intro</p><p>[search-learning-articles]</p><p>[search-contacts]</p>';
|
|
33
|
+
const transformed = replaceShortcode(content);
|
|
34
|
+
|
|
35
|
+
expect(transformed.includes('<p>Intro</p>'));
|
|
36
|
+
expect(transformed.includes('data-shortcode="search-learning-articles"'));
|
|
37
|
+
expect(transformed.includes('data-shortcode="search-contacts"'));
|
|
38
|
+
expect(!transformed.includes('<p>[search-learning-articles]</p>'));
|
|
39
|
+
expect(!transformed.includes('<p>[search-contacts]</p>'));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('creates configured titles and skips missing titles', () => {
|
|
43
|
+
const component = createElement('iam-content', {
|
|
44
|
+
dataTitleTag: 'h2',
|
|
45
|
+
dataTitleClass: 'bg-light',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(createTitle(component, 'Latest news') === '<h2 class="bg-light iam-content--title">Latest news</h2>');
|
|
49
|
+
expect(createTitle(component, '') === '');
|
|
50
|
+
expect(createTitle(createElement('iam-content'), 'Latest news') === '');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('wraps rendered content in the requested transform element', () => {
|
|
54
|
+
const component = createAttributeComponent([
|
|
55
|
+
{ name: 'data-transform', value: 'article' },
|
|
56
|
+
{ name: 'data-url', value: '/wp-json/wp/v2/pages/1' },
|
|
57
|
+
{ name: 'class', value: 'content-panel' },
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const transformed = transformElement(component, '<h2>Heading</h2>', '<p>Body</p>');
|
|
61
|
+
|
|
62
|
+
expect(transformed.startsWith('<article '));
|
|
63
|
+
expect(transformed.includes('data-transform="article"'));
|
|
64
|
+
expect(transformed.includes('data-url="/wp-json/wp/v2/pages/1"'));
|
|
65
|
+
expect(transformed.includes('class="content-panel"'));
|
|
66
|
+
expect(transformed.includes('<h2>Heading</h2><p>Body</p>'));
|
|
67
|
+
expect(transformed.endsWith('</article>'));
|
|
68
|
+
});
|
|
69
|
+
|
|
9
70
|
it('transforms WordPress button wrappers into direct links', () => {
|
|
10
|
-
const parent = createElement('div');
|
|
11
71
|
const buttons = createElement('div', { class: 'wp-block-buttons' });
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
append(
|
|
17
|
-
append(
|
|
72
|
+
const primaryButton = createElement('div', { class: 'btn btn-primary wp-block-button' });
|
|
73
|
+
const secondaryButton = createElement('div', { class: 'btn btn-secondary wp-block-button' });
|
|
74
|
+
const primaryLink = createElement('a', { href: '/primary' }, 'Primary');
|
|
75
|
+
const secondaryLink = createElement('a', { href: '/secondary' }, 'Secondary');
|
|
76
|
+
append(primaryButton, primaryLink);
|
|
77
|
+
append(secondaryButton, secondaryLink);
|
|
78
|
+
append(buttons, primaryButton, secondaryButton);
|
|
79
|
+
|
|
80
|
+
const fragment = transformButtons(buttons);
|
|
81
|
+
|
|
82
|
+
expect(fragment.children.length === 2);
|
|
83
|
+
expect(fragment.children[0] === primaryLink);
|
|
84
|
+
expect(fragment.children[1] === secondaryLink);
|
|
85
|
+
expect(primaryLink.getAttribute('class') === 'btn btn-primary wp-block-button');
|
|
86
|
+
expect(secondaryLink.getAttribute('class') === 'btn btn-secondary wp-block-button');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not load component bundles when content contains no supported custom elements', () => {
|
|
90
|
+
const component = createElement('iam-content');
|
|
18
91
|
|
|
19
|
-
|
|
92
|
+
loadComponents(component);
|
|
20
93
|
|
|
21
|
-
expect(
|
|
22
|
-
expect(parent.children[0].localName === 'fragment');
|
|
94
|
+
expect(component.getElementsByTagName('iam-card').length === 0);
|
|
23
95
|
});
|
|
24
96
|
});
|
|
@@ -1,17 +1,64 @@
|
|
|
1
|
-
export const transformButtons = (component):void => {
|
|
2
1
|
|
|
3
|
-
Array.from(document.querySelectorAll('.wp-block-buttons')).forEach((buttons) => {
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
export const loadComponents = (component):void => {
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
const components = ['skeleton','bone','carousel', 'card', 'banner', 'notification'];
|
|
6
|
+
const assetLocation = document.body.hasAttribute('data-assets-location')
|
|
7
|
+
? document.body.getAttribute('data-assets-location')
|
|
8
|
+
: '/assets';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
link.setAttribute('class',element.getAttribute('class'));
|
|
10
|
+
components.forEach((loadComponent) => {
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
});
|
|
12
|
+
if (component.getElementsByTagName(`iam-${loadComponent}`).length === 0) return;
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
import(/*! @vite-ignore */ `${assetLocation}/js/components/${loadComponent}/${loadComponent}.component.min.js`)
|
|
15
|
+
.then((module) => {
|
|
16
|
+
if (!window.customElements.get(`iam-${loadComponent}`))
|
|
17
|
+
window.customElements.define(`iam-${loadComponent}`, module.default);
|
|
18
|
+
})
|
|
19
|
+
.catch((err) => {
|
|
20
|
+
console.log(err.message);
|
|
21
|
+
});
|
|
16
22
|
});
|
|
17
23
|
}
|
|
24
|
+
|
|
25
|
+
export const replaceShortcode = (content):string => {
|
|
26
|
+
|
|
27
|
+
return content.replaceAll(/<p>\[([^\]]+)\]<\/p>/g, "<span data-shortcode=\"$1\"><iam-skeleton><iam-bone class=\"search\"></iam-bone></iam-skeleton></span>");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const createTitle = (component, title):string => {
|
|
31
|
+
|
|
32
|
+
if (component.hasAttribute('data-title-tag') && title)
|
|
33
|
+
return `<${component.getAttribute('data-title-tag')} class="${component.getAttribute('data-title-class')} iam-content--title">${title}</${component.getAttribute('data-title-tag')}>`;
|
|
34
|
+
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const transformElement = (component,renderedTitle,renderedContent): string => {
|
|
39
|
+
|
|
40
|
+
const transform = component.getAttribute('data-transform');
|
|
41
|
+
|
|
42
|
+
let elementAttributes = '';
|
|
43
|
+
|
|
44
|
+
for (const attr of component.attributes) {
|
|
45
|
+
elementAttributes += `${attr.name}="${attr.value}" `;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return `<${transform} ${elementAttributes}>${renderedTitle+renderedContent}</${transform}>`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const transformButtons = (buttons): DocumentFragment => {
|
|
52
|
+
|
|
53
|
+
const fragment = document.createDocumentFragment();
|
|
54
|
+
|
|
55
|
+
Array.from(buttons.querySelectorAll('.wp-block-button')).forEach((element) => {
|
|
56
|
+
|
|
57
|
+
const link = element.querySelector('a');
|
|
58
|
+
link.setAttribute('class',element.getAttribute('class'));
|
|
59
|
+
|
|
60
|
+
fragment.appendChild(link);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return fragment;
|
|
64
|
+
}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
dataLayer: Record<string, any>[];
|
|
3
|
-
};
|
|
4
|
-
|
|
5
|
-
declare const window: WindowWithDataLayer;
|
|
1
|
+
const dataLayerWindow = window as WindowWithDataLayer;
|
|
6
2
|
|
|
7
3
|
function createDataLayer(): void {
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
dataLayerWindow.dataLayer = dataLayerWindow.dataLayer || [];
|
|
5
|
+
dataLayerWindow.dataLayer.push({
|
|
10
6
|
event: 'Pageview',
|
|
11
7
|
pageTitle: document.title,
|
|
12
8
|
});
|
|
@@ -15,7 +11,7 @@ function createDataLayer(): void {
|
|
|
15
11
|
const target = (event.target as HTMLElement).closest<HTMLElement>('[open] summary');
|
|
16
12
|
|
|
17
13
|
if (target) {
|
|
18
|
-
|
|
14
|
+
dataLayerWindow.dataLayer.push({
|
|
19
15
|
event: 'closeDetails',
|
|
20
16
|
detailsTitle: target.textContent || '',
|
|
21
17
|
});
|
|
@@ -25,14 +21,14 @@ function createDataLayer(): void {
|
|
|
25
21
|
const button = (event.target as HTMLElement).closest<HTMLButtonElement>('button');
|
|
26
22
|
|
|
27
23
|
if (summary) {
|
|
28
|
-
|
|
24
|
+
dataLayerWindow.dataLayer.push({
|
|
29
25
|
event: 'openDetails',
|
|
30
26
|
detailsTitle: summary.textContent || '',
|
|
31
27
|
});
|
|
32
28
|
}
|
|
33
29
|
|
|
34
30
|
if (link) {
|
|
35
|
-
|
|
31
|
+
dataLayerWindow.dataLayer.push({
|
|
36
32
|
event: 'linkClicked',
|
|
37
33
|
linkText: link.hasAttribute('title') ? link.getAttribute('title') || '' : link.textContent || '',
|
|
38
34
|
class: link.hasAttribute('class') ? link.getAttribute('class') || '' : '',
|
|
@@ -41,7 +37,7 @@ function createDataLayer(): void {
|
|
|
41
37
|
}
|
|
42
38
|
|
|
43
39
|
if (button) {
|
|
44
|
-
|
|
40
|
+
dataLayerWindow.dataLayer.push({
|
|
45
41
|
event: 'buttonClicked',
|
|
46
42
|
buttonText: button.textContent || '',
|
|
47
43
|
class: button.hasAttribute('class') ? button.getAttribute('class') || '' : '',
|
|
@@ -25,8 +25,6 @@ export const searchAjax = async (component, search, callback): any => {
|
|
|
25
25
|
const ajaxURL = component.getAttribute('data-url');
|
|
26
26
|
const firstInput = component.querySelector('input');
|
|
27
27
|
|
|
28
|
-
console.log(firstInput);
|
|
29
|
-
|
|
30
28
|
const inputType = firstInput && firstInput.hasAttribute('type') ? firstInput.getAttribute('type') : 'checkbox';
|
|
31
29
|
let inputName = firstInput && firstInput.hasAttribute('name') ? firstInput.getAttribute('name') : 'tags';
|
|
32
30
|
|
package/assets/ts/modules/nav.ts
CHANGED
|
@@ -74,8 +74,9 @@ export const populateLinks = (data):void => {
|
|
|
74
74
|
|
|
75
75
|
export const loadNavData = async(Cookies): any => {
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const ajaxURL = 'https://dev.hub.iamproperty.group/data/ecosystem-switcher.json';
|
|
78
|
+
|
|
79
|
+
//const ajaxURL = '/nav.json';
|
|
79
80
|
|
|
80
81
|
// Setup controller vars if not already set
|
|
81
82
|
if (!window.controller) window.controller = [];
|
|
@@ -145,7 +146,15 @@ export const loadUserData = async(Cookies): any => {
|
|
|
145
146
|
|
|
146
147
|
export const setEnabledLinks = (component,data):void => {
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
const selector = `[data-product][data-feature]`;
|
|
150
|
+
const elements = component
|
|
151
|
+
? [
|
|
152
|
+
...component.querySelectorAll(selector),
|
|
153
|
+
...(component.shadowRoot ? component.shadowRoot.querySelectorAll(selector) : []),
|
|
154
|
+
]
|
|
155
|
+
: document.querySelectorAll(`iam-nav ${selector}`);
|
|
156
|
+
|
|
157
|
+
elements.forEach((element) => {
|
|
149
158
|
const isEnabled = data.attributes.products[element.getAttribute('data-product')].features[element.getAttribute('data-feature')];
|
|
150
159
|
element.setAttribute('data-is-enabled',isEnabled);
|
|
151
160
|
if(isEnabled && element.getAttribute('data-enabled')){
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, expect, it } from './test.ts';
|
|
2
|
+
import { createElement, installTestDom } from './test-dom.ts';
|
|
3
|
+
import { append } from './test-utils.ts';
|
|
4
|
+
import search, { datalistSelectOption, filterDatalist } from './search.ts';
|
|
5
|
+
|
|
6
|
+
installTestDom();
|
|
7
|
+
|
|
8
|
+
if (typeof globalThis.CustomEvent === 'undefined') {
|
|
9
|
+
globalThis.CustomEvent = class extends Event {
|
|
10
|
+
detail;
|
|
11
|
+
|
|
12
|
+
constructor(type, options = {}) {
|
|
13
|
+
super(type, options);
|
|
14
|
+
this.detail = options.detail;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('Search module', () => {
|
|
20
|
+
it('fetches GET results, builds datalist options and filters them by the search term', async () => {
|
|
21
|
+
let requestedUrl = '';
|
|
22
|
+
let requestedOptions;
|
|
23
|
+
|
|
24
|
+
globalThis.fetch = (url, options) => {
|
|
25
|
+
requestedUrl = url;
|
|
26
|
+
requestedOptions = options;
|
|
27
|
+
|
|
28
|
+
return Promise.resolve({
|
|
29
|
+
json: () =>
|
|
30
|
+
Promise.resolve({
|
|
31
|
+
data: [
|
|
32
|
+
{ value: 'alpha-id', label: 'Alpha\nTeam' },
|
|
33
|
+
{ id: 'beta-id', title: 'Beta Team' },
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const component = createElement('iam-search', { dataUrl: '/results' });
|
|
40
|
+
const input = createElement('input', { name: 'q', value: 'alpha & beta' });
|
|
41
|
+
const datalist = createElement('datalist');
|
|
42
|
+
append(component, input, datalist);
|
|
43
|
+
|
|
44
|
+
await search(component, datalist, 'alpha');
|
|
45
|
+
|
|
46
|
+
expect(requestedUrl === '/results?q=alpha%20%26%20beta');
|
|
47
|
+
expect(requestedOptions.method === 'GET');
|
|
48
|
+
expect(datalist.options.length === 2);
|
|
49
|
+
expect(datalist.options[0].value === 'alpha-id');
|
|
50
|
+
expect(datalist.options[0].textContent === 'Alpha, Team');
|
|
51
|
+
expect(!datalist.options[0].classList.contains('js-hide'));
|
|
52
|
+
expect(datalist.options[1].classList.contains('js-hide'));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('posts form values and renders grouped response options with custom schemas', async () => {
|
|
56
|
+
let requestedUrl = '';
|
|
57
|
+
let requestedOptions;
|
|
58
|
+
|
|
59
|
+
globalThis.fetch = (url, options) => {
|
|
60
|
+
requestedUrl = url;
|
|
61
|
+
requestedOptions = options;
|
|
62
|
+
|
|
63
|
+
return Promise.resolve({
|
|
64
|
+
json: () =>
|
|
65
|
+
Promise.resolve({
|
|
66
|
+
results: {
|
|
67
|
+
groups: {
|
|
68
|
+
Products: [{ code: 'eco', name: 'Ecosystem' }],
|
|
69
|
+
Learning: [{ code: 'guide', name: 'Guide' }],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const component = createElement('iam-search', {
|
|
77
|
+
dataDisplaySchema: 'name',
|
|
78
|
+
dataMethod: 'POST',
|
|
79
|
+
dataSchema: 'results.groups',
|
|
80
|
+
dataUrl: '/lookup',
|
|
81
|
+
dataValueSchema: 'code',
|
|
82
|
+
});
|
|
83
|
+
const input = createElement('input', { name: 'market', value: 'auction' });
|
|
84
|
+
const select = createElement('select', { name: 'status', value: 'active' });
|
|
85
|
+
const datalist = createElement('datalist');
|
|
86
|
+
append(component, input, select, datalist);
|
|
87
|
+
|
|
88
|
+
await search(component, datalist, 'guide');
|
|
89
|
+
|
|
90
|
+
expect(requestedUrl === '/lookup');
|
|
91
|
+
expect(requestedOptions.method === 'POST');
|
|
92
|
+
expect(requestedOptions.body === '{"market":"auction","status":"active"}');
|
|
93
|
+
expect(datalist.options.length === 2);
|
|
94
|
+
expect(datalist.options[0].value === 'eco');
|
|
95
|
+
expect(datalist.options[0].textContent === 'Products: Ecosystem');
|
|
96
|
+
expect(datalist.options[0].classList.contains('js-hide'));
|
|
97
|
+
expect(datalist.options[1].textContent === 'Learning: Guide');
|
|
98
|
+
expect(!datalist.options[1].classList.contains('js-hide'));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('filters datalist options using visible text before value', () => {
|
|
102
|
+
const datalist = createElement('datalist');
|
|
103
|
+
const visibleMatch = createElement('option', { value: 'hidden-value' }, 'Matching label');
|
|
104
|
+
const valueMatch = createElement('option', { value: 'value match' });
|
|
105
|
+
const noMatch = createElement('option', { value: 'elsewhere' }, 'Different label');
|
|
106
|
+
append(datalist, visibleMatch, valueMatch, noMatch);
|
|
107
|
+
|
|
108
|
+
filterDatalist(datalist, 'match');
|
|
109
|
+
|
|
110
|
+
expect(!visibleMatch.classList.contains('js-hide'));
|
|
111
|
+
expect(!valueMatch.classList.contains('js-hide'));
|
|
112
|
+
expect(noMatch.classList.contains('js-hide'));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('selects a datalist option, stores alternate values and dispatches selection details', () => {
|
|
116
|
+
const component = createElement('iam-search');
|
|
117
|
+
const input = createElement('input', { name: 'product' });
|
|
118
|
+
const datalist = createElement('datalist');
|
|
119
|
+
const inactiveOption = createElement('option', { value: 'inactive-id' }, 'Inactive');
|
|
120
|
+
const option = createElement('option', { dataUrl: '/products/alpha', value: 'alpha-id' }, 'Alpha');
|
|
121
|
+
let selectedDetail;
|
|
122
|
+
|
|
123
|
+
component.addEventListener('option-selected', (event) => {
|
|
124
|
+
selectedDetail = event.detail;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
append(datalist, inactiveOption, option);
|
|
128
|
+
append(component, input, datalist);
|
|
129
|
+
|
|
130
|
+
datalistSelectOption(component, input, option);
|
|
131
|
+
|
|
132
|
+
expect(input.value === 'Alpha');
|
|
133
|
+
expect(input.getAttribute('data-value') === 'Alpha');
|
|
134
|
+
expect(input.getAttribute('placeholder') === 'Alpha');
|
|
135
|
+
expect(component.innerHTML.includes('name="productAlt" value="alpha-id"'));
|
|
136
|
+
expect(option.classList.contains('active'));
|
|
137
|
+
expect(!inactiveOption.classList.contains('active'));
|
|
138
|
+
expect(selectedDetail.title === 'Alpha');
|
|
139
|
+
expect(selectedDetail.value === 'alpha-id');
|
|
140
|
+
expect(selectedDetail.url === '/products/alpha');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { resolvePath, isTraversable } from './helpers';
|
|
2
|
+
|
|
3
|
+
type SearchComponent = HTMLElement;
|
|
4
|
+
type SearchFormControl = HTMLInputElement | HTMLSelectElement;
|
|
5
|
+
type SearchResultItem = Record<string, unknown> | string | number | boolean | null | undefined;
|
|
6
|
+
type SearchResponse = Record<string, unknown>;
|
|
7
|
+
type WindowWithControllers = Window & {
|
|
8
|
+
controller?: Record<string, AbortController>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
interface OptionSelectedDetail {
|
|
12
|
+
title: string;
|
|
13
|
+
value: string;
|
|
14
|
+
url: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
18
|
+
value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
19
|
+
|
|
20
|
+
const getResultValue = (item: SearchResultItem, key: string): unknown => (isRecord(item) ? item[key] : undefined);
|
|
21
|
+
|
|
22
|
+
const toOptionText = (value: unknown): string => String(value ?? '').replace('\n', ', ');
|
|
23
|
+
|
|
24
|
+
const appendDatalistOption = (
|
|
25
|
+
datalistElement: HTMLDataListElement,
|
|
26
|
+
item: SearchResultItem,
|
|
27
|
+
valueSchema: string,
|
|
28
|
+
displaySchema: string,
|
|
29
|
+
groupLabel = ''
|
|
30
|
+
): void => {
|
|
31
|
+
const resolvedValue = resolvePath(item, valueSchema, undefined);
|
|
32
|
+
const resolvedDisplay = resolvePath(item, displaySchema, undefined);
|
|
33
|
+
const fallbackValue = isTraversable(item) ? '' : item;
|
|
34
|
+
const actualValue =
|
|
35
|
+
resolvedValue ??
|
|
36
|
+
getResultValue(item, 'value') ??
|
|
37
|
+
getResultValue(item, 'id') ??
|
|
38
|
+
resolvedDisplay ??
|
|
39
|
+
getResultValue(item, 'title') ??
|
|
40
|
+
getResultValue(item, 'label') ??
|
|
41
|
+
fallbackValue;
|
|
42
|
+
const displayValue = toOptionText(
|
|
43
|
+
resolvedDisplay ?? getResultValue(item, 'title') ?? getResultValue(item, 'label') ?? actualValue
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (!displayValue) return;
|
|
47
|
+
|
|
48
|
+
const optionElement = document.createElement('option');
|
|
49
|
+
optionElement.value = String(actualValue);
|
|
50
|
+
optionElement.textContent = `${groupLabel}${displayValue}`;
|
|
51
|
+
datalistElement.appendChild(optionElement);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getFormControls = (component: SearchComponent): SearchFormControl[] =>
|
|
55
|
+
Array.from(component.querySelectorAll<SearchFormControl>('input,select'));
|
|
56
|
+
|
|
57
|
+
const getSearchSchema = (component: SearchComponent, attributeName: string, fallback: string): string =>
|
|
58
|
+
component.hasAttribute(attributeName) ? component.getAttribute(attributeName) || '' : fallback;
|
|
59
|
+
|
|
60
|
+
const search = async (
|
|
61
|
+
component: SearchComponent,
|
|
62
|
+
datalistElement: HTMLDataListElement,
|
|
63
|
+
searchTerm: string
|
|
64
|
+
): Promise<void> => {
|
|
65
|
+
let url = component.getAttribute('data-url');
|
|
66
|
+
|
|
67
|
+
if (!url) return;
|
|
68
|
+
|
|
69
|
+
const method = component.getAttribute('data-method') || 'GET';
|
|
70
|
+
const body: Record<string, string> = {};
|
|
71
|
+
const searchWindow = window as WindowWithControllers;
|
|
72
|
+
|
|
73
|
+
// Setup controller vars if not already set
|
|
74
|
+
if (!searchWindow.controller) searchWindow.controller = {};
|
|
75
|
+
|
|
76
|
+
// Abort if controller already present for this url
|
|
77
|
+
if (searchWindow.controller[url]) searchWindow.controller[url].abort();
|
|
78
|
+
|
|
79
|
+
// Create a new controller so it can be aborted if new fetch made
|
|
80
|
+
searchWindow.controller[url] = new AbortController();
|
|
81
|
+
const { signal } = searchWindow.controller[url];
|
|
82
|
+
|
|
83
|
+
const requestOptions: RequestInit = {
|
|
84
|
+
signal,
|
|
85
|
+
method,
|
|
86
|
+
headers: new Headers({
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
Accept: 'application/json',
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
if (method.toUpperCase() === 'GET') {
|
|
93
|
+
getFormControls(component).forEach((input) => {
|
|
94
|
+
const name = input.getAttribute('name');
|
|
95
|
+
const value = input.value;
|
|
96
|
+
|
|
97
|
+
if (name && value) {
|
|
98
|
+
url += `${url.includes('?') ? '&' : '?'}${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
getFormControls(component).forEach((input) => {
|
|
103
|
+
const name = input.getAttribute('name');
|
|
104
|
+
const value = input.value;
|
|
105
|
+
|
|
106
|
+
if (name && value) {
|
|
107
|
+
body[name] = value;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
requestOptions['body'] = JSON.stringify(body);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const response = await fetch(url, requestOptions);
|
|
116
|
+
const responseData = (await response.json()) as SearchResponse;
|
|
117
|
+
const loopSchema = getSearchSchema(component, 'data-schema', 'data');
|
|
118
|
+
const valueSchema = getSearchSchema(component, 'data-value-schema', 'value');
|
|
119
|
+
const displaySchema = getSearchSchema(component, 'data-display-schema', 'label');
|
|
120
|
+
const loopValues = resolvePath(responseData, loopSchema, []);
|
|
121
|
+
|
|
122
|
+
if (Array.isArray(loopValues)) {
|
|
123
|
+
loopValues.forEach((item: SearchResultItem) => {
|
|
124
|
+
appendDatalistOption(datalistElement, item, valueSchema, displaySchema);
|
|
125
|
+
});
|
|
126
|
+
} else if (isRecord(loopValues)) {
|
|
127
|
+
Object.entries(loopValues).forEach(([key, value]) => {
|
|
128
|
+
if (Array.isArray(value)) {
|
|
129
|
+
value.forEach((item: SearchResultItem) => {
|
|
130
|
+
appendDatalistOption(datalistElement, item, valueSchema, displaySchema, `${key}: `);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
filterDatalist(datalistElement, searchTerm);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.log(error);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const filterDatalist = (datalistElement: HTMLDataListElement, searchTerm: string): void => {
|
|
143
|
+
for (const optionElement of datalistElement.options) {
|
|
144
|
+
const optionText = optionElement.textContent?.trim() || optionElement.value;
|
|
145
|
+
if (optionText.toLowerCase().includes(searchTerm.toLowerCase())) {
|
|
146
|
+
optionElement.classList.remove('js-hide');
|
|
147
|
+
} else {
|
|
148
|
+
optionElement.classList.add('js-hide');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const datalistSelectOption = (
|
|
154
|
+
component: SearchComponent,
|
|
155
|
+
inputElement: HTMLInputElement,
|
|
156
|
+
optionElement: HTMLOptionElement
|
|
157
|
+
): void => {
|
|
158
|
+
const datalistElement = optionElement.closest<HTMLDataListElement>('datalist');
|
|
159
|
+
const optionText = optionElement.textContent?.trim() || optionElement.value;
|
|
160
|
+
const inputName = inputElement.getAttribute('name') || '';
|
|
161
|
+
const alternateInputName = `${inputName}Alt`;
|
|
162
|
+
|
|
163
|
+
inputElement.value = optionText;
|
|
164
|
+
inputElement.setAttribute('data-value', optionText);
|
|
165
|
+
//inputElement.setAttribute('data-placeholder', optionText);
|
|
166
|
+
inputElement.setAttribute('placeholder', optionText);
|
|
167
|
+
|
|
168
|
+
// Make sure the value of the option is passed when in a form
|
|
169
|
+
if (optionElement.value && optionElement.value !== optionText) {
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
const alternateInput = component.querySelector<HTMLInputElement>(`input[name="${alternateInputName}"]`);
|
|
173
|
+
|
|
174
|
+
if (!alternateInput)
|
|
175
|
+
component.insertAdjacentHTML(
|
|
176
|
+
'beforeend',
|
|
177
|
+
`<input type="hidden" name="${alternateInputName}" value="${optionElement.value}">`
|
|
178
|
+
);
|
|
179
|
+
else alternateInput.value = optionElement.value;
|
|
180
|
+
} else {
|
|
181
|
+
|
|
182
|
+
const alternateInput = component.querySelector<HTMLInputElement>(`input[name="${alternateInputName}"]`);
|
|
183
|
+
|
|
184
|
+
if (alternateInput) alternateInput.remove();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Set the active value on the datalist option
|
|
188
|
+
if (!datalistElement) return;
|
|
189
|
+
for (const optionLoopElement of datalistElement.options) {
|
|
190
|
+
if (optionLoopElement === optionElement) optionLoopElement.classList.add('active');
|
|
191
|
+
else optionLoopElement.classList.remove('active');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
const customEvent = new CustomEvent<OptionSelectedDetail>('option-selected', {
|
|
196
|
+
detail: {
|
|
197
|
+
title: optionText,
|
|
198
|
+
value: optionElement.value || '',
|
|
199
|
+
url: optionElement.hasAttribute('data-url') ? optionElement.getAttribute('data-url') || '' : '',
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
component.dispatchEvent(customEvent);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export default search;
|