@etsoo/materialui 1.4.26 → 1.4.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/ComboBox.tsx +15 -6
- package/__tests__/ResponsePage.tsx +31 -26
- package/__tests__/SelectEx.tsx +17 -15
- package/__tests__/SwitchAnt.tsx +5 -5
- package/lib/UserAvatarEditor.d.ts +4 -0
- package/lib/UserAvatarEditor.js +5 -5
- package/lib/app/Labels.d.ts +1 -1
- package/lib/app/Labels.js +1 -1
- package/lib/app/ServiceApp.d.ts +2 -1
- package/lib/app/ServiceApp.js +11 -3
- package/package.json +4 -4
- package/src/UserAvatarEditor.tsx +13 -7
- package/src/app/Labels.ts +1 -1
- package/src/app/ServiceApp.ts +10 -3
package/__tests__/ComboBox.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ComboBox } from "../src";
|
|
3
|
-
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { act, fireEvent, render, screen } from "@testing-library/react";
|
|
4
4
|
|
|
5
5
|
it("Render ComboBox", async () => {
|
|
6
6
|
// Arrange
|
|
@@ -10,13 +10,22 @@ it("Render ComboBox", async () => {
|
|
|
10
10
|
{ id: 2, name: "Name 2" }
|
|
11
11
|
];
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
act(() => {
|
|
14
|
+
render(
|
|
15
|
+
<ComboBox<T>
|
|
16
|
+
name="Test"
|
|
17
|
+
options={options}
|
|
18
|
+
labelField="name"
|
|
19
|
+
label="Test"
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
});
|
|
16
23
|
|
|
17
24
|
// Act, click the list
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
act(() => {
|
|
26
|
+
const clicked = fireEvent.click(screen.getByRole("button"));
|
|
27
|
+
expect(clicked).toBeTruthy();
|
|
28
|
+
});
|
|
20
29
|
|
|
21
30
|
// Get list item
|
|
22
31
|
const item = screen.getByText("Name 1");
|
|
@@ -21,33 +21,38 @@ const fieldTemplate = {
|
|
|
21
21
|
} as const;
|
|
22
22
|
|
|
23
23
|
it("Render ResponsePage", async () => {
|
|
24
|
-
// Act
|
|
25
|
-
render(
|
|
26
|
-
<ResponsivePage<Data, typeof fieldTemplate>
|
|
27
|
-
fields={[]}
|
|
28
|
-
columns={[
|
|
29
|
-
{ field: "id", header: "ID" },
|
|
30
|
-
{ field: "name", header: "Name" }
|
|
31
|
-
]}
|
|
32
|
-
height={200}
|
|
33
|
-
itemSize={[118, MUGlobal.pagePaddings]}
|
|
34
|
-
fieldTemplate={fieldTemplate}
|
|
35
|
-
loadData={({ id }) =>
|
|
36
|
-
Promise.resolve([
|
|
37
|
-
{ id: 1, name: "Name 1" },
|
|
38
|
-
{ id: 2, name: "Name 2" },
|
|
39
|
-
{ id: id ?? 0, name: "auto" }
|
|
40
|
-
])
|
|
41
|
-
}
|
|
42
|
-
innerItemRenderer={(props) =>
|
|
43
|
-
MobileListItemRenderer(props, (data) => {
|
|
44
|
-
return [data.name, undefined, [], <React.Fragment></React.Fragment>];
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
/>
|
|
48
|
-
);
|
|
49
|
-
|
|
50
24
|
act(() => {
|
|
25
|
+
// Act
|
|
26
|
+
render(
|
|
27
|
+
<ResponsivePage<Data, typeof fieldTemplate>
|
|
28
|
+
fields={[]}
|
|
29
|
+
columns={[
|
|
30
|
+
{ field: "id", header: "ID" },
|
|
31
|
+
{ field: "name", header: "Name" }
|
|
32
|
+
]}
|
|
33
|
+
height={200}
|
|
34
|
+
itemSize={[118, MUGlobal.pagePaddings]}
|
|
35
|
+
fieldTemplate={fieldTemplate}
|
|
36
|
+
loadData={({ id }) =>
|
|
37
|
+
Promise.resolve([
|
|
38
|
+
{ id: 1, name: "Name 1" },
|
|
39
|
+
{ id: 2, name: "Name 2" },
|
|
40
|
+
{ id: id ?? 0, name: "auto" }
|
|
41
|
+
])
|
|
42
|
+
}
|
|
43
|
+
innerItemRenderer={(props) =>
|
|
44
|
+
MobileListItemRenderer(props, (data) => {
|
|
45
|
+
return [
|
|
46
|
+
data.name,
|
|
47
|
+
undefined,
|
|
48
|
+
[],
|
|
49
|
+
<React.Fragment></React.Fragment>
|
|
50
|
+
];
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
|
|
51
56
|
// Fast forward
|
|
52
57
|
vi.runOnlyPendingTimers();
|
|
53
58
|
});
|
package/__tests__/SelectEx.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { act } from "react";
|
|
2
2
|
import { SelectEx } from "../src";
|
|
3
|
-
import {
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
4
|
import { ListType1, Utils } from "@etsoo/shared";
|
|
5
5
|
|
|
6
6
|
it("Render SelectEx", async () => {
|
|
@@ -19,16 +19,18 @@ it("Render SelectEx", async () => {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
// Render component
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
act(() => {
|
|
23
|
+
render(
|
|
24
|
+
<SelectEx<T>
|
|
25
|
+
options={options}
|
|
26
|
+
name="test"
|
|
27
|
+
onItemChange={itemChangeCallback}
|
|
28
|
+
value={1}
|
|
29
|
+
search
|
|
30
|
+
labelField="name"
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
32
34
|
|
|
33
35
|
expect(itemChangeCallback).toHaveBeenCalled();
|
|
34
36
|
|
|
@@ -45,10 +47,10 @@ it("Render SelectEx", async () => {
|
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
// Get list item
|
|
48
|
-
const itemName2 = await findByText("Name 2");
|
|
50
|
+
const itemName2 = await screen.findByText("Name 2");
|
|
49
51
|
expect(itemName2.nodeName).toBe("SPAN");
|
|
50
52
|
|
|
51
|
-
const itemBlank = await findByText("---");
|
|
53
|
+
const itemBlank = await screen.findByText("---");
|
|
52
54
|
expect(itemBlank.nodeName).toBe("SPAN");
|
|
53
55
|
|
|
54
56
|
act(() => {
|
|
@@ -90,12 +92,12 @@ it("Render multiple SelectEx", async () => {
|
|
|
90
92
|
fireEvent.mouseDown(button); // Not click
|
|
91
93
|
|
|
92
94
|
// Get list item
|
|
93
|
-
const itemName1 = await findByText(
|
|
95
|
+
const itemName1 = await screen.findByText("Name 1");
|
|
94
96
|
const checkbox1 = itemName1.closest("li")?.querySelector("input");
|
|
95
97
|
|
|
96
98
|
expect(checkbox1?.checked).toBeTruthy();
|
|
97
99
|
|
|
98
|
-
const itemName3 = await findByText(
|
|
100
|
+
const itemName3 = await screen.findByText("Name 3");
|
|
99
101
|
expect(itemName3.nodeName).toBe("SPAN");
|
|
100
102
|
|
|
101
103
|
// Checkbox
|
package/__tests__/SwitchAnt.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { screen, render } from "@testing-library/react";
|
|
2
2
|
import React, { act } from "react";
|
|
3
3
|
import { SwitchAnt } from "../src/SwitchAnt";
|
|
4
4
|
|
|
@@ -8,11 +8,11 @@ it("SwitchAnt Tests", () => {
|
|
|
8
8
|
);
|
|
9
9
|
|
|
10
10
|
// Render component
|
|
11
|
-
|
|
12
|
-
<SwitchAnt startLabel="No" endLabel="Yes" onChange={onChange} />
|
|
13
|
-
);
|
|
11
|
+
act(() => {
|
|
12
|
+
render(<SwitchAnt startLabel="No" endLabel="Yes" onChange={onChange} />);
|
|
13
|
+
});
|
|
14
14
|
|
|
15
|
-
const yes = getByText(
|
|
15
|
+
const yes = screen.getByText("Yes");
|
|
16
16
|
|
|
17
17
|
act(() => {
|
|
18
18
|
yes.click();
|
package/lib/UserAvatarEditor.js
CHANGED
|
@@ -4,7 +4,7 @@ import React from "react";
|
|
|
4
4
|
import RotateLeftIcon from "@mui/icons-material/RotateLeft";
|
|
5
5
|
import RotateRightIcon from "@mui/icons-material/RotateRight";
|
|
6
6
|
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
|
7
|
-
import
|
|
7
|
+
import ImageIcon from "@mui/icons-material/Image";
|
|
8
8
|
import DoneIcon from "@mui/icons-material/Done";
|
|
9
9
|
import RemoveIcon from "@mui/icons-material/Remove";
|
|
10
10
|
import AddIcon from "@mui/icons-material/Add";
|
|
@@ -22,14 +22,14 @@ const defaultState = {
|
|
|
22
22
|
* @returns Component
|
|
23
23
|
*/
|
|
24
24
|
export function UserAvatarEditor(props) {
|
|
25
|
+
// Labels
|
|
26
|
+
const labels = Labels.UserAvatarEditor;
|
|
25
27
|
// Destruct
|
|
26
|
-
const { border = 30, image, maxWidth, onDone, scaledResult = false, width = defaultSize, height = defaultSize, range = [0.1, 2, 0.1] } = props;
|
|
28
|
+
const { border = 30, image, maxWidth, onDone, scaledResult = false, width = defaultSize, height = defaultSize, range = [0.1, 2, 0.1], selectFileLabel = labels.selectFile + "..." } = props;
|
|
27
29
|
// Container width
|
|
28
30
|
const containerWidth = width + 2 * border + 44 + 4;
|
|
29
31
|
// Calculated max width
|
|
30
32
|
const maxWidthCalculated = maxWidth == null || maxWidth < defaultSize ? 2 * width : maxWidth;
|
|
31
|
-
// Labels
|
|
32
|
-
const labels = Labels.UserAvatarEditor;
|
|
33
33
|
// Ref
|
|
34
34
|
const ref = React.createRef();
|
|
35
35
|
// Image type
|
|
@@ -144,5 +144,5 @@ export function UserAvatarEditor(props) {
|
|
|
144
144
|
};
|
|
145
145
|
// Load the component
|
|
146
146
|
const AE = React.lazy(() => import("react-avatar-editor"));
|
|
147
|
-
return (_jsxs(Stack, { direction: "column", spacing: 0.5, width: containerWidth, children: [_jsx(FileUploadButton, { variant: "outlined", size: "medium", startIcon: _jsx(
|
|
147
|
+
return (_jsxs(Stack, { direction: "column", spacing: 0.5, width: containerWidth, children: [_jsx(FileUploadButton, { variant: "outlined", size: "medium", startIcon: _jsx(ImageIcon, {}), fullWidth: true, onUploadFiles: handleFileUpload, inputProps: { multiple: false, accept: "image/png, image/jpeg" }, children: selectFileLabel }), _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(React.Suspense, { fallback: _jsx(Skeleton, { variant: "rounded", width: width, height: localHeight }), children: _jsx(AE, { ref: ref, border: border, width: width, height: localHeight, onLoadSuccess: handleLoad, image: previewImage ?? "", scale: editorState.scale, rotate: editorState.rotate }) }), _jsxs(ButtonGroup, { size: "small", orientation: "vertical", disabled: !ready, children: [_jsx(Button, { onClick: () => handleRotate(90), title: labels.rotateRight, children: _jsx(RotateRightIcon, {}) }), _jsx(Button, { onClick: () => handleRotate(-90), title: labels.rotateLeft, children: _jsx(RotateLeftIcon, {}) }), _jsx(Button, { onClick: handleReset, title: labels.reset, children: _jsx(ClearAllIcon, {}) })] })] }), _jsxs(Stack, { spacing: 0.5, direction: "row", sx: { paddingBottom: 2 }, alignItems: "center", children: [_jsx(IconButton, { size: "small", disabled: !ready || editorState.scale <= min, onClick: () => adjustScale(false), children: _jsx(RemoveIcon, {}) }), _jsx(Slider, { title: labels.zoom, disabled: !ready, min: min, max: max, step: step, value: editorState.scale, valueLabelDisplay: "auto", valueLabelFormat: (value) => `${Math.round(100 * value) / 100}`, marks: marks, onChange: handleZoom }), _jsx(IconButton, { size: "small", disabled: !ready || editorState.scale >= max, onClick: () => adjustScale(true), children: _jsx(AddIcon, {}) })] }), _jsx(Button, { ref: buttonRef, variant: "contained", startIcon: _jsx(DoneIcon, {}), disabled: !ready, onClick: handleDone, children: labels.done })] }));
|
|
148
148
|
}
|
package/lib/app/Labels.d.ts
CHANGED
package/lib/app/Labels.js
CHANGED
package/lib/app/ServiceApp.d.ts
CHANGED
|
@@ -32,8 +32,9 @@ export declare class ServiceApp<U extends IServiceUser = IServiceUser, P extends
|
|
|
32
32
|
constructor(settings: S, name: string, debug?: boolean);
|
|
33
33
|
/**
|
|
34
34
|
* Load core system UI
|
|
35
|
+
* @param tryLogin Try login or not
|
|
35
36
|
*/
|
|
36
|
-
loadCore(): void;
|
|
37
|
+
loadCore(tryLogin?: boolean): void;
|
|
37
38
|
/**
|
|
38
39
|
* Go to the login page
|
|
39
40
|
* @param data Login parameters
|
package/lib/app/ServiceApp.js
CHANGED
|
@@ -2,6 +2,7 @@ import { AuthApi, BridgeUtils } from "@etsoo/appscript";
|
|
|
2
2
|
import { ReactApp } from "./ReactApp";
|
|
3
3
|
const coreName = "core";
|
|
4
4
|
const coreTokenKey = "core-refresh-token";
|
|
5
|
+
const tryLoginKey = "tryLogin";
|
|
5
6
|
/**
|
|
6
7
|
* Core Service App
|
|
7
8
|
* Service login to core system, get the refesh token and access token
|
|
@@ -39,13 +40,20 @@ export class ServiceApp extends ReactApp {
|
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
41
42
|
* Load core system UI
|
|
43
|
+
* @param tryLogin Try login or not
|
|
42
44
|
*/
|
|
43
|
-
loadCore() {
|
|
45
|
+
loadCore(tryLogin = false) {
|
|
44
46
|
if (BridgeUtils.host == null) {
|
|
45
|
-
|
|
47
|
+
let url = this.coreEndpoint.webUrl;
|
|
48
|
+
if (!tryLogin)
|
|
49
|
+
url = url.addUrlParam(tryLoginKey, tryLogin);
|
|
50
|
+
globalThis.location.href = url;
|
|
46
51
|
}
|
|
47
52
|
else {
|
|
48
|
-
|
|
53
|
+
const startUrl = tryLogin
|
|
54
|
+
? undefined
|
|
55
|
+
: "".addUrlParam(tryLoginKey, tryLogin);
|
|
56
|
+
BridgeUtils.host.loadApp(coreName, startUrl);
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.28",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@emotion/css": "^11.13.5",
|
|
36
36
|
"@emotion/react": "^11.13.5",
|
|
37
37
|
"@emotion/styled": "^11.13.5",
|
|
38
|
-
"@etsoo/appscript": "^1.5.
|
|
38
|
+
"@etsoo/appscript": "^1.5.70",
|
|
39
39
|
"@etsoo/notificationbase": "^1.1.54",
|
|
40
40
|
"@etsoo/react": "^1.8.3",
|
|
41
41
|
"@etsoo/shared": "^1.2.55",
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"@types/react-dom": "^18.3.1",
|
|
71
71
|
"@types/react-input-mask": "^3.0.6",
|
|
72
72
|
"@types/react-window": "^1.8.8",
|
|
73
|
-
"@vitejs/plugin-react": "^4.3.
|
|
73
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
74
74
|
"jsdom": "^25.0.1",
|
|
75
75
|
"typescript": "^5.7.2",
|
|
76
|
-
"vitest": "^2.1.
|
|
76
|
+
"vitest": "^2.1.6"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/UserAvatarEditor.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import type AvatarEditor from "react-avatar-editor";
|
|
|
11
11
|
import RotateLeftIcon from "@mui/icons-material/RotateLeft";
|
|
12
12
|
import RotateRightIcon from "@mui/icons-material/RotateRight";
|
|
13
13
|
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
|
14
|
-
import
|
|
14
|
+
import ImageIcon from "@mui/icons-material/Image";
|
|
15
15
|
import DoneIcon from "@mui/icons-material/Done";
|
|
16
16
|
import RemoveIcon from "@mui/icons-material/Remove";
|
|
17
17
|
import AddIcon from "@mui/icons-material/Add";
|
|
@@ -67,6 +67,11 @@ export interface UserAvatarEditorProps {
|
|
|
67
67
|
*/
|
|
68
68
|
onDone: UserAvatarEditorOnDoneHandler;
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Select file label
|
|
72
|
+
*/
|
|
73
|
+
selectFileLabel?: string;
|
|
74
|
+
|
|
70
75
|
/**
|
|
71
76
|
* Return scaled result?
|
|
72
77
|
*/
|
|
@@ -105,6 +110,9 @@ const defaultState: EditorState = {
|
|
|
105
110
|
* @returns Component
|
|
106
111
|
*/
|
|
107
112
|
export function UserAvatarEditor(props: UserAvatarEditorProps) {
|
|
113
|
+
// Labels
|
|
114
|
+
const labels = Labels.UserAvatarEditor;
|
|
115
|
+
|
|
108
116
|
// Destruct
|
|
109
117
|
const {
|
|
110
118
|
border = 30,
|
|
@@ -114,7 +122,8 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
|
|
|
114
122
|
scaledResult = false,
|
|
115
123
|
width = defaultSize,
|
|
116
124
|
height = defaultSize,
|
|
117
|
-
range = [0.1, 2, 0.1]
|
|
125
|
+
range = [0.1, 2, 0.1],
|
|
126
|
+
selectFileLabel = labels.selectFile + "..."
|
|
118
127
|
} = props;
|
|
119
128
|
|
|
120
129
|
// Container width
|
|
@@ -124,9 +133,6 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
|
|
|
124
133
|
const maxWidthCalculated =
|
|
125
134
|
maxWidth == null || maxWidth < defaultSize ? 2 * width : maxWidth;
|
|
126
135
|
|
|
127
|
-
// Labels
|
|
128
|
-
const labels = Labels.UserAvatarEditor;
|
|
129
|
-
|
|
130
136
|
// Ref
|
|
131
137
|
const ref = React.createRef<AvatarEditor>();
|
|
132
138
|
|
|
@@ -280,12 +286,12 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
|
|
|
280
286
|
<FileUploadButton
|
|
281
287
|
variant="outlined"
|
|
282
288
|
size="medium"
|
|
283
|
-
startIcon={<
|
|
289
|
+
startIcon={<ImageIcon />}
|
|
284
290
|
fullWidth
|
|
285
291
|
onUploadFiles={handleFileUpload}
|
|
286
292
|
inputProps={{ multiple: false, accept: "image/png, image/jpeg" }}
|
|
287
293
|
>
|
|
288
|
-
{
|
|
294
|
+
{selectFileLabel}
|
|
289
295
|
</FileUploadButton>
|
|
290
296
|
<Stack direction="row" spacing={0.5}>
|
|
291
297
|
<React.Suspense
|
package/src/app/Labels.ts
CHANGED
package/src/app/ServiceApp.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { ReactApp } from "./ReactApp";
|
|
|
15
15
|
|
|
16
16
|
const coreName = "core";
|
|
17
17
|
const coreTokenKey = "core-refresh-token";
|
|
18
|
+
const tryLoginKey = "tryLogin";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Core Service App
|
|
@@ -65,12 +66,18 @@ export class ServiceApp<
|
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
68
|
* Load core system UI
|
|
69
|
+
* @param tryLogin Try login or not
|
|
68
70
|
*/
|
|
69
|
-
loadCore() {
|
|
71
|
+
loadCore(tryLogin: boolean = false) {
|
|
70
72
|
if (BridgeUtils.host == null) {
|
|
71
|
-
|
|
73
|
+
let url = this.coreEndpoint.webUrl;
|
|
74
|
+
if (!tryLogin) url = url.addUrlParam(tryLoginKey, tryLogin);
|
|
75
|
+
globalThis.location.href = url;
|
|
72
76
|
} else {
|
|
73
|
-
|
|
77
|
+
const startUrl = tryLogin
|
|
78
|
+
? undefined
|
|
79
|
+
: "".addUrlParam(tryLoginKey, tryLogin);
|
|
80
|
+
BridgeUtils.host.loadApp(coreName, startUrl);
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
|