diff --git a/py/examples/checklist.py b/py/examples/checklist.py
index f17822594c..63b2aebf4b 100644
--- a/py/examples/checklist.py
+++ b/py/examples/checklist.py
@@ -15,7 +15,13 @@ async def serve(q: Q):
else:
q.page['example'] = ui.form_card(box='1 1 4 7', items=[
ui.checklist(name='checklist', label='Choices',
- choices=[ui.choice(name=x, label=x) for x in ['Egg', 'Bacon', 'Spam']]),
+ choices=[ui.choice(name=x, label=x) for x in ['Egg', 'Bacon', 'Spam']] + [
+ ui.choice(name="cannot select", label="cannot select", disabled=True),
+ ui.choice(name="cannot remove", label="cannot remove", disabled=True)
+ ],
+ values=["cannot remove"]
+ )
+ ,
ui.button(name='show_inputs', label='Submit', primary=True),
])
await q.page.save()
diff --git a/ui/src/checklist.test.tsx b/ui/src/checklist.test.tsx
index b957644ade..7c8e68d2dc 100644
--- a/ui/src/checklist.test.tsx
+++ b/ui/src/checklist.test.tsx
@@ -147,4 +147,44 @@ describe('Checklist.tsx', () => {
expect(pushMock).toHaveBeenCalled()
})
-})
\ No newline at end of file
+
+ it('Keeps selected and disabled choices after select all', () => {
+ const disabledChoices = [
+ { name: 'Choice1', disabled: true },
+ { name: 'Choice2', disabled: false },
+ { name: 'Choice3', disabled: true },
+ ]
+ const { getByText } = render()
+
+ fireEvent.click(getByText('Select All'))
+
+ expect(wave.args[name]).toMatchObject(['Choice1', 'Choice2'])
+ })
+
+ it('Selects only enabled choices when some are disabled', () => {
+ const choices = [
+ { name: 'Choice1', disabled: true },
+ { name: 'Choice2', disabled: false },
+ { name: 'Choice3', disabled: false },
+ ]
+ const { getByText } = render()
+
+ fireEvent.click(getByText('Select All'))
+
+ expect(wave.args[name]).toMatchObject(['Choice2', 'Choice3'])
+ })
+
+ it('Retains unselected and disabled items after deselect all', () => {
+ const disabledChoices = [
+ { name: 'Choice1', disabled: true },
+ { name: 'Choice2', disabled: false },
+ { name: 'Choice3', disabled: true },
+ ]
+ const { getByText } = render()
+
+ fireEvent.click(getByText('Deselect All'))
+
+ expect(wave.args[name]).toMatchObject(['Choice1'])
+ })
+
+})
diff --git a/ui/src/checklist.tsx b/ui/src/checklist.tsx
index fd59f3b3d7..8f9db10608 100644
--- a/ui/src/checklist.tsx
+++ b/ui/src/checklist.tsx
@@ -79,11 +79,20 @@ export const
wave.args[m.name] = choices.filter(({ selected }) => selected).map(({ c }) => c.name)
if (m.trigger) wave.push()
},
- select = (value: B) => {
- const _choices = choices.map(({ c, selected }) => ({ c, selected: c.disabled ? selected : value }))
+ select = (selectAll: B) => {
+ const _choices = choices.map(({ c, selected }) => ({ c, selected: c.disabled ? selected : selectAll }))
setChoices(_choices)
capture(_choices)
- m.values = value ? _choices.map(({ c }) => c.name) : []
+
+ if (selectAll) {
+ m.values = _choices
+ .filter(({ c, selected }) => !c.disabled || selected) // Exclude disabled
+ .map(({ c }) => c.name)
+ } else {
+ m.values = _choices
+ .filter(({ c, selected }) => c.disabled && selected) // Retain selected disabled
+ .map(({ c }) => c.name)
+ }
},
selectAll = () => select(true),
deselectAll = () => select(false),
@@ -126,4 +135,5 @@ export const
{items}
)
- }
\ No newline at end of file
+ }
+