@griddo/ax 11.16.0-rc.3 → 11.17.1-rc.0
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/README.md +1 -1
- package/package.json +2 -2
- package/src/__tests__/components/ConfigPanel/GlobalPageForm/GlobalPageForm.test.tsx +21 -14
- package/src/__tests__/components/RestrictionsModal/CurrentAccessList/CurrentAccessList.test.tsx +66 -20
- package/src/__tests__/components/RestrictionsModal/RestrictionsModal.test.tsx +9 -4
- package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +222 -3
- package/src/api/sites.tsx +16 -0
- package/src/components/Fields/AsyncSelect/index.tsx +5 -0
- package/src/components/Fields/AsyncSelect/style.tsx +57 -60
- package/src/components/Icon/components/Republish.js +22 -0
- package/src/components/Icon/svgs/Republish.svg +4 -0
- package/src/components/Modal/style.tsx +1 -0
- package/src/components/Toast/index.tsx +2 -2
- package/src/containers/Sites/actions.tsx +18 -0
- package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +30 -3
- package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +20 -4
- package/src/modules/Sites/SitesList/SiteModal/index.tsx +2 -0
- package/src/modules/Sites/SitesList/atoms.tsx +36 -2
- package/src/modules/Sites/SitesList/hooks.tsx +37 -6
- package/src/modules/Sites/SitesList/index.tsx +7 -3
- package/src/modules/Sites/SitesList/style.tsx +9 -0
package/README.md
CHANGED
|
@@ -53,4 +53,4 @@ node_modules/.bin/biome lint . --only=noConsole
|
|
|
53
53
|
- :PACKAGE_WITH_PROTOCOL: scoped and bare packages with a protocol.
|
|
54
54
|
- :PATH: absolute and relative paths.
|
|
55
55
|
- :URL: sources starting with https:// and http://.
|
|
56
|
-
- :BLANK_LINE: blank line.
|
|
56
|
+
- :BLANK_LINE: blank line.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "11.
|
|
4
|
+
"version": "11.17.1-rc.0",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Diego M. Béjar <diego.bejar@secuoyas.com>",
|
|
@@ -219,5 +219,5 @@
|
|
|
219
219
|
"publishConfig": {
|
|
220
220
|
"access": "public"
|
|
221
221
|
},
|
|
222
|
-
"gitHead": "
|
|
222
|
+
"gitHead": "4111b59796126ce5a803e97d8a13e3f9f6d433ef"
|
|
223
223
|
}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { ThemeProvider } from "styled-components";
|
|
4
|
-
import { render, cleanup, screen, fireEvent } from "../../../../../config/jest/test-utils";
|
|
5
1
|
import { mock } from "jest-mock-extended";
|
|
6
2
|
import configureStore from "redux-mock-store";
|
|
3
|
+
import { ThemeProvider } from "styled-components";
|
|
4
|
+
|
|
5
|
+
import { cleanup, fireEvent, render, screen, waitFor } from "../../../../../config/jest/test-utils";
|
|
7
6
|
import "@testing-library/jest-dom";
|
|
8
|
-
import thunk from "redux-thunk";
|
|
9
7
|
|
|
8
|
+
import GlobalPageForm, { type IGlobalPageFormProps } from "@ax/components/ConfigPanel/GlobalPageForm";
|
|
10
9
|
import { parseTheme } from "@ax/helpers";
|
|
11
|
-
import GlobalPageForm, { IGlobalPageFormProps } from "@ax/components/ConfigPanel/GlobalPageForm";
|
|
12
10
|
import globalTheme from "@ax/themes/theme.json";
|
|
13
11
|
|
|
12
|
+
import thunk from "redux-thunk";
|
|
13
|
+
|
|
14
|
+
jest.mock("@ax/hooks", () => ({
|
|
15
|
+
...jest.requireActual("@ax/hooks"),
|
|
16
|
+
useGlobalPermission: jest.fn(() => true),
|
|
17
|
+
}));
|
|
18
|
+
|
|
14
19
|
beforeEach(() => {
|
|
15
20
|
cleanup();
|
|
16
21
|
});
|
|
@@ -127,7 +132,7 @@ describe("GlobalPageForm component rendering", () => {
|
|
|
127
132
|
{ store },
|
|
128
133
|
);
|
|
129
134
|
|
|
130
|
-
expect(screen.getByText(/This page doesn
|
|
135
|
+
expect(screen.getByText(/This page doesn't have a header. Click/i)).toBeInTheDocument();
|
|
131
136
|
});
|
|
132
137
|
|
|
133
138
|
it("should render the component with no footer warning text", () => {
|
|
@@ -140,7 +145,7 @@ describe("GlobalPageForm component rendering", () => {
|
|
|
140
145
|
</ThemeProvider>,
|
|
141
146
|
{ store },
|
|
142
147
|
);
|
|
143
|
-
expect(screen.getByText(/This page doesn
|
|
148
|
+
expect(screen.getByText(/This page doesn't have a footer. Click/i)).toBeInTheDocument();
|
|
144
149
|
});
|
|
145
150
|
|
|
146
151
|
it("should render the connectedField component", () => {
|
|
@@ -166,9 +171,9 @@ describe("GlobalPageForm component rendering", () => {
|
|
|
166
171
|
});
|
|
167
172
|
|
|
168
173
|
describe("GlobalPageForm component rendering", () => {
|
|
169
|
-
it("should trigger handleClick", () => {
|
|
174
|
+
it("should trigger handleClick", async () => {
|
|
170
175
|
defaultProps.selectedTab = "content";
|
|
171
|
-
const getGlobalFromLocalPageActionMock = jest.fn();
|
|
176
|
+
const getGlobalFromLocalPageActionMock = jest.fn(() => Promise.resolve());
|
|
172
177
|
const saveCurrentSiteInfoActionMock = jest.fn();
|
|
173
178
|
defaultProps.actions.saveCurrentSiteInfoAction = saveCurrentSiteInfoActionMock;
|
|
174
179
|
defaultProps.actions.getGlobalFromLocalPageAction = getGlobalFromLocalPageActionMock;
|
|
@@ -185,8 +190,10 @@ describe("GlobalPageForm component rendering", () => {
|
|
|
185
190
|
const editButton = screen.getByText<HTMLButtonElement>("Edit");
|
|
186
191
|
expect(editButton).toBeTruthy();
|
|
187
192
|
fireEvent.click(editButton);
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
expect(getGlobalFromLocalPageActionMock).toBeCalled();
|
|
195
|
+
expect(saveCurrentSiteInfoActionMock).toBeCalled();
|
|
196
|
+
});
|
|
190
197
|
});
|
|
191
198
|
|
|
192
199
|
it("should trigger handleRestoreHeader", () => {
|
|
@@ -202,7 +209,7 @@ describe("GlobalPageForm component rendering", () => {
|
|
|
202
209
|
{ store },
|
|
203
210
|
);
|
|
204
211
|
|
|
205
|
-
expect(screen.getByText(/This page doesn
|
|
212
|
+
expect(screen.getByText(/This page doesn't have a header. Click/i)).toBeInTheDocument();
|
|
206
213
|
const headerLink = screen.getByTestId("header-link");
|
|
207
214
|
expect(headerLink).toBeTruthy();
|
|
208
215
|
fireEvent.click(headerLink);
|
|
@@ -222,7 +229,7 @@ describe("GlobalPageForm component rendering", () => {
|
|
|
222
229
|
{ store },
|
|
223
230
|
);
|
|
224
231
|
|
|
225
|
-
expect(screen.getByText(/This page doesn
|
|
232
|
+
expect(screen.getByText(/This page doesn't have a footer. Click/i)).toBeInTheDocument();
|
|
226
233
|
const footerLink = screen.getByTestId("footer-link");
|
|
227
234
|
expect(footerLink).toBeTruthy();
|
|
228
235
|
fireEvent.click(footerLink);
|
package/src/__tests__/components/RestrictionsModal/CurrentAccessList/CurrentAccessList.test.tsx
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { ThemeProvider } from "styled-components";
|
|
2
|
-
import { render, cleanup, screen, fireEvent } from "@testing-library/react";
|
|
3
1
|
import { parseTheme } from "@ax/helpers";
|
|
2
|
+
|
|
3
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import { ThemeProvider } from "styled-components";
|
|
4
5
|
import "@testing-library/jest-dom";
|
|
5
6
|
|
|
6
7
|
import CurrentAccessList from "@ax/components/RestrictionsModal/CurrentAccessList";
|
|
7
|
-
import type { IUser } from "@ax/types";
|
|
8
8
|
import globalTheme from "@ax/themes/theme.json";
|
|
9
|
+
import type { IUser } from "@ax/types";
|
|
9
10
|
|
|
10
11
|
jest.mock("react-redux", () => ({
|
|
11
12
|
...jest.requireActual("react-redux"),
|
|
@@ -29,7 +30,6 @@ const defaultProps: {
|
|
|
29
30
|
currentAccessGrants: { users: number[]; roles: number[] };
|
|
30
31
|
onEdit: jest.Mock;
|
|
31
32
|
users: IUser[];
|
|
32
|
-
canSetPermissions: boolean;
|
|
33
33
|
} = {
|
|
34
34
|
restrictionType: "users",
|
|
35
35
|
currentAccessGrants: {
|
|
@@ -59,7 +59,6 @@ const defaultProps: {
|
|
|
59
59
|
pagePermissions: [],
|
|
60
60
|
},
|
|
61
61
|
],
|
|
62
|
-
canSetPermissions: true,
|
|
63
62
|
};
|
|
64
63
|
|
|
65
64
|
const renderComponent = (props = defaultProps) =>
|
|
@@ -90,23 +89,44 @@ describe("CurrentAccessList component rendering", () => {
|
|
|
90
89
|
expect(screen.getByText(/Only these roles/)).toBeTruthy();
|
|
91
90
|
});
|
|
92
91
|
|
|
93
|
-
it("should render the Manage User Access button
|
|
94
|
-
renderComponent(
|
|
92
|
+
it("should render the Manage User Access button", () => {
|
|
93
|
+
renderComponent();
|
|
95
94
|
|
|
96
95
|
expect(screen.getByText("MANAGE USER ACCESS")).toBeTruthy();
|
|
97
96
|
});
|
|
98
97
|
|
|
99
|
-
it("should not render the Manage User Access button when canSetPermissions is false", () => {
|
|
100
|
-
renderComponent({ ...defaultProps, canSetPermissions: false });
|
|
101
|
-
|
|
102
|
-
expect(screen.queryByText("MANAGE USER ACCESS")).toBeNull();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
98
|
it("should filter to show only selected users when restrictionType is users", () => {
|
|
106
99
|
const allUsers = [
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
{
|
|
101
|
+
id: 1,
|
|
102
|
+
username: "john",
|
|
103
|
+
name: "John Doe",
|
|
104
|
+
email: "john@example.com",
|
|
105
|
+
failed: 0,
|
|
106
|
+
roles: [],
|
|
107
|
+
isSuperAdmin: false,
|
|
108
|
+
pagePermissions: [],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 2,
|
|
112
|
+
username: "jane",
|
|
113
|
+
name: "Jane Smith",
|
|
114
|
+
email: "jane@example.com",
|
|
115
|
+
failed: 0,
|
|
116
|
+
roles: [],
|
|
117
|
+
isSuperAdmin: false,
|
|
118
|
+
pagePermissions: [],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 3,
|
|
122
|
+
username: "bob",
|
|
123
|
+
name: "Bob Wilson",
|
|
124
|
+
email: "bob@example.com",
|
|
125
|
+
failed: 0,
|
|
126
|
+
roles: [],
|
|
127
|
+
isSuperAdmin: false,
|
|
128
|
+
pagePermissions: [],
|
|
129
|
+
},
|
|
110
130
|
];
|
|
111
131
|
|
|
112
132
|
renderComponent({
|
|
@@ -128,7 +148,6 @@ describe("CurrentAccessList component events", () => {
|
|
|
128
148
|
|
|
129
149
|
renderComponent({
|
|
130
150
|
...defaultProps,
|
|
131
|
-
canSetPermissions: true,
|
|
132
151
|
onEdit: onEditMock,
|
|
133
152
|
});
|
|
134
153
|
|
|
@@ -153,9 +172,36 @@ describe("CurrentAccessList component events", () => {
|
|
|
153
172
|
|
|
154
173
|
it("should handle multiple users correctly", () => {
|
|
155
174
|
const allUsers = [
|
|
156
|
-
{
|
|
157
|
-
|
|
158
|
-
|
|
175
|
+
{
|
|
176
|
+
id: 1,
|
|
177
|
+
username: "john",
|
|
178
|
+
name: "John Doe",
|
|
179
|
+
email: "john@example.com",
|
|
180
|
+
failed: 0,
|
|
181
|
+
roles: [],
|
|
182
|
+
isSuperAdmin: false,
|
|
183
|
+
pagePermissions: [],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: 2,
|
|
187
|
+
username: "jane",
|
|
188
|
+
name: "Jane Smith",
|
|
189
|
+
email: "jane@example.com",
|
|
190
|
+
failed: 0,
|
|
191
|
+
roles: [],
|
|
192
|
+
isSuperAdmin: false,
|
|
193
|
+
pagePermissions: [],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 3,
|
|
197
|
+
username: "bob",
|
|
198
|
+
name: "Bob Wilson",
|
|
199
|
+
email: "bob@example.com",
|
|
200
|
+
failed: 0,
|
|
201
|
+
roles: [],
|
|
202
|
+
isSuperAdmin: false,
|
|
203
|
+
pagePermissions: [],
|
|
204
|
+
},
|
|
159
205
|
];
|
|
160
206
|
|
|
161
207
|
renderComponent({
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
2
2
|
import { ThemeProvider } from "styled-components";
|
|
3
|
-
import { render, cleanup, screen, fireEvent } from "@testing-library/react";
|
|
4
3
|
import "@testing-library/jest-dom";
|
|
5
4
|
|
|
6
5
|
import RestrictionsModal from "@ax/components/RestrictionsModal";
|
|
7
6
|
import { parseTheme } from "@ax/helpers";
|
|
8
7
|
import globalTheme from "@ax/themes/theme.json";
|
|
8
|
+
import type { PageAccessGrantsSummary } from "@ax/types";
|
|
9
9
|
|
|
10
10
|
jest.mock("@ax/components/RestrictionsModal/utils", () => ({
|
|
11
11
|
fetchUsers: jest.fn().mockResolvedValue([]),
|
|
@@ -32,13 +32,18 @@ jest.mock("react-redux", () => ({
|
|
|
32
32
|
|
|
33
33
|
afterEach(cleanup);
|
|
34
34
|
|
|
35
|
-
const defaultProps
|
|
35
|
+
const defaultProps: {
|
|
36
|
+
isOpen: boolean;
|
|
37
|
+
toggleModal: jest.Mock;
|
|
38
|
+
currentAccessGrants: PageAccessGrantsSummary | undefined;
|
|
39
|
+
onUpdateAccessGrants: jest.Mock;
|
|
40
|
+
isSaving: boolean;
|
|
41
|
+
} = {
|
|
36
42
|
isOpen: false,
|
|
37
43
|
toggleModal: jest.fn(),
|
|
38
44
|
currentAccessGrants: undefined,
|
|
39
45
|
onUpdateAccessGrants: jest.fn().mockResolvedValue(undefined),
|
|
40
46
|
isSaving: false,
|
|
41
|
-
canSetPermissions: true,
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
const renderComponent = (props = defaultProps) =>
|
|
@@ -386,7 +386,7 @@ describe("Sites module events", () => {
|
|
|
386
386
|
|
|
387
387
|
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
388
388
|
expect(actionMenuItem.length).toEqual(2);
|
|
389
|
-
fireEvent.click(actionMenuItem[
|
|
389
|
+
fireEvent.click(actionMenuItem[1]);
|
|
390
390
|
const deleteModal = screen.getByTestId("delete-modal");
|
|
391
391
|
expect(deleteModal).toBeTruthy();
|
|
392
392
|
});
|
|
@@ -407,7 +407,7 @@ describe("Sites module events", () => {
|
|
|
407
407
|
|
|
408
408
|
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
409
409
|
expect(actionMenuItem.length).toEqual(2);
|
|
410
|
-
fireEvent.click(actionMenuItem[
|
|
410
|
+
fireEvent.click(actionMenuItem[1]);
|
|
411
411
|
const deleteModal = screen.getByTestId("delete-modal");
|
|
412
412
|
expect(deleteModal).toBeTruthy();
|
|
413
413
|
});
|
|
@@ -426,7 +426,7 @@ describe("Sites module events", () => {
|
|
|
426
426
|
|
|
427
427
|
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
428
428
|
expect(actionMenuItem.length).toEqual(2);
|
|
429
|
-
fireEvent.click(actionMenuItem[
|
|
429
|
+
fireEvent.click(actionMenuItem[0]);
|
|
430
430
|
const publishModal = screen.getByTestId("publish-modal");
|
|
431
431
|
expect(publishModal).toBeTruthy();
|
|
432
432
|
});
|
|
@@ -447,8 +447,227 @@ describe("Sites module events", () => {
|
|
|
447
447
|
|
|
448
448
|
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
449
449
|
expect(actionMenuItem.length).toEqual(2);
|
|
450
|
+
fireEvent.click(actionMenuItem[0]);
|
|
451
|
+
const publishModal = screen.getByTestId("publish-modal");
|
|
452
|
+
expect(publishModal).toBeTruthy();
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it("should disable delete button when confirmation input is incorrect", async () => {
|
|
456
|
+
await renderSitesList();
|
|
457
|
+
|
|
458
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
459
|
+
|
|
460
|
+
fireEvent.click(moreInfoButton[0]);
|
|
461
|
+
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
462
|
+
fireEvent.click(actionMenuItem[1]);
|
|
463
|
+
|
|
464
|
+
const deleteModal = screen.getByTestId("delete-modal");
|
|
465
|
+
expect(deleteModal).toBeTruthy();
|
|
466
|
+
|
|
467
|
+
const mainDeleteButton = screen.getByRole("button", { name: "Delete Site" });
|
|
468
|
+
expect((mainDeleteButton as HTMLButtonElement).disabled).toBe(true);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("should enable delete button when confirmation input is correct", async () => {
|
|
472
|
+
await renderSitesList();
|
|
473
|
+
|
|
474
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
475
|
+
|
|
476
|
+
fireEvent.click(moreInfoButton[0]);
|
|
477
|
+
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
478
|
+
fireEvent.click(actionMenuItem[1]);
|
|
479
|
+
|
|
480
|
+
const deleteModal = screen.getByTestId("delete-modal");
|
|
481
|
+
expect(deleteModal).toBeTruthy();
|
|
482
|
+
|
|
483
|
+
const input = screen.getByPlaceholderText("Type here") as HTMLInputElement;
|
|
484
|
+
await act(async () => {
|
|
485
|
+
fireEvent.change(input, { target: { value: "SITE 1" } });
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
await waitFor(() => {
|
|
489
|
+
const mainDeleteButton = screen.getByRole("button", { name: "Delete Site" });
|
|
490
|
+
expect((mainDeleteButton as HTMLButtonElement).disabled).toBe(false);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("should close delete modal when clicking cancel", async () => {
|
|
495
|
+
await renderSitesList();
|
|
496
|
+
|
|
497
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
498
|
+
fireEvent.click(moreInfoButton[0]);
|
|
499
|
+
|
|
500
|
+
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
501
|
+
fireEvent.click(actionMenuItem[1]);
|
|
502
|
+
|
|
503
|
+
const deleteModal = screen.getByTestId("delete-modal");
|
|
504
|
+
expect(deleteModal).toBeTruthy();
|
|
505
|
+
|
|
506
|
+
const cancelButton = screen.getByRole("button", { name: "Cancel" });
|
|
507
|
+
fireEvent.click(cancelButton);
|
|
508
|
+
|
|
509
|
+
expect(screen.queryByTestId("delete-modal")).not.toBeTruthy();
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it("should show view online option for published sites", async () => {
|
|
513
|
+
const publishedSitesData = {
|
|
514
|
+
...initialStore,
|
|
515
|
+
sites: {
|
|
516
|
+
totalItems: 1,
|
|
517
|
+
sites: [
|
|
518
|
+
{
|
|
519
|
+
...sitesDataMock.sites[0],
|
|
520
|
+
isPublished: true,
|
|
521
|
+
updated: true,
|
|
522
|
+
},
|
|
523
|
+
],
|
|
524
|
+
recentSites: [],
|
|
525
|
+
config: sitesDataMock.config,
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
await renderSitesList(publishedSitesData);
|
|
530
|
+
|
|
531
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
532
|
+
fireEvent.click(moreInfoButton[0]);
|
|
533
|
+
|
|
534
|
+
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
535
|
+
expect(actionMenuItem.length).toBeGreaterThan(2);
|
|
536
|
+
|
|
537
|
+
const viewOnlineItem = screen.getByText("View online");
|
|
538
|
+
expect(viewOnlineItem).toBeTruthy();
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("should show unpublish and republish options for published sites", async () => {
|
|
542
|
+
const publishedSitesData = {
|
|
543
|
+
...initialStore,
|
|
544
|
+
sites: {
|
|
545
|
+
totalItems: 1,
|
|
546
|
+
sites: [
|
|
547
|
+
{
|
|
548
|
+
...sitesDataMock.sites[0],
|
|
549
|
+
isPublished: true,
|
|
550
|
+
updated: true,
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
recentSites: [],
|
|
554
|
+
config: sitesDataMock.config,
|
|
555
|
+
},
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
await renderSitesList(publishedSitesData);
|
|
559
|
+
|
|
560
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
561
|
+
fireEvent.click(moreInfoButton[0]);
|
|
562
|
+
|
|
563
|
+
const unpublishItem = screen.getByText("Unpublish site");
|
|
564
|
+
const republishItem = screen.getByText("Republish site");
|
|
565
|
+
|
|
566
|
+
expect(unpublishItem).toBeTruthy();
|
|
567
|
+
expect(republishItem).toBeTruthy();
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it("should open republish modal when clicking republish option", async () => {
|
|
571
|
+
const publishedSitesData = {
|
|
572
|
+
...initialStore,
|
|
573
|
+
sites: {
|
|
574
|
+
totalItems: 1,
|
|
575
|
+
sites: [
|
|
576
|
+
{
|
|
577
|
+
...sitesDataMock.sites[0],
|
|
578
|
+
isPublished: true,
|
|
579
|
+
updated: true,
|
|
580
|
+
},
|
|
581
|
+
],
|
|
582
|
+
recentSites: [],
|
|
583
|
+
config: sitesDataMock.config,
|
|
584
|
+
},
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
await renderSitesList(publishedSitesData);
|
|
588
|
+
|
|
589
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
590
|
+
fireEvent.click(moreInfoButton[0]);
|
|
591
|
+
|
|
592
|
+
const republishItem = screen.getByText("Republish site");
|
|
593
|
+
fireEvent.click(republishItem);
|
|
594
|
+
|
|
595
|
+
const republishModal = screen.getByTestId("republish-site-modal");
|
|
596
|
+
expect(republishModal).toBeTruthy();
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it("should open unpublish modal when clicking unpublish option", async () => {
|
|
600
|
+
const publishedSitesData = {
|
|
601
|
+
...initialStore,
|
|
602
|
+
sites: {
|
|
603
|
+
totalItems: 1,
|
|
604
|
+
sites: [
|
|
605
|
+
{
|
|
606
|
+
...sitesDataMock.sites[0],
|
|
607
|
+
isPublished: true,
|
|
608
|
+
updated: true,
|
|
609
|
+
},
|
|
610
|
+
],
|
|
611
|
+
recentSites: [],
|
|
612
|
+
config: sitesDataMock.config,
|
|
613
|
+
},
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
await renderSitesList(publishedSitesData);
|
|
617
|
+
|
|
618
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
619
|
+
fireEvent.click(moreInfoButton[0]);
|
|
620
|
+
|
|
621
|
+
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
450
622
|
fireEvent.click(actionMenuItem[1]);
|
|
623
|
+
|
|
451
624
|
const publishModal = screen.getByTestId("publish-modal");
|
|
452
625
|
expect(publishModal).toBeTruthy();
|
|
453
626
|
});
|
|
627
|
+
|
|
628
|
+
it("should render publish action for unpublished sites", async () => {
|
|
629
|
+
await renderSitesList();
|
|
630
|
+
|
|
631
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
632
|
+
fireEvent.click(moreInfoButton[0]);
|
|
633
|
+
|
|
634
|
+
const publishItem = screen.getByText("Publish site");
|
|
635
|
+
expect(publishItem).toBeTruthy();
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it("should not show view online option for unpublished sites", async () => {
|
|
639
|
+
await renderSitesList();
|
|
640
|
+
|
|
641
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
642
|
+
fireEvent.click(moreInfoButton[0]);
|
|
643
|
+
|
|
644
|
+
const viewOnlineItem = screen.queryByText("View online");
|
|
645
|
+
expect(viewOnlineItem).not.toBeTruthy();
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it("should show 4 action items for published sites", async () => {
|
|
649
|
+
const publishedSitesData = {
|
|
650
|
+
...initialStore,
|
|
651
|
+
sites: {
|
|
652
|
+
totalItems: 1,
|
|
653
|
+
sites: [
|
|
654
|
+
{
|
|
655
|
+
...sitesDataMock.sites[0],
|
|
656
|
+
isPublished: true,
|
|
657
|
+
updated: true,
|
|
658
|
+
},
|
|
659
|
+
],
|
|
660
|
+
recentSites: [],
|
|
661
|
+
config: sitesDataMock.config,
|
|
662
|
+
},
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
await renderSitesList(publishedSitesData);
|
|
666
|
+
|
|
667
|
+
const moreInfoButton = screen.getAllByTestId("more-info-button");
|
|
668
|
+
fireEvent.click(moreInfoButton[0]);
|
|
669
|
+
|
|
670
|
+
const actionMenuItem = screen.getAllByTestId("action-menu-item");
|
|
671
|
+
expect(actionMenuItem.length).toEqual(4);
|
|
672
|
+
});
|
|
454
673
|
});
|
package/src/api/sites.tsx
CHANGED
|
@@ -86,6 +86,11 @@ const SERVICES: { [key: string]: IServiceConfig } = {
|
|
|
86
86
|
endpoint: "/site/unpublish/bulk",
|
|
87
87
|
method: "POST",
|
|
88
88
|
},
|
|
89
|
+
REPUBLISH_SITE: {
|
|
90
|
+
...template,
|
|
91
|
+
endpoint: ["/site/", "/render-all"],
|
|
92
|
+
method: "POST",
|
|
93
|
+
},
|
|
89
94
|
IMPORT_PAGE: {
|
|
90
95
|
...template,
|
|
91
96
|
endpoint: ["/site/", "/pages/global/imports/"],
|
|
@@ -374,6 +379,16 @@ const unpublishSite = (siteID: number) => {
|
|
|
374
379
|
|
|
375
380
|
const unpublishSiteBulk = (ids: number[]) => sendRequest(SERVICES.UNPUBLISH_SITE_BULK, { sites: ids });
|
|
376
381
|
|
|
382
|
+
const republishSite = (siteID: number) => {
|
|
383
|
+
const {
|
|
384
|
+
host,
|
|
385
|
+
endpoint: [prefix, suffix],
|
|
386
|
+
} = SERVICES.REPUBLISH_SITE;
|
|
387
|
+
|
|
388
|
+
SERVICES.REPUBLISH_SITE.dynamicUrl = `${host}${prefix}${siteID}${suffix}`;
|
|
389
|
+
return sendRequest(SERVICES.REPUBLISH_SITE);
|
|
390
|
+
};
|
|
391
|
+
|
|
377
392
|
const getSelectSites = () => sendRequest(SERVICES.GET_SELECT_SITES);
|
|
378
393
|
|
|
379
394
|
const setSiteActivity = async (siteID: number) => {
|
|
@@ -398,6 +413,7 @@ export default {
|
|
|
398
413
|
publishSiteBulk,
|
|
399
414
|
unpublishSite,
|
|
400
415
|
unpublishSiteBulk,
|
|
416
|
+
republishSite,
|
|
401
417
|
getGlobalPages,
|
|
402
418
|
getGlobalPagesLight,
|
|
403
419
|
importPage,
|
|
@@ -31,6 +31,7 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
|
|
|
31
31
|
languages,
|
|
32
32
|
contentLanguages,
|
|
33
33
|
forceLanguage,
|
|
34
|
+
menuInPortal,
|
|
34
35
|
} = props;
|
|
35
36
|
|
|
36
37
|
const initialState: IState = {
|
|
@@ -175,6 +176,7 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
|
|
|
175
176
|
|
|
176
177
|
return (
|
|
177
178
|
<div data-testid="asyncSelect">
|
|
179
|
+
<S.MenuGlobalStyle />
|
|
178
180
|
<S.StyledSelect
|
|
179
181
|
name={name}
|
|
180
182
|
value={getObjectValue(value, state.items) || ""}
|
|
@@ -194,6 +196,8 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
|
|
|
194
196
|
inputValue={state.inputText}
|
|
195
197
|
components={{ Menu }}
|
|
196
198
|
onKeyDown={handleKeyDown}
|
|
199
|
+
menuPortalTarget={menuInPortal ? document.body : undefined}
|
|
200
|
+
styles={menuInPortal ? { menuPortal: (base) => ({ ...base, zIndex: 1280 }) } : undefined}
|
|
197
201
|
/>
|
|
198
202
|
</div>
|
|
199
203
|
);
|
|
@@ -237,6 +241,7 @@ export interface IAsyncSelectProps {
|
|
|
237
241
|
languages?: ILanguage[];
|
|
238
242
|
contentLanguages?: "current" | "all" | string[];
|
|
239
243
|
forceLanguage?: number;
|
|
244
|
+
menuInPortal?: boolean;
|
|
240
245
|
}
|
|
241
246
|
|
|
242
247
|
export default AsyncSelect;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import styled, { css } from "styled-components";
|
|
2
1
|
import AsyncSelect from "react-select/async";
|
|
2
|
+
import styled, { createGlobalStyle } from "styled-components";
|
|
3
3
|
|
|
4
4
|
export const StyledSelect = styled(AsyncSelect)<{
|
|
5
5
|
isDisabled: boolean | undefined;
|
|
@@ -49,65 +49,6 @@ export const StyledSelect = styled(AsyncSelect)<{
|
|
|
49
49
|
border-color: ${(p) => p.theme.color.interactiveDisabled};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
.react-select__menu {
|
|
53
|
-
box-shadow: none;
|
|
54
|
-
border-radius: 4px;
|
|
55
|
-
min-width: calc(${(p) => p.theme.spacing.xl} * 4);
|
|
56
|
-
max-width: calc(${(p) => p.theme.spacing.xl} * 6);
|
|
57
|
-
margin-top: 0;
|
|
58
|
-
z-index: 99;
|
|
59
|
-
|
|
60
|
-
.react-select__menu-list {
|
|
61
|
-
border: 1px solid ${(p) => p.theme.color.uiLine};
|
|
62
|
-
border-radius: 4px;
|
|
63
|
-
max-height: calc(${(p) => p.theme.spacing.l} * 3);
|
|
64
|
-
overflow: auto;
|
|
65
|
-
padding: 0;
|
|
66
|
-
|
|
67
|
-
&::-webkit-scrollbar {
|
|
68
|
-
-webkit-appearance: none;
|
|
69
|
-
width: 4px;
|
|
70
|
-
height: 100%;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
&::-webkit-scrollbar-thumb {
|
|
74
|
-
border-radius: 4px;
|
|
75
|
-
background-color: ${(p) => p.theme.colors.iconNonActive};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.react-select__option {
|
|
79
|
-
background-color: ${(p) => p.theme.color.interactiveBackground};
|
|
80
|
-
min-height: ${(p) => p.theme.spacing.l};
|
|
81
|
-
padding: ${(p) => `13px ${p.theme.spacing.s} 12px ${p.theme.spacing.s}`};
|
|
82
|
-
&:first-child {
|
|
83
|
-
${(p) =>
|
|
84
|
-
!p.required &&
|
|
85
|
-
p.isClearable &&
|
|
86
|
-
css`
|
|
87
|
-
font-style: italic;
|
|
88
|
-
color: ${(p) => p.theme.color.textLowEmphasis};
|
|
89
|
-
background-color: ${(p) => p.theme.color.uiLineInverse};
|
|
90
|
-
`}
|
|
91
|
-
}
|
|
92
|
-
:hover {
|
|
93
|
-
cursor: pointer;
|
|
94
|
-
background-color: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
95
|
-
}
|
|
96
|
-
:focus {
|
|
97
|
-
background-color: ${(p) => p.theme.color.overlayFocusPrimary};
|
|
98
|
-
border: 0.5px solid ${(p) => p.theme.color.interactive02};
|
|
99
|
-
}
|
|
100
|
-
:active {
|
|
101
|
-
background-color: ${(p) => p.theme.color.overlayPressedPrimary};
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
.react-select__option--is-selected {
|
|
105
|
-
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
106
|
-
background-color: ${(p) => p.theme.color.overlayPressedPrimary};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
52
|
&.inline {
|
|
112
53
|
${(p) => p.theme.textStyle.uiS};
|
|
113
54
|
text-transform: capitalize;
|
|
@@ -161,3 +102,59 @@ export const StyledSelect = styled(AsyncSelect)<{
|
|
|
161
102
|
}
|
|
162
103
|
}
|
|
163
104
|
`;
|
|
105
|
+
|
|
106
|
+
// Estilos del menú aplicados de forma global: cuando el select usa menuInPortal
|
|
107
|
+
// (menuPortalTarget={document.body}) el menú queda fuera del árbol de StyledSelect,
|
|
108
|
+
// por lo que las reglas anidadas no le alcanzarían. Al ser global sirve para ambos casos.
|
|
109
|
+
export const MenuGlobalStyle = createGlobalStyle`
|
|
110
|
+
.react-select__menu {
|
|
111
|
+
${(p: any) => p.theme.textStyle.fieldContent};
|
|
112
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
113
|
+
box-shadow: none !important;
|
|
114
|
+
border-radius: 4px;
|
|
115
|
+
min-width: calc(${(p) => p.theme.spacing.xl} * 4);
|
|
116
|
+
max-width: calc(${(p) => p.theme.spacing.xl} * 6);
|
|
117
|
+
margin-top: 0 !important;
|
|
118
|
+
|
|
119
|
+
.react-select__menu-list {
|
|
120
|
+
border: 1px solid ${(p) => p.theme.color.uiLine};
|
|
121
|
+
border-radius: 4px;
|
|
122
|
+
max-height: calc(${(p) => p.theme.spacing.l} * 3);
|
|
123
|
+
overflow: auto;
|
|
124
|
+
padding: 0;
|
|
125
|
+
|
|
126
|
+
&::-webkit-scrollbar {
|
|
127
|
+
-webkit-appearance: none;
|
|
128
|
+
width: 4px;
|
|
129
|
+
height: 100%;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&::-webkit-scrollbar-thumb {
|
|
133
|
+
border-radius: 4px;
|
|
134
|
+
background-color: ${(p) => p.theme.colors.iconNonActive};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.react-select__option {
|
|
138
|
+
background-color: ${(p) => p.theme.color.interactiveBackground};
|
|
139
|
+
min-height: ${(p) => p.theme.spacing.l};
|
|
140
|
+
padding: ${(p) => `13px ${p.theme.spacing.s} 12px ${p.theme.spacing.s}`};
|
|
141
|
+
|
|
142
|
+
:hover {
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
background-color: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
145
|
+
}
|
|
146
|
+
:focus {
|
|
147
|
+
background-color: ${(p) => p.theme.color.overlayFocusPrimary};
|
|
148
|
+
border: 0.5px solid ${(p) => p.theme.color.interactive02};
|
|
149
|
+
}
|
|
150
|
+
:active {
|
|
151
|
+
background-color: ${(p) => p.theme.color.overlayPressedPrimary};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
.react-select__option--is-selected {
|
|
155
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
156
|
+
background-color: ${(p) => p.theme.color.overlayPressedPrimary};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
const SvgRepublish = (props) => (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width={24}
|
|
6
|
+
height={24}
|
|
7
|
+
fill="none"
|
|
8
|
+
{...props}
|
|
9
|
+
>
|
|
10
|
+
<path
|
|
11
|
+
fill="#AFC628"
|
|
12
|
+
d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96ZM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95A5.469 5.469 0 0 1 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11A2.98 2.98 0 0 1 22 15c0 1.65-1.35 3-3 3Z"
|
|
13
|
+
/>
|
|
14
|
+
<path
|
|
15
|
+
fill="#AFC628"
|
|
16
|
+
stroke="#AFC628"
|
|
17
|
+
strokeWidth={0.25}
|
|
18
|
+
d="M15.784 12.895a4.125 4.125 0 0 1-4.104 3.713 4.092 4.092 0 0 1-2.92-1.228l-.791.791-.214.213v-2.826h2.827l-.214.213-.864.865a3.046 3.046 0 0 0 2.176.922 3.078 3.078 0 0 0 3.048-2.69l.014-.11h1.056l-.014.137Zm-8.222-.688.014-.137a4.124 4.124 0 0 1 4.104-3.712c1.145 0 2.177.472 2.921 1.226l.79-.79.214-.213v2.826h-2.827l.214-.213.865-.866a3.05 3.05 0 0 0-2.177-.92 3.077 3.077 0 0 0-3.048 2.69l-.014.11H7.562Z"
|
|
19
|
+
/>
|
|
20
|
+
</svg>
|
|
21
|
+
);
|
|
22
|
+
export default SvgRepublish;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4C9.11 4 6.6 5.64 5.35 8.04C2.34 8.36 0 10.91 0 14C0 17.31 2.69 20 6 20H19C21.76 20 24 17.76 24 15C24 12.36 21.95 10.22 19.35 10.04ZM19 18H6C3.79 18 2 16.21 2 14C2 11.95 3.53 10.24 5.56 10.03L6.63 9.92L7.13 8.97C8.08 7.14 9.94 6 12 6C14.62 6 16.88 7.86 17.39 10.43L17.69 11.93L19.22 12.04C20.78 12.14 22 13.45 22 15C22 16.65 20.65 18 19 18Z" fill="#AFC628"/>
|
|
3
|
+
<path d="M15.784 12.8946C15.5777 14.9779 13.8204 16.6075 11.6795 16.6075C10.5338 16.6074 9.50405 16.1335 8.75958 15.38L7.96857 16.171L7.7547 16.3839L7.7547 13.5577L10.5818 13.5577L10.368 13.7706L9.50372 14.6358C10.0603 15.2013 10.8279 15.5576 11.6795 15.5577C13.2435 15.5577 14.5399 14.3815 14.7283 12.8673L14.742 12.7579L15.7977 12.7579L15.784 12.8946ZM7.56232 12.2071L7.57599 12.0704C7.78224 9.98727 9.53878 8.35775 11.6795 8.35751C12.8254 8.35751 13.8568 8.83042 14.6014 9.58408L15.3914 8.79404L15.6053 8.58115L15.6053 11.4073L12.7781 11.4073L12.992 11.1944L13.8572 10.3282C13.3005 9.76263 12.5313 9.40732 11.6795 9.40732C10.1157 9.40755 8.82002 10.5837 8.63165 12.0977L8.61798 12.2071L7.56232 12.2071Z" fill="#AFC628" stroke="#AFC628" stroke-width="0.25"/>
|
|
4
|
+
</svg>
|
|
@@ -66,6 +66,7 @@ const Modal = styled.div<{ size?: string; height?: number | string }>`
|
|
|
66
66
|
border-radius: 4px;
|
|
67
67
|
width: ${(p) => getWidth(p.size)};
|
|
68
68
|
height: ${(p) => getModalHeight(p.height, p.size)};
|
|
69
|
+
min-height: ${(p) => getModalHeight(p.height, p.size)};
|
|
69
70
|
max-height: 80vh;
|
|
70
71
|
box-shadow: ${(p) => p.theme.shadow.shadowXL};
|
|
71
72
|
top: 50%;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
2
3
|
import { useLocation } from "react-router-dom";
|
|
3
4
|
|
|
4
|
-
import { createPortal } from "react-dom";
|
|
5
5
|
import { Button, IconAction } from "@ax/components";
|
|
6
6
|
|
|
7
7
|
import * as S from "./style";
|
|
@@ -815,6 +815,23 @@ function updateCurrentSearch(query: string): (dispatch: Dispatch) => Promise<voi
|
|
|
815
815
|
};
|
|
816
816
|
}
|
|
817
817
|
|
|
818
|
+
function republishSite(siteID: number): (dispatch: Dispatch) => Promise<boolean> {
|
|
819
|
+
return async (dispatch) => {
|
|
820
|
+
try {
|
|
821
|
+
const responseActions = {
|
|
822
|
+
handleSuccess: () => {},
|
|
823
|
+
handleError: (response: any) => appActions.handleError(response)(dispatch),
|
|
824
|
+
};
|
|
825
|
+
const callback = async () => await sites.republishSite(siteID);
|
|
826
|
+
|
|
827
|
+
return await handleRequest(callback, responseActions, [])(dispatch);
|
|
828
|
+
} catch (e) {
|
|
829
|
+
console.log(e);
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
818
835
|
export {
|
|
819
836
|
setCurrentSiteInfo,
|
|
820
837
|
setCurrentSitePages,
|
|
@@ -849,4 +866,5 @@ export {
|
|
|
849
866
|
updateCurrentSearch,
|
|
850
867
|
setIsSitesLoading,
|
|
851
868
|
getAllSites,
|
|
869
|
+
republishSite,
|
|
852
870
|
};
|
|
@@ -6,13 +6,23 @@ import { sitesActions } from "@ax/containers/Sites";
|
|
|
6
6
|
import { isDevelopment } from "@ax/helpers";
|
|
7
7
|
import type { IGetSitesParams, ISite } from "@ax/types";
|
|
8
8
|
|
|
9
|
-
import { ItemName, ItemThumbnail } from "./../../atoms";
|
|
9
|
+
import { ItemName, ItemThumbnail, RepublishSiteModal } from "./../../atoms";
|
|
10
10
|
import { useSiteActions } from "./../../hooks";
|
|
11
11
|
|
|
12
12
|
import * as S from "./style";
|
|
13
13
|
|
|
14
14
|
const GridSiteItem = (props: IGridSiteItemProps): JSX.Element => {
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
site,
|
|
17
|
+
setSiteInfo,
|
|
18
|
+
setHistoryPush,
|
|
19
|
+
deleteSite,
|
|
20
|
+
publishSite,
|
|
21
|
+
unpublishSite,
|
|
22
|
+
republishSite,
|
|
23
|
+
getParams,
|
|
24
|
+
toggleToast,
|
|
25
|
+
} = props;
|
|
16
26
|
const { hidden } = site;
|
|
17
27
|
|
|
18
28
|
const {
|
|
@@ -29,7 +39,20 @@ const GridSiteItem = (props: IGridSiteItemProps): JSX.Element => {
|
|
|
29
39
|
isOpenPublish,
|
|
30
40
|
togglePublishModal,
|
|
31
41
|
publishModal,
|
|
32
|
-
|
|
42
|
+
isOpenRepublish,
|
|
43
|
+
toggleRepublishModal,
|
|
44
|
+
onRepublish,
|
|
45
|
+
} = useSiteActions({
|
|
46
|
+
site,
|
|
47
|
+
setSiteInfo,
|
|
48
|
+
setHistoryPush,
|
|
49
|
+
deleteSite,
|
|
50
|
+
publishSite,
|
|
51
|
+
unpublishSite,
|
|
52
|
+
republishSite,
|
|
53
|
+
getParams,
|
|
54
|
+
toggleToast,
|
|
55
|
+
});
|
|
33
56
|
|
|
34
57
|
const { title, mainAction, secondaryAction, content } = publishModal;
|
|
35
58
|
|
|
@@ -92,6 +115,7 @@ const GridSiteItem = (props: IGridSiteItemProps): JSX.Element => {
|
|
|
92
115
|
>
|
|
93
116
|
{isOpenPublish ? <S.ModalContent data-testid="publish-modal">{content}</S.ModalContent> : null}
|
|
94
117
|
</Modal>
|
|
118
|
+
<RepublishSiteModal isOpen={isOpenRepublish} toggleModal={toggleRepublishModal} onRepublish={onRepublish} />
|
|
95
119
|
</>
|
|
96
120
|
);
|
|
97
121
|
};
|
|
@@ -103,7 +127,9 @@ interface IGridSiteItemProps {
|
|
|
103
127
|
deleteSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
104
128
|
publishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
105
129
|
unpublishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
130
|
+
republishSite(siteID: number): Promise<boolean>;
|
|
106
131
|
getParams: () => IGetSitesParams;
|
|
132
|
+
toggleToast(message: string): void;
|
|
107
133
|
}
|
|
108
134
|
|
|
109
135
|
const mapDispatchToProps = {
|
|
@@ -112,6 +138,7 @@ const mapDispatchToProps = {
|
|
|
112
138
|
deleteSite: sitesActions.deleteSite,
|
|
113
139
|
publishSite: sitesActions.publishSite,
|
|
114
140
|
unpublishSite: sitesActions.unpublishSite,
|
|
141
|
+
republishSite: sitesActions.republishSite,
|
|
115
142
|
};
|
|
116
143
|
|
|
117
144
|
export default connect(null, mapDispatchToProps)(GridSiteItem);
|
|
@@ -6,7 +6,7 @@ import { sitesActions } from "@ax/containers/Sites";
|
|
|
6
6
|
import { getFormattedDateWithTimezone } from "@ax/helpers";
|
|
7
7
|
import type { ICheck, IGetSitesParams, ISite } from "@ax/types";
|
|
8
8
|
|
|
9
|
-
import { ItemName, ItemThumbnail } from "./../../atoms";
|
|
9
|
+
import { ItemName, ItemThumbnail, RepublishSiteModal } from "./../../atoms";
|
|
10
10
|
import { useSiteActions } from "./../../hooks";
|
|
11
11
|
|
|
12
12
|
import * as S from "./style";
|
|
@@ -19,10 +19,12 @@ const ListSiteItem = (props: IListSiteItemProps): JSX.Element => {
|
|
|
19
19
|
deleteSite,
|
|
20
20
|
publishSite,
|
|
21
21
|
unpublishSite,
|
|
22
|
+
republishSite,
|
|
22
23
|
isSelected,
|
|
23
24
|
onCheck,
|
|
24
25
|
getParams,
|
|
25
26
|
hoverCheck,
|
|
27
|
+
toggleToast,
|
|
26
28
|
} = props;
|
|
27
29
|
|
|
28
30
|
const { hidden } = site;
|
|
@@ -41,14 +43,24 @@ const ListSiteItem = (props: IListSiteItemProps): JSX.Element => {
|
|
|
41
43
|
isOpenPublish,
|
|
42
44
|
togglePublishModal,
|
|
43
45
|
publishModal,
|
|
44
|
-
|
|
46
|
+
isOpenRepublish,
|
|
47
|
+
toggleRepublishModal,
|
|
48
|
+
} = useSiteActions({
|
|
49
|
+
site,
|
|
50
|
+
setSiteInfo,
|
|
51
|
+
setHistoryPush,
|
|
52
|
+
deleteSite,
|
|
53
|
+
publishSite,
|
|
54
|
+
unpublishSite,
|
|
55
|
+
republishSite,
|
|
56
|
+
getParams,
|
|
57
|
+
toggleToast,
|
|
58
|
+
});
|
|
45
59
|
|
|
46
60
|
const { title, mainAction, secondaryAction, content } = publishModal;
|
|
47
61
|
|
|
48
62
|
const handleOnChange = (value: ICheck) => onCheck(value);
|
|
49
63
|
|
|
50
|
-
console.log(site, "site");
|
|
51
|
-
|
|
52
64
|
return (
|
|
53
65
|
<>
|
|
54
66
|
<S.SiteItemRow key={site.id} role="rowgroup" selected={isSelected} data-testid="list-site-item">
|
|
@@ -113,6 +125,7 @@ const ListSiteItem = (props: IListSiteItemProps): JSX.Element => {
|
|
|
113
125
|
>
|
|
114
126
|
{isOpenPublish ? <S.ModalContent data-testid="publish-modal">{content}</S.ModalContent> : null}
|
|
115
127
|
</Modal>
|
|
128
|
+
<RepublishSiteModal isOpen={isOpenRepublish} toggleModal={toggleRepublishModal} onRepublish={() => null} />
|
|
116
129
|
</>
|
|
117
130
|
);
|
|
118
131
|
};
|
|
@@ -126,8 +139,10 @@ interface IListSiteItemProps {
|
|
|
126
139
|
deleteSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
127
140
|
publishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
128
141
|
unpublishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
142
|
+
republishSite(siteID: number): Promise<boolean>;
|
|
129
143
|
onCheck: (e: ICheck) => void;
|
|
130
144
|
getParams: () => IGetSitesParams;
|
|
145
|
+
toggleToast(message: string): void;
|
|
131
146
|
}
|
|
132
147
|
|
|
133
148
|
const mapDispatchToProps = {
|
|
@@ -136,6 +151,7 @@ const mapDispatchToProps = {
|
|
|
136
151
|
deleteSite: sitesActions.deleteSite,
|
|
137
152
|
publishSite: sitesActions.publishSite,
|
|
138
153
|
unpublishSite: sitesActions.unpublishSite,
|
|
154
|
+
republishSite: sitesActions.republishSite,
|
|
139
155
|
};
|
|
140
156
|
|
|
141
157
|
export default connect(null, mapDispatchToProps)(ListSiteItem);
|
|
@@ -101,6 +101,7 @@ const SiteModal = (props: ISiteModalProps): JSX.Element => {
|
|
|
101
101
|
entity="languages"
|
|
102
102
|
onChange={setLangValue}
|
|
103
103
|
mandatory={true}
|
|
104
|
+
menuInPortal={true}
|
|
104
105
|
/>
|
|
105
106
|
</S.FieldsWrapper>
|
|
106
107
|
<S.FieldsWrapper>
|
|
@@ -112,6 +113,7 @@ const SiteModal = (props: ISiteModalProps): JSX.Element => {
|
|
|
112
113
|
value={domain}
|
|
113
114
|
onChange={setDomainValue}
|
|
114
115
|
mandatory={true}
|
|
116
|
+
menuInPortal={true}
|
|
115
117
|
/>
|
|
116
118
|
<FieldsBehavior
|
|
117
119
|
title="Path"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Image, Tooltip } from "@ax/components";
|
|
1
|
+
import { Image, Modal, Tooltip } from "@ax/components";
|
|
2
2
|
import { trimText } from "@ax/helpers";
|
|
3
|
+
import type { IModal } from "@ax/types";
|
|
3
4
|
|
|
4
5
|
import * as S from "./style";
|
|
5
6
|
|
|
@@ -44,4 +45,37 @@ interface IItemThumbnailProps {
|
|
|
44
45
|
borderRadius?: boolean;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
const RepublishSiteModal = (props: IRepublishSiteModal): JSX.Element => {
|
|
49
|
+
const { isOpen, toggleModal, onRepublish } = props;
|
|
50
|
+
|
|
51
|
+
const mainAction = { title: "Yes, republish now", onClick: onRepublish };
|
|
52
|
+
|
|
53
|
+
const secondaryAction = { title: "Cancel", onClick: toggleModal };
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Modal
|
|
57
|
+
isOpen={isOpen}
|
|
58
|
+
hide={toggleModal}
|
|
59
|
+
size="S"
|
|
60
|
+
height="auto"
|
|
61
|
+
title="Republish site?"
|
|
62
|
+
mainAction={mainAction}
|
|
63
|
+
secondaryAction={secondaryAction}
|
|
64
|
+
>
|
|
65
|
+
{isOpen && (
|
|
66
|
+
<S.ModalContent data-testid="republish-site-modal">
|
|
67
|
+
<p>
|
|
68
|
+
This <strong>will refresh all currently public pages</strong> to reflect the latest global changes. Drafts
|
|
69
|
+
and unpublished content will remain hidden.
|
|
70
|
+
</p>
|
|
71
|
+
</S.ModalContent>
|
|
72
|
+
)}
|
|
73
|
+
</Modal>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
interface IRepublishSiteModal extends IModal {
|
|
78
|
+
onRepublish: () => void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { ItemName, ItemThumbnail, RepublishSiteModal };
|
|
@@ -117,7 +117,9 @@ interface IUseSiteActionsParams {
|
|
|
117
117
|
deleteSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
118
118
|
publishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
119
119
|
unpublishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
120
|
+
republishSite(siteID: number): Promise<boolean>;
|
|
120
121
|
getParams: () => IGetSitesParams;
|
|
122
|
+
toggleToast(message: string): void;
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
const publishedTooltip: Record<string, string> = {
|
|
@@ -143,9 +145,11 @@ const useSiteActions = ({
|
|
|
143
145
|
deleteSite,
|
|
144
146
|
publishSite,
|
|
145
147
|
unpublishSite,
|
|
148
|
+
republishSite,
|
|
146
149
|
getParams,
|
|
150
|
+
toggleToast,
|
|
147
151
|
}: IUseSiteActionsParams) => {
|
|
148
|
-
const { isOpen, toggleModal } = useModals(["delete", "publish"]);
|
|
152
|
+
const { isOpen, toggleModal } = useModals(["delete", "publish", "republish"]);
|
|
149
153
|
const [inputValue, setInputValue] = useState("");
|
|
150
154
|
|
|
151
155
|
const { allowedToDeleteSite, allowedToPublishSite, allowedToUnpublishSite } = usePermissionsForSite(site.id, {
|
|
@@ -154,7 +158,7 @@ const useSiteActions = ({
|
|
|
154
158
|
allowedToUnpublishSite: "general.unpublishSite",
|
|
155
159
|
});
|
|
156
160
|
|
|
157
|
-
const { updated, isPublished } = site;
|
|
161
|
+
const { updated, isPublished, home } = site;
|
|
158
162
|
const publishedState = getPublishedState(isPublished, updated);
|
|
159
163
|
|
|
160
164
|
const setSite = async () => {
|
|
@@ -163,21 +167,30 @@ const useSiteActions = ({
|
|
|
163
167
|
};
|
|
164
168
|
|
|
165
169
|
const deleteOption = allowedToDeleteSite
|
|
166
|
-
? { label: "
|
|
170
|
+
? { label: "Delete site", icon: "delete", action: () => toggleModal("delete") }
|
|
167
171
|
: undefined;
|
|
168
172
|
|
|
169
173
|
const publishOptionProps =
|
|
170
174
|
isPublished && allowedToUnpublishSite
|
|
171
|
-
? { label: "Unpublish", icon: "offlinePending" }
|
|
175
|
+
? { label: "Unpublish site", icon: "offlinePending" }
|
|
172
176
|
: !isPublished && allowedToPublishSite
|
|
173
|
-
? { label: "Publish", icon: "uploadPending" }
|
|
177
|
+
? { label: "Publish site", icon: "uploadPending" }
|
|
174
178
|
: null;
|
|
175
179
|
|
|
176
180
|
const publishOption = publishOptionProps
|
|
177
181
|
? { label: publishOptionProps.label, icon: publishOptionProps.icon, action: () => toggleModal("publish") }
|
|
178
182
|
: undefined;
|
|
179
183
|
|
|
180
|
-
const
|
|
184
|
+
const republishOption =
|
|
185
|
+
isPublished && allowedToPublishSite
|
|
186
|
+
? { label: "Republish site", icon: "republish", action: () => toggleModal("republish") }
|
|
187
|
+
: undefined;
|
|
188
|
+
|
|
189
|
+
const viewOption = isPublished
|
|
190
|
+
? { label: "View online", icon: "view", action: () => goToPublishedSite() }
|
|
191
|
+
: undefined;
|
|
192
|
+
|
|
193
|
+
const menuOptions = [viewOption, publishOption, republishOption, deleteOption];
|
|
181
194
|
|
|
182
195
|
const handleDeleteSite = async () => {
|
|
183
196
|
const params = getParams();
|
|
@@ -190,6 +203,7 @@ const useSiteActions = ({
|
|
|
190
203
|
onClick: handleDeleteSite,
|
|
191
204
|
disabled: inputValue !== site.name.toUpperCase(),
|
|
192
205
|
};
|
|
206
|
+
|
|
193
207
|
const secondaryDeleteAction = { title: "Cancel", onClick: () => toggleModal("delete") };
|
|
194
208
|
|
|
195
209
|
const handlePublishSite = async () => {
|
|
@@ -204,6 +218,20 @@ const useSiteActions = ({
|
|
|
204
218
|
toggleModal("publish");
|
|
205
219
|
};
|
|
206
220
|
|
|
221
|
+
const handleRepublishSite = async () => {
|
|
222
|
+
const isRepublished = await republishSite(site.id);
|
|
223
|
+
toggleModal("republish");
|
|
224
|
+
if (isRepublished) {
|
|
225
|
+
toggleToast("Republishing site. Changes will be live shortly.");
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const goToPublishedSite = () => {
|
|
230
|
+
if (home) {
|
|
231
|
+
window.open(home, "_blank");
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
207
235
|
const publishModal = isPublished
|
|
208
236
|
? {
|
|
209
237
|
mainAction: { title: "Unpublish Site", onClick: handleUnpublishSite },
|
|
@@ -244,6 +272,9 @@ const useSiteActions = ({
|
|
|
244
272
|
isOpenPublish: isOpen("publish"),
|
|
245
273
|
togglePublishModal: () => toggleModal("publish"),
|
|
246
274
|
publishModal,
|
|
275
|
+
isOpenRepublish: isOpen("republish"),
|
|
276
|
+
toggleRepublishModal: () => toggleModal("republish"),
|
|
277
|
+
onRepublish: handleRepublishSite,
|
|
247
278
|
};
|
|
248
279
|
};
|
|
249
280
|
|
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
Pagination,
|
|
11
11
|
SearchTagsBar,
|
|
12
12
|
TableList,
|
|
13
|
+
Toast,
|
|
13
14
|
} from "@ax/components";
|
|
14
15
|
import { appActions } from "@ax/containers/App";
|
|
15
16
|
import type { IError } from "@ax/containers/App/reducer";
|
|
16
17
|
import { sitesActions } from "@ax/containers/Sites";
|
|
17
|
-
import { useBulkSelection, useModal, usePermission } from "@ax/hooks";
|
|
18
|
+
import { useBulkSelection, useModal, usePermission, useToast } from "@ax/hooks";
|
|
18
19
|
import type { ICheck, IGetSitesParams, IQueryValue, IRootState, ISite, ISiteListConfig, IUser } from "@ax/types";
|
|
19
20
|
|
|
20
21
|
import AllSitesHeader from "./AllSitesHeader";
|
|
@@ -83,6 +84,7 @@ const SitesList = (props: ISitesListProps) => {
|
|
|
83
84
|
const errorRef = useRef<HTMLDivElement>(null);
|
|
84
85
|
const { isOpen, toggleModal } = useModal();
|
|
85
86
|
const { isOpen: isWelcomeOpen, toggleModal: toggleWelcomeModal } = useModal();
|
|
87
|
+
const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
|
|
86
88
|
|
|
87
89
|
const scrollToTop = useCallback(() => {
|
|
88
90
|
if (tableRef.current) {
|
|
@@ -264,7 +266,7 @@ const SitesList = (props: ISitesListProps) => {
|
|
|
264
266
|
sites.map((site: ISite) => {
|
|
265
267
|
const isItemSelected = isSelected(site.id);
|
|
266
268
|
return displayMode === "grid" ? (
|
|
267
|
-
<GridSiteItem key={site.id} site={site} getParams={getParams} />
|
|
269
|
+
<GridSiteItem key={site.id} site={site} getParams={getParams} toggleToast={toggleToast} />
|
|
268
270
|
) : (
|
|
269
271
|
<ListSiteItem
|
|
270
272
|
key={site.id}
|
|
@@ -273,6 +275,7 @@ const SitesList = (props: ISitesListProps) => {
|
|
|
273
275
|
onCheck={handleAddToBulk}
|
|
274
276
|
getParams={getParams}
|
|
275
277
|
hoverCheck={checkState.hoverCheck}
|
|
278
|
+
toggleToast={toggleToast}
|
|
276
279
|
/>
|
|
277
280
|
);
|
|
278
281
|
})
|
|
@@ -281,7 +284,7 @@ const SitesList = (props: ISitesListProps) => {
|
|
|
281
284
|
<EmptyState icon="search" message="We couldn't find what you are looking for. Please, try another search." />
|
|
282
285
|
</S.EmptyStateWrapper>
|
|
283
286
|
),
|
|
284
|
-
[sites, displayMode, isSelected, handleAddToBulk, getParams, checkState.hoverCheck],
|
|
287
|
+
[sites, displayMode, isSelected, handleAddToBulk, getParams, checkState.hoverCheck, toggleToast],
|
|
285
288
|
);
|
|
286
289
|
|
|
287
290
|
const showPagination = totalItems > itemsPerPage;
|
|
@@ -374,6 +377,7 @@ const SitesList = (props: ISitesListProps) => {
|
|
|
374
377
|
setHistoryPush={setHistoryPush}
|
|
375
378
|
/>
|
|
376
379
|
<WelcomeModal isOpen={isWelcomeOpen} toggleModal={toggleWelcomeModal} />
|
|
380
|
+
{isVisible && <Toast setIsVisible={setIsVisible} message={toastState} />}
|
|
377
381
|
</MainWrapper>
|
|
378
382
|
);
|
|
379
383
|
};
|
|
@@ -101,6 +101,14 @@ const SearchTagsGrid = styled.div`
|
|
|
101
101
|
margin: ${(p) => `${p.theme.spacing.xs} ${p.theme.spacing.m}`};
|
|
102
102
|
`;
|
|
103
103
|
|
|
104
|
+
const ModalContent = styled.div`
|
|
105
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
106
|
+
|
|
107
|
+
p {
|
|
108
|
+
margin-bottom: ${(p) => p.theme.spacing.m};
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
|
|
104
112
|
export {
|
|
105
113
|
SitesListWrapper,
|
|
106
114
|
GridList,
|
|
@@ -112,4 +120,5 @@ export {
|
|
|
112
120
|
AllSitesListWrapper,
|
|
113
121
|
SearchTags,
|
|
114
122
|
SearchTagsGrid,
|
|
123
|
+
ModalContent,
|
|
115
124
|
};
|