Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion chartlets.js/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
* In `chartlets.js` we no longer emit warnings and errors in common
situations to avoid too much spam in the browser console.

* New (MUI) components
- `DataGrid`
- `Dialog`

## Version 0.1.3 (from 2025/01/28)

* **Chore:** Version bump to align CI process with GitHub release flow.
Expand Down Expand Up @@ -55,7 +59,6 @@
- `Switch`
- `Tabs`
- `Slider`
- `DataGrid`

* Supporting `tooltip` property for interactive MUI components.

Expand Down
7 changes: 4 additions & 3 deletions chartlets.js/packages/lib/src/plugins/mui/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type MouseEvent } from "react";
import MuiButton from "@mui/material/Button";
import MuiIcon from "@mui/material/Icon";

import type { ComponentState, ComponentProps } from "@/index";
import type { ComponentProps, ComponentState } from "@/index";
import { Tooltip } from "./Tooltip";

interface ButtonState extends ComponentState {
Expand All @@ -20,7 +20,7 @@ interface ButtonState extends ComponentState {
| "warning";
}

interface ButtonProps extends ComponentProps, ButtonState {}
export interface ButtonProps extends ComponentProps, ButtonState {}

export function Button({
type,
Expand All @@ -33,6 +33,7 @@ export function Button({
text,
startIcon,
endIcon,
tooltip,
onChange,
}: ButtonProps) {
const handleClick = (_event: MouseEvent<HTMLButtonElement>) => {
Expand All @@ -46,7 +47,7 @@ export function Button({
}
};
return (
<Tooltip>
<Tooltip title={tooltip}>
<MuiButton
id={id}
name={name}
Expand Down
90 changes: 90 additions & 0 deletions chartlets.js/packages/lib/src/plugins/mui/Dialog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { describe, expect, it } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import { registry } from "@/components/registry";
import { createChangeHandler } from "@/plugins/mui/common.test";
import { Button, type ButtonProps } from "@/plugins/mui/Button";
import { Dialog } from "./Dialog";

describe("Dialog", () => {
it("should render the Dialog component", () => {
render(
<Dialog
id="test-dialog"
type="Dialog"
open={true}
title="Test Title"
content="Test Content"
onChange={() => {}}
/>,
);

expect(screen.getByRole("dialog")).toBeInTheDocument();
expect(screen.getByText("Test Title")).toBeInTheDocument();
expect(screen.getByText("Test Content")).toBeInTheDocument();
});

it("should not render the Dialog if open is false", () => {
render(
<Dialog
id="test-dialog"
type="Dialog"
open={false}
onChange={() => {}}
/>,
);
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});

it("should handle onClose event and call onChange", () => {
const { recordedEvents, onChange } = createChangeHandler();

render(
<Dialog
id="test-dialog"
type="Dialog"
open={true}
title="Test Title"
content="Test Content"
onChange={onChange}
/>,
);

const backdrop = document.querySelector(".MuiBackdrop-root");
expect(backdrop).toBeInTheDocument();
if (backdrop) {
fireEvent.click(backdrop);
}

expect(recordedEvents.length).toBe(1);
expect(recordedEvents[0]).toEqual({
componentType: "Dialog",
id: "test-dialog",
property: "open",
value: false,
});
});

it("should render children within DialogActions", () => {
registry.register("Button", Button);
render(
<Dialog
id="test-dialog"
type="Dialog"
open={true}
title="Test Title"
content="Test Content"
children={[
{
id: "test-button",
type: "Button",
text: "Click me!",
} as ButtonProps,
]}
onChange={() => {}}
></Dialog>,
);

expect(screen.getByRole("button")).toBeInTheDocument();
expect(screen.getByText("Click me!")).toBeInTheDocument();
});
});
94 changes: 94 additions & 0 deletions chartlets.js/packages/lib/src/plugins/mui/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
Dialog as MuiDialog,
DialogActions,
DialogContent,
type DialogContentProps,
DialogContentText,
type DialogProps as MuiDialogProps,
DialogTitle,
type DialogTitleProps,
} from "@mui/material";

import type { TypographyProps } from "@mui/material/Typography";
import { Children, type ComponentProps, type ComponentState } from "@/index";

interface DialogState extends ComponentState {
open?: boolean;
title?: string;
titleProps?: DialogTitleProps & TypographyProps;
content?: string;
contentProps?: DialogContentProps & TypographyProps;
disableEscapeKeyDown?: boolean;
fullScreen?: boolean;
fullWidth?: boolean;
maxWidth?: MuiDialogProps["maxWidth"];
scroll?: MuiDialogProps["scroll"];
ariaLabel?: string;
ariaDescribedBy?: string;
}

interface DialogProps extends ComponentProps, DialogState {}

export const Dialog = ({
id,
type,
style,
open,
title,
titleProps,
content,
contentProps,
disableEscapeKeyDown,
fullScreen,
fullWidth,
maxWidth,
scroll,
ariaLabel,
ariaDescribedBy,
children: nodes,
onChange,
}: DialogProps) => {
if (!open) {
return;
}
const handleClose: MuiDialogProps["onClose"] = (_event, _reason) => {
if (id) {
onChange({
componentType: type,
id: id,
property: "open",
value: false,
});
}
};

return (
<MuiDialog
id={id}
style={style}
open={open}
onClose={handleClose}
disableEscapeKeyDown={disableEscapeKeyDown}
fullScreen={fullScreen}
fullWidth={fullWidth}
maxWidth={maxWidth}
scroll={scroll}
aria-label={ariaLabel}
aria-describedby={ariaDescribedBy}
>
{title && (
<DialogTitle {...titleProps}>{title}</DialogTitle>
)}
{content && (
<DialogContent {...contentProps}>
<DialogContentText>{content}</DialogContentText>
</DialogContent>
)}
{nodes && (
<DialogActions>
<Children nodes={nodes} onChange={onChange} />
</DialogActions>
)}
</MuiDialog>
);
};
2 changes: 2 additions & 0 deletions chartlets.js/packages/lib/src/plugins/mui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Tabs } from "./Tabs";
import { Typography } from "./Typography";
import { Slider } from "./Slider";
import { DataGrid } from "@/plugins/mui/DataGrid";
import { Dialog } from "@/plugins/mui/Dialog";

export default function mui(): Plugin {
return {
Expand All @@ -22,6 +23,7 @@ export default function mui(): Plugin {
["Checkbox", Checkbox],
["CircularProgress", CircularProgress],
["DataGrid", DataGrid],
["Dialog", Dialog],
["Divider", Divider],
["IconButton", IconButton],
["LinearProgress", LinearProgress],
Expand Down
9 changes: 8 additions & 1 deletion chartlets.py/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
## Version 0.1.4 (in development)

* New (MUI) components
- `DataGrid`
- `Dialog`


## Version 0.1.3 (from 2025/01/28)

* **Chore:** Version bump to align CI process with GitHub release flow.
No functional changes. This release ensures proper triggering of the CI
pipeline for publishing to PyPI.


## Version 0.1.0 (from 2025/01/14)

* Reorganised Chartlets project to better separate demo from library code.
Expand All @@ -30,7 +38,6 @@
- `Switch`
- `Slider`
- `Tabs` and `Tab`
- `DataGrid`

## Version 0.0.29 (from 2024/11/26)

Expand Down
1 change: 1 addition & 0 deletions chartlets.py/chartlets/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .button import Button
from .button import IconButton
from .checkbox import Checkbox
from .dialog import Dialog
from .charts.vega import VegaChart
from .divider import Divider
from .progress import CircularProgress
Expand Down
50 changes: 50 additions & 0 deletions chartlets.py/chartlets/components/dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from dataclasses import dataclass, field
from typing import Literal, Any

from chartlets import Container


@dataclass(frozen=True)
class Dialog(Container):
"""A modal dialog that presents content and actions in a focused interface."""

open: bool = field(default=False)
"""Controls whether the dialog is open."""

title: str | None = None
"""The title of the dialog."""

titleProps: dict[str, Any] | None = None
"""Additional properties for the dialog title. Can include
typography-related attributes.
https://mui.com/material-ui/api/dialog-title/"""

content: str | None = None
"""The content of the dialog."""

contentProps: dict[str, Any] | None = None
"""Additional properties for the dialog content. Can include
typography-related attributes.
https://mui.com/material-ui/api/dialog-content-text/"""

disableEscapeKeyDown: bool | None = None
"""If true, pressing the Escape key does not close the dialog."""

fullScreen: bool | None = None
"""If true, the dialog will be displayed in full-screen mode."""

fullWidth: bool | None = None
"""If true, the dialog will take up the full width of the screen."""

maxWidth: Literal["xs", "sm", "md", "lg", "xl", False] | str | None = None
"""The maximum width of the dialog."""

scroll: Literal["body", "paper"] | None = None
"""Determines the scroll behavior of the dialog's content."""

ariaLabel: str | None = None
"""Defines a string value that labels the dialog for accessibility."""

ariaDescribedBy: str | None = None
"""Defines the ID of an element that describes the dialog for
accessibility."""
2 changes: 2 additions & 0 deletions chartlets.py/demo/my_extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from .my_panel_2 import panel as my_panel_2
from .my_panel_3 import panel as my_panel_3
from .my_panel_4 import panel as my_panel_4
from .my_panel_5 import panel as my_panel_5

ext = Extension(__name__)
ext.add(my_panel_1)
ext.add(my_panel_2)
ext.add(my_panel_3)
ext.add(my_panel_4)
ext.add(my_panel_5)
Loading