Skip to content

Commit 7501a3d

Browse files
committed
Merge branch 'main' into forman-41-Tabs
# Conflicts: # chartlets.py/CHANGES.md
2 parents b8a0fa0 + af749d2 commit 7501a3d

29 files changed

+643
-59
lines changed

.github/workflows/backend-ci.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,11 @@ jobs:
2121
shell: bash -l {0}
2222
run: |
2323
cd chartlets.py
24-
pytest
24+
pytest --cov=chartlets.py --cov-report=xml
25+
26+
- name: Upload coverage reports to Codecov
27+
uses: codecov/codecov-action@v4
28+
with:
29+
directory: chartlets.py/
30+
verbose: true
31+
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/frontend-ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ jobs:
3232
cd chartlets.js/packages/lib
3333
npm run test
3434
35+
- name: Lib coverage
36+
run: |
37+
cd chartlets.js/packages/lib
38+
npm run coverage
39+
40+
- name: Upload coverage reports for lib to Codecov
41+
uses: codecov/codecov-action@v4
42+
with:
43+
directory: chartlets.js/packages/lib/
44+
verbose: true
45+
token: ${{ secrets.CODECOV_TOKEN }}
46+
3547
- name: Lib build
3648
run: |
3749
cd chartlets.js/packages/lib

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Chartlets
22

3+
[![CI](https://github.com/bcdev/chartlets/actions/workflows/frontend-ci.yml/badge.svg)](https://github.com/bcdev/chartlets/actions/workflows/frontend-ci.yml)
4+
[![npm](https://badge.fury.io/js/chartlets.svg)](https://npmjs.org/package/chartlets)
5+
6+
[![CI](https://github.com/bcdev/chartlets/actions/workflows/backend-ci.yml/badge.svg)](https://github.com/bcdev/chartlets/actions/workflows/backend-ci.yml)
7+
[![PyPI](https://img.shields.io/pypi/v/chartlets)](https://pypi.org/project/chartlets/)
8+
39
Chartlets is a software framework that allows websites developed with
410
React to be extended by server-side UI contributions programmed in Python
511
or other programming languages.

chartlets.js/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@
3939

4040
* New (MUI) components
4141
- `LinearProgress`
42+
- `RadioGroup` and `Radio`
4243
- `Switch`
4344
- `Tabs`
4445

46+
* Supporting `tooltip` property for interactive MUI components.
47+
4548
## Version 0.0.29 (from 2024/11/26)
4649

4750
* Resolved warnings that appeared when using Vega charts.

chartlets.js/package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

chartlets.js/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
22
"private": true,
3-
"workspaces": [ "packages/*"]
3+
"workspaces": [
4+
"packages/lib",
5+
"packages/demo"
6+
]
47
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {
2+
describe,
3+
it,
4+
vi,
5+
expect,
6+
beforeEach,
7+
afterEach,
8+
type Mock,
9+
} from "vitest";
10+
import { fetchCallback } from "./fetchCallback";
11+
12+
describe("fetchCallback", () => {
13+
beforeEach(() => {
14+
// Mock the global fetch function
15+
globalThis.fetch = vi.fn();
16+
});
17+
18+
afterEach(() => {
19+
// Restore the original fetch implementation
20+
vi.restoreAllMocks();
21+
});
22+
23+
it("should return expected data on success", async () => {
24+
// Define a mock response
25+
const expectedResult = {
26+
contribPoint: "panels",
27+
contribIndex: 0,
28+
stateChanges: [
29+
{
30+
id: "checkbox0",
31+
property: "value",
32+
value: true,
33+
},
34+
],
35+
};
36+
const mockResponse = {
37+
ok: true,
38+
status: 200,
39+
statusText: "ok",
40+
json: vi.fn().mockResolvedValue({
41+
result: expectedResult,
42+
}),
43+
};
44+
45+
// Mock fetch to resolve with the mock response
46+
(globalThis.fetch as Mock).mockResolvedValue(mockResponse);
47+
48+
// Call fetch
49+
const response = await fetchCallback([], {
50+
serverUrl: "https://chartlets-test",
51+
endpointName: "api",
52+
});
53+
54+
// Assertions
55+
expect(fetch).toHaveBeenCalledOnce();
56+
expect(response).toEqual({
57+
status: "ok",
58+
data: expectedResult,
59+
});
60+
});
61+
62+
it("should return error for bad API responses", async () => {
63+
// Define a mock response
64+
const mockResponse = {
65+
ok: true,
66+
status: 200,
67+
statusText: "ok",
68+
json: vi.fn().mockResolvedValue({ message: "Hello, world!" }),
69+
};
70+
71+
// Mock fetch to resolve with the mock response
72+
(globalThis.fetch as Mock).mockResolvedValue(mockResponse);
73+
74+
// Call fetch
75+
const response = await fetchCallback([], {
76+
serverUrl: "https://chartlets-test",
77+
endpointName: "api",
78+
});
79+
// Assertions
80+
expect(fetch).toHaveBeenCalledOnce();
81+
expect(response).toEqual({
82+
status: "failed",
83+
error: {
84+
message: "unexpected response from https://chartlets-test/api/callback",
85+
},
86+
});
87+
});
88+
});
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {
2+
describe,
3+
it,
4+
vi,
5+
expect,
6+
beforeEach,
7+
afterEach,
8+
type Mock,
9+
} from "vitest";
10+
import { fetchContributions } from "./fetchContributions";
11+
12+
describe("fetchContributions", () => {
13+
beforeEach(() => {
14+
// Mock the global fetch function
15+
globalThis.fetch = vi.fn();
16+
});
17+
18+
afterEach(() => {
19+
// Restore the original fetch implementation
20+
vi.restoreAllMocks();
21+
});
22+
23+
it("should return expected data on success", async () => {
24+
// Define a mock response
25+
const expectedResult = {
26+
extensions: [{ name: "e0", version: "1", contributes: ["panels"] }],
27+
contributions: {
28+
panels: [{ name: "p0", extension: "e0", layout: {} }],
29+
},
30+
};
31+
const mockResponse = {
32+
ok: true,
33+
status: 200,
34+
statusText: "ok",
35+
json: vi.fn().mockResolvedValue({
36+
result: expectedResult,
37+
}),
38+
};
39+
40+
// Mock fetch to resolve with the mock response
41+
(globalThis.fetch as Mock).mockResolvedValue(mockResponse);
42+
43+
// Call fetch
44+
const response = await fetchContributions({
45+
serverUrl: "https://chartlets-test",
46+
endpointName: "api",
47+
});
48+
49+
// Assertions
50+
expect(fetch).toHaveBeenCalledOnce();
51+
expect(response).toEqual({
52+
status: "ok",
53+
data: {
54+
extensions: [{ name: "e0", version: "1", contributes: ["panels"] }],
55+
contributions: {
56+
panels: [
57+
{
58+
name: "p0",
59+
extension: "e0",
60+
layout: { inputs: [], outputs: [] },
61+
callbacks: [],
62+
},
63+
],
64+
},
65+
},
66+
});
67+
});
68+
69+
it("should return error for bad responses", async () => {
70+
// Define a mock response
71+
const mockResponse = {
72+
ok: true,
73+
status: 200,
74+
statusText: "ok",
75+
json: vi.fn().mockResolvedValue({ message: "Hello, world!" }),
76+
};
77+
78+
// Mock fetch to resolve with the mock response
79+
(globalThis.fetch as Mock).mockResolvedValue(mockResponse);
80+
81+
// Call fetch
82+
const response = await fetchContributions({
83+
serverUrl: "https://chartlets-test",
84+
endpointName: "api",
85+
});
86+
// Assertions
87+
expect(fetch).toHaveBeenCalledOnce();
88+
expect(response).toEqual({
89+
status: "failed",
90+
error: {
91+
message:
92+
"unexpected response from https://chartlets-test/api/contributions",
93+
},
94+
});
95+
});
96+
});
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {
2+
describe,
3+
it,
4+
vi,
5+
expect,
6+
beforeEach,
7+
afterEach,
8+
type Mock,
9+
} from "vitest";
10+
import { fetchLayout } from "./fetchLayout";
11+
12+
describe("fetchLayout", () => {
13+
beforeEach(() => {
14+
// Mock the global fetch function
15+
globalThis.fetch = vi.fn();
16+
});
17+
18+
afterEach(() => {
19+
// Restore the original fetch implementation
20+
vi.restoreAllMocks();
21+
});
22+
23+
it("should return expected data on success", async () => {
24+
// Define a mock response
25+
const expectedResult = {
26+
type: "Button",
27+
text: "Click me!",
28+
};
29+
const mockResponse = {
30+
ok: true,
31+
status: 200,
32+
statusText: "ok",
33+
json: vi.fn().mockResolvedValue({
34+
result: expectedResult,
35+
}),
36+
};
37+
38+
// Mock fetch to resolve with the mock response
39+
(globalThis.fetch as Mock).mockResolvedValue(mockResponse);
40+
41+
// Call fetch
42+
const response = await fetchLayout("panels", 0, [], {
43+
serverUrl: "https://chartlets-test",
44+
endpointName: "api",
45+
});
46+
47+
// Assertions
48+
expect(fetch).toHaveBeenCalledOnce();
49+
expect(response).toEqual({
50+
status: "ok",
51+
data: expectedResult,
52+
});
53+
});
54+
55+
it("should return error for bad responses", async () => {
56+
// Define a mock response
57+
const mockResponse = {
58+
ok: true,
59+
status: 200,
60+
statusText: "ok",
61+
json: vi.fn().mockResolvedValue({ message: "Hello, world!" }),
62+
};
63+
64+
// Mock fetch to resolve with the mock response
65+
(globalThis.fetch as Mock).mockResolvedValue(mockResponse);
66+
67+
// Call fetch
68+
const response = await fetchLayout("panels", 0, [], {
69+
serverUrl: "https://chartlets-test",
70+
endpointName: "api",
71+
});
72+
// Assertions
73+
expect(fetch).toHaveBeenCalledOnce();
74+
expect(response).toEqual({
75+
status: "failed",
76+
error: {
77+
message:
78+
"unexpected response from https://chartlets-test/api/layout/panels/0",
79+
},
80+
});
81+
});
82+
});

chartlets.js/packages/lib/src/plugins/mui/Button.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import MuiButton from "@mui/material/Button";
33
import MuiIcon from "@mui/material/Icon";
44

55
import type { ComponentState, ComponentProps } from "@/index";
6+
import { Tooltip } from "./Tooltip";
67

78
interface ButtonState extends ComponentState {
89
text?: string;
@@ -45,18 +46,20 @@ export function Button({
4546
}
4647
};
4748
return (
48-
<MuiButton
49-
id={id}
50-
name={name}
51-
style={style}
52-
variant={variant}
53-
color={color}
54-
disabled={disabled}
55-
startIcon={startIcon && <MuiIcon>{startIcon}</MuiIcon>}
56-
endIcon={endIcon && <MuiIcon>{endIcon}</MuiIcon>}
57-
onClick={handleClick}
58-
>
59-
{text}
60-
</MuiButton>
49+
<Tooltip>
50+
<MuiButton
51+
id={id}
52+
name={name}
53+
style={style}
54+
variant={variant}
55+
color={color}
56+
disabled={disabled}
57+
startIcon={startIcon && <MuiIcon>{startIcon}</MuiIcon>}
58+
endIcon={endIcon && <MuiIcon>{endIcon}</MuiIcon>}
59+
onClick={handleClick}
60+
>
61+
{text}
62+
</MuiButton>
63+
</Tooltip>
6164
);
6265
}

0 commit comments

Comments
 (0)