Skip to content

Commit

Permalink
Allow multiple selections on select box (google#481)
Browse files Browse the repository at this point in the history
* Allow multiple selections on select box

* Fix typing
  • Loading branch information
richard-to authored and wwwillchen committed Jun 25, 2024
1 parent 8135587 commit 153f222
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 19 deletions.
7 changes: 4 additions & 3 deletions demo/select_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

@me.stateclass
class State:
selected_value: str = ""
selected_values: list[str]


def on_selection_change(e: me.SelectSelectionChangeEvent):
s = me.state(State)
s.selected_value = e.value
s.selected_values = e.values


@me.page(
Expand All @@ -28,6 +28,7 @@ def app():
],
on_selection_change=on_selection_change,
style=me.Style(width=500),
multiple=True,
)
s = me.state(State)
me.text(text="Selected value: " + s.selected_value)
me.text(text="Selected values: " + ", ".join(s.selected_values))
1 change: 1 addition & 0 deletions mesop/components/select/e2e/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import select_app as select_app
from . import select_app_multiple as select_app_multiple
29 changes: 29 additions & 0 deletions mesop/components/select/e2e/select_app_multiple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import mesop as me


@me.stateclass
class State:
selected_values: list[str]


def on_selection_change(e: me.SelectSelectionChangeEvent):
s = me.state(State)
s.selected_values = e.values


@me.page(path="/components/select/e2e/select_app_multiple")
def app():
me.text(text="Select")
me.select(
label="Select",
options=[
me.SelectOption(label="label 1", value="value1"),
me.SelectOption(label="label 2", value="value2"),
me.SelectOption(label="label 3", value="value3"),
],
on_selection_change=on_selection_change,
multiple=True,
style=me.Style(width=500),
)
s = me.state(State)
me.text(text="Selected values: " + ", ".join(s.selected_values))
22 changes: 18 additions & 4 deletions mesop/components/select/e2e/select_test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import {test, expect} from '@playwright/test';

test('test', async ({page}) => {
test('single selection', async ({page}) => {
await page.goto('/components/select/e2e/select_app');
await page.getByRole('combobox').click();
await page.getByRole('option', {name: 'label 2'}).click();
await expect(page.getByText('Selected value: value2')).toBeAttached();

await page.getByRole('combobox').click();
await page.getByRole('option', {name: 'label 3'}).click();
await expect(page.getByText('Selected value: value3')).toBeAttached();
});

test('multiple selection', async ({page}) => {
await page.goto('/components/select/e2e/select_app_multiple');
await page.getByLabel('Select').click();

await page.getByRole('option', {name: 'label 2'}).click();
await expect(page.getByText('Selected values: value2')).toBeAttached();

expect(
await page.getByText('Selected value: value2').textContent(),
).toContain('Selected value: value2');
await page.getByRole('option', {name: 'label 1'}).click();
await expect(
page.getByText('Selected values: value1, value2'),
).toBeAttached();
});
1 change: 1 addition & 0 deletions mesop/components/select/select.ng.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[value]="config().getValue()"
(openedChange)="onSelectOpenedChangeEvent($event)"
(selectionChange)="onSelectSelectionChangeEvent($event)"
[multiple]="config().getMultiple()"
>
@for(option of config().getOptionsList(); track $index) {
<mat-option [value]="option.getValue()">{{option.getLabel()}}</mat-option>
Expand Down
5 changes: 5 additions & 0 deletions mesop/components/select/select.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ syntax = "proto2";

package mesop.components.select;

message SelectChangeEvent {
repeated string values = 1;
}

message SelectType {
optional bool disabled = 2;
optional bool disable_ripple = 3;
optional bool multiple = 18;
optional double tab_index = 4;
optional string placeholder = 6;
optional string value = 9;
Expand Down
31 changes: 22 additions & 9 deletions mesop/components/select/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,33 @@ class SelectOpenedChangeEvent(MesopEvent):

@dataclass(kw_only=True)
class SelectSelectionChangeEvent(MesopEvent):
"""Event representing a change in the select component's value.
"""Event representing a change in the select component's value(s).
Attributes:
value: The new value of the select component after the change.
values: New values of the select component after the change.
key (str): Key of the component that emitted this event.
"""

value: str
values: list[str]

@property
def value(self):
"""Shortcut for returning a single value."""
if not self.values:
return ""
return self.values[0]

register_event_mapper(
SelectSelectionChangeEvent,
lambda event, key: SelectSelectionChangeEvent(

def map_select_change_event(event, key):
select_event = select_pb.SelectChangeEvent()
select_event.ParseFromString(event.bytes_value)
return SelectSelectionChangeEvent(
key=key.key,
value=event.string_value,
),
)
values=list(select_event.values),
)


register_event_mapper(SelectSelectionChangeEvent, map_select_change_event)


@dataclass(kw_only=True)
Expand Down Expand Up @@ -82,6 +92,7 @@ def select(
placeholder: str = "",
value: str = "",
style: Style | None = None,
multiple: bool = False,
):
"""Creates a Select component.
Expand All @@ -91,6 +102,7 @@ def select(
on_opened_change: Event emitted when the select panel has been toggled.
disabled: Whether the select is disabled.
disable_ripple: Whether ripples in the select are disabled.
multiple: Whether multiple selections are allowed.
tab_index: Tab index of the select.
placeholder: Placeholder to be shown if no value has been selected.
value: Value of the select control.
Expand All @@ -109,6 +121,7 @@ def select(
label=label,
disabled=disabled,
disable_ripple=disable_ripple,
multiple=multiple,
tab_index=tab_index,
placeholder=placeholder,
value=value,
Expand Down
16 changes: 13 additions & 3 deletions mesop/components/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
Type,
Style,
} from 'mesop/mesop/protos/ui_jspb_proto_pb/mesop/protos/ui_pb';
import {SelectType} from 'mesop/mesop/components/select/select_jspb_proto_pb/mesop/components/select/select_pb';
import {
SelectChangeEvent,
SelectType,
} from 'mesop/mesop/components/select/select_jspb_proto_pb/mesop/components/select/select_pb';
import {Channel} from '../../web/src/services/channel';
import {formatStyle} from '../../web/src/utils/styles';

Expand Down Expand Up @@ -45,11 +48,18 @@ export class SelectComponent {

onSelectSelectionChangeEvent(event: MatSelectChange): void {
const userEvent = new UserEvent();

userEvent.setHandlerId(
this.config().getOnSelectSelectionChangeEventHandlerId()!,
);
userEvent.setStringValue(event.value);
const changeEvent = new SelectChangeEvent();
if (typeof event.value === 'string') {
changeEvent.addValues(event.value);
} else {
for (const value of event.value) {
changeEvent.addValues(value);
}
}
userEvent.setBytesValue(changeEvent.serializeBinary());
userEvent.setKey(this.key);
this.channel.dispatch(userEvent);
}
Expand Down

0 comments on commit 153f222

Please sign in to comment.