From 0f8b034f019837436f6b05e14138d3f328523b2c Mon Sep 17 00:00:00 2001 From: Norman Fomferra Date: Sun, 15 Dec 2024 08:30:32 +0100 Subject: [PATCH 1/2] added `RadioGroup` and `Radio` --- chartlets.js/CHANGES.md | 1 + .../lib/src/plugins/mui/RadioGroup.test.tsx | 62 ++++++++++++++ .../lib/src/plugins/mui/RadioGroup.tsx | 85 +++++++++++++++++++ .../packages/lib/src/plugins/mui/index.ts | 2 + chartlets.py/CHANGES.md | 1 + chartlets.py/chartlets/components/__init__.py | 2 + .../chartlets/components/radiogroup.py | 42 +++++++++ .../tests/components/radiogroup_test.py | 28 ++++++ 8 files changed, 223 insertions(+) create mode 100644 chartlets.js/packages/lib/src/plugins/mui/RadioGroup.test.tsx create mode 100644 chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx create mode 100644 chartlets.py/chartlets/components/radiogroup.py create mode 100644 chartlets.py/tests/components/radiogroup_test.py diff --git a/chartlets.js/CHANGES.md b/chartlets.js/CHANGES.md index 4e691374..ecad08f7 100644 --- a/chartlets.js/CHANGES.md +++ b/chartlets.js/CHANGES.md @@ -39,6 +39,7 @@ * New (MUI) components - `LinearProgress` + - `RadioGroup` and `Radio` - `Switch` * Supporting `tooltip` property for interactive MUI components. diff --git a/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.test.tsx b/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.test.tsx new file mode 100644 index 00000000..c87e612f --- /dev/null +++ b/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.test.tsx @@ -0,0 +1,62 @@ +import { describe, it, expect } from "vitest"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { createChangeHandler } from "./common.test"; +import { RadioGroup } from "./RadioGroup"; + +describe("RadioGroup", () => { + it("should render the component", () => { + render( + {}} + row + dense + />, + ); + // to inspect rendered component, do: + // expect(document.querySelector("#rg")).toEqual({}); + expect(screen.getByRole("radiogroup")).not.toBeUndefined(); + }); + + it("should fire 'value' property with text options", () => { + const { recordedEvents, onChange } = createChangeHandler(); + render( + , + ); + fireEvent.click(screen.getByLabelText("Varying")); + expect(recordedEvents.length).toBe(1); + expect(recordedEvents[0]).toEqual({ + componentType: "RadioGroup", + id: "rg", + property: "value", + value: "v", + }); + fireEvent.click(screen.getByLabelText("Female")); + expect(recordedEvents.length).toBe(2); + expect(recordedEvents[1]).toEqual({ + componentType: "RadioGroup", + id: "rg", + property: "value", + value: "f", + }); + }); +}); diff --git a/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx b/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx new file mode 100644 index 00000000..6ac77bec --- /dev/null +++ b/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx @@ -0,0 +1,85 @@ +import { type ChangeEvent } from "react"; +import MuiRadio from "@mui/material/Radio"; +import MuiRadioGroup from "@mui/material/RadioGroup"; +import MuiFormControl from "@mui/material/FormControl"; +import MuiFormControlLabel from "@mui/material/FormControlLabel"; +import MuiFormLabel from "@mui/material/FormLabel"; +import { Tooltip } from "./Tooltip"; + +import type { ComponentState, ComponentProps } from "@/index"; + +interface RadioState extends ComponentState { + type: "Radio"; + value?: boolean | number | string | undefined; + label?: string; + size?: "medium" | "small" | string; +} + +interface RadioGroupState extends ComponentState { + type: "RadioGroup"; + children?: RadioState[]; + label?: string; + row?: boolean; + dense?: boolean; + tooltip?: string; +} + +interface RadioGroupProps extends ComponentProps, RadioGroupState {} + +export function RadioGroup({ + type, + id, + name, + value, + disabled, + style, + label, + row, + tooltip, + dense, + children: radioButtons, + onChange, +}: RadioGroupProps) { + const handleChange = ( + _event: ChangeEvent, + value: string, + ) => { + if (id) { + return onChange({ + componentType: type, + id: id, + property: "value", + value, + }); + } + }; + return ( + + + {label} + + {radioButtons && + radioButtons.map((radioButton) => ( + + } + /> + ))} + + + + ); +} diff --git a/chartlets.js/packages/lib/src/plugins/mui/index.ts b/chartlets.js/packages/lib/src/plugins/mui/index.ts index 06b0219d..833e43b4 100644 --- a/chartlets.js/packages/lib/src/plugins/mui/index.ts +++ b/chartlets.js/packages/lib/src/plugins/mui/index.ts @@ -4,6 +4,7 @@ import { Button } from "./Button"; import { Checkbox } from "./Checkbox"; import { CircularProgress } from "./CircularProgress"; import { IconButton } from "./IconButton"; +import { RadioGroup } from "./RadioGroup"; import { Select } from "./Select"; import { Switch } from "./Switch"; import { Typography } from "./Typography"; @@ -16,6 +17,7 @@ export default function mui(): Plugin { ["Checkbox", Checkbox], ["CircularProgress", CircularProgress], ["IconButton", IconButton], + ["RadioGroup", RadioGroup], ["Select", Select], ["Switch", Switch], ["Typography", Typography], diff --git a/chartlets.py/CHANGES.md b/chartlets.py/CHANGES.md index 7d2b4512..d9d226f3 100644 --- a/chartlets.py/CHANGES.md +++ b/chartlets.py/CHANGES.md @@ -20,6 +20,7 @@ * New components - `Switch` + - `RadioGroup` and `Radio` ## Version 0.0.29 (from 2024/11/26) diff --git a/chartlets.py/chartlets/components/__init__.py b/chartlets.py/chartlets/components/__init__.py index 388a67f3..f8ee948e 100644 --- a/chartlets.py/chartlets/components/__init__.py +++ b/chartlets.py/chartlets/components/__init__.py @@ -7,6 +7,8 @@ from .progress import LinearProgress from .progress import LinearProgressWithLabel from .charts.vega import VegaChart +from .radiogroup import Radio +from .radiogroup import RadioGroup from .select import Select from .switch import Switch from .typography import Typography diff --git a/chartlets.py/chartlets/components/radiogroup.py b/chartlets.py/chartlets/components/radiogroup.py new file mode 100644 index 00000000..7c178184 --- /dev/null +++ b/chartlets.py/chartlets/components/radiogroup.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass, field + +from chartlets import Component + + +@dataclass(frozen=True) +class Radio(Component): + """Select components are used for collecting user provided + information from a list of options.""" + + value: bool | int | float | str | None = None + """The value of the component. + The DOM API casts this to a string. + """ + + label: str | None = None + """Button label. Optional.""" + + +@dataclass(frozen=True) +class RadioGroup(Component): + """The Radio Group allows the user to select one option from a set. + + Use radio buttons when the user needs to see all available options. + If available options can be collapsed, consider using a `Select` + component because it uses less space. + + Radio buttons should have the most commonly used option selected + by default. + """ + + children: list[Radio] = field(default_factory=list) + """The list of radio buttons.""" + + label: str | None = None + """A label for the group. Optional""" + + tooltip: str | None = None + """Tooltip title. Optional.""" + + dense: bool | None = None + """Dense styling with smaller radio buttons.""" diff --git a/chartlets.py/tests/components/radiogroup_test.py b/chartlets.py/tests/components/radiogroup_test.py new file mode 100644 index 00000000..f2ab6e57 --- /dev/null +++ b/chartlets.py/tests/components/radiogroup_test.py @@ -0,0 +1,28 @@ +from chartlets.components import RadioGroup, Radio +from tests.component_test import make_base + + +class RadioGroupTest(make_base(RadioGroup)): + + def test_is_json_serializable(self): + self.assert_is_json_serializable( + self.cls( + label="Gender", + tooltip="Select your gender", + children=[ + Radio(value="f", label="Female"), + Radio(value="m", label="Male"), + Radio(value="o", label="Other"), + ], + ), + { + "type": "RadioGroup", + "label": "Gender", + "tooltip": "Select your gender", + "children": [ + {"type": "Radio", "value": "f", "label": "Female"}, + {"type": "Radio", "value": "m", "label": "Male"}, + {"type": "Radio", "value": "o", "label": "Other"}, + ], + }, + ) From c44a57f981995b574f6c4f85f0377a37dc8e1278 Mon Sep 17 00:00:00 2001 From: Norman Fomferra Date: Sun, 15 Dec 2024 08:46:15 +0100 Subject: [PATCH 2/2] strange fix --- chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx b/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx index 6ac77bec..d721352a 100644 --- a/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx +++ b/chartlets.js/packages/lib/src/plugins/mui/RadioGroup.tsx @@ -16,7 +16,6 @@ interface RadioState extends ComponentState { } interface RadioGroupState extends ComponentState { - type: "RadioGroup"; children?: RadioState[]; label?: string; row?: boolean;