Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple sections #131393

Draft
wants to merge 13 commits into
base: dev
Choose a base branch
from
30 changes: 25 additions & 5 deletions homeassistant/components/kitchen_sink/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ async def async_step_options_1(
if user_input is not None:
return self.async_create_entry(data=self.config_entry.options | user_input)

section_1 = self.config_entry.options.get("section_1", {})
return self.async_show_form(
step_id="options_1",
data_schema=vol.Schema(
Expand All @@ -79,17 +80,36 @@ async def async_step_options_1(
{
vol.Optional(
CONF_BOOLEAN,
default=self.config_entry.options.get(
CONF_BOOLEAN, False
),
default=section_1.get(CONF_BOOLEAN, False),
): bool,
vol.Optional(
CONF_INT,
default=self.config_entry.options.get(CONF_INT, 10),
default=section_1.get(CONF_INT, 10),
): int,
}
),
{"collapsed": False},
{
"collapsed": False,
},
),
vol.Required("section_2"): data_entry_flow.section(
vol.Schema(
{
vol.Optional(
"a",
default=2,
): int,
vol.Optional(
"b",
default=4,
): int,
}
),
{
"collapsed": False,
"multiple": True,
"default": self.config_entry.options.get("section_2", []),
},
),
}
),
Expand Down
8 changes: 8 additions & 0 deletions homeassistant/components/kitchen_sink/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
},
"description": "This section allows input of some extra data",
"name": "Collapsible section"
},
"section_2": {
"data": {
"a": "A",
"b": "B"
},
"description": "Some datapoints",
"name": "Data point"
}
},
"submit": "Save!"
Expand Down
10 changes: 9 additions & 1 deletion homeassistant/data_entry_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,8 @@ class SectionConfig(TypedDict, total=False):
"""Class to represent a section config."""

collapsed: bool
multiple: bool
default: list[dict[str, Any]]


class section:
Expand All @@ -916,6 +918,8 @@ class section:
CONFIG_SCHEMA = vol.Schema(
{
vol.Optional("collapsed", default=False): bool,
vol.Optional("multiple", default=False): bool,
vol.Optional("default"): list[dict[str, Any]],
},
)

Expand All @@ -928,7 +932,11 @@ def __init__(

def __call__(self, value: Any) -> Any:
"""Validate input."""
return self.schema(value)
if not self.options.get("multiple"):
return self.schema(value)
if not isinstance(value, list):
raise vol.Invalid("Value should be a list")
return [vol.Schema(dict)(val) for val in value]


# These can be removed if no deprecated constant are in this module anymore
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/helpers/config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,7 @@ def _custom_serializer(schema: Any, *, allow_section: bool) -> Any:
if isinstance(schema, data_entry_flow.section):
if not allow_section:
raise ValueError("Nesting expandable sections is not supported")
return {
section_schema = {
"type": "expandable",
"schema": voluptuous_serialize.convert(
schema.schema,
Expand All @@ -1145,7 +1145,11 @@ def _custom_serializer(schema: Any, *, allow_section: bool) -> Any:
),
),
"expanded": not schema.options["collapsed"],
"multiple": schema.options["multiple"],
}
if schema.options["multiple"]:
section_schema["default"] = schema.options["default"]
return section_schema

if isinstance(schema, multi_select):
return {"type": "multi_select", "options": schema.options}
Expand Down
34 changes: 32 additions & 2 deletions tests/components/kitchen_sink/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,39 @@ async def test_options_flow(hass: HomeAssistant) -> None:

result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"section_1": {"bool": True, "int": 15}},
user_input={
"section_1": {
"bool": True,
"int": 15,
},
"section_2": [
{
"a": 2,
"b": 4,
},
{
"a": 5,
"b": 7,
},
],
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert config_entry.options == {"section_1": {"bool": True, "int": 15}}
assert config_entry.options == {
"section_1": {
"bool": True,
"int": 15,
},
"section_2": [
{
"a": 2,
"b": 4,
},
{
"a": 5,
"b": 7,
},
],
}

await hass.async_block_till_done()
30 changes: 30 additions & 0 deletions tests/test_data_entry_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,36 @@ def test_section_in_serializer() -> None:
{"name": "option_2", "required": True, "type": "integer"},
],
"type": "expandable",
"multiple": False,
}


def test_section_multiple_in_serializer() -> None:
"""Test section with multiple with custom_serializer."""
assert cv.custom_serializer(
data_entry_flow.section(
vol.Schema(
{
vol.Optional("option_1", default=False): bool,
vol.Required("option_2"): int,
}
),
{"collapsed": False, "multiple": True, "default": [{True, 10}]},
)
) == {
"expanded": True,
"schema": [
{"default": False, "name": "option_1", "optional": True, "type": "boolean"},
{"name": "option_2", "required": True, "type": "integer"},
],
"type": "expandable",
"multiple": True,
"default": [
{
True,
10,
},
],
}


Expand Down
Loading