Skip to content

Commit 844226f

Browse files
committed
0.0.21
1 parent 9247557 commit 844226f

23 files changed

+284
-181
lines changed

chartlets.js/CHANGES.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## Version 0.0.21 (from 2024/11/19)
2+
3+
* `Component` children can now also be text nodes (of type `string`).
4+
5+
* `Typography` component has children instead of `text`.
6+
7+
* A component's `children` property can now be changed, even from a
8+
scalar.
9+
10+
* Renamed `Dropdown` component into `Select`
11+
(to refer to MUI component with same name).
12+
13+
* `Select` component has more flexible options.
14+
115
## Version 0.0.20 (from 2024/11/19)
216

317
* Using `FrameworkOptions.getDerivedHostState` also in

chartlets.js/src/lib/actions/helpers/applyStateChangeRequests.test.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ import {
1515
const componentTree: ComponentState = {
1616
type: "Box",
1717
id: "b1",
18-
components: [
18+
children: [
1919
{ type: "Plot", id: "p1", chart: null } as PlotState,
2020
{
2121
type: "Box",
2222
id: "b2",
23-
components: [
23+
children: [
2424
{ type: "Checkbox", id: "cb1", value: true },
25-
{ type: "Dropdown", id: "dd1", value: 13 },
25+
{ type: "Select", id: "dd1", value: 13 },
2626
],
2727
},
2828
],
@@ -73,7 +73,10 @@ describe("Test that applyContributionChangeRequests()", () => {
7373
]);
7474
expect(newState).not.toBe(contributionsRecord);
7575
expect(
76-
newState["panels"][0].component!.components![1].components![1].value,
76+
(
77+
(newState["panels"][0].component!.children![1] as ComponentState)
78+
.children![1] as ComponentState
79+
).value,
7780
).toEqual(14);
7881
});
7982

@@ -94,7 +97,10 @@ describe("Test that applyComponentStateChange()", () => {
9497
value: false,
9598
});
9699
expect(newState).not.toBe(componentTree);
97-
expect(newState.components![1].components![0].value).toEqual(false);
100+
expect(
101+
((newState.children![1] as ComponentState).children![0] as ComponentState)
102+
.value,
103+
).toEqual(false);
98104
});
99105

100106
it("doesn't change the state if value stays the same", () => {

chartlets.js/src/lib/actions/helpers/applyStateChangeRequests.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import type {
33
StateChangeRequest,
44
} from "@/lib/types/model/callback";
55
import { store } from "@/lib/store";
6-
import type {
7-
ComponentState,
8-
ContainerState,
6+
import {
7+
type ComponentState,
8+
type ContainerState,
9+
isComponentState,
10+
isContainerState,
911
} from "@/lib/types/state/component";
1012
import type { ContribPoint } from "@/lib/types/model/extension";
1113
import type { ContributionState } from "@/lib";
1214
import { updateArray } from "@/lib/utils/updateArray";
13-
import { isContainerState } from "@/lib/actions/helpers/isContainerState";
14-
import { getValue, setValue } from "@/lib/utils/objPath";
15+
import { getValue, normalizeObjPath, setValue } from "@/lib/utils/objPath";
1516

1617
export function applyStateChangeRequests(
1718
stateChangeRequests: StateChangeRequest[],
@@ -106,26 +107,37 @@ export function applyComponentStateChange(
106107
stateChange: StateChange,
107108
): ComponentState {
108109
if (component.id === stateChange.id) {
109-
const property = stateChange.property;
110+
const property = normalizeObjPath(stateChange.property);
110111
const valueOld = getValue(component, property);
111112
const valueNew = stateChange.value;
112-
if (valueOld !== valueNew) {
113+
if (
114+
property[property.length - 1] === "children" &&
115+
!Array.isArray(valueNew) &&
116+
valueNew !== null &&
117+
valueNew !== undefined
118+
) {
119+
// Special case if the value of "children" is changed:
120+
// convert scalar valueNew into one-element array
121+
return setValue(component, property, [valueNew]);
122+
} else if (valueOld !== valueNew) {
113123
return setValue(component, property, valueNew);
114124
}
115125
} else if (isContainerState(component)) {
116126
const containerOld: ContainerState = component;
117127
let containerNew: ContainerState = containerOld;
118-
for (let i = 0; i < containerOld.components.length; i++) {
119-
const itemOld = containerOld.components[i];
120-
const itemNew = applyComponentStateChange(itemOld, stateChange);
121-
if (itemNew !== itemOld) {
122-
if (containerNew === containerOld) {
123-
containerNew = {
124-
...containerOld,
125-
components: [...containerOld.components],
126-
};
128+
for (let i = 0; i < containerOld.children.length; i++) {
129+
const itemOld = containerOld.children[i];
130+
if (isComponentState(itemOld)) {
131+
const itemNew = applyComponentStateChange(itemOld, stateChange);
132+
if (itemNew !== itemOld) {
133+
if (containerNew === containerOld) {
134+
containerNew = {
135+
...containerOld,
136+
children: [...containerOld.children],
137+
};
138+
}
139+
containerNew.children[i] = itemNew;
127140
}
128-
containerNew.components[i] = itemNew;
129141
}
130142
}
131143
return containerNew;

chartlets.js/src/lib/actions/helpers/getInputValues.test.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import {
99
const componentState: ComponentState = {
1010
type: "Box",
1111
id: "b1",
12-
components: [
12+
children: [
1313
{ type: "Plot", id: "p1", chart: null } as PlotState,
1414
{
1515
type: "Box",
1616
id: "b2",
17-
components: [
17+
children: [
1818
{ type: "Checkbox", id: "cb1", value: true },
19-
{ type: "Dropdown", id: "dd1", value: 13 },
19+
{ type: "Select", id: "dd1", value: 13 },
2020
],
2121
},
2222
],
@@ -119,15 +119,18 @@ describe("Test that getInputValueFromState()", () => {
119119

120120
it("works with derived state", () => {
121121
const state = { x: { y: [4, 5, 6] } };
122-
const getDerivedState = (_state: object, name: string): number | undefined =>
123-
name === "x.ySum"
122+
const getDerivedState = (
123+
_state: object,
124+
name: string,
125+
): number | undefined =>
126+
name === "x.ySum"
124127
? state.x.y[0] + state.x.y[1] + state.x.y[2]
125128
: undefined;
126129
expect(
127130
getInputValueFromState(
128-
{ link: "container", property: "x.ySum" },
129-
state,
130-
getDerivedState,
131+
{ link: "container", property: "x.ySum" },
132+
state,
133+
getDerivedState,
131134
),
132135
).toEqual(4 + 5 + 6);
133136
});

chartlets.js/src/lib/actions/helpers/getInputValues.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { Input } from "@/lib/types/model/channel";
22
import type { ContributionState } from "@/lib/types/state/contribution";
3-
import type { ComponentState } from "@/lib/types/state/component";
4-
import { isContainerState } from "@/lib/actions/helpers/isContainerState";
3+
import {
4+
type ComponentState,
5+
isComponentState,
6+
isContainerState,
7+
} from "@/lib/types/state/component";
58
import { formatObjPath, getValue, normalizeObjPath } from "@/lib/utils/objPath";
69
import { isObject } from "@/lib/utils/isObject";
710
import type { GetDerivedState } from "@/lib/types/state/store";
@@ -56,11 +59,13 @@ export function getInputValueFromComponent(
5659
if (componentState.id === input.id) {
5760
return getValue(componentState, input.property);
5861
} else if (isContainerState(componentState)) {
59-
for (let i = 0; i < componentState.components.length; i++) {
60-
const item = componentState.components[i];
61-
const itemValue = getInputValueFromComponent(input, item);
62-
if (itemValue !== noValue) {
63-
return itemValue;
62+
for (let i = 0; i < componentState.children.length; i++) {
63+
const item = componentState.children[i];
64+
if (isComponentState(item)) {
65+
const itemValue = getInputValueFromComponent(input, item);
66+
if (itemValue !== noValue) {
67+
return itemValue;
68+
}
6469
}
6570
}
6671
}
Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +0,0 @@
1-
import {
2-
type ComponentState,
3-
type ContainerState,
4-
} from "@/lib/types/state/component";
5-
6-
export function isContainerState(
7-
component: ComponentState,
8-
): component is ContainerState {
9-
return !!component.components;
10-
}

chartlets.js/src/lib/components/Box.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface BoxProps extends Omit<BoxState, "type"> {
88
onChange: ComponentChangeHandler;
99
}
1010

11-
export function Box({ id, style, components, onChange }: BoxProps) {
11+
export function Box({ id, style, children: components, onChange }: BoxProps) {
1212
return (
1313
<MuiBox id={id} style={style}>
1414
<ComponentChildren components={components} onChange={onChange} />

chartlets.js/src/lib/components/Component.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { type ComponentChangeHandler } from "@/lib/types/state/event";
33
import { Button, type ButtonProps } from "./Button";
44
import { Box, type BoxProps } from "./Box";
55
import { Checkbox, type CheckboxProps } from "./Checkbox";
6-
import { Dropdown, type DropdownProps } from "./Dropdown";
6+
import { Select, type SelectProps } from "./Select";
77
import { Plot, type PlotProps } from "./Plot";
88
import { Typography, type TypographyProps } from "@/lib/components/Typography";
99

@@ -12,16 +12,16 @@ export interface ComponentProps extends ComponentState {
1212
}
1313

1414
export function Component({ type, ...props }: ComponentProps) {
15-
// TODO: allow for registering components via their types
15+
// TODO: allow for registering children via their types
1616
// and make following code generic.
1717
//
1818
// const DashiComp = Registry.getComponent(link);
1919
// return return <DashiComp {...props} />;
2020
//
2121
if (type === "Plot") {
2222
return <Plot {...(props as PlotProps)} />;
23-
} else if (type === "Dropdown") {
24-
return <Dropdown {...(props as DropdownProps)} />;
23+
} else if (type === "Select") {
24+
return <Select {...(props as SelectProps)} />;
2525
} else if (type === "Button") {
2626
return <Button {...(props as ButtonProps)} />;
2727
} else if (type === "Box") {

chartlets.js/src/lib/components/ComponentChildren.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { type ComponentChangeHandler } from "@/lib/types/state/event";
2-
import { type ComponentState } from "@/lib/types/state/component";
2+
import {
3+
type ComponentItem,
4+
isComponentState,
5+
} from "@/lib/types/state/component";
36
import { Component } from "./Component";
47

58
export interface ComponentChildrenProps {
6-
components?: ComponentState[];
9+
components?: ComponentItem[];
710
onChange: ComponentChangeHandler;
811
}
912

@@ -16,9 +19,13 @@ export function ComponentChildren({
1619
}
1720
return (
1821
<>
19-
{components.map((component, index) => {
20-
const key = component.id || index;
21-
return <Component key={key} {...component} onChange={onChange} />;
22+
{components.map((item, index) => {
23+
if (isComponentState(item)) {
24+
const key = item.id || index;
25+
return <Component key={key} {...item} onChange={onChange} />;
26+
} else {
27+
return item;
28+
}
2229
})}
2330
</>
2431
);

chartlets.js/src/lib/components/Dropdown.tsx

Lines changed: 0 additions & 59 deletions
This file was deleted.

0 commit comments

Comments
 (0)