Skip to content

Commit

Permalink
handle lov id as empty string (#2244)
Browse files Browse the repository at this point in the history
* handle lov id as empty string
default unselected value is null
reflect unselection
resolves #2235

* remove unselected_value

---------

Co-authored-by: Fred Lefévère-Laoide <[email protected]>
  • Loading branch information
FredLL-Avaiga and Fred Lefévère-Laoide authored Nov 14, 2024
1 parent 4936044 commit c594615
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 51 deletions.
22 changes: 11 additions & 11 deletions frontend/taipy-gui/src/components/Taipy/Toggle.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,34 +124,34 @@ describe("Toggle Component", () => {
type: "SEND_UPDATE_ACTION",
});
});
it("dispatch unselected_value on deselection when allowUnselect", async () => {
it("dispatch nothing on deselection by default", async () => {
const dispatch = jest.fn();
const state: TaipyState = INITIAL_STATE;
const { getByText } = render(
<TaipyContext.Provider value={{ state, dispatch }}>
<Toggle lov={lov} updateVarName="varname" unselectedValue="uv" value="id2" allowUnselect={true} />
<Toggle lov={lov} updateVarName="varname" value="id2" />
</TaipyContext.Provider>
);
const elt = getByText("Item 2");
await userEvent.click(elt);
expect(dispatch).toHaveBeenCalledWith({
name: "varname",
payload: { value: "uv" },
propagate: true,
type: "SEND_UPDATE_ACTION",
});
expect(dispatch).not.toHaveBeenCalled();
});
it("dispatch nothing on deselection by default", async () => {
it("dispatch null on deselection when allowUnselect", async () => {
const dispatch = jest.fn();
const state: TaipyState = INITIAL_STATE;
const { getByText } = render(
<TaipyContext.Provider value={{ state, dispatch }}>
<Toggle lov={lov} updateVarName="varname" unselectedValue="uv" value="id2" />
<Toggle lov={lov} updateVarName="varname" value="id2" allowUnselect={true} />
</TaipyContext.Provider>
);
const elt = getByText("Item 2");
await userEvent.click(elt);
expect(dispatch).not.toHaveBeenCalled();
expect(dispatch).toHaveBeenCalledWith({
name: "varname",
payload: { value: null },
propagate: true,
type: "SEND_UPDATE_ACTION",
});
});

describe("As Switch", () => {
Expand Down
28 changes: 8 additions & 20 deletions frontend/taipy-gui/src/components/Taipy/Toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,25 @@
import React, { MouseEvent, SyntheticEvent, useCallback, useEffect, useMemo, useState } from "react";
import Box from "@mui/material/Box";
import Switch from "@mui/material/Switch";
import Typography from "@mui/material/Typography";
import ToggleButton from "@mui/material/ToggleButton";
import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography";

import { FormControlLabel, SxProps } from "@mui/material";
import { createSendUpdateAction } from "../../context/taipyReducers";
import ThemeToggle, { emptyStyle } from "./ThemeToggle";
import { LovProps, useLovListMemo } from "./lovUtils";
import { useClassNames, useDispatch, useDynamicProperty, useModule } from "../../utils/hooks";
import { getCssSize, getSuffixedClassNames, getUpdateVar } from "./utils";
import { Icon, IconAvatar } from "../../utils/icon";
import { FormControlLabel, SxProps } from "@mui/material";
import { getComponentClassName } from "./TaipyStyle";
import ThemeToggle, { emptyStyle } from "./ThemeToggle";
import { LovProps, useLovListMemo } from "./lovUtils";
import { getCssSize, getSuffixedClassNames, getUpdateVar } from "./utils";

const baseGroupSx = { verticalAlign: "middle" };

interface ToggleProps extends LovProps<string> {
style?: SxProps;
label?: string;
unselectedValue?: string;
allowUnselect?: boolean;
mode?: string;
isSwitch?: boolean;
Expand All @@ -49,14 +48,13 @@ const Toggle = (props: ToggleProps) => {
propagate = true,
lov,
defaultLov = "",
unselectedValue = "",
updateVars = "",
valueById,
mode = "",
isSwitch = false,
} = props;
const dispatch = useDispatch();
const [value, setValue] = useState(props.defaultValue);
const [value, setValue] = useState<string | null | undefined>(props.defaultValue);
const [bVal, setBVal] = useState(() =>
typeof props.defaultValue === "boolean"
? props.defaultValue
Expand Down Expand Up @@ -85,25 +83,15 @@ const Toggle = (props: ToggleProps) => {
dispatch(
createSendUpdateAction(
updateVarName,
val === null ? unselectedValue : val,
val,
module,
props.onChange,
propagate,
valueById ? undefined : getUpdateVar(updateVars, "lov")
)
);
},
[
unselectedValue,
updateVarName,
propagate,
dispatch,
updateVars,
valueById,
props.onChange,
props.allowUnselect,
module,
]
[updateVarName, propagate, dispatch, updateVars, valueById, props.onChange, props.allowUnselect, module]
);

const changeSwitchValue = useCallback(
Expand Down
1 change: 0 additions & 1 deletion taipy/gui/_renderers/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,6 @@ class _Factory:
("hover_text", PropertyType.dynamic_string),
("label",),
("value_by_id", PropertyType.boolean),
("unselected_value", PropertyType.string, ""),
("allow_unselect", PropertyType.boolean),
("on_change", PropertyType.function),
("mode",),
Expand Down
2 changes: 1 addition & 1 deletion taipy/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ def __front_end_update(
elif rel_var and isinstance(current_value, _TaipyLovValue): # pragma: no cover
lov_holder = _getscopeattr_drill(self, self.__evaluator.get_hash_from_expr(rel_var))
if isinstance(lov_holder, _TaipyLov):
if value:
if isinstance(value, str):
val = value if isinstance(value, list) else [value]
elt_4_ids = self.__adapter._get_elt_per_ids(lov_holder.get_name(), lov_holder.get())
ret_val = [elt_4_ids.get(x, x) for x in val]
Expand Down
12 changes: 3 additions & 9 deletions taipy/gui/viselements.json
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,6 @@
"default_value": "False",
"doc": "If set, this allows de-selection and the value is set to unselected_value."
},
{
"name": "unselected_value",
"type": "Any",
"default_value": "None",
"doc": "Value assigned to <i>value</i> when no item is selected."
},
{
"name": "mode",
"type": "str",
Expand Down Expand Up @@ -1632,7 +1626,7 @@
}
],
[
"alert",
"alert",
{
"inherits": ["shared"],
"properties": [
Expand Down Expand Up @@ -1662,7 +1656,7 @@
"doc": "If False, the alert is hidden."
}
]
}
}
],
[
"status",
Expand Down Expand Up @@ -1842,7 +1836,7 @@
}
]
}
]
]
],
"blocks": [
[
Expand Down
5 changes: 2 additions & 3 deletions tests/gui/builder/control/test_toggle.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
def test_toggle_builder(gui: Gui, helpers):
with tgb.Page(frame=None) as page:
tgb.toggle(theme=True) # type: ignore[attr-defined]
expected_list = ["<Toggle", 'mode="theme"', 'unselectedValue=""']
expected_list = ["<Toggle", 'mode="theme"']
helpers.test_control_builder(gui, page, expected_list)


def test_toggle_allow_unselected_builder(gui: Gui, helpers):
with tgb.Page(frame=None) as page:
tgb.toggle(allow_unselect=True, lov="1;2") # type: ignore[attr-defined]
expected_list = ["<Toggle", 'unselectedValue=""', "allowUnselect={true}"]
expected_list = ["<Toggle", "allowUnselect={true}"]
helpers.test_control_builder(gui, page, expected_list)


Expand All @@ -40,7 +40,6 @@ def test_toggle_lov_builder(gui: Gui, test_client, helpers):
"lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0}",
'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0"',
'updateVarName="_TpLv_tpec_TpExPr_x_TPMDL_0"',
'unselectedValue=""',
"value={_TpLv_tpec_TpExPr_x_TPMDL_0}",
]
helpers.test_control_builder(gui, page, expected_list)
10 changes: 4 additions & 6 deletions tests/gui/control/test_toggle.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@

def test_toggle_md(gui: Gui, helpers):
md_string = "<|toggle|theme|>"
expected_list = ["<Toggle", 'mode="theme"', 'unselectedValue=""']
expected_list = ["<Toggle", 'mode="theme"']
helpers.test_control_md(gui, md_string, expected_list)


def test_toggle_width_md(gui: Gui, helpers):
md_string = "<|toggle|theme|width=70%|>"
expected_list = ["<Toggle", 'mode="theme"', 'unselectedValue=""', 'width="70%"']
expected_list = ["<Toggle", 'mode="theme"', 'width="70%"']
helpers.test_control_md(gui, md_string, expected_list)


def test_toggle_allow_unselected_md(gui: Gui, helpers):
md_string = "<|toggle|lov=1;2|allow_unselect|>"
expected_list = ["<Toggle", 'unselectedValue=""', "allowUnselect={true}"]
expected_list = ["<Toggle", "allowUnselect={true}"]
helpers.test_control_md(gui, md_string, expected_list)


Expand All @@ -42,15 +42,14 @@ def test_toggle_lov_md(gui: Gui, test_client, helpers):
"lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0}",
'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0"',
'updateVarName="_TpLv_tpec_TpExPr_x_TPMDL_0"',
'unselectedValue=""',
"value={_TpLv_tpec_TpExPr_x_TPMDL_0}",
]
helpers.test_control_md(gui, md_string, expected_list)


def test_toggle_html_1(gui: Gui, helpers):
html_string = '<taipy:toggle theme="True" />'
expected_list = ["<Toggle", 'mode="theme"', 'unselectedValue=""']
expected_list = ["<Toggle", 'mode="theme"']
helpers.test_control_html(gui, html_string, expected_list)


Expand All @@ -66,7 +65,6 @@ def test_toggle_html_2(gui: Gui, test_client, helpers):
"lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0}",
'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0"',
'updateVarName="_TpLv_tpec_TpExPr_x_TPMDL_0"',
'unselectedValue=""',
"value={_TpLv_tpec_TpExPr_x_TPMDL_0}",
]
helpers.test_control_html(gui, html_string, expected_list)
Expand Down

0 comments on commit c594615

Please sign in to comment.