From c6aecdb2fa4970ff379300e585e9775ba5e5fc77 Mon Sep 17 00:00:00 2001 From: Norman Fomferra Date: Tue, 10 Dec 2024 18:13:27 +0100 Subject: [PATCH] Added new `Switch` component --- chartlets.js/CHANGES.md | 2 + .../lib/src/plugins/mui/Switch.test.tsx | 37 ++++++++++++++ .../packages/lib/src/plugins/mui/Switch.tsx | 51 +++++++++++++++++++ .../packages/lib/src/plugins/mui/index.ts | 2 + chartlets.py/CHANGES.md | 2 + chartlets.py/chartlets/components/__init__.py | 1 + chartlets.py/chartlets/components/switch.py | 14 +++++ chartlets.py/tests/components/switch_test.py | 11 ++++ 8 files changed, 120 insertions(+) create mode 100644 chartlets.js/packages/lib/src/plugins/mui/Switch.test.tsx create mode 100644 chartlets.js/packages/lib/src/plugins/mui/Switch.tsx create mode 100644 chartlets.py/chartlets/components/switch.py create mode 100644 chartlets.py/tests/components/switch_test.py diff --git a/chartlets.js/CHANGES.md b/chartlets.js/CHANGES.md index c3c041e..00c6aec 100644 --- a/chartlets.js/CHANGES.md +++ b/chartlets.js/CHANGES.md @@ -37,6 +37,8 @@ - using `schema` instead of `type` property for callback arguments - using `return` object with `schema` property for callback return values +* New (MUI) components + - `Switch` ## Version 0.0.29 (from 2024/11/26) diff --git a/chartlets.js/packages/lib/src/plugins/mui/Switch.test.tsx b/chartlets.js/packages/lib/src/plugins/mui/Switch.test.tsx new file mode 100644 index 0000000..5d50321 --- /dev/null +++ b/chartlets.js/packages/lib/src/plugins/mui/Switch.test.tsx @@ -0,0 +1,37 @@ +import { describe, it, expect } from "vitest"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { createChangeHandler } from "./common.test"; +import { Switch } from "./Switch"; + +describe("Switch", () => { + it("should render the Switch component", () => { + render( + {}} />, + ); + // to inspect rendered component, do: + // expect(document.querySelector("#cb")).toEqual({}); + expect(screen.getByText("Click!")).not.toBeUndefined(); + }); + + it("should fire 'value' property", () => { + const { recordedEvents, onChange } = createChangeHandler(); + render( + , + ); + fireEvent.click(screen.getByText("Click!")); + expect(recordedEvents.length).toEqual(1); + expect(recordedEvents[0]).toEqual({ + componentType: "Switch", + id: "cb", + property: "value", + value: true, + }); + }); +}); diff --git a/chartlets.js/packages/lib/src/plugins/mui/Switch.tsx b/chartlets.js/packages/lib/src/plugins/mui/Switch.tsx new file mode 100644 index 0000000..a8c0a82 --- /dev/null +++ b/chartlets.js/packages/lib/src/plugins/mui/Switch.tsx @@ -0,0 +1,51 @@ +import { type ChangeEvent } from "react"; +import MuiSwitch from "@mui/material/Switch"; +import MuiFormControl from "@mui/material/FormControl"; +import MuiFormControlLabel from "@mui/material/FormControlLabel"; + +import type { ComponentState, ComponentProps } from "@/index"; + +interface SwitchState extends ComponentState { + label?: string; + value?: boolean | undefined; +} + +interface SwitchProps extends ComponentProps, SwitchState {} + +export function Switch({ + type, + id, + name, + value, + disabled, + style, + label, + onChange, +}: SwitchProps) { + const handleChange = (event: ChangeEvent) => { + if (id) { + return onChange({ + componentType: type, + id: id, + property: "value", + value: event.currentTarget.checked, + }); + } + }; + return ( + + + } + /> + + ); +} diff --git a/chartlets.js/packages/lib/src/plugins/mui/index.ts b/chartlets.js/packages/lib/src/plugins/mui/index.ts index 956d02f..06b0219 100644 --- a/chartlets.js/packages/lib/src/plugins/mui/index.ts +++ b/chartlets.js/packages/lib/src/plugins/mui/index.ts @@ -5,6 +5,7 @@ import { Checkbox } from "./Checkbox"; import { CircularProgress } from "./CircularProgress"; import { IconButton } from "./IconButton"; import { Select } from "./Select"; +import { Switch } from "./Switch"; import { Typography } from "./Typography"; export default function mui(): Plugin { @@ -16,6 +17,7 @@ export default function mui(): Plugin { ["CircularProgress", CircularProgress], ["IconButton", IconButton], ["Select", Select], + ["Switch", Switch], ["Typography", Typography], ], }; diff --git a/chartlets.py/CHANGES.md b/chartlets.py/CHANGES.md index 776fb51..be740ff 100644 --- a/chartlets.py/CHANGES.md +++ b/chartlets.py/CHANGES.md @@ -16,6 +16,8 @@ - using `schema` instead of `type` property for callback arguments - using `return` object with `schema` property for callback return values +* New components + - `Switch` ## 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 2190802..388a67f 100644 --- a/chartlets.py/chartlets/components/__init__.py +++ b/chartlets.py/chartlets/components/__init__.py @@ -8,4 +8,5 @@ from .progress import LinearProgressWithLabel from .charts.vega import VegaChart from .select import Select +from .switch import Switch from .typography import Typography diff --git a/chartlets.py/chartlets/components/switch.py b/chartlets.py/chartlets/components/switch.py new file mode 100644 index 0000000..97d726c --- /dev/null +++ b/chartlets.py/chartlets/components/switch.py @@ -0,0 +1,14 @@ +from dataclasses import dataclass + +from chartlets import Component + + +@dataclass(frozen=True) +class Switch(Component): + """Switches toggle the state of a single setting on or off.""" + + value: bool | None = None + """The switch value.""" + + label: str = "" + """The switch label.""" diff --git a/chartlets.py/tests/components/switch_test.py b/chartlets.py/tests/components/switch_test.py new file mode 100644 index 0000000..2ea151e --- /dev/null +++ b/chartlets.py/tests/components/switch_test.py @@ -0,0 +1,11 @@ +from chartlets.components import Switch +from tests.component_test import make_base + + +class SwitchTest(make_base(Switch)): + + def test_is_json_serializable(self): + self.assert_is_json_serializable( + self.cls(value=True, label="Auto-safe"), + {"type": "Switch", "value": True, "label": "Auto-safe"}, + )