@etsoo/materialui 1.6.30 → 1.6.32
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__/NotifierMUTests.tsx +87 -12
- package/lib/cjs/NotifierMU.d.ts +13 -0
- package/lib/cjs/NotifierMU.js +72 -14
- package/lib/mjs/NotifierMU.d.ts +13 -0
- package/lib/mjs/NotifierMU.js +72 -14
- package/package.json +11 -10
- package/setupTests.ts +1 -0
- package/src/NotifierMU.tsx +191 -19
|
@@ -4,7 +4,14 @@ import {
|
|
|
4
4
|
} from "@etsoo/notificationbase";
|
|
5
5
|
import React, { act } from "react";
|
|
6
6
|
import { createRoot } from "react-dom/client";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
NotificationMUDataMethods,
|
|
9
|
+
NotificationMUDataProps,
|
|
10
|
+
NotifierMU,
|
|
11
|
+
VBox
|
|
12
|
+
} from "../src";
|
|
13
|
+
import TextField from "@mui/material/TextField";
|
|
14
|
+
import { screen } from "@testing-library/react";
|
|
8
15
|
|
|
9
16
|
// Without it will popup error:
|
|
10
17
|
// The current testing environment is not configured to support act
|
|
@@ -15,11 +22,11 @@ const root = document.body;
|
|
|
15
22
|
const container: HTMLElement = document.createElement("div");
|
|
16
23
|
root.append(container);
|
|
17
24
|
|
|
18
|
-
// The state provider
|
|
19
|
-
const Provider = NotifierMU.setup();
|
|
20
|
-
const reactRoot = createRoot(container);
|
|
21
|
-
|
|
22
25
|
act(() => {
|
|
26
|
+
// The state provider
|
|
27
|
+
const Provider = NotifierMU.setup();
|
|
28
|
+
const reactRoot = createRoot(container);
|
|
29
|
+
|
|
23
30
|
// Concorrent renderer needs act block
|
|
24
31
|
reactRoot.render(<Provider />);
|
|
25
32
|
});
|
|
@@ -137,6 +144,77 @@ test("Prompt tests", async () => {
|
|
|
137
144
|
vi.runOnlyPendingTimers();
|
|
138
145
|
});
|
|
139
146
|
|
|
147
|
+
type DataType = {
|
|
148
|
+
name: string;
|
|
149
|
+
age: number;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
function DataCollector(props: NotificationMUDataProps) {
|
|
153
|
+
const { mRef } = props;
|
|
154
|
+
const nameRef = React.createRef<HTMLInputElement>();
|
|
155
|
+
const ageRef = React.createRef<HTMLInputElement>();
|
|
156
|
+
|
|
157
|
+
function getValue(): DataType | undefined {
|
|
158
|
+
if (!nameRef.current?.value) {
|
|
159
|
+
nameRef.current?.focus();
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!ageRef.current?.valueAsNumber) {
|
|
164
|
+
ageRef.current?.focus();
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
name: nameRef.current.value,
|
|
170
|
+
age: ageRef.current.valueAsNumber
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
React.useImperativeHandle(mRef, () => ({
|
|
175
|
+
getValue
|
|
176
|
+
}));
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<VBox>
|
|
180
|
+
<TextField name="name" required inputRef={nameRef} />
|
|
181
|
+
<TextField name="age" type="number" required inputRef={ageRef} />
|
|
182
|
+
</VBox>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
test("Data tests", async () => {
|
|
187
|
+
const name = "John Doe";
|
|
188
|
+
const age = 30;
|
|
189
|
+
|
|
190
|
+
act(() => {
|
|
191
|
+
// Add the notification
|
|
192
|
+
notifier.data<DataType>(
|
|
193
|
+
<DataCollector mRef={React.createRef<NotificationMUDataMethods>()} />,
|
|
194
|
+
(result) => {
|
|
195
|
+
expect(result).toEqual({ name, age });
|
|
196
|
+
return true;
|
|
197
|
+
},
|
|
198
|
+
"Data Modal"
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const button = screen.getByText("OK");
|
|
203
|
+
expect(button).toBeInTheDocument();
|
|
204
|
+
|
|
205
|
+
const nameInput = root.querySelector<HTMLInputElement>('input[name="name"]');
|
|
206
|
+
expect(nameInput).not.toBeNull();
|
|
207
|
+
nameInput!.value = name;
|
|
208
|
+
|
|
209
|
+
const ageInput = root.querySelector<HTMLInputElement>('input[name="age"]');
|
|
210
|
+
expect(ageInput).not.toBeNull();
|
|
211
|
+
ageInput!.value = age.toString();
|
|
212
|
+
|
|
213
|
+
await act(async () => {
|
|
214
|
+
button.click();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
140
218
|
test("Prompt tests with form submit", async () => {
|
|
141
219
|
// Click
|
|
142
220
|
const handleClick = vi.fn((result: boolean) => {
|
|
@@ -151,17 +229,14 @@ test("Prompt tests with form submit", async () => {
|
|
|
151
229
|
});
|
|
152
230
|
});
|
|
153
231
|
|
|
232
|
+
const button = screen.getByText("OK");
|
|
233
|
+
expect(button).toBeInTheDocument();
|
|
234
|
+
|
|
154
235
|
await act(async () => {
|
|
155
|
-
(
|
|
156
|
-
root
|
|
157
|
-
.getElementsByTagName("form")[0]
|
|
158
|
-
.elements.namedItem("okButton") as HTMLButtonElement
|
|
159
|
-
)?.click();
|
|
236
|
+
button.click();
|
|
160
237
|
});
|
|
161
238
|
|
|
162
239
|
expect(handleClick).toHaveBeenCalled();
|
|
163
|
-
|
|
164
|
-
vi.runOnlyPendingTimers();
|
|
165
240
|
});
|
|
166
241
|
|
|
167
242
|
test("Message tests", () => {
|
package/lib/cjs/NotifierMU.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { NotificationAlign, NotificationRenderProps } from "@etsoo/notificationbase";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { INotificationBaseReact, INotificationReact, NotificationReact, NotifierReact } from "@etsoo/react";
|
|
4
|
+
/**
|
|
5
|
+
* MU notification data methods
|
|
6
|
+
*/
|
|
7
|
+
export interface NotificationMUDataMethods {
|
|
8
|
+
getValue(): unknown;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* MU notification data props
|
|
12
|
+
*/
|
|
13
|
+
export type NotificationMUDataProps = {
|
|
14
|
+
mRef: React.RefObject<NotificationMUDataMethods | null>;
|
|
15
|
+
};
|
|
4
16
|
/**
|
|
5
17
|
* MU notification
|
|
6
18
|
*/
|
|
@@ -9,6 +21,7 @@ export declare class NotificationMU extends NotificationReact {
|
|
|
9
21
|
private createConfirm;
|
|
10
22
|
private createMessageColor;
|
|
11
23
|
private createMessage;
|
|
24
|
+
private createData;
|
|
12
25
|
private createPrompt;
|
|
13
26
|
private createPopup;
|
|
14
27
|
private createLoading;
|
package/lib/cjs/NotifierMU.js
CHANGED
|
@@ -59,6 +59,14 @@ const IconDialogTitle = (0, styles_1.styled)(DialogTitle_1.default, {
|
|
|
59
59
|
}
|
|
60
60
|
`}
|
|
61
61
|
`;
|
|
62
|
+
function isFunctionComponentElement(node) {
|
|
63
|
+
return (react_1.default.isValidElement(node) &&
|
|
64
|
+
typeof node.type === "function" &&
|
|
65
|
+
!(node.type.prototype && node.type.prototype.isReactComponent) &&
|
|
66
|
+
node.props != null &&
|
|
67
|
+
typeof node.props === "object" &&
|
|
68
|
+
"mRef" in node.props);
|
|
69
|
+
}
|
|
62
70
|
/**
|
|
63
71
|
* MU notification
|
|
64
72
|
*/
|
|
@@ -134,6 +142,53 @@ class NotificationMU extends react_2.NotificationReact {
|
|
|
134
142
|
const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
|
|
135
143
|
return ((0, jsx_runtime_1.jsx)(Fade_1.default, { in: true, children: (0, jsx_runtime_1.jsxs)(Alert_1.default, { ...setupProps, ...options, action: closable ? ((0, jsx_runtime_1.jsx)(IconButton_1.default, { size: "small", onClick: () => this.returnValue("CLOSE"), children: (0, jsx_runtime_1.jsx)(Close_1.default, {}) })) : undefined, onClose: () => this.dismiss(), className: className, children: [this.title && (0, jsx_runtime_1.jsx)(AlertTitle_1.default, { children: this.title }), this.content] }) }, this.id));
|
|
136
144
|
}
|
|
145
|
+
// Create data collector
|
|
146
|
+
createData(_props, className) {
|
|
147
|
+
const labels = Labels_1.Labels.NotificationMU;
|
|
148
|
+
const title = this.title ?? labels.promptTitle;
|
|
149
|
+
const { buttons, cancelLabel = labels.promptCancel, okLabel = labels.promptOK, cancelButton = true, inputs,
|
|
150
|
+
// type, not used
|
|
151
|
+
fullScreen, fullWidth = true, maxWidth, primaryButton = true, primaryButtonProps, inputProps, closable = false, draggable = fullScreen === true ? false : true } = this.inputProps ?? {};
|
|
152
|
+
const content = inputs ?? this.content;
|
|
153
|
+
if (!isFunctionComponentElement(content) || content.props.mRef == null) {
|
|
154
|
+
throw new Error("Data collector content must be a function component with mRef prop");
|
|
155
|
+
}
|
|
156
|
+
const mRef = content.props.mRef;
|
|
157
|
+
const errorRef = react_1.default.createRef();
|
|
158
|
+
const setError = (error) => {
|
|
159
|
+
if (errorRef.current == null)
|
|
160
|
+
return;
|
|
161
|
+
errorRef.current.innerText = error ?? "";
|
|
162
|
+
};
|
|
163
|
+
// Setup callback
|
|
164
|
+
const options = this.renderSetup ? this.renderSetup({}) : undefined;
|
|
165
|
+
const handleSubmit = async () => {
|
|
166
|
+
if (this.onReturn) {
|
|
167
|
+
// Get the value
|
|
168
|
+
const value = mRef.current?.getValue();
|
|
169
|
+
if (value == null) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const result = this.onReturn(value);
|
|
173
|
+
// returns false to prevent default dismiss
|
|
174
|
+
const v = await result;
|
|
175
|
+
if (v === false) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (typeof v === "string") {
|
|
179
|
+
setError(v);
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
this.dismiss();
|
|
184
|
+
return true;
|
|
185
|
+
};
|
|
186
|
+
return ((0, jsx_runtime_1.jsxs)(Dialog_1.default, { open: this.open, PaperComponent: draggable ? DraggablePaperComponent_1.DraggablePaperComponent : undefined, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options, children: [(0, jsx_runtime_1.jsxs)(IconDialogTitle, { draggable: draggable, className: draggable ? "dialog-title draggable-dialog-title" : "dialog-title", children: [(0, jsx_runtime_1.jsx)(Info_1.default, { color: "primary" }), (0, jsx_runtime_1.jsx)("span", { className: "dialogTitle", children: title }), closable && ((0, jsx_runtime_1.jsx)(IconButton_1.default, { className: "MuiDialogContent-root-close-button", size: "small", onClick: () => this.returnValue("CLOSE"), children: (0, jsx_runtime_1.jsx)(Close_1.default, {}) }))] }), (0, jsx_runtime_1.jsxs)(DialogContent_1.default, { ...inputProps, children: [content, (0, jsx_runtime_1.jsx)(Typography_1.default, { variant: "caption", display: "block", ref: errorRef, color: "error" })] }), (0, jsx_runtime_1.jsx)(DialogActions_1.default, { children: buttons ? (buttons(this, handleSubmit)) : ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [cancelButton && ((0, jsx_runtime_1.jsx)(Button_1.default, { color: "secondary", onClick: () => {
|
|
187
|
+
if (this.onReturn)
|
|
188
|
+
this.onReturn(undefined);
|
|
189
|
+
this.dismiss();
|
|
190
|
+
}, children: cancelLabel })), primaryButton && ((0, jsx_runtime_1.jsx)(LoadingButton_1.LoadingButton, { color: "primary", autoFocus: true, onClick: handleSubmit, name: "okButton", ...primaryButtonProps, children: okLabel }))] })) })] }, this.id));
|
|
191
|
+
}
|
|
137
192
|
// Create prompt
|
|
138
193
|
createPrompt(_props, className) {
|
|
139
194
|
const labels = Labels_1.Labels.NotificationMU;
|
|
@@ -147,13 +202,13 @@ class NotificationMU extends react_2.NotificationReact {
|
|
|
147
202
|
errorRef.current.innerText = error ?? "";
|
|
148
203
|
};
|
|
149
204
|
const handleSubmit = async (event) => {
|
|
150
|
-
// Result
|
|
151
|
-
let result = undefined;
|
|
152
205
|
const input = inputRef.current;
|
|
153
206
|
if (this.onReturn) {
|
|
154
207
|
// Inputs case, no HTMLForm set to value, set the current form
|
|
155
208
|
if (inputs && value == null)
|
|
156
209
|
value = event.currentTarget.form;
|
|
210
|
+
// Result
|
|
211
|
+
let result = undefined;
|
|
157
212
|
if (input) {
|
|
158
213
|
if (type === "switch") {
|
|
159
214
|
const boolValue = input.value === "true";
|
|
@@ -171,18 +226,18 @@ class NotificationMU extends react_2.NotificationReact {
|
|
|
171
226
|
else if (value != null) {
|
|
172
227
|
result = this.onReturn(value);
|
|
173
228
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
229
|
+
// Get the value
|
|
230
|
+
// returns false to prevent default dismiss
|
|
231
|
+
const v = await result;
|
|
232
|
+
if (v === false) {
|
|
233
|
+
input?.focus();
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
if (typeof v === "string") {
|
|
237
|
+
setError(v);
|
|
238
|
+
input?.focus();
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
186
241
|
}
|
|
187
242
|
this.dismiss();
|
|
188
243
|
return true;
|
|
@@ -259,6 +314,9 @@ class NotificationMU extends react_2.NotificationReact {
|
|
|
259
314
|
else if (this.type === notificationbase_1.NotificationType.Confirm) {
|
|
260
315
|
return this.createConfirm(props, className);
|
|
261
316
|
}
|
|
317
|
+
else if (this.type === notificationbase_1.NotificationType.Data) {
|
|
318
|
+
return this.createData(props, className);
|
|
319
|
+
}
|
|
262
320
|
else if (this.type === notificationbase_1.NotificationType.Prompt) {
|
|
263
321
|
return this.createPrompt(props, className);
|
|
264
322
|
}
|
package/lib/mjs/NotifierMU.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { NotificationAlign, NotificationRenderProps } from "@etsoo/notificationbase";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { INotificationBaseReact, INotificationReact, NotificationReact, NotifierReact } from "@etsoo/react";
|
|
4
|
+
/**
|
|
5
|
+
* MU notification data methods
|
|
6
|
+
*/
|
|
7
|
+
export interface NotificationMUDataMethods {
|
|
8
|
+
getValue(): unknown;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* MU notification data props
|
|
12
|
+
*/
|
|
13
|
+
export type NotificationMUDataProps = {
|
|
14
|
+
mRef: React.RefObject<NotificationMUDataMethods | null>;
|
|
15
|
+
};
|
|
4
16
|
/**
|
|
5
17
|
* MU notification
|
|
6
18
|
*/
|
|
@@ -9,6 +21,7 @@ export declare class NotificationMU extends NotificationReact {
|
|
|
9
21
|
private createConfirm;
|
|
10
22
|
private createMessageColor;
|
|
11
23
|
private createMessage;
|
|
24
|
+
private createData;
|
|
12
25
|
private createPrompt;
|
|
13
26
|
private createPopup;
|
|
14
27
|
private createLoading;
|
package/lib/mjs/NotifierMU.js
CHANGED
|
@@ -53,6 +53,14 @@ const IconDialogTitle = styled(DialogTitle, {
|
|
|
53
53
|
}
|
|
54
54
|
`}
|
|
55
55
|
`;
|
|
56
|
+
function isFunctionComponentElement(node) {
|
|
57
|
+
return (React.isValidElement(node) &&
|
|
58
|
+
typeof node.type === "function" &&
|
|
59
|
+
!(node.type.prototype && node.type.prototype.isReactComponent) &&
|
|
60
|
+
node.props != null &&
|
|
61
|
+
typeof node.props === "object" &&
|
|
62
|
+
"mRef" in node.props);
|
|
63
|
+
}
|
|
56
64
|
/**
|
|
57
65
|
* MU notification
|
|
58
66
|
*/
|
|
@@ -128,6 +136,53 @@ export class NotificationMU extends NotificationReact {
|
|
|
128
136
|
const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
|
|
129
137
|
return (_jsx(Fade, { in: true, children: _jsxs(Alert, { ...setupProps, ...options, action: closable ? (_jsx(IconButton, { size: "small", onClick: () => this.returnValue("CLOSE"), children: _jsx(CloseIcon, {}) })) : undefined, onClose: () => this.dismiss(), className: className, children: [this.title && _jsx(AlertTitle, { children: this.title }), this.content] }) }, this.id));
|
|
130
138
|
}
|
|
139
|
+
// Create data collector
|
|
140
|
+
createData(_props, className) {
|
|
141
|
+
const labels = Labels.NotificationMU;
|
|
142
|
+
const title = this.title ?? labels.promptTitle;
|
|
143
|
+
const { buttons, cancelLabel = labels.promptCancel, okLabel = labels.promptOK, cancelButton = true, inputs,
|
|
144
|
+
// type, not used
|
|
145
|
+
fullScreen, fullWidth = true, maxWidth, primaryButton = true, primaryButtonProps, inputProps, closable = false, draggable = fullScreen === true ? false : true } = this.inputProps ?? {};
|
|
146
|
+
const content = inputs ?? this.content;
|
|
147
|
+
if (!isFunctionComponentElement(content) || content.props.mRef == null) {
|
|
148
|
+
throw new Error("Data collector content must be a function component with mRef prop");
|
|
149
|
+
}
|
|
150
|
+
const mRef = content.props.mRef;
|
|
151
|
+
const errorRef = React.createRef();
|
|
152
|
+
const setError = (error) => {
|
|
153
|
+
if (errorRef.current == null)
|
|
154
|
+
return;
|
|
155
|
+
errorRef.current.innerText = error ?? "";
|
|
156
|
+
};
|
|
157
|
+
// Setup callback
|
|
158
|
+
const options = this.renderSetup ? this.renderSetup({}) : undefined;
|
|
159
|
+
const handleSubmit = async () => {
|
|
160
|
+
if (this.onReturn) {
|
|
161
|
+
// Get the value
|
|
162
|
+
const value = mRef.current?.getValue();
|
|
163
|
+
if (value == null) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
const result = this.onReturn(value);
|
|
167
|
+
// returns false to prevent default dismiss
|
|
168
|
+
const v = await result;
|
|
169
|
+
if (v === false) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
if (typeof v === "string") {
|
|
173
|
+
setError(v);
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this.dismiss();
|
|
178
|
+
return true;
|
|
179
|
+
};
|
|
180
|
+
return (_jsxs(Dialog, { open: this.open, PaperComponent: draggable ? DraggablePaperComponent : undefined, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options, children: [_jsxs(IconDialogTitle, { draggable: draggable, className: draggable ? "dialog-title draggable-dialog-title" : "dialog-title", children: [_jsx(InfoIcon, { color: "primary" }), _jsx("span", { className: "dialogTitle", children: title }), closable && (_jsx(IconButton, { className: "MuiDialogContent-root-close-button", size: "small", onClick: () => this.returnValue("CLOSE"), children: _jsx(CloseIcon, {}) }))] }), _jsxs(DialogContent, { ...inputProps, children: [content, _jsx(Typography, { variant: "caption", display: "block", ref: errorRef, color: "error" })] }), _jsx(DialogActions, { children: buttons ? (buttons(this, handleSubmit)) : (_jsxs(React.Fragment, { children: [cancelButton && (_jsx(Button, { color: "secondary", onClick: () => {
|
|
181
|
+
if (this.onReturn)
|
|
182
|
+
this.onReturn(undefined);
|
|
183
|
+
this.dismiss();
|
|
184
|
+
}, children: cancelLabel })), primaryButton && (_jsx(LoadingButton, { color: "primary", autoFocus: true, onClick: handleSubmit, name: "okButton", ...primaryButtonProps, children: okLabel }))] })) })] }, this.id));
|
|
185
|
+
}
|
|
131
186
|
// Create prompt
|
|
132
187
|
createPrompt(_props, className) {
|
|
133
188
|
const labels = Labels.NotificationMU;
|
|
@@ -141,13 +196,13 @@ export class NotificationMU extends NotificationReact {
|
|
|
141
196
|
errorRef.current.innerText = error ?? "";
|
|
142
197
|
};
|
|
143
198
|
const handleSubmit = async (event) => {
|
|
144
|
-
// Result
|
|
145
|
-
let result = undefined;
|
|
146
199
|
const input = inputRef.current;
|
|
147
200
|
if (this.onReturn) {
|
|
148
201
|
// Inputs case, no HTMLForm set to value, set the current form
|
|
149
202
|
if (inputs && value == null)
|
|
150
203
|
value = event.currentTarget.form;
|
|
204
|
+
// Result
|
|
205
|
+
let result = undefined;
|
|
151
206
|
if (input) {
|
|
152
207
|
if (type === "switch") {
|
|
153
208
|
const boolValue = input.value === "true";
|
|
@@ -165,18 +220,18 @@ export class NotificationMU extends NotificationReact {
|
|
|
165
220
|
else if (value != null) {
|
|
166
221
|
result = this.onReturn(value);
|
|
167
222
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
223
|
+
// Get the value
|
|
224
|
+
// returns false to prevent default dismiss
|
|
225
|
+
const v = await result;
|
|
226
|
+
if (v === false) {
|
|
227
|
+
input?.focus();
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
if (typeof v === "string") {
|
|
231
|
+
setError(v);
|
|
232
|
+
input?.focus();
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
180
235
|
}
|
|
181
236
|
this.dismiss();
|
|
182
237
|
return true;
|
|
@@ -253,6 +308,9 @@ export class NotificationMU extends NotificationReact {
|
|
|
253
308
|
else if (this.type === NotificationType.Confirm) {
|
|
254
309
|
return this.createConfirm(props, className);
|
|
255
310
|
}
|
|
311
|
+
else if (this.type === NotificationType.Data) {
|
|
312
|
+
return this.createData(props, className);
|
|
313
|
+
}
|
|
256
314
|
else if (this.type === NotificationType.Prompt) {
|
|
257
315
|
return this.createPrompt(props, className);
|
|
258
316
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.32",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/mjs/index.js",
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/ETSOO/ReactMU#readme",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@base-ui/react": "^1.
|
|
39
|
+
"@base-ui/react": "^1.3.0",
|
|
40
40
|
"@dnd-kit/react": "^0.3.2",
|
|
41
41
|
"@emotion/react": "^11.14.0",
|
|
42
42
|
"@emotion/styled": "^11.14.1",
|
|
43
|
-
"@etsoo/appscript": "^1.6.
|
|
44
|
-
"@etsoo/notificationbase": "^1.1.
|
|
45
|
-
"@etsoo/react": "^1.8.
|
|
43
|
+
"@etsoo/appscript": "^1.6.57",
|
|
44
|
+
"@etsoo/notificationbase": "^1.1.67",
|
|
45
|
+
"@etsoo/react": "^1.8.81",
|
|
46
46
|
"@etsoo/shared": "^1.2.80",
|
|
47
47
|
"@mui/icons-material": "^7.3.9",
|
|
48
48
|
"@mui/material": "^7.3.9",
|
|
49
|
-
"@mui/x-data-grid": "^8.27.
|
|
49
|
+
"@mui/x-data-grid": "^8.27.5",
|
|
50
50
|
"chart.js": "^4.5.1",
|
|
51
51
|
"chartjs-plugin-datalabels": "^2.2.0",
|
|
52
|
-
"dompurify": "^3.3.
|
|
52
|
+
"dompurify": "^3.3.3",
|
|
53
53
|
"eventemitter3": "^5.0.4",
|
|
54
54
|
"pica": "^9.0.1",
|
|
55
55
|
"pulltorefreshjs": "^0.1.22",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"@babel/preset-react": "^7.28.5",
|
|
72
72
|
"@babel/preset-typescript": "^7.28.5",
|
|
73
73
|
"@babel/runtime-corejs3": "^7.29.0",
|
|
74
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
74
75
|
"@testing-library/react": "^16.3.2",
|
|
75
76
|
"@types/pica": "^9.0.5",
|
|
76
77
|
"@types/pulltorefreshjs": "^0.1.7",
|
|
@@ -78,9 +79,9 @@
|
|
|
78
79
|
"@types/react-avatar-editor": "^13.0.4",
|
|
79
80
|
"@types/react-dom": "^19.2.3",
|
|
80
81
|
"@types/react-input-mask": "^3.0.6",
|
|
81
|
-
"@vitejs/plugin-react": "^
|
|
82
|
-
"jsdom": "^
|
|
82
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
83
|
+
"jsdom": "^29.0.0",
|
|
83
84
|
"typescript": "^5.9.3",
|
|
84
|
-
"vitest": "^4.0
|
|
85
|
+
"vitest": "^4.1.0"
|
|
85
86
|
}
|
|
86
87
|
}
|
package/setupTests.ts
CHANGED
package/src/NotifierMU.tsx
CHANGED
|
@@ -70,6 +70,36 @@ const IconDialogTitle = styled(DialogTitle, {
|
|
|
70
70
|
`}
|
|
71
71
|
`;
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* MU notification data methods
|
|
75
|
+
*/
|
|
76
|
+
export interface NotificationMUDataMethods {
|
|
77
|
+
getValue(): unknown;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* MU notification data props
|
|
82
|
+
*/
|
|
83
|
+
export type NotificationMUDataProps = {
|
|
84
|
+
mRef: React.RefObject<NotificationMUDataMethods | null>;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function isFunctionComponentElement(
|
|
88
|
+
node: React.ReactNode
|
|
89
|
+
): node is React.ReactElement<
|
|
90
|
+
NotificationMUDataProps,
|
|
91
|
+
React.FunctionComponent<NotificationMUDataProps>
|
|
92
|
+
> {
|
|
93
|
+
return (
|
|
94
|
+
React.isValidElement(node) &&
|
|
95
|
+
typeof node.type === "function" &&
|
|
96
|
+
!(node.type.prototype && node.type.prototype.isReactComponent) &&
|
|
97
|
+
node.props != null &&
|
|
98
|
+
typeof node.props === "object" &&
|
|
99
|
+
"mRef" in node.props
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
73
103
|
/**
|
|
74
104
|
* MU notification
|
|
75
105
|
*/
|
|
@@ -319,6 +349,145 @@ export class NotificationMU extends NotificationReact {
|
|
|
319
349
|
);
|
|
320
350
|
}
|
|
321
351
|
|
|
352
|
+
// Create data collector
|
|
353
|
+
private createData(_props: NotificationRenderProps, className?: string) {
|
|
354
|
+
const labels = Labels.NotificationMU;
|
|
355
|
+
const title = this.title ?? labels.promptTitle;
|
|
356
|
+
|
|
357
|
+
const {
|
|
358
|
+
buttons,
|
|
359
|
+
cancelLabel = labels.promptCancel,
|
|
360
|
+
okLabel = labels.promptOK,
|
|
361
|
+
cancelButton = true,
|
|
362
|
+
inputs,
|
|
363
|
+
// type, not used
|
|
364
|
+
fullScreen,
|
|
365
|
+
fullWidth = true,
|
|
366
|
+
maxWidth,
|
|
367
|
+
primaryButton = true,
|
|
368
|
+
primaryButtonProps,
|
|
369
|
+
inputProps,
|
|
370
|
+
closable = false,
|
|
371
|
+
draggable = fullScreen === true ? false : true
|
|
372
|
+
} = this.inputProps ?? {};
|
|
373
|
+
|
|
374
|
+
const content = inputs ?? this.content;
|
|
375
|
+
if (!isFunctionComponentElement(content) || content.props.mRef == null) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
"Data collector content must be a function component with mRef prop"
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const mRef = content.props.mRef;
|
|
382
|
+
|
|
383
|
+
const errorRef = React.createRef<HTMLSpanElement>();
|
|
384
|
+
|
|
385
|
+
const setError = (error?: string) => {
|
|
386
|
+
if (errorRef.current == null) return;
|
|
387
|
+
errorRef.current.innerText = error ?? "";
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// Setup callback
|
|
391
|
+
const options = this.renderSetup ? this.renderSetup({}) : undefined;
|
|
392
|
+
|
|
393
|
+
const handleSubmit = async () => {
|
|
394
|
+
if (this.onReturn) {
|
|
395
|
+
// Get the value
|
|
396
|
+
const value = mRef.current?.getValue();
|
|
397
|
+
if (value == null) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const result = this.onReturn(value);
|
|
402
|
+
|
|
403
|
+
// returns false to prevent default dismiss
|
|
404
|
+
const v = await result;
|
|
405
|
+
if (v === false) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (typeof v === "string") {
|
|
410
|
+
setError(v);
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
this.dismiss();
|
|
416
|
+
return true;
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
return (
|
|
420
|
+
<Dialog
|
|
421
|
+
key={this.id}
|
|
422
|
+
open={this.open}
|
|
423
|
+
PaperComponent={draggable ? DraggablePaperComponent : undefined}
|
|
424
|
+
className={className}
|
|
425
|
+
fullWidth={fullWidth}
|
|
426
|
+
maxWidth={maxWidth}
|
|
427
|
+
fullScreen={fullScreen}
|
|
428
|
+
{...options}
|
|
429
|
+
>
|
|
430
|
+
<IconDialogTitle
|
|
431
|
+
draggable={draggable}
|
|
432
|
+
className={
|
|
433
|
+
draggable ? "dialog-title draggable-dialog-title" : "dialog-title"
|
|
434
|
+
}
|
|
435
|
+
>
|
|
436
|
+
<InfoIcon color="primary" />
|
|
437
|
+
<span className="dialogTitle">{title}</span>
|
|
438
|
+
{closable && (
|
|
439
|
+
<IconButton
|
|
440
|
+
className="MuiDialogContent-root-close-button"
|
|
441
|
+
size="small"
|
|
442
|
+
onClick={() => this.returnValue("CLOSE")}
|
|
443
|
+
>
|
|
444
|
+
<CloseIcon />
|
|
445
|
+
</IconButton>
|
|
446
|
+
)}
|
|
447
|
+
</IconDialogTitle>
|
|
448
|
+
<DialogContent {...inputProps}>
|
|
449
|
+
{content}
|
|
450
|
+
<Typography
|
|
451
|
+
variant="caption"
|
|
452
|
+
display="block"
|
|
453
|
+
ref={errorRef}
|
|
454
|
+
color="error"
|
|
455
|
+
/>
|
|
456
|
+
</DialogContent>
|
|
457
|
+
<DialogActions>
|
|
458
|
+
{buttons ? (
|
|
459
|
+
buttons(this, handleSubmit)
|
|
460
|
+
) : (
|
|
461
|
+
<React.Fragment>
|
|
462
|
+
{cancelButton && (
|
|
463
|
+
<Button
|
|
464
|
+
color="secondary"
|
|
465
|
+
onClick={() => {
|
|
466
|
+
if (this.onReturn) this.onReturn(undefined);
|
|
467
|
+
this.dismiss();
|
|
468
|
+
}}
|
|
469
|
+
>
|
|
470
|
+
{cancelLabel}
|
|
471
|
+
</Button>
|
|
472
|
+
)}
|
|
473
|
+
{primaryButton && (
|
|
474
|
+
<LoadingButton
|
|
475
|
+
color="primary"
|
|
476
|
+
autoFocus
|
|
477
|
+
onClick={handleSubmit}
|
|
478
|
+
name="okButton"
|
|
479
|
+
{...primaryButtonProps}
|
|
480
|
+
>
|
|
481
|
+
{okLabel}
|
|
482
|
+
</LoadingButton>
|
|
483
|
+
)}
|
|
484
|
+
</React.Fragment>
|
|
485
|
+
)}
|
|
486
|
+
</DialogActions>
|
|
487
|
+
</Dialog>
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
322
491
|
// Create prompt
|
|
323
492
|
private createPrompt(_props: NotificationRenderProps, className?: string) {
|
|
324
493
|
const labels = Labels.NotificationMU;
|
|
@@ -350,19 +519,19 @@ export class NotificationMU extends NotificationReact {
|
|
|
350
519
|
};
|
|
351
520
|
|
|
352
521
|
const handleSubmit = async (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
353
|
-
// Result
|
|
354
|
-
let result:
|
|
355
|
-
| boolean
|
|
356
|
-
| string
|
|
357
|
-
| void
|
|
358
|
-
| PromiseLike<boolean | string | void> = undefined;
|
|
359
|
-
|
|
360
522
|
const input = inputRef.current;
|
|
361
523
|
|
|
362
524
|
if (this.onReturn) {
|
|
363
525
|
// Inputs case, no HTMLForm set to value, set the current form
|
|
364
526
|
if (inputs && value == null) value = event.currentTarget.form;
|
|
365
527
|
|
|
528
|
+
// Result
|
|
529
|
+
let result:
|
|
530
|
+
| boolean
|
|
531
|
+
| string
|
|
532
|
+
| void
|
|
533
|
+
| PromiseLike<boolean | string | void> = undefined;
|
|
534
|
+
|
|
366
535
|
if (input) {
|
|
367
536
|
if (type === "switch") {
|
|
368
537
|
const boolValue = input.value === "true";
|
|
@@ -378,19 +547,20 @@ export class NotificationMU extends NotificationReact {
|
|
|
378
547
|
} else if (value != null) {
|
|
379
548
|
result = this.onReturn(value);
|
|
380
549
|
}
|
|
381
|
-
}
|
|
382
550
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
551
|
+
// Get the value
|
|
552
|
+
// returns false to prevent default dismiss
|
|
553
|
+
const v = await result;
|
|
554
|
+
if (v === false) {
|
|
555
|
+
input?.focus();
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (typeof v === "string") {
|
|
560
|
+
setError(v);
|
|
561
|
+
input?.focus();
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
394
564
|
}
|
|
395
565
|
|
|
396
566
|
this.dismiss();
|
|
@@ -606,6 +776,8 @@ export class NotificationMU extends NotificationReact {
|
|
|
606
776
|
return this.createLoading(props, className);
|
|
607
777
|
} else if (this.type === NotificationType.Confirm) {
|
|
608
778
|
return this.createConfirm(props, className);
|
|
779
|
+
} else if (this.type === NotificationType.Data) {
|
|
780
|
+
return this.createData(props, className);
|
|
609
781
|
} else if (this.type === NotificationType.Prompt) {
|
|
610
782
|
return this.createPrompt(props, className);
|
|
611
783
|
} else if (this.type === NotificationType.Popup) {
|