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 @@ -16,8 +16,11 @@

* Typology component now allows color and text arguments.
If a user uses text and children, the text argument replaces the
children.
children.

* Applied changes to the return value of `Tabs` component and added
demo panel H, that showcases the use of `Tabs` component. (#129).

## Version 0.1.6 (from 2025/06/18)

* Implemented dynamic resizing for Vega Charts when the side panel's
Expand Down
44 changes: 34 additions & 10 deletions chartlets.js/packages/lib/src/plugins/mui/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import MuiBox from "@mui/material/Box";
import MuiIcon from "@mui/material/Icon";
import MuiTabs from "@mui/material/Tabs";
import MuiTab from "@mui/material/Tab";

import type { ComponentProps, ComponentState } from "@/index";
import type { SyntheticEvent } from "react";
import { Box } from "@/plugins/mui/Box";
import { isString } from "@/utils/isString";
import { isComponentState } from "@/types/state/component";

Expand All @@ -12,6 +14,7 @@ interface TabState {
label?: string;
icon?: string;
disabled?: boolean;
children?: ComponentProps[];
}

interface TabsState extends ComponentState {
Expand Down Expand Up @@ -41,19 +44,40 @@ export function Tabs({
}
};
return (
<MuiTabs id={id} style={style} value={value} onChange={handleChange}>
{tabItems?.map((tab) => {
<MuiBox sx={{ width: "100%" }}>
<MuiBox sx={{ borderBottom: 1, borderColor: "divider" }}>
<MuiTabs id={id} style={style} value={value} onChange={handleChange}>
{tabItems?.map((tab, index) => {
const tabState = isComponentState(tab)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this cleaner code if you introduce a type guard isTabState().

? (tab as TabState)
: undefined;
return (
<MuiTab
key={index}
label={tabState ? tabState.label : isString(tab) ? tab : ""}
icon={
tabState &&
tabState.icon && <MuiIcon>{tabState.icon}</MuiIcon>
}
disabled={disabled || (tabState && tabState.disabled)}
/>
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this cleaner by constructing separate MuiTab components, one for the case where !tabState and the other where you have it.

Even better would be to ensure / assert upfront whether tabItems contains only TabState instances.

})}
</MuiTabs>
</MuiBox>
{tabItems?.map((tab, index) => {
const tabState = isComponentState(tab) ? (tab as TabState) : undefined;
return (
<MuiTab
label={tabState ? tabState.label : isString(tab) ? tab : ""}
icon={
tabState && tabState.icon && <MuiIcon>{tabState.icon}</MuiIcon>
}
disabled={disabled || (tabState && tabState.disabled)}
/>
value === index && (
<Box
key={index}
type={type}
onChange={onChange}
children={tabState?.children ?? undefined}
/>
)
);
})}
</MuiTabs>
</MuiBox>
);
}
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 @@ -6,6 +6,7 @@
from .my_panel_5 import panel as my_panel_5
from .my_panel_6 import panel as my_panel_6
from .my_panel_7 import panel as my_panel_7
from .my_panel_8 import panel as my_panel_8


ext = Extension(__name__)
Expand All @@ -16,3 +17,4 @@
ext.add(my_panel_5)
ext.add(my_panel_6)
ext.add(my_panel_7)
ext.add(my_panel_8)
69 changes: 69 additions & 0 deletions chartlets.py/demo/my_extension/my_panel_8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import altair as alt

from chartlets import Component, Input, State, Output
from chartlets.components import (Tabs, Tab, Typography, Box,
VegaChart, Table)
from chartlets.components.table import TableColumn, TableRow

from server.context import Context
from server.panel import Panel


panel = Panel(__name__, title="Panel H")


@panel.layout(State("@app", "selectedDatasetId"))
def render_panel(
ctx: Context,
selected_dataset_id: str = "",
) -> Component:
dataset = ctx.datasets.get(selected_dataset_id)

columns: list[TableColumn] = [
{"id": "id", "label": "ID", "sortDirection": "desc"},
{
"id": "firstName",
"label": "First Name",
"align": "left",
"sortDirection": "desc",
},
{"id": "lastName", "label": "Last Name", "align": "center"},
{"id": "age", "label": "Age"},
]

rows: TableRow = [
["1", "John", "Doe", 30],
["2", "Jane", "Smith", 25],
["3", "Peter", "Jones", 40],
]

table = Table(id="table", rows=rows, columns=columns, hover=True)

info_text = Typography(id="info_text", children=["This is a text."])
chart = VegaChart(
id="chart", chart=(
alt.Chart(dataset)
.mark_bar()
.encode(
x=alt.X("x:N", title="x"),
y=alt.Y("a:Q", title="a"))
.properties(width=290, height=300, title="Vega charts")
), style={"flexGrow": 1}
)

tab1 = Tab(id = "tab1", label="Tab 1", children=[table])
tab2 = Tab(id = "tab2", label="Tab 2", children=[info_text])
tab3 = Tab(id="tab3", label="Tab 3", children=[chart])

tabs = Tabs(id = "tabs", value = 0, children=[tab1,tab2,tab3])

return Box(
style={
"display": "flex",
"flexDirection": "column",
"width": "100%",
"height": "100%",
},
children=[ tabs ],
)