From 0c7a5321fe0370dab0c01a19cf7c510481aba2c8 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Mon, 25 Aug 2025 19:09:07 +0200 Subject: [PATCH 1/7] 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 55e51c299e19ad9e345a8f5caad0d5685b38c985 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Mon, 25 Aug 2025 19:23:15 +0200 Subject: [PATCH 2/7] 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 14e66777faa916eacd4693f2fdbe9f7393c209aa Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:35:44 +0200 Subject: [PATCH 3/7] 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 5fba3a1852f5f6cd0138f997b7833b481ede6f7a Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:37:55 +0200 Subject: [PATCH 4/7] 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 e7f8571870b60449993993d317534655a5e0d4e4 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:42:41 +0200 Subject: [PATCH 5/7] 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 69bf1588..fe082d8e 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,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", @@ -74,4 +74,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 0aba4b2d33c8852b1c7f28339e1c4dd74108ada2 Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:44:24 +0200 Subject: [PATCH 6/7] rollback package json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fe082d8e..d1c9a583 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,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 85027e200457caf3dca6a71df429e1f24510ca4e Mon Sep 17 00:00:00 2001 From: Sergey Kuchmistov Date: Tue, 26 Aug 2025 13:46:53 +0200 Subject: [PATCH 7/7] Fix package.json formatting