@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.
@@ -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
- render(
14
- <ComboBox<T> name="Test" options={options} labelField="name" label="Test" />
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
- const clicked = fireEvent.click(screen.getByRole("button"));
19
- expect(clicked).toBeTruthy();
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
  });
@@ -1,6 +1,6 @@
1
1
  import React, { act } from "react";
2
2
  import { SelectEx } from "../src";
3
- import { findByText, fireEvent, render, screen } from "@testing-library/react";
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
- const { findByText } = render(
23
- <SelectEx<T>
24
- options={options}
25
- name="test"
26
- onItemChange={itemChangeCallback}
27
- value={1}
28
- search
29
- labelField="name"
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(baseElement, "Name 1");
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(baseElement, "Name 3");
100
+ const itemName3 = await screen.findByText("Name 3");
99
101
  expect(itemName3.nodeName).toBe("SPAN");
100
102
 
101
103
  // Checkbox
@@ -1,4 +1,4 @@
1
- import { getByText, render } from "@testing-library/react";
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
- const { baseElement } = render(
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(baseElement, "Yes");
15
+ const yes = screen.getByText("Yes");
16
16
 
17
17
  act(() => {
18
18
  yes.click();
@@ -30,6 +30,10 @@ export interface UserAvatarEditorProps {
30
30
  * On done handler
31
31
  */
32
32
  onDone: UserAvatarEditorOnDoneHandler;
33
+ /**
34
+ * Select file label
35
+ */
36
+ selectFileLabel?: string;
33
37
  /**
34
38
  * Return scaled result?
35
39
  */
@@ -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 ComputerIcon from "@mui/icons-material/Computer";
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(ComputerIcon, {}), fullWidth: true, onUploadFiles: handleFileUpload, inputProps: { multiple: false, accept: "image/png, image/jpeg" }, children: labels.upload }), _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 })] }));
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
  }
@@ -45,7 +45,7 @@ export declare namespace Labels {
45
45
  reset: string;
46
46
  rotateLeft: string;
47
47
  rotateRight: string;
48
- upload: string;
48
+ selectFile: string;
49
49
  zoom: string;
50
50
  };
51
51
  /**
package/lib/app/Labels.js CHANGED
@@ -46,7 +46,7 @@ export var Labels;
46
46
  reset: "Reset",
47
47
  rotateLeft: "Rotate left 90°",
48
48
  rotateRight: "Rotate right 90°",
49
- upload: "Upload",
49
+ selectFile: "Select file",
50
50
  zoom: "Zoom"
51
51
  };
52
52
  /**
@@ -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
@@ -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
- globalThis.location.href = this.coreEndpoint.webUrl;
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
- BridgeUtils.host.loadApp(coreName);
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.26",
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.68",
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.3",
73
+ "@vitejs/plugin-react": "^4.3.4",
74
74
  "jsdom": "^25.0.1",
75
75
  "typescript": "^5.7.2",
76
- "vitest": "^2.1.5"
76
+ "vitest": "^2.1.6"
77
77
  }
78
78
  }
@@ -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 ComputerIcon from "@mui/icons-material/Computer";
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={<ComputerIcon />}
289
+ startIcon={<ImageIcon />}
284
290
  fullWidth
285
291
  onUploadFiles={handleFileUpload}
286
292
  inputProps={{ multiple: false, accept: "image/png, image/jpeg" }}
287
293
  >
288
- {labels.upload}
294
+ {selectFileLabel}
289
295
  </FileUploadButton>
290
296
  <Stack direction="row" spacing={0.5}>
291
297
  <React.Suspense
package/src/app/Labels.ts CHANGED
@@ -48,7 +48,7 @@ export namespace Labels {
48
48
  reset: "Reset",
49
49
  rotateLeft: "Rotate left 90°",
50
50
  rotateRight: "Rotate right 90°",
51
- upload: "Upload",
51
+ selectFile: "Select file",
52
52
  zoom: "Zoom"
53
53
  };
54
54
 
@@ -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
- globalThis.location.href = this.coreEndpoint.webUrl;
73
+ let url = this.coreEndpoint.webUrl;
74
+ if (!tryLogin) url = url.addUrlParam(tryLoginKey, tryLogin);
75
+ globalThis.location.href = url;
72
76
  } else {
73
- BridgeUtils.host.loadApp(coreName);
77
+ const startUrl = tryLogin
78
+ ? undefined
79
+ : "".addUrlParam(tryLoginKey, tryLogin);
80
+ BridgeUtils.host.loadApp(coreName, startUrl);
74
81
  }
75
82
  }
76
83