@griddo/ax 12.8.1 → 12.8.3
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "12.8.
|
|
4
|
+
"version": "12.8.3",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Diego M. Béjar <diego.bejar@secuoyas.com>",
|
|
@@ -44,36 +44,36 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@dnd-kit/core": "6.3.1",
|
|
46
46
|
"@dnd-kit/sortable": "10.0.0",
|
|
47
|
-
"@griddo/api-types": "
|
|
48
|
-
"@pmmmwh/react-refresh-webpack-plugin": "0.5.
|
|
47
|
+
"@griddo/api-types": "1.1.3",
|
|
48
|
+
"@pmmmwh/react-refresh-webpack-plugin": "0.5.17",
|
|
49
49
|
"@styled-system/prop-types": "5.1.5",
|
|
50
50
|
"@styled-system/theme-get": "5.1.2",
|
|
51
51
|
"@svgr/webpack": "8.1.0",
|
|
52
|
-
"@testing-library/jest-dom": "5.
|
|
52
|
+
"@testing-library/jest-dom": "5.17.0",
|
|
53
53
|
"@testing-library/react": "13.4.0",
|
|
54
|
-
"@testing-library/user-event": "14.6.
|
|
54
|
+
"@testing-library/user-event": "14.6.6",
|
|
55
55
|
"@types/compress.js": "1.1.3",
|
|
56
|
-
"@types/draft-js": "0.11.
|
|
56
|
+
"@types/draft-js": "0.11.20",
|
|
57
57
|
"@types/lodash.isequal": "4.5.8",
|
|
58
58
|
"@types/markdown-draft-js": "2.2.7",
|
|
59
|
-
"@types/node": "20.
|
|
59
|
+
"@types/node": "20.19.43",
|
|
60
60
|
"@types/react": "18.2.79",
|
|
61
|
-
"@types/react-beautiful-dnd": "13.1.
|
|
61
|
+
"@types/react-beautiful-dnd": "13.1.8",
|
|
62
62
|
"@types/react-datepicker": "4.19.6",
|
|
63
63
|
"@types/react-dom": "18.2.25",
|
|
64
|
-
"@types/react-draft-wysiwyg": "1.13.
|
|
65
|
-
"@types/react-redux": "7.1.
|
|
64
|
+
"@types/react-draft-wysiwyg": "1.13.9",
|
|
65
|
+
"@types/react-redux": "7.1.34",
|
|
66
66
|
"@types/react-router-dom": "5.3.3",
|
|
67
67
|
"@types/react-slick": "0.23.13",
|
|
68
|
-
"@types/react-test-renderer": "17.0.
|
|
69
|
-
"@types/styled-components": "5.1.
|
|
68
|
+
"@types/react-test-renderer": "17.0.9",
|
|
69
|
+
"@types/styled-components": "5.1.36",
|
|
70
70
|
"@types/uuid": "8.3.4",
|
|
71
|
-
"@types/webpack-env": "1.18.
|
|
71
|
+
"@types/webpack-env": "1.18.8",
|
|
72
72
|
"axios": "0.19.0",
|
|
73
73
|
"babel-loader": "9.2.1",
|
|
74
74
|
"babel-plugin-named-asset-import": "0.3.8",
|
|
75
75
|
"babel-plugin-root-import": "6.6.0",
|
|
76
|
-
"babel-plugin-styled-components": "2.
|
|
76
|
+
"babel-plugin-styled-components": "2.3.0",
|
|
77
77
|
"babel-preset-react-app": "10.1.0",
|
|
78
78
|
"case-sensitive-paths-webpack-plugin": "2.4.0",
|
|
79
79
|
"compress.js": "1.2.2",
|
|
@@ -102,12 +102,12 @@
|
|
|
102
102
|
"mini-css-extract-plugin": "2.10.2",
|
|
103
103
|
"node-polyfill-webpack-plugin": "3.0.0",
|
|
104
104
|
"pkg-dir": "5.0.0",
|
|
105
|
-
"polished": "3.
|
|
106
|
-
"postcss": "8.5.
|
|
105
|
+
"polished": "3.7.2",
|
|
106
|
+
"postcss": "8.5.26",
|
|
107
107
|
"postcss-flexbugs-fixes": "5.0.2",
|
|
108
108
|
"postcss-loader": "7.3.4",
|
|
109
109
|
"postcss-normalize": "7.0.1",
|
|
110
|
-
"postcss-preset-env": "6.7.
|
|
110
|
+
"postcss-preset-env": "6.7.2",
|
|
111
111
|
"react": "18.2.0",
|
|
112
112
|
"react-app-polyfill": "1.0.6",
|
|
113
113
|
"react-datepicker": "4.25.0",
|
|
@@ -115,13 +115,13 @@
|
|
|
115
115
|
"react-dom": "18.2.0",
|
|
116
116
|
"react-draft-wysiwyg": "1.15.0",
|
|
117
117
|
"react-easy-crop": "5.5.7",
|
|
118
|
-
"react-error-boundary": "4.
|
|
118
|
+
"react-error-boundary": "4.1.2",
|
|
119
119
|
"react-froala-wysiwyg": "4.0.4",
|
|
120
|
-
"react-qr-code": "2.0
|
|
120
|
+
"react-qr-code": "2.2.0",
|
|
121
121
|
"react-redux": "7.2.9",
|
|
122
122
|
"react-refresh": "0.14.2",
|
|
123
|
-
"react-router-dom": "5.
|
|
124
|
-
"react-select": "5.10.
|
|
123
|
+
"react-router-dom": "5.3.4",
|
|
124
|
+
"react-select": "5.10.2",
|
|
125
125
|
"react-slick": "0.30.3",
|
|
126
126
|
"react-textarea-autosize": "8.5.9",
|
|
127
127
|
"react-timezone-map-select": "1.2.25",
|
|
@@ -129,11 +129,11 @@
|
|
|
129
129
|
"redux-devtools-extension": "2.13.9",
|
|
130
130
|
"redux-persist": "6.0.0",
|
|
131
131
|
"reflect-metadata": "0.1.13",
|
|
132
|
-
"resolve": "1.22.
|
|
132
|
+
"resolve": "1.22.12",
|
|
133
133
|
"resolve-url-loader": "4.0.0",
|
|
134
134
|
"sass-alias": "1.0.5",
|
|
135
135
|
"sass-loader": "16.0.8",
|
|
136
|
-
"semver": "7.
|
|
136
|
+
"semver": "7.8.5",
|
|
137
137
|
"slick-carousel": "1.8.1",
|
|
138
138
|
"source-map-loader": "1.1.3",
|
|
139
139
|
"string-replace-loader": "3.1.0",
|
|
@@ -149,18 +149,18 @@
|
|
|
149
149
|
"webpack-manifest-plugin": "5.0.1"
|
|
150
150
|
},
|
|
151
151
|
"devDependencies": {
|
|
152
|
-
"@babel/core": "7.
|
|
152
|
+
"@babel/core": "7.29.7",
|
|
153
153
|
"@babel/helper-environment-visitor": "7.24.7",
|
|
154
154
|
"@babel/helper-function-name": "7.24.7",
|
|
155
155
|
"@babel/helper-hoist-variables": "7.24.7",
|
|
156
156
|
"@babel/helper-split-export-declaration": "7.24.7",
|
|
157
|
-
"@babel/plugin-transform-optional-chaining": "7.
|
|
158
|
-
"@babel/preset-env": "7.
|
|
159
|
-
"@babel/preset-react": "7.
|
|
160
|
-
"@babel/preset-typescript": "7.
|
|
157
|
+
"@babel/plugin-transform-optional-chaining": "7.29.7",
|
|
158
|
+
"@babel/preset-env": "7.29.7",
|
|
159
|
+
"@babel/preset-react": "7.29.7",
|
|
160
|
+
"@babel/preset-typescript": "7.29.7",
|
|
161
161
|
"@biomejs/biome": "2.3.8",
|
|
162
|
-
"@types/redux-mock-store": "1.0
|
|
163
|
-
"@vitest/coverage-v8": "4.1.
|
|
162
|
+
"@types/redux-mock-store": "1.5.0",
|
|
163
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
164
164
|
"axios-mock-adapter": "1.22.0",
|
|
165
165
|
"prettier": "3.4.2",
|
|
166
166
|
"react-test-renderer": "18.2.0",
|
|
@@ -201,5 +201,5 @@
|
|
|
201
201
|
"publishConfig": {
|
|
202
202
|
"access": "public"
|
|
203
203
|
},
|
|
204
|
-
"gitHead": "
|
|
204
|
+
"gitHead": "2583a2f1211251425b5766dd3377e5ec38db7a89"
|
|
205
205
|
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { Router } from "react-router-dom";
|
|
2
|
+
|
|
3
|
+
import { parseTheme } from "@ax/helpers";
|
|
4
|
+
import Content from "@ax/modules/Content";
|
|
5
|
+
import { history } from "@ax/routes";
|
|
6
|
+
import globalTheme from "@ax/themes/theme.json";
|
|
7
|
+
|
|
8
|
+
import configureStore from "redux-mock-store";
|
|
9
|
+
import thunk from "redux-thunk";
|
|
10
|
+
import { ThemeProvider } from "styled-components";
|
|
11
|
+
import { mock } from "vitest-mock-extended";
|
|
12
|
+
|
|
13
|
+
import { act, cleanup, fireEvent, render, screen } from "./../../../../config/tests/test-utils";
|
|
14
|
+
|
|
15
|
+
const bulkDelete = vi.fn();
|
|
16
|
+
const getSitePages = vi.fn();
|
|
17
|
+
|
|
18
|
+
vi.mock("@ax/api", async (importOriginal) => {
|
|
19
|
+
const actual = (await importOriginal()) as any;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
...actual,
|
|
23
|
+
pages: {
|
|
24
|
+
...actual.pages,
|
|
25
|
+
bulkDelete: (...args: any[]) => bulkDelete(...args),
|
|
26
|
+
getSitePages: (...args: any[]) => getSitePages(...args),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// El mock global de `components` (config/tests/componentsMock.cjs) trae una plantilla
|
|
32
|
+
// `default` sin `type` que no existe en el catálogo real. getTemplatesFilters lee
|
|
33
|
+
// `type.value` de cada plantilla, así que esa entrada rompe el montaje del listado.
|
|
34
|
+
vi.mock("components", async (importOriginal) => {
|
|
35
|
+
const actual = (await importOriginal()) as any;
|
|
36
|
+
const source = actual.config ? actual : actual.default;
|
|
37
|
+
const { default: _defaultTemplate, ...templates } = source.config.schemas.ui.templates;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...source,
|
|
41
|
+
config: {
|
|
42
|
+
...source.config,
|
|
43
|
+
schemas: {
|
|
44
|
+
...source.config.schemas,
|
|
45
|
+
ui: { ...source.config.schemas.ui, templates },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(cleanup);
|
|
52
|
+
|
|
53
|
+
const buildPage = (id: number) => ({
|
|
54
|
+
id,
|
|
55
|
+
editorID: id,
|
|
56
|
+
title: `Page ${id}`,
|
|
57
|
+
slug: `/page-${id}`,
|
|
58
|
+
fullPath: { site: "/site-1", page: `/page-${id}` },
|
|
59
|
+
templateId: "BasicTemplate",
|
|
60
|
+
origin: "SITE",
|
|
61
|
+
isHome: false,
|
|
62
|
+
canBeUnpublished: true,
|
|
63
|
+
manuallyImported: false,
|
|
64
|
+
structuredDataContent: null,
|
|
65
|
+
publicationScheduled: null,
|
|
66
|
+
accessGrants: null,
|
|
67
|
+
haveDraftPage: null,
|
|
68
|
+
liveStatus: { status: "offline" },
|
|
69
|
+
pageLanguages: [
|
|
70
|
+
{ pageId: id, languageId: 4, locale: "en_GB" },
|
|
71
|
+
{ pageId: id + 100, languageId: 2, locale: "es_ES" },
|
|
72
|
+
],
|
|
73
|
+
modified: "2026-08-01T10:00:00.000Z",
|
|
74
|
+
categories: [],
|
|
75
|
+
isIndexed: true,
|
|
76
|
+
metaTitle: "",
|
|
77
|
+
metaDescription: "",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const pages = [1, 2, 3, 4, 5].map(buildPage);
|
|
81
|
+
const language = { id: 4, locale: "en_GB", language: "English", label: "EN", isDefault: true };
|
|
82
|
+
|
|
83
|
+
const initialStore = {
|
|
84
|
+
app: {
|
|
85
|
+
isLoading: false,
|
|
86
|
+
isSaving: false,
|
|
87
|
+
lang: { id: 4, locale: "en_GB" },
|
|
88
|
+
globalLangs: [language],
|
|
89
|
+
globalSettings: { skipReviewOnPublish: false },
|
|
90
|
+
error: null,
|
|
91
|
+
token: "eyJhbGciOiJIUzI",
|
|
92
|
+
},
|
|
93
|
+
sites: {
|
|
94
|
+
currentSiteInfo: { id: 1, name: "Site 1", hidden: false },
|
|
95
|
+
currentSitePages: pages,
|
|
96
|
+
allSitePages: pages,
|
|
97
|
+
currentFilter: "unique-pages",
|
|
98
|
+
totalItems: pages.length,
|
|
99
|
+
currentSiteLanguages: [language],
|
|
100
|
+
currentSiteErrorPages: [],
|
|
101
|
+
sitesByLang: [],
|
|
102
|
+
contentFilters: null,
|
|
103
|
+
currentSearch: "",
|
|
104
|
+
},
|
|
105
|
+
pageEditor: { template: null, currentPageLanguages: [], errors: [] },
|
|
106
|
+
structuredData: {
|
|
107
|
+
structuredData: { global: [], site: [] },
|
|
108
|
+
currentDataContent: [],
|
|
109
|
+
currentStructuredData: null,
|
|
110
|
+
},
|
|
111
|
+
dataPacks: { activated: [], templates: [] },
|
|
112
|
+
users: {
|
|
113
|
+
currentUser: { id: 1, isSuperAdmin: true },
|
|
114
|
+
currentPermissions: [],
|
|
115
|
+
globalPermissions: [],
|
|
116
|
+
roles: [],
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// El store se lee de una variable mutable para poder simular el paginador: cambiar
|
|
121
|
+
// de página sustituye `currentSitePages` sin desmontar el componente, que es lo que
|
|
122
|
+
// hace la app de verdad. `redux-mock-store` admite una función como estado.
|
|
123
|
+
const renderContent = async (visiblePages = pages) => {
|
|
124
|
+
let state = { ...initialStore, sites: { ...initialStore.sites, currentSitePages: visiblePages } };
|
|
125
|
+
const store = configureStore([thunk])(() => state);
|
|
126
|
+
|
|
127
|
+
await act(async () => {
|
|
128
|
+
render(
|
|
129
|
+
<Router history={history}>
|
|
130
|
+
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
131
|
+
<Content {...mock<any>()} />
|
|
132
|
+
</ThemeProvider>
|
|
133
|
+
</Router>,
|
|
134
|
+
{ store },
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const goToPageWith = async (nextPages: ReturnType<typeof buildPage>[]) => {
|
|
139
|
+
state = { ...state, sites: { ...state.sites, currentSitePages: nextPages } };
|
|
140
|
+
await act(async () => {
|
|
141
|
+
store.dispatch({ type: "@@test/PAGINATE" });
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return { store, goToPageWith };
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const checkboxOf = (pageId: number) =>
|
|
149
|
+
screen
|
|
150
|
+
.getAllByTestId("check-field-input")
|
|
151
|
+
.find((input) => input.getAttribute("value") === String(pageId)) as HTMLElement;
|
|
152
|
+
|
|
153
|
+
const selectPage = async (pageId: number) => {
|
|
154
|
+
await act(async () => {
|
|
155
|
+
fireEvent.click(checkboxOf(pageId));
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const selectRangeTo = async (pageId: number) => {
|
|
160
|
+
await act(async () => {
|
|
161
|
+
fireEvent.keyDown(window, { key: "Shift" });
|
|
162
|
+
});
|
|
163
|
+
await selectPage(pageId);
|
|
164
|
+
await act(async () => {
|
|
165
|
+
fireEvent.keyUp(window, { key: "Shift" });
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const confirmBulkDelete = async (allLanguages = false) => {
|
|
170
|
+
await act(async () => {
|
|
171
|
+
fireEvent.click(screen.getByText("delete"));
|
|
172
|
+
});
|
|
173
|
+
if (allLanguages) {
|
|
174
|
+
await act(async () => {
|
|
175
|
+
fireEvent.click(screen.getByText("Delete all languages versions"));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
await act(async () => {
|
|
179
|
+
fireEvent.click(screen.getByText("Delete page"));
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
describe("[UC-AX-explore-manage-pages] Content — borrado en bulk", () => {
|
|
184
|
+
beforeEach(() => {
|
|
185
|
+
bulkDelete.mockResolvedValue({ status: 200, data: {} });
|
|
186
|
+
getSitePages.mockResolvedValue({ status: 200, data: { items: [], totalItems: 0 } });
|
|
187
|
+
window.HTMLElement.prototype.scrollTo = vi.fn();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("borra todas las páginas de un rango seleccionado con Shift+click", async () => {
|
|
191
|
+
await renderContent();
|
|
192
|
+
|
|
193
|
+
await selectPage(1);
|
|
194
|
+
await selectRangeTo(5);
|
|
195
|
+
|
|
196
|
+
// El contador de la barra y el borrado tienen que ver lo mismo.
|
|
197
|
+
expect(screen.getByText("5 selected")).toBeInTheDocument();
|
|
198
|
+
|
|
199
|
+
await confirmBulkDelete();
|
|
200
|
+
|
|
201
|
+
expect(bulkDelete).toHaveBeenCalledTimes(1);
|
|
202
|
+
expect(bulkDelete.mock.calls[0][0]).toEqual([1, 2, 3, 4, 5]);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("con 'all languages versions' borra las versiones de todo el rango", async () => {
|
|
206
|
+
await renderContent();
|
|
207
|
+
|
|
208
|
+
await selectPage(1);
|
|
209
|
+
await selectRangeTo(5);
|
|
210
|
+
|
|
211
|
+
await confirmBulkDelete(true);
|
|
212
|
+
|
|
213
|
+
expect(bulkDelete).toHaveBeenCalledTimes(1);
|
|
214
|
+
// Cada página lleva su versión en inglés (id) y en español (id + 100).
|
|
215
|
+
expect([...bulkDelete.mock.calls[0][0]].sort((a, b) => a - b)).toEqual([1, 2, 3, 4, 5, 101, 102, 103, 104, 105]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("borra las páginas seleccionadas en otra página del paginador", async () => {
|
|
219
|
+
const firstPage = pages.slice(0, 3);
|
|
220
|
+
const secondPage = [6, 7, 8].map(buildPage);
|
|
221
|
+
const { goToPageWith } = await renderContent(firstPage);
|
|
222
|
+
|
|
223
|
+
await selectPage(1);
|
|
224
|
+
await goToPageWith(secondPage);
|
|
225
|
+
await selectPage(7);
|
|
226
|
+
|
|
227
|
+
await confirmBulkDelete();
|
|
228
|
+
|
|
229
|
+
expect(bulkDelete).toHaveBeenCalledTimes(1);
|
|
230
|
+
expect(bulkDelete.mock.calls[0][0]).toEqual([1, 7]);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("con 'all languages versions' incluye las versiones de las páginas fuera de vista", async () => {
|
|
234
|
+
const firstPage = pages.slice(0, 3);
|
|
235
|
+
const secondPage = [6, 7, 8].map(buildPage);
|
|
236
|
+
const { goToPageWith } = await renderContent(firstPage);
|
|
237
|
+
|
|
238
|
+
await selectPage(1);
|
|
239
|
+
await goToPageWith(secondPage);
|
|
240
|
+
await selectPage(7);
|
|
241
|
+
|
|
242
|
+
await confirmBulkDelete(true);
|
|
243
|
+
|
|
244
|
+
expect(bulkDelete).toHaveBeenCalledTimes(1);
|
|
245
|
+
// La página 1 ya no está a la vista, pero sus dos versiones tienen que caer igual.
|
|
246
|
+
expect([...bulkDelete.mock.calls[0][0]].sort((a, b) => a - b)).toEqual([1, 7, 101, 107]);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("borra todas las páginas seleccionadas con clics sueltos", async () => {
|
|
250
|
+
await renderContent();
|
|
251
|
+
|
|
252
|
+
await selectPage(1);
|
|
253
|
+
await selectPage(3);
|
|
254
|
+
await selectPage(5);
|
|
255
|
+
|
|
256
|
+
expect(screen.getByText("3 selected")).toBeInTheDocument();
|
|
257
|
+
|
|
258
|
+
await confirmBulkDelete();
|
|
259
|
+
|
|
260
|
+
expect(bulkDelete).toHaveBeenCalledTimes(1);
|
|
261
|
+
expect(bulkDelete.mock.calls[0][0]).toEqual([1, 3, 5]);
|
|
262
|
+
});
|
|
263
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useSelectedPages } from "@ax/modules/Content/hooks";
|
|
2
|
+
import type { IPage } from "@ax/types";
|
|
3
|
+
|
|
4
|
+
import { renderHook } from "@testing-library/react";
|
|
5
|
+
|
|
6
|
+
const buildPage = (id: number, languages = 1) =>
|
|
7
|
+
({
|
|
8
|
+
id,
|
|
9
|
+
title: `Page ${id}`,
|
|
10
|
+
pageLanguages: Array.from({ length: languages }, (_, index) => ({ pageId: id + index * 100 })),
|
|
11
|
+
}) as unknown as IPage;
|
|
12
|
+
|
|
13
|
+
describe("[UC-AX-explore-manage-pages] useSelectedPages", () => {
|
|
14
|
+
it("resuelve una página por cada id seleccionado", () => {
|
|
15
|
+
const visiblePages = [buildPage(1), buildPage(2), buildPage(3)];
|
|
16
|
+
|
|
17
|
+
const { result } = renderHook(() => useSelectedPages([1, 3], visiblePages));
|
|
18
|
+
|
|
19
|
+
expect(result.current.map((page) => page.id)).toEqual([1, 3]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("recuerda las páginas seleccionadas en otra página del paginador", () => {
|
|
23
|
+
const firstPage = [buildPage(1), buildPage(2)];
|
|
24
|
+
const secondPage = [buildPage(3), buildPage(4)];
|
|
25
|
+
|
|
26
|
+
const { result, rerender } = renderHook(
|
|
27
|
+
({ selectedIds, visiblePages }) => useSelectedPages(selectedIds, visiblePages),
|
|
28
|
+
{ initialProps: { selectedIds: [1], visiblePages: firstPage } },
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// El usuario pasa a la siguiente página del paginador y sigue seleccionando.
|
|
32
|
+
rerender({ selectedIds: [1, 3], visiblePages: secondPage });
|
|
33
|
+
|
|
34
|
+
expect(result.current.map((page) => page.id)).toEqual([1, 3]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("descarta los ids que no corresponden a ninguna página conocida", () => {
|
|
38
|
+
const { result } = renderHook(() => useSelectedPages([1, 999], [buildPage(1)]));
|
|
39
|
+
|
|
40
|
+
expect(result.current.map((page) => page.id)).toEqual([1]);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import type { IPage, IQueryValue } from "@ax/types";
|
|
3
4
|
|
|
4
5
|
const useSortedListStatus = () => {
|
|
5
6
|
const sortedInitialState: {
|
|
@@ -106,4 +107,16 @@ const useFilterQuery = (values?: Record<string, IQueryValue[]> | null) => {
|
|
|
106
107
|
};
|
|
107
108
|
};
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
const useSelectedPages = (selectedIds: number[], visiblePages: IPage[]): IPage[] => {
|
|
111
|
+
// La selección sobrevive al cambio de página del paginador, así que hay que
|
|
112
|
+
// recordar las páginas que ya no están a la vista para poder resolverlas.
|
|
113
|
+
const knownPages = useRef(new Map<number, IPage>());
|
|
114
|
+
|
|
115
|
+
visiblePages.forEach((page) => {
|
|
116
|
+
knownPages.current.set(page.id, page);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return selectedIds.map((id) => knownPages.current.get(id)).filter((page): page is IPage => !!page);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export { useSortedListStatus, useFilterQuery, useSelectedPages };
|
|
@@ -58,7 +58,7 @@ import StructuredDataItem from "./../StructuredData/StructuredDataList/Structure
|
|
|
58
58
|
import { DeleteModal } from "./atoms";
|
|
59
59
|
import BulkHeader from "./BulkHeader";
|
|
60
60
|
import ContentFilters from "./ContentFilters";
|
|
61
|
-
import { useFilterQuery, useSortedListStatus } from "./hooks";
|
|
61
|
+
import { useFilterQuery, useSelectedPages, useSortedListStatus } from "./hooks";
|
|
62
62
|
import NewContentModal from "./NewContentModal";
|
|
63
63
|
import PageItem from "./PageItem";
|
|
64
64
|
import {
|
|
@@ -194,7 +194,6 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
194
194
|
const [deleteAllVersions, setDeleteAllVersions] = useState(false);
|
|
195
195
|
const [arePagesTranslated, setArePagesTranslated] = useState(false);
|
|
196
196
|
const [templateInstanceError, setTemplateInstanceError] = useState({ error: false, templateName: "" });
|
|
197
|
-
const [pagesSelected, setPagesSelected] = useState<any[]>([]);
|
|
198
197
|
const [selectedOption, setSelectedOption] = useState<string | null>(null);
|
|
199
198
|
const history = useHistory();
|
|
200
199
|
|
|
@@ -220,6 +219,8 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
220
219
|
setHoverCheck,
|
|
221
220
|
} = useBulkSelection(contentIds);
|
|
222
221
|
|
|
222
|
+
const pagesSelected = useSelectedPages(selectedItems.all, currentSitePages);
|
|
223
|
+
|
|
223
224
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
224
225
|
const {
|
|
225
226
|
isVisible: isVisibleRemovedToast,
|
|
@@ -390,25 +391,11 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
390
391
|
|
|
391
392
|
const bulkFilter = (bulkSelection: number[]) => filterByStatus(bulkSelection, currentSitePages);
|
|
392
393
|
|
|
393
|
-
const handleAddToBulk = (item: ICheck) =>
|
|
394
|
-
const page = currentSitePages.find((page: IPage) => page.id === item.value);
|
|
394
|
+
const handleAddToBulk = (item: ICheck) => addToBulkSelection(item, bulkFilter);
|
|
395
395
|
|
|
396
|
-
|
|
397
|
-
? setPagesSelected((pagesSelected) => [...pagesSelected, page])
|
|
398
|
-
: setPagesSelected(pagesSelected.filter((page: IPage) => page.id !== item.value));
|
|
396
|
+
const handleSelectAll = () => selectAllItems(bulkFilter);
|
|
399
397
|
|
|
400
|
-
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
const handleSelectAll = () => {
|
|
404
|
-
selectAllItems(bulkFilter);
|
|
405
|
-
setPagesSelected(isStructuredData ? currentDataContent : currentSitePages);
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
const unselectAllItems = () => {
|
|
409
|
-
resetBulkSelection();
|
|
410
|
-
setPagesSelected([]);
|
|
411
|
-
};
|
|
398
|
+
const unselectAllItems = () => resetBulkSelection();
|
|
412
399
|
|
|
413
400
|
const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
|
|
414
401
|
|
|
@@ -485,12 +472,9 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
485
472
|
const bulkDelete = async () => {
|
|
486
473
|
let allPageVersions: number[] = [];
|
|
487
474
|
if (deleteAllVersions) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const ids = element.map((lang: IPageLanguage) => lang.pageId);
|
|
492
|
-
allPageVersions = [...allPageVersions, ...ids];
|
|
493
|
-
});
|
|
475
|
+
allPageVersions = pagesSelected.flatMap((page: IPage) =>
|
|
476
|
+
page.pageLanguages.map((lang: IPageLanguage) => lang.pageId),
|
|
477
|
+
);
|
|
494
478
|
}
|
|
495
479
|
|
|
496
480
|
isStructuredData
|
|
@@ -511,8 +495,7 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
511
495
|
};
|
|
512
496
|
|
|
513
497
|
const handleToggleDeleteModal = () => {
|
|
514
|
-
const
|
|
515
|
-
const hasTranslations = selectedPages.some((page) => page.pageLanguages.length > 1);
|
|
498
|
+
const hasTranslations = pagesSelected.some((page: IPage) => page.pageLanguages.length > 1);
|
|
516
499
|
setArePagesTranslated(hasTranslations);
|
|
517
500
|
toggleDeleteModal();
|
|
518
501
|
};
|