@griddo/ax 12.4.0 → 12.5.1
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/config/__tests__/ModulePathInMessagesPlugin.test.js +93 -0
- package/config/webpack/ModulePathInMessagesPlugin.js +47 -0
- package/config/webpack.config.js +10 -0
- package/package.json +2 -2
- package/src/__tests__/components/ConfigPanel/Form/Form.test.tsx +47 -57
- package/src/__tests__/components/ConfigPanel/Header/Header.test.tsx +46 -42
- package/src/__tests__/components/ElementsTooltip/ElementsTooltip.test.tsx +6 -17
- package/src/__tests__/components/EmptyState/EmptyState.test.tsx +9 -9
- package/src/__tests__/components/Fields/AsyncSelect/AsyncSelect.test.tsx +60 -50
- package/src/__tests__/components/Fields/ColorPicker/ColorPicker.test.tsx +66 -58
- package/src/__tests__/components/Fields/ComponentContainer/ComponentContainer.test.tsx +222 -425
- package/src/__tests__/components/Fields/FileField/FileField.test.tsx +13 -14
- package/src/__tests__/components/Fields/ImageField/ImageField.test.tsx +275 -259
- package/src/__tests__/components/Fields/IntegrationsField/IntegrationsField.test.tsx +50 -48
- package/src/__tests__/components/Fields/NoteField/NoteField.test.tsx +9 -8
- package/src/__tests__/components/Fields/NumberField/NumberField.test.tsx +8 -12
- package/src/__tests__/components/Fields/TextArea/TextArea.test.tsx +6 -2
- package/src/__tests__/components/Fields/ToggleField/ToggleField.test.tsx +15 -23
- package/src/__tests__/components/Fields/Tooltip/Tooltip.test.tsx +8 -2
- package/src/__tests__/components/Fields/VisualUniqueSelection/VisualUniqueSelection.test.tsx +29 -21
- package/src/__tests__/components/Fields/Wysiwyg/Wysiwyg.config.test.tsx +103 -0
- package/src/__tests__/components/FieldsBehavior/FieldsBehavior.test.tsx +7 -2
- package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +8 -6
- package/src/__tests__/components/SideModal/SideModal.test.tsx +150 -104
- package/src/__tests__/components/TableFilters/LiveFilter/LiveFilter.test.tsx +15 -29
- package/src/__tests__/components/TableFilters/StatusFilter/StatusFilter.test.tsx +61 -36
- package/src/__tests__/components/Tabs/Tabs.test.tsx +65 -61
- package/src/__tests__/components/Toast/Toast.test.tsx +19 -21
- package/src/__tests__/modules/FramePreview/FramePreview.test.tsx +16 -0
- package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +24 -19
- package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +15 -6
- package/src/__tests__/modules/Users/Roles/BulkHeader/BulkHeader.test.tsx +69 -64
- package/src/__tests__/modules/Users/Roles/Roles.test.tsx +7 -4
- package/src/components/Fields/Wysiwyg/config.tsx +68 -17
- package/src/components/Fields/Wysiwyg/helpers.tsx +2 -0
- package/src/components/Fields/Wysiwyg/index.tsx +41 -10
- package/src/components/ResizePanel/index.tsx +7 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards the fix for webpack 5 dropping the file name from module warnings.
|
|
3
|
+
*
|
|
4
|
+
* webpack 4 gave CRA warnings as strings carrying their own `./src/Foo.tsx` header;
|
|
5
|
+
* webpack 5 moves it to a `moduleName` field that `formatWebpackMessages` ignores,
|
|
6
|
+
* which left "Attempted import error: 'x' is not exported from './styles.module.css'"
|
|
7
|
+
* pointing at no file in particular.
|
|
8
|
+
*/
|
|
9
|
+
const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
|
|
10
|
+
|
|
11
|
+
const ModulePathInMessagesPlugin = require("../webpack/ModulePathInMessagesPlugin");
|
|
12
|
+
|
|
13
|
+
/** Minimal stand-in for the two tapable hooks the plugin uses. */
|
|
14
|
+
const runPlugin = ({ warnings = [], errors = [] } = {}) => {
|
|
15
|
+
const compilation = {
|
|
16
|
+
warnings,
|
|
17
|
+
errors,
|
|
18
|
+
requestShortener: {},
|
|
19
|
+
hooks: { afterSeal: { tap: (_name, fn) => fn() } },
|
|
20
|
+
};
|
|
21
|
+
const compiler = { hooks: { compilation: { tap: (_name, fn) => fn(compilation) } } };
|
|
22
|
+
|
|
23
|
+
new ModulePathInMessagesPlugin().apply(compiler);
|
|
24
|
+
|
|
25
|
+
return compilation;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const makeWarning = (message, { module = true, loc = { start: { line: 2, column: 12 } } } = {}) => ({
|
|
29
|
+
message,
|
|
30
|
+
loc,
|
|
31
|
+
module: module ? { readableIdentifier: () => "./src/ui/modules/Wysiwyg/index.tsx" } : undefined,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("ModulePathInMessagesPlugin", () => {
|
|
35
|
+
test("prefixes the warning with the module path and location", () => {
|
|
36
|
+
const warning = makeWarning("export 'intro' was not found in './styles.module.css'");
|
|
37
|
+
|
|
38
|
+
runPlugin({ warnings: [warning] });
|
|
39
|
+
|
|
40
|
+
expect(warning.message).toBe(
|
|
41
|
+
"./src/ui/modules/Wysiwyg/index.tsx:2:12\nexport 'intro' was not found in './styles.module.css'",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("decorates errors too", () => {
|
|
46
|
+
const error = makeWarning("Module not found");
|
|
47
|
+
|
|
48
|
+
runPlugin({ errors: [error] });
|
|
49
|
+
|
|
50
|
+
expect(error.message).toBe("./src/ui/modules/Wysiwyg/index.tsx:2:12\nModule not found");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("omits the location when the warning has none", () => {
|
|
54
|
+
const warning = makeWarning("boom", { loc: null });
|
|
55
|
+
|
|
56
|
+
runPlugin({ warnings: [warning] });
|
|
57
|
+
|
|
58
|
+
expect(warning.message).toBe("./src/ui/modules/Wysiwyg/index.tsx\nboom");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("leaves warnings that are not attached to a module alone", () => {
|
|
62
|
+
const warning = makeWarning("something global", { module: false });
|
|
63
|
+
|
|
64
|
+
runPlugin({ warnings: [warning] });
|
|
65
|
+
|
|
66
|
+
expect(warning.message).toBe("something global");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("does not prefix twice across watch-mode rebuilds", () => {
|
|
70
|
+
const warning = makeWarning("export 'intro' was not found in './styles.module.css'");
|
|
71
|
+
|
|
72
|
+
runPlugin({ warnings: [warning] });
|
|
73
|
+
const afterFirstBuild = warning.message;
|
|
74
|
+
runPlugin({ warnings: [warning] });
|
|
75
|
+
|
|
76
|
+
expect(warning.message).toBe(afterFirstBuild);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// The contract that makes the whole thing worthwhile: formatWebpackMessages
|
|
80
|
+
// rewrites the export line wholesale and strips a trailing `2:12-24` off line 0,
|
|
81
|
+
// so the header has to survive both to reach the terminal.
|
|
82
|
+
test("the path survives formatWebpackMessages", () => {
|
|
83
|
+
const warning = makeWarning("export 'intro' (imported as 'styles') was not found in './styles.module.css'");
|
|
84
|
+
|
|
85
|
+
runPlugin({ warnings: [warning] });
|
|
86
|
+
const [formatted] = formatWebpackMessages({ errors: [], warnings: [{ message: warning.message }] }).warnings;
|
|
87
|
+
|
|
88
|
+
expect(formatted).toBe(
|
|
89
|
+
"./src/ui/modules/Wysiwyg/index.tsx:2:12\n" +
|
|
90
|
+
"Attempted import error: 'intro' is not exported from './styles.module.css' (imported as 'styles').",
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const NAME = "GriddoModulePathInMessages";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prefixes every module-attached warning/error with the file that caused it.
|
|
5
|
+
*
|
|
6
|
+
* webpack 4 handed CRA its warnings as plain strings that already carried the
|
|
7
|
+
* `./src/Foo.tsx 2:12-24` header. webpack 5 hands them over as objects
|
|
8
|
+
* (`{ message, moduleName, loc }`), and `react-dev-utils`'s `formatWebpackMessages`
|
|
9
|
+
* only reads `.message` — so the file and location are dropped on the floor.
|
|
10
|
+
* That leaves messages like "Attempted import error: 'intro' is not exported
|
|
11
|
+
* from './styles.module.css'" with no way to tell which of the many
|
|
12
|
+
* `styles.module.css` files is at fault.
|
|
13
|
+
*
|
|
14
|
+
* Folding the location back into `.message` restores the webpack 4 shape that
|
|
15
|
+
* `formatWebpackMessages` expects (line 0 = file, line 1 = message), and it fixes
|
|
16
|
+
* both paths at once: `griddo build` (which calls the formatter directly) and
|
|
17
|
+
* `griddo start` (where `createCompiler` calls it internally, out of our reach).
|
|
18
|
+
*/
|
|
19
|
+
class ModulePathInMessagesPlugin {
|
|
20
|
+
apply(compiler) {
|
|
21
|
+
compiler.hooks.compilation.tap(NAME, (compilation) => {
|
|
22
|
+
// `afterSeal` runs once dependency warnings have been collected and before
|
|
23
|
+
// anything can call `stats.toJson()`.
|
|
24
|
+
compilation.hooks.afterSeal.tap(NAME, () => {
|
|
25
|
+
const decorate = (err) => {
|
|
26
|
+
const module = err.module;
|
|
27
|
+
if (!module || typeof module.readableIdentifier !== "function") {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const name = module.readableIdentifier(compilation.requestShortener);
|
|
31
|
+
const loc = err.loc?.start ? `:${err.loc.start.line}:${err.loc.start.column}` : "";
|
|
32
|
+
const header = `${name}${loc}`;
|
|
33
|
+
// Guard against double-prefixing on watch-mode rebuilds that reuse errors.
|
|
34
|
+
if (typeof err.message !== "string" || err.message.startsWith(header)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
err.message = `${header}\n${err.message}`;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
compilation.warnings.forEach(decorate);
|
|
41
|
+
compilation.errors.forEach(decorate);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = ModulePathInMessagesPlugin;
|
package/config/webpack.config.js
CHANGED
|
@@ -18,6 +18,7 @@ const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin");
|
|
|
18
18
|
const ForkTsCheckerWebpackPlugin = require("react-dev-utils/ForkTsCheckerWebpackPlugin");
|
|
19
19
|
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
|
|
20
20
|
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
|
|
21
|
+
const ModulePathInMessagesPlugin = require("./webpack/ModulePathInMessagesPlugin");
|
|
21
22
|
const createEnvironmentHash = require("./webpack/persistentCache/createEnvironmentHash");
|
|
22
23
|
|
|
23
24
|
const postcssNormalize = require("postcss-normalize");
|
|
@@ -466,6 +467,9 @@ module.exports = function (webpackEnv) {
|
|
|
466
467
|
importLoaders: 1,
|
|
467
468
|
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
|
468
469
|
modules: {
|
|
470
|
+
// Both keys below are a contract with client projects, not an
|
|
471
|
+
// internal build detail: packages/griddo-ax/docs/adr/0001-contrato-de-css-modules-con-los-clientes.md
|
|
472
|
+
//
|
|
469
473
|
// css-loader v6 defaults `esModule: true`, so `import * as styles`
|
|
470
474
|
// only sees locals as named exports when `namedExport` is on.
|
|
471
475
|
// griddo-components relies on `import * as styles` heavily.
|
|
@@ -509,6 +513,8 @@ module.exports = function (webpackEnv) {
|
|
|
509
513
|
importLoaders: 3,
|
|
510
514
|
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
|
511
515
|
modules: {
|
|
516
|
+
// Same client-facing contract as the CSS modules block above
|
|
517
|
+
// (ADR 0001): keep both keys in sync with it.
|
|
512
518
|
namedExport: true,
|
|
513
519
|
exportLocalsConvention: "asIs",
|
|
514
520
|
getLocalIdent: getCSSModuleLocalIdent,
|
|
@@ -543,6 +549,10 @@ module.exports = function (webpackEnv) {
|
|
|
543
549
|
],
|
|
544
550
|
},
|
|
545
551
|
plugins: [
|
|
552
|
+
// webpack 5 moves the offending file out of the warning text and into a
|
|
553
|
+
// separate `moduleName` field that react-dev-utils' formatter ignores. This
|
|
554
|
+
// folds it back in so messages point at a file again.
|
|
555
|
+
new ModulePathInMessagesPlugin(),
|
|
546
556
|
// Restore webpack 4's Node core-module handling for browser bundles. Clients run
|
|
547
557
|
// `griddo build` (this config) against their own instance, so this guards legacy
|
|
548
558
|
// instances against "Module not found" for Node builtins that webpack 5 no longer
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "12.
|
|
4
|
+
"version": "12.5.1",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Diego M. Béjar <diego.bejar@secuoyas.com>",
|
|
@@ -199,5 +199,5 @@
|
|
|
199
199
|
"publishConfig": {
|
|
200
200
|
"access": "public"
|
|
201
201
|
},
|
|
202
|
-
"gitHead": "
|
|
202
|
+
"gitHead": "0341f4c939ce5e316e37effdaca2ef26dee8b8af"
|
|
203
203
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { MockedFunction } from "vitest";
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
|
|
4
3
|
import { ThemeProvider } from "styled-components";
|
|
@@ -113,49 +112,49 @@ const initialStore = {
|
|
|
113
112
|
};
|
|
114
113
|
const store = mockStore(initialStore);
|
|
115
114
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
115
|
+
// Props por test. Los tres `const ...Mock = defaultProps.x as MockedFunction<>`
|
|
116
|
+
// que había aquí no se usaban en ningún assert: solo servían, sin querer, para
|
|
117
|
+
// materializar esas props en el proxy de `mock<T>()` y que el spread de JSX las
|
|
118
|
+
// llevara al componente. Ahora van declaradas en la factoría.
|
|
119
|
+
const makeProps = (overrides: Partial<IFormProps> = {}): IFormProps =>
|
|
120
|
+
Object.assign(
|
|
121
|
+
mock<IFormProps>(),
|
|
123
122
|
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
123
|
+
schema: {
|
|
124
|
+
title: "Header",
|
|
125
|
+
component: "Header",
|
|
126
|
+
type: "header",
|
|
127
|
+
configTabs: [
|
|
128
|
+
{
|
|
129
|
+
title: "content",
|
|
130
|
+
fields: [
|
|
131
|
+
{
|
|
132
|
+
title: "Name",
|
|
133
|
+
key: "title",
|
|
134
|
+
type: "TextField",
|
|
135
|
+
mandatory: true,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
schemaType: "module",
|
|
141
|
+
},
|
|
142
|
+
selectedTab: "content",
|
|
143
|
+
isPage: false,
|
|
144
|
+
isGlobal: false,
|
|
145
|
+
theme: "default-theme",
|
|
146
|
+
setSelectedContent: vi.fn(),
|
|
147
|
+
setSelectedTab: vi.fn(),
|
|
148
|
+
setHistoryPush: vi.fn(),
|
|
133
149
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
};
|
|
137
|
-
const restorePageNavigationActionMock = vi.fn();
|
|
138
|
-
defaultProps.actions = {
|
|
139
|
-
restorePageNavigationAction: restorePageNavigationActionMock,
|
|
140
|
-
};
|
|
141
|
-
const setSelectedContentMock = defaultProps.setSelectedContent as MockedFunction<(editorID: number) => void>;
|
|
142
|
-
const setSelectedTabMocked = defaultProps.setSelectedTab as MockedFunction<(tab: string) => void>;
|
|
143
|
-
const setHistoryPushMocked = defaultProps.setHistoryPush as MockedFunction<
|
|
144
|
-
(path: string, isEditor: boolean) => void
|
|
145
|
-
>;
|
|
146
|
-
defaultProps.selectedTab = "content";
|
|
147
|
-
defaultProps.isPage = false;
|
|
148
|
-
defaultProps.isGlobal = false;
|
|
149
|
-
defaultProps.theme = "default-theme";
|
|
150
|
+
overrides,
|
|
151
|
+
);
|
|
150
152
|
|
|
151
153
|
describe("Form component rendering", () => {
|
|
152
154
|
it("should render the component", () => {
|
|
153
|
-
defaultProps.isPage = true;
|
|
154
|
-
defaultProps.isGlobal = true;
|
|
155
|
-
|
|
156
155
|
render(
|
|
157
156
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
158
|
-
<Form {...
|
|
157
|
+
<Form {...makeProps({ isPage: true, isGlobal: true })} />
|
|
159
158
|
</ThemeProvider>,
|
|
160
159
|
{ store },
|
|
161
160
|
);
|
|
@@ -163,13 +162,9 @@ describe("Form component rendering", () => {
|
|
|
163
162
|
});
|
|
164
163
|
|
|
165
164
|
it("should render the component with no header warning text", () => {
|
|
166
|
-
defaultProps.isPage = false;
|
|
167
|
-
defaultProps.isGlobal = false;
|
|
168
|
-
defaultProps.header = 0;
|
|
169
|
-
|
|
170
165
|
render(
|
|
171
166
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
172
|
-
<Form {...
|
|
167
|
+
<Form {...makeProps({ header: 0 })} />
|
|
173
168
|
</ThemeProvider>,
|
|
174
169
|
{ store },
|
|
175
170
|
);
|
|
@@ -178,11 +173,11 @@ describe("Form component rendering", () => {
|
|
|
178
173
|
});
|
|
179
174
|
|
|
180
175
|
it("should call the function to restore the header", () => {
|
|
181
|
-
|
|
176
|
+
const restorePageNavigationAction = vi.fn();
|
|
182
177
|
|
|
183
178
|
render(
|
|
184
179
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
185
|
-
<Form {...
|
|
180
|
+
<Form {...makeProps({ header: 0, actions: { restorePageNavigationAction } })} />
|
|
186
181
|
</ThemeProvider>,
|
|
187
182
|
{ store },
|
|
188
183
|
);
|
|
@@ -190,16 +185,14 @@ describe("Form component rendering", () => {
|
|
|
190
185
|
expect(screen.getByText(/This page doesn\'t have a header. Click/i)).toBeInTheDocument();
|
|
191
186
|
const headerLink = screen.getByTestId("header-link");
|
|
192
187
|
fireEvent.click(headerLink);
|
|
193
|
-
expect(
|
|
194
|
-
expect(
|
|
188
|
+
expect(restorePageNavigationAction).toBeCalled();
|
|
189
|
+
expect(restorePageNavigationAction).toBeCalledWith("header");
|
|
195
190
|
});
|
|
196
191
|
|
|
197
192
|
it("should render the component with no footer warning text", () => {
|
|
198
|
-
defaultProps.footer = 0;
|
|
199
|
-
|
|
200
193
|
render(
|
|
201
194
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
202
|
-
<Form {...
|
|
195
|
+
<Form {...makeProps({ header: 0, footer: 0 })} />
|
|
203
196
|
</ThemeProvider>,
|
|
204
197
|
{ store },
|
|
205
198
|
);
|
|
@@ -208,11 +201,11 @@ describe("Form component rendering", () => {
|
|
|
208
201
|
});
|
|
209
202
|
|
|
210
203
|
it("should call the function to restore the header", () => {
|
|
211
|
-
|
|
204
|
+
const restorePageNavigationAction = vi.fn();
|
|
212
205
|
|
|
213
206
|
render(
|
|
214
207
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
215
|
-
<Form {...
|
|
208
|
+
<Form {...makeProps({ header: 0, footer: 0, actions: { restorePageNavigationAction } })} />
|
|
216
209
|
</ThemeProvider>,
|
|
217
210
|
{ store },
|
|
218
211
|
);
|
|
@@ -220,17 +213,14 @@ describe("Form component rendering", () => {
|
|
|
220
213
|
expect(screen.getByText(/This page doesn\'t have a header. Click/i)).toBeInTheDocument();
|
|
221
214
|
const footerLink = screen.getByTestId("footer-link");
|
|
222
215
|
fireEvent.click(footerLink);
|
|
223
|
-
expect(
|
|
224
|
-
expect(
|
|
216
|
+
expect(restorePageNavigationAction).toBeCalled();
|
|
217
|
+
expect(restorePageNavigationAction).toBeCalledWith("footer");
|
|
225
218
|
});
|
|
226
219
|
|
|
227
220
|
it("should render the tabs", () => {
|
|
228
|
-
defaultProps.isPage = true;
|
|
229
|
-
defaultProps.isGlobal = true;
|
|
230
|
-
|
|
231
221
|
render(
|
|
232
222
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
233
|
-
<Form {...
|
|
223
|
+
<Form {...makeProps({ isPage: true, isGlobal: true, header: 0, footer: 0 })} />
|
|
234
224
|
</ThemeProvider>,
|
|
235
225
|
{ store },
|
|
236
226
|
);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { MockedFunction } from "vitest";
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
|
|
4
3
|
import { ThemeProvider } from "styled-components";
|
|
@@ -73,52 +72,57 @@ const initialStore = {
|
|
|
73
72
|
|
|
74
73
|
const store = mockStore(initialStore);
|
|
75
74
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
deleteModuleAction: vi.fn(),
|
|
80
|
-
copyModuleAction: vi.fn(),
|
|
81
|
-
};
|
|
82
|
-
const setSelectedContentMock = defaultProps.setSelectedContent as MockedFunction<(editorID: number) => void>;
|
|
83
|
-
|
|
84
|
-
defaultProps.schema = {
|
|
85
|
-
title: "Header",
|
|
86
|
-
component: "Header",
|
|
87
|
-
type: "header",
|
|
88
|
-
configTabs: [
|
|
75
|
+
const makeProps = (overrides: Partial<IHeaderProps> = {}): IHeaderProps =>
|
|
76
|
+
Object.assign(
|
|
77
|
+
mock<IHeaderProps>(),
|
|
89
78
|
{
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
actions: {
|
|
80
|
+
duplicateModuleAction: vi.fn(),
|
|
81
|
+
deleteModuleAction: vi.fn(),
|
|
82
|
+
copyModuleAction: vi.fn(),
|
|
83
|
+
},
|
|
84
|
+
schema: {
|
|
85
|
+
title: "Header",
|
|
86
|
+
component: "Header",
|
|
87
|
+
type: "header",
|
|
88
|
+
configTabs: [
|
|
89
|
+
{
|
|
90
|
+
title: "content",
|
|
91
|
+
fields: [
|
|
92
|
+
{
|
|
93
|
+
title: "Name",
|
|
94
|
+
key: "title",
|
|
95
|
+
type: "TextField",
|
|
96
|
+
mandatory: true,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
schemaType: "module",
|
|
102
|
+
},
|
|
103
|
+
breadcrumb: [
|
|
92
104
|
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
mandatory: true,
|
|
105
|
+
editorID: 0,
|
|
106
|
+
component: "TextField",
|
|
107
|
+
displayName: "Texto",
|
|
97
108
|
},
|
|
98
109
|
],
|
|
110
|
+
activatedModules: ["TextField"],
|
|
111
|
+
headerRef: { current: { offsetHeight: 63, offsetTop: 24 } } as React.RefObject<HTMLDivElement>,
|
|
112
|
+
// El proxy de `mock<T>()` solo materializa una prop cuando se accede a
|
|
113
|
+
// ella, y el spread de JSX únicamente copia las propias: los callbacks
|
|
114
|
+
// que el componente invoca tienen que estar puestos aquí.
|
|
115
|
+
setSelectedContent: vi.fn(),
|
|
116
|
+
setHeaderHeight: vi.fn(),
|
|
99
117
|
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
defaultProps.breadcrumb = [
|
|
105
|
-
{
|
|
106
|
-
editorID: 0,
|
|
107
|
-
component: "TextField",
|
|
108
|
-
displayName: "Texto",
|
|
109
|
-
},
|
|
110
|
-
];
|
|
111
|
-
|
|
112
|
-
defaultProps.activatedModules = ["TextField"];
|
|
113
|
-
|
|
114
|
-
defaultProps.headerRef = { current: { offsetHeight: 63, offsetTop: 24 } } as React.RefObject<HTMLDivElement>;
|
|
115
|
-
const setHeaderHeightMock = defaultProps.setHeaderHeight as MockedFunction<(height: number) => void>;
|
|
118
|
+
overrides,
|
|
119
|
+
);
|
|
116
120
|
|
|
117
121
|
describe("Header component rendering", () => {
|
|
118
122
|
it("should render component", () => {
|
|
119
123
|
render(
|
|
120
124
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
121
|
-
<Header {...
|
|
125
|
+
<Header {...makeProps()} />
|
|
122
126
|
</ThemeProvider>,
|
|
123
127
|
{ store },
|
|
124
128
|
);
|
|
@@ -129,11 +133,9 @@ describe("Header component rendering", () => {
|
|
|
129
133
|
});
|
|
130
134
|
|
|
131
135
|
it("should render the actions menu when clicking the more info button", () => {
|
|
132
|
-
defaultProps.selectedParent = [];
|
|
133
|
-
|
|
134
136
|
render(
|
|
135
137
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
136
|
-
<Header {...
|
|
138
|
+
<Header {...makeProps({ selectedParent: [] })} />
|
|
137
139
|
</ThemeProvider>,
|
|
138
140
|
{ store },
|
|
139
141
|
);
|
|
@@ -145,9 +147,11 @@ describe("Header component rendering", () => {
|
|
|
145
147
|
});
|
|
146
148
|
|
|
147
149
|
it("should trigger the duplicate action", () => {
|
|
150
|
+
const setSelectedContent = vi.fn();
|
|
151
|
+
|
|
148
152
|
render(
|
|
149
153
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
150
|
-
<Header {...
|
|
154
|
+
<Header {...makeProps({ selectedParent: [], setSelectedContent })} />
|
|
151
155
|
</ThemeProvider>,
|
|
152
156
|
{ store },
|
|
153
157
|
);
|
|
@@ -157,6 +161,6 @@ describe("Header component rendering", () => {
|
|
|
157
161
|
const listItem = screen.getAllByTestId("action-menu-item");
|
|
158
162
|
expect(listItem).toHaveLength(3);
|
|
159
163
|
fireEvent.click(listItem[0]);
|
|
160
|
-
expect(
|
|
164
|
+
expect(setSelectedContent).toBeCalled();
|
|
161
165
|
});
|
|
162
166
|
});
|
|
@@ -11,13 +11,14 @@ import globalTheme from "@ax/themes/theme.json";
|
|
|
11
11
|
|
|
12
12
|
afterEach(cleanup);
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const makeProps = (overrides: Partial<IElementsTooltipProps> = {}): IElementsTooltipProps =>
|
|
15
|
+
Object.assign(mock<IElementsTooltipProps>(), overrides);
|
|
15
16
|
|
|
16
17
|
describe("Tooltip component rendering", () => {
|
|
17
18
|
it("should not render the component", () => {
|
|
18
19
|
render(
|
|
19
20
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
20
|
-
<ElementsTooltip {...
|
|
21
|
+
<ElementsTooltip {...makeProps()} />
|
|
21
22
|
</ThemeProvider>,
|
|
22
23
|
);
|
|
23
24
|
|
|
@@ -27,13 +28,9 @@ describe("Tooltip component rendering", () => {
|
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
it("should render the component with one element visible", async () => {
|
|
30
|
-
defaultProps.elements = ["uno", "dos", "tres"];
|
|
31
|
-
defaultProps.colors = { uno: "#fff" };
|
|
32
|
-
defaultProps.rounded = true;
|
|
33
|
-
|
|
34
31
|
render(
|
|
35
32
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
36
|
-
<ElementsTooltip {...
|
|
33
|
+
<ElementsTooltip {...makeProps({ elements: ["uno", "dos", "tres"], colors: { uno: "#fff" }, rounded: true })} />
|
|
37
34
|
</ThemeProvider>,
|
|
38
35
|
);
|
|
39
36
|
|
|
@@ -59,13 +56,9 @@ describe("Tooltip component rendering", () => {
|
|
|
59
56
|
});
|
|
60
57
|
|
|
61
58
|
it("should render the component with three elements visibles", () => {
|
|
62
|
-
defaultProps.elements = ["uno", "dos", "tres"];
|
|
63
|
-
defaultProps.defaultElements = 3;
|
|
64
|
-
defaultProps.maxChar = 1;
|
|
65
|
-
|
|
66
59
|
render(
|
|
67
60
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
68
|
-
<ElementsTooltip {...
|
|
61
|
+
<ElementsTooltip {...makeProps({ elements: ["uno", "dos", "tres"], defaultElements: 3, maxChar: 1 })} />
|
|
69
62
|
</ThemeProvider>,
|
|
70
63
|
);
|
|
71
64
|
|
|
@@ -79,13 +72,9 @@ describe("Tooltip component rendering", () => {
|
|
|
79
72
|
});
|
|
80
73
|
|
|
81
74
|
it("should render the component with one row", async () => {
|
|
82
|
-
defaultProps.elements = ["uno", "dos", "tres"];
|
|
83
|
-
defaultProps.defaultElements = 1;
|
|
84
|
-
defaultProps.elementsPerRow = 3;
|
|
85
|
-
|
|
86
75
|
render(
|
|
87
76
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
88
|
-
<ElementsTooltip {...
|
|
77
|
+
<ElementsTooltip {...makeProps({ elements: ["uno", "dos", "tres"], defaultElements: 1, elementsPerRow: 3 })} />
|
|
89
78
|
</ThemeProvider>,
|
|
90
79
|
);
|
|
91
80
|
|
|
@@ -12,13 +12,17 @@ import globalTheme from "@ax/themes/theme.json";
|
|
|
12
12
|
|
|
13
13
|
afterEach(cleanup);
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// Props nuevos en cada test: antes se compartía un solo objeto mutable y el
|
|
16
|
+
// primer test, que comprueba el estado por defecto, fallaba en cuanto los
|
|
17
|
+
// siguientes le habían puesto title, message o button.
|
|
18
|
+
const makeProps = (overrides: Partial<IEmptyStateProps> = {}): IEmptyStateProps =>
|
|
19
|
+
Object.assign(mock<IEmptyStateProps>(), overrides);
|
|
16
20
|
|
|
17
21
|
describe("EmptyState component rendering", () => {
|
|
18
22
|
it("should render the component", () => {
|
|
19
23
|
render(
|
|
20
24
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
21
|
-
<EmptyState {...
|
|
25
|
+
<EmptyState {...makeProps()} />
|
|
22
26
|
</ThemeProvider>,
|
|
23
27
|
);
|
|
24
28
|
|
|
@@ -28,10 +32,9 @@ describe("EmptyState component rendering", () => {
|
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
it("should render the component with title", () => {
|
|
31
|
-
defaultProps.title = "The title";
|
|
32
35
|
render(
|
|
33
36
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
34
|
-
<EmptyState {...
|
|
37
|
+
<EmptyState {...makeProps({ title: "The title" })} />
|
|
35
38
|
</ThemeProvider>,
|
|
36
39
|
);
|
|
37
40
|
|
|
@@ -41,10 +44,9 @@ describe("EmptyState component rendering", () => {
|
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
it("should render the component with message", () => {
|
|
44
|
-
defaultProps.message = "A message";
|
|
45
47
|
render(
|
|
46
48
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
47
|
-
<EmptyState {...
|
|
49
|
+
<EmptyState {...makeProps({ message: "A message" })} />
|
|
48
50
|
</ThemeProvider>,
|
|
49
51
|
);
|
|
50
52
|
|
|
@@ -57,12 +59,10 @@ describe("EmptyState component rendering", () => {
|
|
|
57
59
|
describe("EmptyState component events", () => {
|
|
58
60
|
it("should trigger the onClick", () => {
|
|
59
61
|
const actionFunc = vi.fn();
|
|
60
|
-
defaultProps.button = "A button";
|
|
61
|
-
defaultProps.action = actionFunc;
|
|
62
62
|
|
|
63
63
|
render(
|
|
64
64
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
65
|
-
<EmptyState {...
|
|
65
|
+
<EmptyState {...makeProps({ button: "A button", action: actionFunc })} />
|
|
66
66
|
</ThemeProvider>,
|
|
67
67
|
);
|
|
68
68
|
|