diff --git a/src/LiveComponent/assets/dist/live_controller.js b/src/LiveComponent/assets/dist/live_controller.js index 251d1d81a19..36ca1f70d2e 100644 --- a/src/LiveComponent/assets/dist/live_controller.js +++ b/src/LiveComponent/assets/dist/live_controller.js @@ -311,18 +311,19 @@ function getElementAsTagText(element) { : element.outerHTML; } const getMultipleCheckboxValue = function (element, currentValues) { + const finalValues = [...currentValues]; const value = inputValue(element); const index = currentValues.indexOf(value); if (element.checked) { if (index === -1) { - currentValues.push(value); + finalValues.push(value); } - return currentValues; + return finalValues; } if (index > -1) { - currentValues.splice(index, 1); + finalValues.splice(index, 1); } - return currentValues; + return finalValues; }; const inputValue = function (element) { return element.dataset.value ? element.dataset.value : element.value; diff --git a/src/LiveComponent/assets/src/dom_utils.ts b/src/LiveComponent/assets/src/dom_utils.ts index 096c5321cca..f4994aa441f 100644 --- a/src/LiveComponent/assets/src/dom_utils.ts +++ b/src/LiveComponent/assets/src/dom_utils.ts @@ -266,24 +266,25 @@ export function getElementAsTagText(element: HTMLElement): string { } const getMultipleCheckboxValue = function (element: HTMLInputElement, currentValues: Array): Array { + const finalValues = [...currentValues]; const value = inputValue(element); const index = currentValues.indexOf(value); if (element.checked) { // Add value to an array if it's not in it already if (index === -1) { - currentValues.push(value); + finalValues.push(value); } - return currentValues; + return finalValues; } // Remove value from an array if (index > -1) { - currentValues.splice(index, 1); + finalValues.splice(index, 1); } - return currentValues; + return finalValues; }; const inputValue = function (element: HTMLInputElement): string { diff --git a/src/LiveComponent/assets/test/controller/model.test.ts b/src/LiveComponent/assets/test/controller/model.test.ts index 426d6dfe1a8..a68f8607351 100644 --- a/src/LiveComponent/assets/test/controller/model.test.ts +++ b/src/LiveComponent/assets/test/controller/model.test.ts @@ -335,6 +335,38 @@ describe('LiveController data-model Tests', () => { expect(test.component.valueStore.getOriginalProps()).toEqual({form: {check: ['foo', 'bar']}}); }); + it('sends correct data for array valued checkbox fields with non-form object', async () => { + const test = await createTest({ check: [] }, (data: any) => ` +
+
+ + + +
+ + Checkbox 2 is ${data.check.indexOf('bar') > -1 ? 'checked' : 'unchecked' } +
+ `); + + const check1Element = getByLabelText(test.element, 'Checkbox 1:'); + const check2Element = getByLabelText(test.element, 'Checkbox 2:'); + + // only 1 Ajax call will be made thanks to debouncing + test.expectsAjaxCall() + .expectUpdatedData({ 'check': ['foo', 'bar'] }); + + await userEvent.click(check1Element); + await userEvent.click(check2Element); + + await waitFor(() => expect(test.element).toHaveTextContent('Checkbox 2 is checked')); + + expect(test.component.valueStore.getOriginalProps()).toEqual({check: ['foo', 'bar']}); + }); + it('sends correct data for array valued checkbox fields with initial data', async () => { const test = await createTest({ form: { check: ['foo']} }, (data: any) => `
@@ -367,6 +399,36 @@ describe('LiveController data-model Tests', () => { expect(test.component.valueStore.getOriginalProps()).toEqual({form: {check: ['bar']}}); }); + it('sends correct data for array valued checkbox fields with non-form object and with initial data', async () => { + const test = await createTest({ check: ['foo'] }, (data: any) => ` +
+ + + + + Checkbox 1 is ${data.check.indexOf('foo') > -1 ? 'checked' : 'unchecked' } +
+ `); + + const check1Element = getByLabelText(test.element, 'Checkbox 1:'); + const check2Element = getByLabelText(test.element, 'Checkbox 2:'); + + // only 1 Ajax call will be made thanks to debouncing + test.expectsAjaxCall() + .expectUpdatedData({ 'check': ['bar'] }); + + await userEvent.click(check1Element); + await userEvent.click(check2Element); + + await waitFor(() => expect(test.element).toHaveTextContent('Checkbox 1 is unchecked')); + + expect(test.component.valueStore.getOriginalProps()).toEqual({check: ['bar']}); + }); + it('sends correct data for select multiple field', async () => { const test = await createTest({ form: { select: []} }, (data: any) => `
diff --git a/src/LiveComponent/doc/index.rst b/src/LiveComponent/doc/index.rst index 50631d8ee89..ac5faa9bafe 100644 --- a/src/LiveComponent/doc/index.rst +++ b/src/LiveComponent/doc/index.rst @@ -531,9 +531,9 @@ value on checked. If no ``value`` is set, the checkbox will set a boolean value: - - - + + + ``select`` and ``radio`` elements are a bit easier: use these to either set a single value or an array of values::