From d42c5bdd1bf5ee41b0dc87437067339a25434feb Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Mon, 25 Aug 2025 19:09:07 +0200 Subject: [PATCH 01/15] fix: formdata is not updating when options populated dynamically --- packages/demo/src/examples/example01.html | 14 ++++++++------ packages/demo/src/examples/example08.html | 10 ++++++++++ packages/demo/src/examples/example08.ts | 21 +++++++++++++++++++++ playwright/e2e/example08.spec.ts | 14 ++++++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/packages/demo/src/examples/example01.html b/packages/demo/src/examples/example01.html index b0d5d9d0..9116ba55 100644 --- a/packages/demo/src/examples/example01.html +++ b/packages/demo/src/examples/example01.html @@ -39,12 +39,14 @@

- +
+ +
diff --git a/packages/demo/src/examples/example08.html b/packages/demo/src/examples/example08.html index b81ed0ad..514724e5 100644 --- a/packages/demo/src/examples/example08.html +++ b/packages/demo/src/examples/example08.html @@ -63,4 +63,14 @@

+ +
+ + +
+
+ +
+
+
diff --git a/packages/demo/src/examples/example08.ts b/packages/demo/src/examples/example08.ts index 4b58f94c..d76e8edc 100644 --- a/packages/demo/src/examples/example08.ts +++ b/packages/demo/src/examples/example08.ts @@ -6,6 +6,7 @@ export default class Example { ms3?: MultipleSelectInstance; ms4?: MultipleSelectInstance; ms5?: MultipleSelectInstance; + ms6?: MultipleSelectInstance; mount() { this.ms1 = multipleSelect('#basic', { @@ -157,6 +158,24 @@ export default class Example { }, ], }) as MultipleSelectInstance; + + this.ms6 = multipleSelect('#withform', { + dataTest: 'select6', + data: [ + { + text: 'June', + value: 6, + }, + { + text: 'July', + value: 7, + }, + { + text: 'August', + value: 8, + }, + ], + }) as MultipleSelectInstance; } unmount() { @@ -166,10 +185,12 @@ export default class Example { this.ms3?.destroy(); this.ms4?.destroy(); this.ms5?.destroy(); + this.ms6?.destroy(); this.ms1 = undefined; this.ms2 = undefined; this.ms3 = undefined; this.ms4 = undefined; this.ms5 = undefined; + this.ms6 = undefined; } } diff --git a/playwright/e2e/example08.spec.ts b/playwright/e2e/example08.spec.ts index 7eca0e5f..177266ec 100644 --- a/playwright/e2e/example08.spec.ts +++ b/playwright/e2e/example08.spec.ts @@ -43,4 +43,18 @@ test.describe('Example 08 - Data Property', () => { await page.getByRole('option', { name: 'Group 1' }).click(); await page.getByRole('button', { name: '11 of 12 selected' }).click(); }); + + test('formdata should be updated after select', async({ page }) => { + await page.goto('#/example08'); + await page.locator('div[data-test=select6].ms-parent').click(); + await page.getByRole('option', { name: 'July' }).click(); + + const selectedItemValue = await page.evaluate(() => { + const form = document.getElementById('form'); + const formData = new FormData(form); + return formData.get('select6') + }) + + expect(selectedItemValue).toBe(7) + }); }); From 441cadd81b722a160441611bce04d8206097afe6 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Mon, 25 Aug 2025 19:23:15 +0200 Subject: [PATCH 02/15] remove unused changes --- packages/demo/src/examples/example01.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/demo/src/examples/example01.html b/packages/demo/src/examples/example01.html index 9116ba55..b0d5d9d0 100644 --- a/packages/demo/src/examples/example01.html +++ b/packages/demo/src/examples/example01.html @@ -39,14 +39,12 @@

-
- -
+
From faaa8e64a053289ebb23bee958d7a4101e450336 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:35:44 +0200 Subject: [PATCH 03/15] Add options (and optgroups) when select built not from html --- packages/demo/src/examples/example08.html | 16 ++++++++-- packages/demo/src/examples/example08.ts | 22 ++++++++++++- .../src/MultipleSelectInstance.ts | 32 ++++++++++++++++++- playwright/e2e/example08.spec.ts | 23 ++++++++++--- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/packages/demo/src/examples/example08.html b/packages/demo/src/examples/example08.html index 514724e5..5155bb93 100644 --- a/packages/demo/src/examples/example08.html +++ b/packages/demo/src/examples/example08.html @@ -65,11 +65,21 @@

- +
-
- + + +
+
+
+ +
+ + +
+
+
diff --git a/packages/demo/src/examples/example08.ts b/packages/demo/src/examples/example08.ts index d76e8edc..d7481c81 100644 --- a/packages/demo/src/examples/example08.ts +++ b/packages/demo/src/examples/example08.ts @@ -159,7 +159,7 @@ export default class Example { ], }) as MultipleSelectInstance; - this.ms6 = multipleSelect('#withform', { + this.ms6 = multipleSelect('#single-form', { dataTest: 'select6', data: [ { @@ -176,6 +176,24 @@ export default class Example { }, ], }) as MultipleSelectInstance; + + this.ms7 = multipleSelect('#multiple-form', { + dataTest: 'select7', + data: [ + { + text: 'June', + value: 6, + }, + { + text: 'July', + value: 7, + }, + { + text: 'August', + value: 8, + }, + ], + }) as MultipleSelectInstance; } unmount() { @@ -186,11 +204,13 @@ export default class Example { this.ms4?.destroy(); this.ms5?.destroy(); this.ms6?.destroy(); + this.ms7?.destroy(); this.ms1 = undefined; this.ms2 = undefined; this.ms3 = undefined; this.ms4 = undefined; this.ms5 = undefined; this.ms6 = undefined; + this.ms7 = undefined; } } diff --git a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts index 43006d53..e286b33d 100644 --- a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts +++ b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts @@ -335,9 +335,38 @@ export class MultipleSelectInstance { this.data = this.data.sort(this.options.preSort); } + if (!this.fromHtml) { + this.initHtmlRows(); + } + this.dataTotal = setDataKeys(this.data || []); } + protected initHtmlRows() { + this.elm.innerHTML = ''; + if (!this.data) return + this.data.forEach((it: OptGroupRowData | OptionRowData) => { + console.log({ it }) + if (it.type == 'optgroup') { + const optgroup = document.createElement('optgroup'); + optgroup.label = it.label; + (it as OptGroupRowData).children.forEach((opt: OptionRowData) => { + this.buildOption(optgroup, opt); + }) + this.elm.appendChild(optgroup); + } else { + this.buildOption(this.elm, (it as OptionRowData)); + } + }); + } + + protected buildOption(parent: HTMLElement, it: OptionRowData) { + const option = document.createElement('option'); + option.value = it.value.toString(); + option.text = it.text; + parent.appendChild(option); + } + protected initRow(elm: HTMLOptionElement, groupDisabled?: boolean) { const row = {} as OptionRowData | OptGroupRowData; if (elm.tagName?.toLowerCase() === 'option') { @@ -1243,6 +1272,7 @@ export class MultipleSelectInstance { isLazyProcess = true; this.dropElm?.querySelector('ul.ms-list')?.remove(); this.options.lazyData().then(data => { + this.fromHtml = false; // when data is ready, remove spinner & update dropdown and selection this.options.data = data; this._isLazyLoaded = true; @@ -1553,7 +1583,7 @@ export class MultipleSelectInstance { } else { // when multiple values could be set, we need to loop through each Array.from(this.elm.options).forEach(option => { - option.selected = selectedValues.some(val => val === option.value); + option.selected = selectedValues.some(val => val.toString() === option.value); }); } diff --git a/playwright/e2e/example08.spec.ts b/playwright/e2e/example08.spec.ts index 177266ec..d37e624c 100644 --- a/playwright/e2e/example08.spec.ts +++ b/playwright/e2e/example08.spec.ts @@ -44,17 +44,32 @@ test.describe('Example 08 - Data Property', () => { await page.getByRole('button', { name: '11 of 12 selected' }).click(); }); - test('formdata should be updated after select', async({ page }) => { + test('formdata should be updated after select for regular select', async({ page }) => { await page.goto('#/example08'); await page.locator('div[data-test=select6].ms-parent').click(); await page.getByRole('option', { name: 'July' }).click(); const selectedItemValue = await page.evaluate(() => { - const form = document.getElementById('form'); + const form = document.getElementById('form1'); const formData = new FormData(form); - return formData.get('select6') + return formData.get('select6'); }) - expect(selectedItemValue).toBe(7) + expect(selectedItemValue).toBe("7"); + }); + + test('formdata should be updated after select for multiple select', async({ page }) => { + await page.goto('#/example08'); + await page.locator('div[data-test=select7].ms-parent').click(); + await page.getByRole('option', { name: 'July' }).click(); + await page.getByRole('option', { name: 'August' }).click(); + + const selectedItemValue = await page.evaluate(() => { + const form = document.getElementById('form2'); + const formData = new FormData(form); + return formData.getAll('select7'); + }) + + expect(selectedItemValue).toEqual(["7", "8"]); }); }); From 84f478cc3a8ac5633ed0b122d4dba34961f16934 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:37:55 +0200 Subject: [PATCH 04/15] linter fix --- packages/multiple-select-vanilla/src/MultipleSelectInstance.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts index e286b33d..e08983f3 100644 --- a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts +++ b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts @@ -346,8 +346,7 @@ export class MultipleSelectInstance { this.elm.innerHTML = ''; if (!this.data) return this.data.forEach((it: OptGroupRowData | OptionRowData) => { - console.log({ it }) - if (it.type == 'optgroup') { + if (it.type === 'optgroup') { const optgroup = document.createElement('optgroup'); optgroup.label = it.label; (it as OptGroupRowData).children.forEach((opt: OptionRowData) => { From d9ee5177c1d1104f6996e177b57112d367f30bbe Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:42:41 +0200 Subject: [PATCH 05/15] small lint/format/ts fixes --- package.json | 4 ++-- packages/demo/src/examples/example08.ts | 1 + .../multiple-select-vanilla/src/MultipleSelectInstance.ts | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e82a006e..64b1d7bf 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "predev": "pnpm -r dev:init", "dev": "run-p dev:watch build:watch --npm-path pnpm", "dev:watch": "pnpm -r --parallel --stream dev", - "build:watch": "lerna watch --no-bail --file-delimiter=\",\" --glob=\"src/**/*.{ts,scss}\" -- cross-env-shell pnpm -r --filter $LERNA_PACKAGE_NAME build:watch --files=$LERNA_FILE_CHANGES", + "build:watch": "lerna watch --no-bail --file-delimiter=\",\" --glob=\"src/**/*.{ts,scss}\" -- cross-env-shell pnpm -r --filter \"$LERNA_PACKAGE_NAME\" run build:watch -- --files=\"$LERNA_FILE_CHANGES\"", "dev:demo": "pnpm -r --stream --filter=\"{packages/demo/**}\" dev", "dev:lib": "pnpm -r --stream --filter=\"{packages/multiple-select-vanilla/**}\" dev", "biome:lint:check": "biome lint ./packages", @@ -73,4 +73,4 @@ "typescript": "catalog:" }, "packageManager": "pnpm@10.10.0" -} \ No newline at end of file +} diff --git a/packages/demo/src/examples/example08.ts b/packages/demo/src/examples/example08.ts index d7481c81..8c2c976d 100644 --- a/packages/demo/src/examples/example08.ts +++ b/packages/demo/src/examples/example08.ts @@ -7,6 +7,7 @@ export default class Example { ms4?: MultipleSelectInstance; ms5?: MultipleSelectInstance; ms6?: MultipleSelectInstance; + ms7?: MultipleSelectInstance; mount() { this.ms1 = multipleSelect('#basic', { diff --git a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts index e08983f3..17c9edb4 100644 --- a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts +++ b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts @@ -344,17 +344,17 @@ export class MultipleSelectInstance { protected initHtmlRows() { this.elm.innerHTML = ''; - if (!this.data) return + if (!this.data) return; this.data.forEach((it: OptGroupRowData | OptionRowData) => { if (it.type === 'optgroup') { const optgroup = document.createElement('optgroup'); - optgroup.label = it.label; + optgroup.label = (it as OptGroupRowData).label; (it as OptGroupRowData).children.forEach((opt: OptionRowData) => { this.buildOption(optgroup, opt); - }) + }); this.elm.appendChild(optgroup); } else { - this.buildOption(this.elm, (it as OptionRowData)); + this.buildOption(this.elm, it as OptionRowData); } }); } From aac5f4035773d992ff7def2b59ca01792d874393 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:44:24 +0200 Subject: [PATCH 06/15] rollback package json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 64b1d7bf..1a47a0d3 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "predev": "pnpm -r dev:init", "dev": "run-p dev:watch build:watch --npm-path pnpm", "dev:watch": "pnpm -r --parallel --stream dev", - "build:watch": "lerna watch --no-bail --file-delimiter=\",\" --glob=\"src/**/*.{ts,scss}\" -- cross-env-shell pnpm -r --filter \"$LERNA_PACKAGE_NAME\" run build:watch -- --files=\"$LERNA_FILE_CHANGES\"", + "build:watch": "lerna watch --no-bail --file-delimiter=\",\" --glob=\"src/**/*.{ts,scss}\" -- cross-env-shell pnpm -r --filter $LERNA_PACKAGE_NAME build:watch --files=$LERNA_FILE_CHANGES", "dev:demo": "pnpm -r --stream --filter=\"{packages/demo/**}\" dev", "dev:lib": "pnpm -r --stream --filter=\"{packages/multiple-select-vanilla/**}\" dev", "biome:lint:check": "biome lint ./packages", From dbe6f2be5d0a4e3bc8dd83965c08ce6958e6beb3 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:46:53 +0200 Subject: [PATCH 07/15] Fix package.json formatting From ddbc32c2ee2618ddf6d8e3221eb1d3b55633ef64 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 21 Oct 2025 17:34:28 +0200 Subject: [PATCH 08/15] create helpers + add example --- packages/demo/src/examples/example07.html | 13 ++++++-- packages/demo/src/examples/example07.ts | 11 ++++++- .../src/MultipleSelectInstance.ts | 27 +++------------- .../src/utils/domUtils.ts | 32 ++++++++++++++++++- 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/packages/demo/src/examples/example07.html b/packages/demo/src/examples/example07.html index d69bb902..095da4f0 100644 --- a/packages/demo/src/examples/example07.html +++ b/packages/demo/src/examples/example07.html @@ -29,7 +29,7 @@

- @@ -42,7 +42,7 @@

- @@ -51,6 +51,15 @@

+
+ + +
+ +
+
+
diff --git a/packages/demo/src/examples/example07.ts b/packages/demo/src/examples/example07.ts index 91ffdbe3..0be8d39a 100644 --- a/packages/demo/src/examples/example07.ts +++ b/packages/demo/src/examples/example07.ts @@ -5,7 +5,14 @@ export default class Example { ms: MultipleSelectInstance[] = []; mount() { - this.ms = multipleSelect('select') as MultipleSelectInstance[]; + this.ms = multipleSelect('#select1, #select2') as MultipleSelectInstance[]; + this.ms3 = multipleSelect('#select3', { + lazyData: () => { + return new Promise(resolve => { + resolve({ '1': 'First', '2': 'Second', '3': 'Third', '4': 'Fourth', '5': 'Fifth'}) + }) + } + }); this.btnElm = document.querySelector('.submit7'); this.btnElm!.addEventListener('click', this.clickListener); } @@ -16,6 +23,8 @@ export default class Example { // destroy ms instance(s) to avoid DOM leaks this.ms.forEach(m => m.destroy()); this.ms = []; + this.ms3.destroy(); + this.ms = undefined; } clickListener = () => { diff --git a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts index 17c9edb4..e2552e30 100644 --- a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts +++ b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts @@ -13,6 +13,7 @@ import { classNameToList, convertItemRowToHtml, createDomElement, + createDomStructureFromData, emptyElement, findParent, getComputedSize, @@ -336,7 +337,7 @@ export class MultipleSelectInstance { } if (!this.fromHtml) { - this.initHtmlRows(); + // this.initHtmlRows(); } this.dataTotal = setDataKeys(this.data || []); @@ -345,25 +346,7 @@ export class MultipleSelectInstance { protected initHtmlRows() { this.elm.innerHTML = ''; if (!this.data) return; - this.data.forEach((it: OptGroupRowData | OptionRowData) => { - if (it.type === 'optgroup') { - const optgroup = document.createElement('optgroup'); - optgroup.label = (it as OptGroupRowData).label; - (it as OptGroupRowData).children.forEach((opt: OptionRowData) => { - this.buildOption(optgroup, opt); - }); - this.elm.appendChild(optgroup); - } else { - this.buildOption(this.elm, it as OptionRowData); - } - }); - } - - protected buildOption(parent: HTMLElement, it: OptionRowData) { - const option = document.createElement('option'); - option.value = it.value.toString(); - option.text = it.text; - parent.appendChild(option); + return createDomStructureFromData(this.data, this.elm); } protected initRow(elm: HTMLOptionElement, groupDisabled?: boolean) { @@ -1271,10 +1254,10 @@ export class MultipleSelectInstance { isLazyProcess = true; this.dropElm?.querySelector('ul.ms-list')?.remove(); this.options.lazyData().then(data => { - this.fromHtml = false; // when data is ready, remove spinner & update dropdown and selection this.options.data = data; this._isLazyLoaded = true; + this.fromHtml = false; this.dropElm?.querySelector('.ms-loading')?.remove(); this.initData(); this.initList(true); @@ -1582,7 +1565,7 @@ export class MultipleSelectInstance { } else { // when multiple values could be set, we need to loop through each Array.from(this.elm.options).forEach(option => { - option.selected = selectedValues.some(val => val.toString() === option.value); + option.selected = selectedValues.some(val => val().toString() === option.value); }); } diff --git a/packages/multiple-select-vanilla/src/utils/domUtils.ts b/packages/multiple-select-vanilla/src/utils/domUtils.ts index 7a0e6eb3..7fa0d3b2 100644 --- a/packages/multiple-select-vanilla/src/utils/domUtils.ts +++ b/packages/multiple-select-vanilla/src/utils/domUtils.ts @@ -1,4 +1,4 @@ -import type { HtmlStruct, InferDOMType } from '../models/interfaces.js'; +import type { HtmlStruct, InferDOMType, OptGroupRowData, OptionRowData } from '../models/interfaces.js'; import { isDefined, objectRemoveEmptyProps } from './utils.js'; export interface HtmlElementPosition { @@ -102,6 +102,36 @@ export function createDomStructure(item: HtmlStruct, appendToElm?: HTMLElement, return elm; } + +/** + * Create html node with optgroups and options from data + * @param data - array of options and/or optgroups + * @param parent - parent element to append to + * @return {object} element - updated element + */ +export function createDomStructureFromData(data: Array, parent: HTMLElement): HTMLElement { + data.forEach(row => { + if (row.type === 'optgroup') { + const optgroup = createDomElement('optgroup', { label: (row as OptGroupRowData).label }, parent); + if ((row as OptGroupRowData).children) { + createDomStructureFromData((row as OptGroupRowData).children, optgroup); + } + } else { + const optionProps: any = { + value: row.value, + disabled: row.disabled || false, + selected: row.selected || false + }; + if (row.classes) { + optionProps.className = row.classes; + } + const option = createDomElement('option', optionProps, parent); + option.textContent = (row as OptionRowData).text; + } + }); + return parent; +} + /** takes an html block object and converts to a real HTMLElement */ export function convertItemRowToHtml(item: HtmlStruct): HTMLElement { if (item.hasOwnProperty('tagName')) { From a2b5c0bb8d1d007fb46fc2a2b98ec12254be99cd Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 21 Oct 2025 17:38:27 +0200 Subject: [PATCH 09/15] formatter updates --- packages/demo/src/examples/example07.html | 3 +-- packages/demo/src/examples/example07.ts | 6 +++--- .../multiple-select-vanilla/src/MultipleSelectInstance.ts | 2 +- packages/multiple-select-vanilla/src/utils/domUtils.ts | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/demo/src/examples/example07.html b/packages/demo/src/examples/example07.html index 095da4f0..b793ef9a 100644 --- a/packages/demo/src/examples/example07.html +++ b/packages/demo/src/examples/example07.html @@ -55,8 +55,7 @@

- +

diff --git a/packages/demo/src/examples/example07.ts b/packages/demo/src/examples/example07.ts index 0be8d39a..6b0af806 100644 --- a/packages/demo/src/examples/example07.ts +++ b/packages/demo/src/examples/example07.ts @@ -9,9 +9,9 @@ export default class Example { this.ms3 = multipleSelect('#select3', { lazyData: () => { return new Promise(resolve => { - resolve({ '1': 'First', '2': 'Second', '3': 'Third', '4': 'Fourth', '5': 'Fifth'}) - }) - } + resolve({ '1': 'First', '2': 'Second', '3': 'Third', '4': 'Fourth', '5': 'Fifth' }); + }); + }, }); this.btnElm = document.querySelector('.submit7'); this.btnElm!.addEventListener('click', this.clickListener); diff --git a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts index e2552e30..dd7618f2 100644 --- a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts +++ b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts @@ -1565,7 +1565,7 @@ export class MultipleSelectInstance { } else { // when multiple values could be set, we need to loop through each Array.from(this.elm.options).forEach(option => { - option.selected = selectedValues.some(val => val().toString() === option.value); + option.selected = selectedValues.some(val => val() === option.value); }); } diff --git a/packages/multiple-select-vanilla/src/utils/domUtils.ts b/packages/multiple-select-vanilla/src/utils/domUtils.ts index 7fa0d3b2..75102b42 100644 --- a/packages/multiple-select-vanilla/src/utils/domUtils.ts +++ b/packages/multiple-select-vanilla/src/utils/domUtils.ts @@ -102,7 +102,6 @@ export function createDomStructure(item: HtmlStruct, appendToElm?: HTMLElement, return elm; } - /** * Create html node with optgroups and options from data * @param data - array of options and/or optgroups @@ -120,7 +119,7 @@ export function createDomStructureFromData(data: Array Date: Tue, 21 Oct 2025 17:41:50 +0200 Subject: [PATCH 10/15] fix run --- .../multiple-select-vanilla/src/MultipleSelectInstance.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts index dd7618f2..8303dae8 100644 --- a/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts +++ b/packages/multiple-select-vanilla/src/MultipleSelectInstance.ts @@ -337,7 +337,7 @@ export class MultipleSelectInstance { } if (!this.fromHtml) { - // this.initHtmlRows(); + this.initHtmlRows(); } this.dataTotal = setDataKeys(this.data || []); @@ -1565,7 +1565,7 @@ export class MultipleSelectInstance { } else { // when multiple values could be set, we need to loop through each Array.from(this.elm.options).forEach(option => { - option.selected = selectedValues.some(val => val() === option.value); + option.selected = selectedValues.some(val => val.toString() === option.value); }); } From 9447fbce450d58961fd6883c23ba33011a111d90 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 21 Oct 2025 18:04:18 +0200 Subject: [PATCH 11/15] update types on example page --- packages/demo/src/examples/example07.ts | 7 ++++--- playwright/e2e/example07.spec.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/demo/src/examples/example07.ts b/packages/demo/src/examples/example07.ts index 6b0af806..0a6921dc 100644 --- a/packages/demo/src/examples/example07.ts +++ b/packages/demo/src/examples/example07.ts @@ -3,6 +3,7 @@ import { type MultipleSelectInstance, multipleSelect } from 'multiple-select-van export default class Example { btnElm?: HTMLButtonElement | null; ms: MultipleSelectInstance[] = []; + ms3?: MultipleSelectInstance; mount() { this.ms = multipleSelect('#select1, #select2') as MultipleSelectInstance[]; @@ -12,7 +13,7 @@ export default class Example { resolve({ '1': 'First', '2': 'Second', '3': 'Third', '4': 'Fourth', '5': 'Fifth' }); }); }, - }); + }) as MultipleSelectInstance; this.btnElm = document.querySelector('.submit7'); this.btnElm!.addEventListener('click', this.clickListener); } @@ -23,8 +24,8 @@ export default class Example { // destroy ms instance(s) to avoid DOM leaks this.ms.forEach(m => m.destroy()); this.ms = []; - this.ms3.destroy(); - this.ms = undefined; + this.ms3?.destroy(); + this.ms3 = undefined; } clickListener = () => { diff --git a/playwright/e2e/example07.spec.ts b/playwright/e2e/example07.spec.ts index 39d23bf4..2522da16 100644 --- a/playwright/e2e/example07.spec.ts +++ b/playwright/e2e/example07.spec.ts @@ -21,10 +21,12 @@ test.describe('Example 07 - Submit Data', () => { let dialogText = ''; page.on('dialog', async (alert) => { dialogText = alert.message(); + console.log('closing') await alert.dismiss(); }); await page.goto('#/example07'); + await page.locator('[data-test=select2].ms-parent').click(); await page.getByRole('option').filter({ hasText: 'Third' }).locator('span').click(); await page.getByRole('option').filter({ hasText: 'Fourth' }).locator('span').click(); @@ -41,5 +43,13 @@ test.describe('Example 07 - Submit Data', () => { await page.locator('[data-test=select2].ms-parent').click(); await page.getByTestId('submit').click(); await expect(dialogText).toBe('select1=1&select2=1&select2=2'); + + // select lazy loaded data + await page.waitForTimeout(1); + await page.locator('[data-test=select3].ms-parent').click(); + await page.getByRole('option').filter({ hasText: 'First' }).locator('span').click(); + await page.locator('[data-test=select3].ms-parent').click(); + await page.getByTestId('submit').click(); + await expect(dialogText).toBe('select1=1&select2=1&select2=2&select3=1'); }); }); From a585169cfc79ed2bad173181e0b201e2f1ffa23e Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 21 Oct 2025 18:13:46 +0200 Subject: [PATCH 12/15] update tests --- playwright/e2e/example07.spec.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/playwright/e2e/example07.spec.ts b/playwright/e2e/example07.spec.ts index 2522da16..ce1c66d7 100644 --- a/playwright/e2e/example07.spec.ts +++ b/playwright/e2e/example07.spec.ts @@ -21,7 +21,6 @@ test.describe('Example 07 - Submit Data', () => { let dialogText = ''; page.on('dialog', async (alert) => { dialogText = alert.message(); - console.log('closing') await alert.dismiss(); }); @@ -44,12 +43,24 @@ test.describe('Example 07 - Submit Data', () => { await page.getByTestId('submit').click(); await expect(dialogText).toBe('select1=1&select2=1&select2=2'); - // select lazy loaded data + }); +}); + +test('submit form with multiple select populated via lazy load', async ({ + page +}) => { + let dialogText = ''; + page.on('dialog', async (alert) => { + dialogText = alert.message(); + await alert.dismiss(); + }); + + await page.goto('#/example07'); await page.waitForTimeout(1); + + // select lazy loaded data await page.locator('[data-test=select3].ms-parent').click(); await page.getByRole('option').filter({ hasText: 'First' }).locator('span').click(); - await page.locator('[data-test=select3].ms-parent').click(); await page.getByTestId('submit').click(); - await expect(dialogText).toBe('select1=1&select2=1&select2=2&select3=1'); - }); + await expect(dialogText).toBe('select1=1&select3=1'); }); From a9953a67fbbdc496f32a773868c8e42ac83b3e68 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 21 Oct 2025 18:24:48 +0200 Subject: [PATCH 13/15] update styles --- playwright/e2e/example08.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playwright/e2e/example08.spec.ts b/playwright/e2e/example08.spec.ts index d37e624c..10c1a4da 100644 --- a/playwright/e2e/example08.spec.ts +++ b/playwright/e2e/example08.spec.ts @@ -53,7 +53,7 @@ test.describe('Example 08 - Data Property', () => { const form = document.getElementById('form1'); const formData = new FormData(form); return formData.get('select6'); - }) + }); expect(selectedItemValue).toBe("7"); }); @@ -68,7 +68,7 @@ test.describe('Example 08 - Data Property', () => { const form = document.getElementById('form2'); const formData = new FormData(form); return formData.getAll('select7'); - }) + }); expect(selectedItemValue).toEqual(["7", "8"]); }); From 6254d76e0f8cf5f43b01aed72821e8d35f2fe838 Mon Sep 17 00:00:00 2001 From: "Ghislain B." Date: Tue, 21 Oct 2025 13:16:12 -0400 Subject: [PATCH 14/15] chore: commit copilot suggestion Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- playwright/e2e/example07.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/e2e/example07.spec.ts b/playwright/e2e/example07.spec.ts index ce1c66d7..de4e3d6f 100644 --- a/playwright/e2e/example07.spec.ts +++ b/playwright/e2e/example07.spec.ts @@ -56,7 +56,7 @@ test('submit form with multiple select populated via lazy load', async ({ }); await page.goto('#/example07'); - await page.waitForTimeout(1); + await page.waitForSelector('[data-test=select3] .ms-options .ms-option span:text("First")'); // select lazy loaded data await page.locator('[data-test=select3].ms-parent').click(); From ee7cd4ce9739309900b39d4220ea21bb08b5ce50 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Wed, 22 Oct 2025 07:32:32 +0200 Subject: [PATCH 15/15] Revert "chore: commit copilot suggestion" This reverts commit 6254d76e0f8cf5f43b01aed72821e8d35f2fe838. --- playwright/e2e/example07.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/e2e/example07.spec.ts b/playwright/e2e/example07.spec.ts index de4e3d6f..ce1c66d7 100644 --- a/playwright/e2e/example07.spec.ts +++ b/playwright/e2e/example07.spec.ts @@ -56,7 +56,7 @@ test('submit form with multiple select populated via lazy load', async ({ }); await page.goto('#/example07'); - await page.waitForSelector('[data-test=select3] .ms-options .ms-option span:text("First")'); + await page.waitForTimeout(1); // select lazy loaded data await page.locator('[data-test=select3].ms-parent').click();