@griddo/ax 10.1.32 → 10.1.33

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.
Files changed (30) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/components/Gallery/Gallery.test.tsx +63 -121
  3. package/src/__tests__/components/Gallery/GalleryPanel/DetailPanel/DetailPanel.test.tsx +3 -1
  4. package/src/__tests__/components/Gallery/GalleryPanel/GalleryPanel.test.tsx +0 -2
  5. package/src/components/FieldContainer/index.tsx +2 -2
  6. package/src/components/Fields/ImageField/index.tsx +1 -1
  7. package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/index.tsx +6 -2
  8. package/src/components/FieldsBehavior/index.tsx +1 -1
  9. package/src/components/Gallery/GalleryPanel/DetailPanel/index.tsx +7 -2
  10. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +1 -2
  11. package/src/components/Gallery/GalleryPanel/index.tsx +6 -5
  12. package/src/components/Gallery/index.tsx +24 -22
  13. package/src/components/TableFilters/CategoryFilter/style.tsx +1 -0
  14. package/src/components/Tooltip/index.tsx +13 -3
  15. package/src/components/Tooltip/style.tsx +9 -5
  16. package/src/containers/Gallery/actions.tsx +3 -24
  17. package/src/containers/Gallery/reducer.tsx +0 -4
  18. package/src/containers/StructuredData/actions.tsx +14 -1
  19. package/src/modules/Categories/CategoriesList/index.tsx +13 -4
  20. package/src/modules/Content/PageItem/index.tsx +15 -2
  21. package/src/modules/Content/PageItem/style.tsx +1 -2
  22. package/src/modules/Content/index.tsx +8 -7
  23. package/src/modules/Login/index.tsx +2 -2
  24. package/src/modules/Sites/index.tsx +18 -18
  25. package/src/modules/StructuredData/StructuredDataList/ContentFilters/index.tsx +2 -2
  26. package/src/modules/StructuredData/StructuredDataList/ContentFilters/utils.tsx +1 -0
  27. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +30 -12
  28. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +9 -1
  29. package/src/modules/StructuredData/StructuredDataList/index.tsx +13 -15
  30. package/src/modules/Users/UserList/index.tsx +6 -3
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "10.1.32",
4
+ "version": "10.1.33",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Carlos Torres <carlos.torres@secuoyas.com>",
@@ -230,5 +230,5 @@
230
230
  "publishConfig": {
231
231
  "access": "public"
232
232
  },
233
- "gitHead": "4070adfcad5961ec9ed3395c0d445c586768b0ca"
233
+ "gitHead": "804e37adc98f5d55bb6536ce7df748b9315a04fa"
234
234
  }
@@ -149,72 +149,55 @@ describe("Gallery component rendering", () => {
149
149
  expect(screen.queryAllByTestId("grid-item")).toHaveLength(2);
150
150
  });
151
151
 
152
- it("should activate the handleClick with an image", () => {
152
+ it("should show image data after click", () => {
153
153
  const defaultGalleryProps = mock<IGalleryProps>();
154
154
  const defaultDispatchProps = mock<IDispatchProps>();
155
155
 
156
- const dataProps = {
157
- items: [
158
- {
159
- id: 1001,
160
- name: "Imagen_Interior_fullstack.png",
161
- title: "",
162
- description: "",
163
- alt: "",
164
- tags: [],
165
- url: "https://images.dev.griddo.io/imagen-interior-fullstack",
166
- thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
167
- publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
168
- damId: "imagen-interior-fullstack",
169
- published: new Date("2022-06-30T07:06:54.352Z"),
170
- size: 55286,
171
- width: 680,
172
- height: 353,
173
- orientation: "L",
174
- site: "Global",
175
- },
176
- {
177
- id: 1002,
178
- name: "Imagen_Interior_fullstack_2.png",
179
- title: "",
180
- description: "",
181
- alt: "",
182
- tags: [],
183
- url: "https://images.dev.griddo.io/imagen-interior-fullstack",
184
- thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
185
- publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
186
- damId: "imagen-interior-fullstack",
187
- published: new Date("2022-06-30T07:06:54.352Z"),
188
- size: 55286,
189
- width: 680,
190
- height: 353,
191
- orientation: "L",
192
- site: "Global",
193
- },
194
- ],
195
- isImageSelected: true,
196
- imageSelected: {
197
- id: 1001,
198
- name: "Imagen_Interior_fullstack.png",
199
- title: "",
200
- description: "",
201
- alt: "",
202
- tags: [],
203
- url: "https://images.dev.griddo.io/imagen-interior-fullstack",
204
- thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
205
- publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
206
- damId: "imagen-interior-fullstack",
207
- published: new Date("2022-06-30T07:06:54.352Z"),
208
- size: 55286,
209
- width: 680,
210
- height: 353,
211
- orientation: "L",
212
- site: "Global",
156
+ const responsePromise = Promise.resolve({
157
+ status: 200,
158
+ statusText: "ok",
159
+ data: {
160
+ items: [],
213
161
  },
214
- page: 1,
215
- isFinished: false,
162
+ } as AxiosResponse);
163
+
164
+ mockedAxios.mockResolvedValue(responsePromise);
165
+
166
+ defaultGalleryProps.data = defaultDataProps;
167
+ defaultGalleryProps.site = defaultSiteProps;
168
+ defaultGalleryProps.isLoading = {
169
+ init: false,
170
+ more: false,
171
+ };
172
+
173
+ const defaultProps = {
174
+ ...defaultGalleryProps,
175
+ ...defaultDispatchProps,
216
176
  };
217
177
 
178
+ render(
179
+ <ThemeProvider theme={parseTheme(globalTheme)}>
180
+ <Gallery {...defaultProps} />
181
+ </ThemeProvider>,
182
+ { store }
183
+ );
184
+ store.clearActions();
185
+
186
+ const images = screen.queryAllByTestId("image-item");
187
+ expect(screen.queryAllByTestId("image-item")).toHaveLength(2);
188
+
189
+ act(() => {
190
+ fireEvent.click(images[0]);
191
+ });
192
+
193
+ const imageName = screen.getByTestId("gallery-image-name");
194
+ expect(imageName).toHaveTextContent(defaultDataProps.items[0].name);
195
+ });
196
+
197
+ it("should hide image data after second click", () => {
198
+ const defaultGalleryProps = mock<IGalleryProps>();
199
+ const defaultDispatchProps = mock<IDispatchProps>();
200
+
218
201
  const responsePromise = Promise.resolve({
219
202
  status: 200,
220
203
  statusText: "ok",
@@ -252,10 +235,13 @@ describe("Gallery component rendering", () => {
252
235
  fireEvent.click(images[0]);
253
236
  });
254
237
 
255
- expect(store.getActions()).toContainEqual({
256
- payload: { data: dataProps },
257
- type: "gallery/SET_DATA",
238
+ expect(screen.getByTestId("gallery-image-name")).toHaveTextContent(defaultDataProps.items[0].name);
239
+
240
+ act(() => {
241
+ fireEvent.click(images[0]);
258
242
  });
243
+
244
+ expect(screen.queryByTestId("gallery-image-name")).toBeFalsy();
259
245
  });
260
246
 
261
247
  it("should activate the setImage action", async () => {
@@ -296,25 +282,6 @@ describe("Gallery component rendering", () => {
296
282
  site: "Global",
297
283
  },
298
284
  ],
299
- isImageSelected: true,
300
- imageSelected: {
301
- id: 1002,
302
- name: "Imagen_Interior_fullstack_2.png",
303
- title: "",
304
- description: "",
305
- alt: "",
306
- tags: [],
307
- url: "https://images.dev.griddo.io/imagen-interior-fullstack",
308
- thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
309
- publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
310
- damId: "imagen-interior-fullstack",
311
- published: new Date("2022-06-30T07:06:54.352Z"),
312
- size: 55286,
313
- width: 680,
314
- height: 353,
315
- orientation: "L",
316
- site: "Global",
317
- },
318
285
  page: 1,
319
286
  isFinished: false,
320
287
  };
@@ -359,11 +326,18 @@ describe("Gallery component rendering", () => {
359
326
  { store }
360
327
  );
361
328
 
329
+ const images = screen.queryAllByTestId("image-item");
330
+
331
+ act(() => {
332
+ fireEvent.click(images[0]);
333
+ });
334
+
362
335
  const buttonDetailPanel = screen.getByTestId("button-default");
363
336
 
364
337
  act(() => {
365
338
  fireEvent.click(buttonDetailPanel);
366
339
  });
340
+
367
341
  await waitFor(() => expect(getImageSelectedMocked).toBeCalled());
368
342
  expect(toggleModalMock).toBeCalled();
369
343
  });
@@ -426,25 +400,6 @@ describe("Gallery component rendering", () => {
426
400
  site: "Global",
427
401
  },
428
402
  ],
429
- isImageSelected: true,
430
- imageSelected: {
431
- id: 1002,
432
- name: "Imagen_Interior_fullstack_2.png",
433
- title: "",
434
- description: "",
435
- alt: "",
436
- tags: [],
437
- url: "https://images.dev.griddo.io/imagen-interior-fullstack",
438
- thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
439
- publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
440
- damId: "imagen-interior-fullstack",
441
- published: new Date("2022-06-30T07:06:54.352Z"),
442
- size: 55286,
443
- width: 680,
444
- height: 353,
445
- orientation: "L",
446
- site: "Global",
447
- },
448
403
  page: 2,
449
404
  isFinished: false,
450
405
  };
@@ -469,26 +424,7 @@ describe("Gallery component rendering", () => {
469
424
  };
470
425
 
471
426
  const dataProps = {
472
- imageSelected: {
473
- alt: "",
474
- damId: "imagen-interior-fullstack",
475
- description: "",
476
- height: 353,
477
- id: 1002,
478
- name: "Imagen_Interior_fullstack_2.png",
479
- orientation: "L",
480
- publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
481
- published: new Date("2022-06-30T07:06:54.352Z"),
482
- site: "Global",
483
- size: 55286,
484
- tags: [],
485
- thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
486
- title: "",
487
- url: "https://images.dev.griddo.io/imagen-interior-fullstack",
488
- width: 680,
489
- },
490
427
  isFinished: true,
491
- isImageSelected: true,
492
428
  items: [
493
429
  {
494
430
  alt: "",
@@ -551,9 +487,15 @@ describe("Gallery component rendering", () => {
551
487
  { store }
552
488
  );
553
489
 
490
+ const images = screen.queryAllByTestId("image-item");
491
+
492
+ act(() => {
493
+ fireEvent.click(images[0]);
494
+ });
495
+
554
496
  const buttonDetailPanel = screen.getAllByTestId("button-line-inverse");
555
497
 
556
- await act(() => {
498
+ await act(async () => {
557
499
  fireEvent.click(buttonDetailPanel[2]);
558
500
  });
559
501
 
@@ -12,7 +12,7 @@ import { parseTheme } from "@ax/helpers";
12
12
  import { history } from "@ax/routes";
13
13
  import GalleryDetailPanel, { IDetailPanelProps, IDispatchProps } from "@ax/components/Gallery/GalleryPanel/DetailPanel";
14
14
  import globalTheme from "@ax/themes/theme.json";
15
- import { IImageForm } from "@ax/types";
15
+ import { IImage, IImageForm } from "@ax/types";
16
16
 
17
17
  // for testing copy to clipboard
18
18
  Object.assign(navigator, {
@@ -182,6 +182,7 @@ describe("Gallery component rendering", () => {
182
182
  const deleteImageMocked = defaultDispatchProps.deleteImage as jest.MockedFunction<
183
183
  (imageID: number) => Promise<boolean>
184
184
  >;
185
+ const selectImageMocked = defaultDetailProps.selectImage as jest.MockedFunction<(img: IImage | null) => void>;
185
186
 
186
187
  const defaultProps = {
187
188
  ...defaultDetailProps,
@@ -205,6 +206,7 @@ describe("Gallery component rendering", () => {
205
206
  fireEvent.click(buttonWrapper);
206
207
  });
207
208
  expect(screen.getByText(/1 image deleted/i)).toBeInTheDocument();
209
+ expect(selectImageMocked).toBeCalled();
208
210
  expect(refreshImagesMocked).toBeCalled();
209
211
  });
210
212
 
@@ -21,8 +21,6 @@ const mockStore = configureStore(middlewares);
21
21
 
22
22
  const defaultProps = mock<IGalleryPanelProps>();
23
23
 
24
- defaultProps.isImageSelected = true;
25
- defaultProps.imageSelected = null;
26
24
  defaultProps.validFormats = ["png", "jpg"];
27
25
  const setImageMocked = defaultProps.setImage as jest.MockedFunction<(imageData: any) => void>;
28
26
  defaultProps.isGlobalTab = true;
@@ -13,7 +13,7 @@ const FieldContainer = (props: IFieldContainerProps) => {
13
13
 
14
14
  const hideField = () => updateValue(key, nullValue);
15
15
 
16
- const containerValue = selectedContent[objKey];
16
+ const containerValue = selectedContent[objKey] !== undefined ? selectedContent[objKey] : null;
17
17
 
18
18
  const fieldProps = {
19
19
  fieldType: type,
@@ -28,7 +28,7 @@ const FieldContainer = (props: IFieldContainerProps) => {
28
28
  };
29
29
 
30
30
  const hasMultipleOptions = field.whiteList && field.whiteList.length > 1;
31
- const nullValue = isContainer ? getNullValue(containerValue, hasMultipleOptions) : null;
31
+ const nullValue = isContainer && containerValue ? getNullValue(containerValue, hasMultipleOptions) : null;
32
32
  const hasNullValue = JSON.stringify(containerValue) === JSON.stringify(nullValue);
33
33
 
34
34
  const getField = () => <FieldsBehavior {...fieldProps} key={key} />;
@@ -81,7 +81,7 @@ const ImageField = (props: IImageFieldProps) => {
81
81
  </S.IconWrapper>
82
82
  <S.Text>Add image</S.Text>
83
83
  </S.FieldWrapper>
84
- {value && Object.keys(value).length > 1 && typeof value !== "string" && (
84
+ {value && previewSrc && Object.keys(value).length > 1 && typeof value !== "string" && (
85
85
  <S.ImageDataWrapper data-testid="imageDataWrapper">
86
86
  <S.FileName>{value.name}</S.FileName>
87
87
  <S.ImageData>
@@ -87,8 +87,12 @@ const AutoItem = (props: IProps) => {
87
87
  return false;
88
88
  };
89
89
 
90
- const handleSelectOnChange = (newValue: any) => {
91
- getCategoryContent(newValue);
90
+ const handleSelectOnChange = (newValue: string | null) => {
91
+ if(newValue){
92
+ getCategoryContent(newValue);
93
+ } else {
94
+ setState({ ...state, selected: "", options: [] });
95
+ }
92
96
  };
93
97
 
94
98
  const handleCheckChange = (checkValue: any) => {
@@ -52,7 +52,7 @@ const FieldsBehavior = (props: any): JSX.Element => {
52
52
 
53
53
  const containerInfo: any = isComponentContainer && value && isEmptyContainer(value, hasMultipleOptions);
54
54
 
55
- const { isEmpty } = containerInfo;
55
+ const isEmpty = containerInfo ? containerInfo.isEmpty : false;
56
56
  const showTitle = hasTitle && (!isEmpty || hasMultipleOptions);
57
57
 
58
58
  const Asterisk = () => (mandatory ? <S.Asterisk>*</S.Asterisk> : null);
@@ -19,6 +19,7 @@ const GalleryDetailPanel = (props: IProps) => {
19
19
  isSaving,
20
20
  setImage,
21
21
  uploadImage,
22
+ selectImage,
22
23
  } = props;
23
24
 
24
25
  const initialState: IImageForm = {
@@ -96,7 +97,10 @@ const GalleryDetailPanel = (props: IProps) => {
96
97
  const handleDelete = async () => {
97
98
  if (imageForm.id) {
98
99
  const deleted = await deleteImage(imageForm.id);
99
- if (deleted) setDeletedToast(true);
100
+ if (deleted) {
101
+ selectImage(null);
102
+ setDeletedToast(true);
103
+ }
100
104
  refreshImages();
101
105
  }
102
106
  };
@@ -145,7 +149,7 @@ const GalleryDetailPanel = (props: IProps) => {
145
149
  </Button>
146
150
  <IconAction icon="OpenOutside" size="m" onClick={handleOpenUrl} />
147
151
  </S.ImageWrapper>
148
- <S.ImageName>{imageSelected.name}</S.ImageName>
152
+ <S.ImageName data-testid="gallery-image-name">{imageSelected.name}</S.ImageName>
149
153
  <S.Date>
150
154
  <strong>Uploaded:</strong> {getFormattedDateWithTimezone(imageSelected.published, "d MMM Y")}
151
155
  </S.Date>
@@ -226,6 +230,7 @@ export interface IDetailPanelProps {
226
230
  isGlobalTab: boolean;
227
231
  refreshImages: () => void;
228
232
  isSaving: IIsSaving;
233
+ selectImage(item: IImage | null): void;
229
234
  }
230
235
 
231
236
  const mapStateToProps = (state: IRootState) => ({
@@ -206,14 +206,13 @@ const mapStateToProps = (state: IRootState) => ({
206
206
 
207
207
  export interface IDispatchProps {
208
208
  getSiteImages(params: IGetSiteImages): Promise<void>;
209
- selectImage(item: IImage): void;
209
+ selectImage(item: IImage | null): void;
210
210
  uploadError: (error: boolean, msg?: string) => Promise<void>;
211
211
  uploadImage: (imageFiles: File | File[], site: number | string) => Promise<IImage>;
212
212
  }
213
213
 
214
214
  const mapDispatchToProps = {
215
215
  getSiteImages: galleryActions.getSiteImages,
216
- selectImage: galleryActions.selectImage,
217
216
  uploadError: galleryActions.uploadError,
218
217
  uploadImage: galleryActions.uploadImage,
219
218
  };
@@ -7,32 +7,32 @@ import DetailPanel from "./DetailPanel";
7
7
  import * as S from "./style";
8
8
 
9
9
  const GalleryPanel = (props: IGalleryPanelProps) => {
10
- const { isImageSelected, imageSelected, validFormats, setImage, isGlobalTab, site, selectedTab, refreshImages } =
11
- props;
10
+ const { imageSelected, validFormats, setImage, isGlobalTab, site, selectedTab, refreshImages, selectImage } = props;
12
11
  const allowUpload = !isGlobalTab || !selectedTab;
13
12
 
14
13
  return (
15
14
  <S.GalleryPanel>
16
15
  <GalleryDragAndDrop
17
- isImageSelected={isImageSelected}
16
+ isImageSelected={!!imageSelected}
18
17
  validFormats={validFormats}
19
18
  site={site}
20
19
  allowUpload={allowUpload}
21
20
  refreshImages={refreshImages}
21
+ selectImage={selectImage}
22
22
  />
23
23
  <DetailPanel
24
24
  imageSelected={imageSelected}
25
- isImageSelected={isImageSelected}
25
+ isImageSelected={!!imageSelected}
26
26
  setImage={setImage}
27
27
  isGlobalTab={isGlobalTab}
28
28
  refreshImages={refreshImages}
29
+ selectImage={selectImage}
29
30
  />
30
31
  </S.GalleryPanel>
31
32
  );
32
33
  };
33
34
 
34
35
  export interface IGalleryPanelProps {
35
- isImageSelected: boolean;
36
36
  imageSelected: IImage | null;
37
37
  validFormats: string[];
38
38
  setImage: (imageData: any) => void;
@@ -40,6 +40,7 @@ export interface IGalleryPanelProps {
40
40
  site: number | string;
41
41
  selectedTab: string;
42
42
  refreshImages: () => Promise<void>;
43
+ selectImage(item: IImage | null): void;
43
44
  }
44
45
 
45
46
  export default memo(GalleryPanel);
@@ -10,21 +10,21 @@ import Orientation from "./GalleryFilters/Orientation";
10
10
  import SortBy from "./GalleryFilters/SortBy";
11
11
  import Type from "./GalleryFilters/Type";
12
12
  import GalleryPanel from "./GalleryPanel";
13
- import * as S from "./style";
14
13
  import { useFilterQuery, useSortedListStatus } from "./hooks";
15
14
  import { getSortedListStatus } from "./utils";
16
15
 
16
+ import * as S from "./style";
17
+
17
18
  const itemsPerPage = 50;
18
19
  const firstPage = 1;
19
20
 
20
21
  const Gallery = (props: IProps): JSX.Element => {
21
- const { data, isLoading, getSiteImages, selectImage, getImageSelected, toggleModal, site, uploadError } = props;
22
+ const { data, isLoading, getSiteImages, getImageSelected, toggleModal, site, uploadError } = props;
22
23
 
23
24
  const tabs = [];
24
25
  if (site) tabs.unshift(...["Local", "Global"]);
25
26
  const [selectedTab, setSelectedTab] = useState(tabs[0]);
26
- const [selectedImage, setSelectedImage] = useState<number>();
27
- const [imageSelectedData, setImageSelectedData] = useState<IData>(data);
27
+ const [selectedImage, setSelectedImage] = useState<IImage | null>(null);
28
28
  const isLocalTab = selectedTab === "Local";
29
29
  const isGlobalTab = selectedTab === "Global";
30
30
  const galleryScope = isLocalTab ? site.id : "global";
@@ -57,10 +57,6 @@ const Gallery = (props: IProps): JSX.Element => {
57
57
  // eslint-disable-next-line react-hooks/exhaustive-deps
58
58
  }, [galleryScope]);
59
59
 
60
- useEffect(() => {
61
- setImageSelectedData(data);
62
- }, [data]);
63
-
64
60
  useEffect(() => {
65
61
  const handleScroll = () => {
66
62
  const loadMore =
@@ -88,17 +84,22 @@ const Gallery = (props: IProps): JSX.Element => {
88
84
  };
89
85
 
90
86
  useEffect(() => {
87
+ setSelectedImage(null);
91
88
  const params = getParams();
92
89
  getSiteImages({ ...params, page: firstPage });
93
90
  // eslint-disable-next-line react-hooks/exhaustive-deps
94
91
  }, [currentFilterQuery, searchQuery]);
95
92
 
96
- const handleClick = (item: IImage) => (e: React.MouseEvent<HTMLDivElement>) => {
97
- selectImage(item);
98
- };
93
+ const handleClick = (item: IImage) => () => {
94
+ if(item.id !== selectedImage?.id) {
95
+ setSelectedImage(item);
96
+ } else {
97
+ setSelectedImage(null);
98
+ }
99
+ }
99
100
 
100
101
  const setImage = (imageData: any) => {
101
- const updatedImage = { ...imageSelectedData.imageSelected, ...imageData };
102
+ const updatedImage = { ... selectedImage,...imageData };
102
103
  getImageSelected(updatedImage);
103
104
  toggleModal();
104
105
  };
@@ -118,6 +119,13 @@ const Gallery = (props: IProps): JSX.Element => {
118
119
  setCurrentFilterQuery(filterQuery);
119
120
  };
120
121
 
122
+ const handleSelectedTab = (tab: string) => {
123
+ if (tab !== selectedTab) {
124
+ setSelectedImage(null);
125
+ setSelectedTab(tab);
126
+ }
127
+ };
128
+
121
129
  const emptyStateProps = {
122
130
  icon: "search",
123
131
  title: "Oh! No Results Found",
@@ -132,7 +140,7 @@ const Gallery = (props: IProps): JSX.Element => {
132
140
  <S.Header>
133
141
  {!!tabs.length && (
134
142
  <S.TabsWrapper>
135
- <Tabs tabs={tabs} active={selectedTab} setSelectedTab={setSelectedTab} noMargins />
143
+ <Tabs tabs={tabs} active={selectedTab} setSelectedTab={handleSelectedTab} noMargins />
136
144
  </S.TabsWrapper>
137
145
  )}
138
146
  <S.Filters>
@@ -169,11 +177,7 @@ const Gallery = (props: IProps): JSX.Element => {
169
177
  {data &&
170
178
  data.items &&
171
179
  data.items.map((item: IImage, index: number) => {
172
- const isSelected = data.imageSelected ? item.id === data.imageSelected.id : false;
173
- if (isSelected && selectedImage !== item.id) {
174
- setSelectedImage(item.id);
175
- setImageSelectedData(data);
176
- }
180
+ const isSelected = item.id === selectedImage?.id;
177
181
  return (
178
182
  <S.GridItem data-testid="grid-item" key={item.name + index} orientation={item.orientation}>
179
183
  <S.ImageWrapper
@@ -205,14 +209,14 @@ const Gallery = (props: IProps): JSX.Element => {
205
209
  </S.GalleryWrapper>
206
210
  </S.GalleryTabs>
207
211
  <GalleryPanel
208
- isImageSelected={imageSelectedData?.isImageSelected}
209
- imageSelected={imageSelectedData?.imageSelected}
212
+ imageSelected={selectedImage}
210
213
  validFormats={validFormats}
211
214
  setImage={setImage}
212
215
  isGlobalTab={!isLocalTab}
213
216
  site={galleryScope}
214
217
  selectedTab={selectedTab}
215
218
  refreshImages={refreshImages}
219
+ selectImage={setSelectedImage}
216
220
  />
217
221
  </S.Wrapper>
218
222
  );
@@ -233,13 +237,11 @@ const mapStateToProps = (state: IRootState) => ({
233
237
 
234
238
  export interface IDispatchProps {
235
239
  getSiteImages(params: IGetSiteImages): Promise<void>;
236
- selectImage(item: IImage): void;
237
240
  uploadError: (error: boolean, msg?: string) => Promise<void>;
238
241
  }
239
242
 
240
243
  const mapDispatchToProps = {
241
244
  getSiteImages: galleryActions.getSiteImages,
242
- selectImage: galleryActions.selectImage,
243
245
  uploadError: galleryActions.uploadError,
244
246
  };
245
247
 
@@ -22,6 +22,7 @@ const IconsWrapper = styled.div`
22
22
  const ChecksWrapper = styled.div`
23
23
  padding: ${(p) => p.theme.spacing.xs} ${(p) => p.theme.spacing.s};
24
24
  overflow-y: auto;
25
+ max-height: 300px;
25
26
  `;
26
27
 
27
28
  export { HeaderWrapper, IconsWrapper, ChecksWrapper };
@@ -4,7 +4,7 @@ import { useHandleClickOutside } from "@ax/hooks";
4
4
  import * as S from "./style";
5
5
 
6
6
  const Tooltip = (props: ITooltipProps): JSX.Element => {
7
- const { content, children, hideOnClick = true, bottom, left } = props;
7
+ const { content, children, hideOnClick = true, bottom, left, expanded, top } = props;
8
8
 
9
9
  const initialState: IState = {
10
10
  active: false,
@@ -79,7 +79,13 @@ const Tooltip = (props: ITooltipProps): JSX.Element => {
79
79
  if (!content) return children;
80
80
 
81
81
  return (
82
- <S.Tooltip data-testid="tooltip-component" onMouseEnter={showTip} onMouseLeave={hideTip} onMouseDown={handleClick}>
82
+ <S.Tooltip
83
+ data-testid="tooltip-component"
84
+ onMouseEnter={showTip}
85
+ onMouseLeave={hideTip}
86
+ onMouseDown={handleClick}
87
+ expanded={expanded}
88
+ >
83
89
  <div ref={childrenRef}>{children}</div>
84
90
  <S.Tip
85
91
  data-testid="tip-component"
@@ -89,6 +95,8 @@ const Tooltip = (props: ITooltipProps): JSX.Element => {
89
95
  ref={tipRef}
90
96
  fixOutOfBounds={fixOutOfBounds}
91
97
  left={left}
98
+ expanded={expanded}
99
+ top={top}
92
100
  >
93
101
  {content}
94
102
  </S.Tip>
@@ -104,11 +112,13 @@ interface IState {
104
112
  }
105
113
 
106
114
  export interface ITooltipProps {
107
- content: string | boolean | undefined;
115
+ content?: string | boolean | JSX.Element[];
108
116
  children: any;
109
117
  hideOnClick?: boolean;
110
118
  bottom?: boolean;
111
119
  left?: number;
120
+ expanded?: boolean;
121
+ top?: number;
112
122
  }
113
123
 
114
124
  export default Tooltip;
@@ -1,7 +1,7 @@
1
1
  import styled from "styled-components";
2
2
 
3
- const Tooltip = styled.div`
4
- position: relative;
3
+ const Tooltip = styled.div<{ expanded?: boolean }>`
4
+ position: ${(p) => (p.expanded ? "static" : "relative")};
5
5
  `;
6
6
 
7
7
  const Tip = styled.span<{
@@ -10,24 +10,28 @@ const Tip = styled.span<{
10
10
  bottom?: boolean;
11
11
  fixOutOfBounds: number;
12
12
  left?: number;
13
+ top?: number;
14
+ expanded?: boolean;
13
15
  }>`
14
16
  position: absolute;
15
- top: ${(p) => (p.bottom ? "auto" : "-32px")};
17
+ top: ${(p) => (p.top ? `${p.top}px` : p.bottom ? "auto" : "-32px")};
16
18
  bottom: ${(p) => (p.bottom ? "-32px" : "auto")};
17
19
  left: ${(p) => (p.left ? `${p.left}px` : "auto")};
18
20
  border-radius: 2px;
19
- transform: translateX(calc(-50% + ${(p) => `${p.childrenWidth / 2}px`} - ${(p) => `${p.fixOutOfBounds}px`}));
21
+ transform: ${(p) =>
22
+ p.expanded ? "none" : `translateX(calc(-50% + ${p.childrenWidth / 2}px - ${p.fixOutOfBounds}px))`};
20
23
  padding: 8px;
21
24
  color: #fff;
22
25
  background: ${(p) => p.theme.color.uiMainMenuBackground};
23
26
  font-size: 12px;
24
27
  line-height: 1;
25
28
  z-index: 100;
26
- white-space: nowrap;
29
+ white-space: ${(p) => (p.expanded ? "normal" : "nowrap")};
27
30
  display: ${(p) => (p.active ? "block" : "none")};
28
31
  animation: fadeIn 0.3s;
29
32
  font-weight: normal;
30
33
  text-transform: none;
34
+ max-width: 350px;
31
35
 
32
36
  @keyframes fadeIn {
33
37
  from {
@@ -67,8 +67,6 @@ function getSiteImages(props: IGetSiteImages): (dispatch: Dispatch, getState: ()
67
67
  setData({
68
68
  items: images,
69
69
  page,
70
- isImageSelected: data.isImageSelected,
71
- imageSelected: data.imageSelected,
72
70
  isFinished: result.data.items.length < items,
73
71
  })
74
72
  );
@@ -81,19 +79,6 @@ function getSiteImages(props: IGetSiteImages): (dispatch: Dispatch, getState: ()
81
79
  };
82
80
  }
83
81
 
84
- function selectImage(item: IImage): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
85
- return async (dispatch, getState) => {
86
- const {
87
- gallery: { data },
88
- } = getState();
89
- if (data?.imageSelected?.id === item.id) {
90
- dispatch(setData({ ...data, imageSelected: null, isImageSelected: false }));
91
- } else {
92
- dispatch(setData({ ...data, imageSelected: item, isImageSelected: true }));
93
- }
94
- };
95
- }
96
-
97
82
  function uploadImage(imageFiles: File | File[], site: number | string): (dispatch: Dispatch) => Promise<IImage> {
98
83
  return async (dispatch) => {
99
84
  try {
@@ -162,14 +147,8 @@ function deleteImage(imageID: number): (dispatch: Dispatch, getState: () => IRoo
162
147
  gallery: { data },
163
148
  } = getState();
164
149
  const responseActions = {
165
- handleSuccess: () => {
166
- dispatch(setData({ ...data, isImageSelected: false, imageSelected: null }));
167
- return true;
168
- },
169
- handleError: (response: any) => {
170
- appActions.handleError(response)(dispatch);
171
- return false;
172
- },
150
+ handleSuccess: () => null,
151
+ handleError: (response: any) => appActions.handleError(response)(dispatch),
173
152
  };
174
153
 
175
154
  const callback = async () => images.deleteImage(imageID);
@@ -182,4 +161,4 @@ function deleteImage(imageID: number): (dispatch: Dispatch, getState: () => IRoo
182
161
  };
183
162
  }
184
163
 
185
- export { setIsLoading, getSiteImages, uploadImage, updateImage, deleteImage, selectImage, uploadError };
164
+ export { setIsLoading, getSiteImages, uploadImage, updateImage, deleteImage, uploadError };
@@ -11,8 +11,6 @@ import { GalleryActionsCreators } from "./interfaces";
11
11
 
12
12
  export interface IData {
13
13
  items: IImage[];
14
- isImageSelected: boolean;
15
- imageSelected: IImage | null;
16
14
  page: number;
17
15
  isFinished: boolean;
18
16
  }
@@ -41,8 +39,6 @@ export interface IGalleryState {
41
39
  export const initialState = {
42
40
  data: {
43
41
  items: [],
44
- isImageSelected: false,
45
- imageSelected: null,
46
42
  page: 1,
47
43
  isFinished: false,
48
44
  },
@@ -577,7 +577,7 @@ function getDataContentTranslation(langID: number): (dispatch: Dispatch, getStat
577
577
 
578
578
  const responseActions = {
579
579
  handleSuccess: (data: IStructuredDataContent) => {
580
- const newForm = {...data, canBeTranslated: false };
580
+ const newForm = { ...data, canBeTranslated: false };
581
581
  dispatch(setForm(newForm));
582
582
  dispatch(setIsIATranslated(true));
583
583
  },
@@ -614,6 +614,18 @@ function setIsTranslated(isTranslated: boolean): (dispatch: Dispatch) => Promise
614
614
  };
615
615
  }
616
616
 
617
+ function resetCurrentData(): (dispatch: Dispatch) => Promise<void> {
618
+ return async (dispatch) => {
619
+ try {
620
+ dispatch(setCurrentData(null));
621
+ dispatch(setCurrentDataID(null));
622
+ dispatch(setCurrentDataContent([]));
623
+ } catch (e) {
624
+ console.log("Error", e);
625
+ }
626
+ };
627
+ }
628
+
617
629
  export {
618
630
  setIsActive,
619
631
  setCategories,
@@ -649,4 +661,5 @@ export {
649
661
  getDataContentTranslation,
650
662
  setFormValues,
651
663
  setIsTranslated,
664
+ resetCurrentData,
652
665
  };
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useCallback, useState, useRef } from "react";
1
+ import React, { useEffect, useCallback, useState, useRef, useLayoutEffect } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { structuredDataActions } from "@ax/containers/StructuredData";
@@ -32,6 +32,7 @@ const CategoriesList = (props: IProps): JSX.Element => {
32
32
  deleteDataContent,
33
33
  restoreDataContent,
34
34
  setHistoryPush,
35
+ resetCurrentData,
35
36
  } = props;
36
37
 
37
38
  const itemsPerPage = 50;
@@ -67,11 +68,11 @@ const CategoriesList = (props: IProps): JSX.Element => {
67
68
  }, [page]);
68
69
 
69
70
  const getContents = useCallback(
70
- (id: string) => {
71
+ async (id: string) => {
71
72
  const params: any = getParams();
72
73
  params.dataID = id;
73
74
  setSelectedCategory(id, scope);
74
- getStructuredDataContents(params, currentSiteID);
75
+ await getStructuredDataContents(params, currentSiteID);
75
76
  },
76
77
  [getStructuredDataContents, currentSiteID, getParams, setSelectedCategory, scope]
77
78
  );
@@ -81,6 +82,12 @@ const CategoriesList = (props: IProps): JSX.Element => {
81
82
  // eslint-disable-next-line react-hooks/exhaustive-deps
82
83
  }, [lang, currentStructuredData, page]);
83
84
 
85
+ useLayoutEffect(() => {
86
+ return () => {
87
+ resetCurrentData();
88
+ };
89
+ }, []);
90
+
84
91
  const handleClick = (dataID: string) => {
85
92
  setSelectedCategory(dataID, scope);
86
93
  };
@@ -235,13 +242,14 @@ const mapStateToProps = (state: IRootState) => ({
235
242
  });
236
243
 
237
244
  interface IDispatchProps {
238
- getStructuredDataContents(params: IGetStructuredDataParams, siteID?: number | undefined): void;
245
+ getStructuredDataContents(params: IGetStructuredDataParams, siteID?: number | undefined): Promise<void>;
239
246
  resetCategoryValues(): void;
240
247
  setLanguage(lang: { locale: string; id: number | null }): void;
241
248
  setSelectedCategory(id: string, scope: string): void;
242
249
  deleteDataContent(catID: number[]): Promise<boolean>;
243
250
  restoreDataContent(catID: number | number[]): void;
244
251
  setHistoryPush(path: string): void;
252
+ resetCurrentData(): Promise<void>;
245
253
  }
246
254
 
247
255
  interface ICategoriesProps {
@@ -266,6 +274,7 @@ const mapDispatchToProps = {
266
274
  deleteDataContent: structuredDataActions.deleteStructuredDataContent,
267
275
  restoreDataContent: structuredDataActions.restoreStructuredDataContent,
268
276
  setHistoryPush: appActions.setHistoryPush,
277
+ resetCurrentData: structuredDataActions.resetCurrentData,
269
278
  };
270
279
 
271
280
  export default connect(mapStateToProps, mapDispatchToProps)(CategoriesList);
@@ -487,8 +487,21 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
487
487
  Home
488
488
  </S.Home>
489
489
  )}
490
- <S.Title width={title.width}>{title.text}</S.Title>
491
- {!isHome && <S.Slug width={path.width}>{path.text}</S.Slug>}
490
+ <Tooltip
491
+ content={[
492
+ <>
493
+ <strong>{page.title}</strong>
494
+ <br />
495
+ </>,
496
+ <>{fullPath.page}</>,
497
+ ]}
498
+ left={0}
499
+ top={1}
500
+ expanded
501
+ >
502
+ <S.Title width={title.width}>{title.text}</S.Title>
503
+ {!isHome && <S.Slug width={path.width}>{path.text}</S.Slug>}
504
+ </Tooltip>
492
505
  </S.NameCell>
493
506
  {CategoryColumns}
494
507
  {activeColumns.includes("type") && (
@@ -27,7 +27,6 @@ const NameCell = styled(Cell)`
27
27
  const Title = styled.div<{ width: number }>`
28
28
  ${(p) => p.theme.textStyle.uiL};
29
29
  color: ${(p) => p.theme.color.textHighEmphasis};
30
- display: inline;
31
30
  overflow: hidden;
32
31
  white-space: nowrap;
33
32
  text-overflow: ellipsis;
@@ -35,7 +34,6 @@ const Title = styled.div<{ width: number }>`
35
34
  `;
36
35
 
37
36
  const Slug = styled.div<{ width: number }>`
38
- display: inline;
39
37
  overflow: hidden;
40
38
  white-space: nowrap;
41
39
  text-overflow: ellipsis;
@@ -121,6 +119,7 @@ const StyledActionMenu = styled(ActionMenu)`
121
119
  `;
122
120
 
123
121
  const PageRow = styled(Row)<{ global?: boolean }>`
122
+ position: relative;
124
123
  &:hover {
125
124
  ${TypeCell}, ${SeoCell} {
126
125
  color: ${(p) => p.theme.color.textHighEmphasis};
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useCallback, useState, useRef } from "react";
1
+ import React, { useEffect, useCallback, useState, useRef, useLayoutEffect } from "react";
2
2
  import { connect } from "react-redux";
3
3
  import { useHistory, useLocation } from "react-router-dom";
4
4
  import { schemas } from "components";
@@ -237,17 +237,17 @@ const Content = (props: IProps): JSX.Element => {
237
237
  return params;
238
238
  }, [filter, currentSiteInfo, isStructuredData, page, searchQuery, lang]);
239
239
 
240
- const getPages = (params: any, filterQuery?: any) => {
240
+ const getPages = async (params: any, filterQuery?: any) => {
241
241
  const isStructuredDataPage = filter !== "unique-pages";
242
242
  const pageFilter = isStructuredDataPage ? filter : undefined;
243
- getSitePages(params, pageFilter, filterQuery);
243
+ await getSitePages(params, pageFilter, filterQuery);
244
244
  };
245
245
 
246
246
  const getSiteContent = useCallback(
247
- (filterQuery?: any) => {
247
+ async (filterQuery?: any) => {
248
248
  const params = getParams();
249
249
  isStructuredData && params.siteID
250
- ? getStructuredDataContents(params, params.siteID)
250
+ ? await getStructuredDataContents(params, params.siteID)
251
251
  : getPages(params, filterQuery);
252
252
  },
253
253
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -263,16 +263,17 @@ const Content = (props: IProps): JSX.Element => {
263
263
  // eslint-disable-next-line
264
264
  }, [page, filter, currentFilterQuery, searchQuery]);
265
265
 
266
- useEffect(() => {
266
+ useLayoutEffect(() => {
267
267
  setPage(firstPage);
268
268
  const initialColumns = filter !== "unique-pages" ? filterColumns : defaultColumns;
269
269
  setColumnsState(initialColumns);
270
270
  // eslint-disable-next-line react-hooks/exhaustive-deps
271
271
  }, [filter]);
272
+
272
273
  const params = { language: lang.id, recentSitesNumber: 7 };
273
274
  const fetchSitesByLang = async () => await getSitesByLang(params);
274
275
 
275
- useEffect(() => {
276
+ useLayoutEffect(() => {
276
277
  checkUserSession();
277
278
  if (history.action !== "POP" && (!locationState || locationState.isFromEditor !== true)) {
278
279
  setFilter("unique-pages");
@@ -28,7 +28,7 @@ const LoginModule = (props: IProps) => {
28
28
 
29
29
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
30
30
  e.preventDefault();
31
- login(state.email, state.password, state.rememberMe);
31
+ await login(state.email, state.password, state.rememberMe);
32
32
  };
33
33
 
34
34
  const _handleEmail = (email: string) => {
@@ -63,7 +63,7 @@ interface IProps {
63
63
  isLoggingIn: boolean;
64
64
  children: any;
65
65
  globalSettings: IGlobalSettings;
66
- login(email: string, password: string, rememberMe: boolean): void;
66
+ login(email: string, password: string, rememberMe: boolean): Promise<void>;
67
67
  resetError(): void;
68
68
  getGlobalSettings(): void;
69
69
  }
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from "react";
1
+ import React, { useLayoutEffect } from "react";
2
2
 
3
3
  import { connect } from "react-redux";
4
4
  import { withRouter, RouteComponentProps } from "react-router-dom";
@@ -25,25 +25,25 @@ const Sites = (props: ISitesProps): JSX.Element => {
25
25
  globalLangs,
26
26
  } = props;
27
27
 
28
- const fetchInitialData = async () => {
29
- setCurrentSiteInfo(null);
30
- await getStructuredData(token);
31
- await getAllDataPacks();
32
- await getUser("me");
28
+ useLayoutEffect(() => {
29
+ const fetchInitialData = async () => {
30
+ setCurrentSiteInfo(null);
31
+ await getUser("me", token);
32
+ await getStructuredData(token);
33
+ await getAllDataPacks();
33
34
 
34
- const defaultLanguage = globalLangs.find((language: any) => language.isDefault);
35
- if (defaultLanguage) {
36
- const { locale, id } = defaultLanguage;
37
- const lang = {
38
- locale,
39
- id,
40
- };
35
+ const defaultLanguage = globalLangs.find((language: any) => language.isDefault);
36
+ if (defaultLanguage) {
37
+ const { locale, id } = defaultLanguage;
38
+ const lang = {
39
+ locale,
40
+ id,
41
+ };
41
42
 
42
- setLanguage(lang);
43
- }
44
- };
43
+ setLanguage(lang);
44
+ }
45
+ };
45
46
 
46
- useEffect(() => {
47
47
  fetchInitialData();
48
48
  // eslint-disable-next-line react-hooks/exhaustive-deps
49
49
  }, [token]);
@@ -68,7 +68,7 @@ interface IDispatchProps {
68
68
  getStructuredData(token: string, siteId?: number): Promise<void>;
69
69
  setLanguage(lang: { locale: string; id: number | null }): void;
70
70
  getAllDataPacks: () => Promise<void>;
71
- getUser: (id: string) => Promise<void>;
71
+ getUser: (id: string, token?: string) => Promise<void>;
72
72
  }
73
73
 
74
74
  export type ISitesProps = IStateProps & IDispatchProps & RouteComponentProps;
@@ -26,7 +26,7 @@ const ContentFilters = (props: IProps): JSX.Element => {
26
26
  <S.Heading>{category.title}</S.Heading>
27
27
  {category.filters &&
28
28
  category.filters.map((filter: any, filterKey: number) => {
29
- const { name, value } = filter;
29
+ const { name, value, editable } = filter;
30
30
 
31
31
  const handleClick = () => {
32
32
  onClick(value);
@@ -43,7 +43,7 @@ const ContentFilters = (props: IProps): JSX.Element => {
43
43
  };
44
44
 
45
45
  return (
46
- <MenuItem key={filterKey} onClick={handleClick} extendedAction={isSelected ? extendedAction : null}>
46
+ <MenuItem key={filterKey} onClick={handleClick} extendedAction={isSelected && editable ? extendedAction : null}>
47
47
  <NavLink to="#" className={selectedClass}>
48
48
  <S.Link active={isSelected}>
49
49
  {name}
@@ -4,6 +4,7 @@ const getDynamicFilters = (values: any) =>
4
4
  name: value.title,
5
5
  value: value.id,
6
6
  fromPage: value.fromPage,
7
+ editable: value.editable ? value.editable : false,
7
8
  }));
8
9
 
9
10
  const getFilters = (staticFilters: any, dynamicValues: any) => {
@@ -54,6 +54,20 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
54
54
  skipReview,
55
55
  } = props;
56
56
 
57
+ const { locale } = lang;
58
+ const {
59
+ pageLanguages,
60
+ metaDescription,
61
+ metaTitle,
62
+ isIndexed,
63
+ availableSites,
64
+ structuredData,
65
+ structuredDataContent,
66
+ fullUrl,
67
+ } = globalPage;
68
+
69
+ const slug = fullUrl ? fullUrl.replace(/^.*\/\/[^\/]+\/[\w-.]+/, "") : "";
70
+
57
71
  const activeColumns = Object.keys(columns).filter((col: string) => columns[col].show);
58
72
 
59
73
  const initValue = { title: "", slug: "" };
@@ -67,19 +81,9 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
67
81
  const theme: any = useTheme();
68
82
  const nameCellPadding = Number(theme.spacing.s.slice(0, -2));
69
83
  const title = useAdaptiveText(nameCellRef, globalPage.title, nameCellPadding);
84
+ const path = useAdaptiveText(nameCellRef, slug, nameCellPadding);
70
85
  const API_URL = process.env.REACT_APP_API_ENDPOINT;
71
86
 
72
- const { locale } = lang;
73
- const {
74
- pageLanguages,
75
- metaDescription,
76
- metaTitle,
77
- isIndexed,
78
- availableSites,
79
- structuredData,
80
- structuredDataContent,
81
- } = globalPage;
82
-
83
87
  const publishedTooltip: Record<string, string> = {
84
88
  active: "Live",
85
89
  "upload-pending": "Publication pending",
@@ -361,7 +365,21 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
361
365
  <CheckField name="check" value={globalPage.id} checked={isSelected} onChange={handleOnChange} />
362
366
  </S.CheckCell>
363
367
  <S.NameCell role="cell" onClick={_handleClick} ref={nameCellRef}>
364
- <S.Title width={title.width}>{title.text}</S.Title>
368
+ <Tooltip
369
+ content={[
370
+ <>
371
+ <strong>{globalPage.title}</strong>
372
+ <br />
373
+ </>,
374
+ <>{slug}</>,
375
+ ]}
376
+ left={0}
377
+ top={1}
378
+ expanded
379
+ >
380
+ <S.Title width={title.width}>{title.text}</S.Title>
381
+ <S.Slug width={path.width}>{path.text}</S.Slug>
382
+ </Tooltip>
365
383
  </S.NameCell>
366
384
  {isAllPages && activeColumns.includes("type") && (
367
385
  <S.TypeCell role="cell">{structuredData && getStructuredDataTitle(structuredData)}</S.TypeCell>
@@ -19,7 +19,13 @@ const NameCell = styled(Cell)`
19
19
  const Title = styled.div<{ width: number }>`
20
20
  ${(p) => p.theme.textStyle.uiL};
21
21
  color: ${(p) => p.theme.color.textHighEmphasis};
22
- display: inline;
22
+ overflow: hidden;
23
+ white-space: nowrap;
24
+ text-overflow: ellipsis;
25
+ width: ${(p) => `${p.width}px`};
26
+ `;
27
+
28
+ const Slug = styled.div<{ width: number }>`
23
29
  overflow: hidden;
24
30
  white-space: nowrap;
25
31
  text-overflow: ellipsis;
@@ -83,6 +89,7 @@ const StyledActionMenu = styled(ActionMenu)`
83
89
  `;
84
90
 
85
91
  const StructuredDataRow = styled(Row)<{ disabled: boolean }>`
92
+ position: relative;
86
93
  cursor: ${(p) => (p.disabled ? "default" : "pointer")};
87
94
  &:hover {
88
95
  ${StyledActionMenu} {
@@ -174,4 +181,5 @@ export {
174
181
  FloatingSeo,
175
182
  ModalContent,
176
183
  Title,
184
+ Slug,
177
185
  };
@@ -148,7 +148,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
148
148
  live: { title: "Live", show: true },
149
149
  status: { title: "Status", show: true },
150
150
  translation: { title: "Trans.", show: true },
151
- ...(isStructuredDataFromPage && { seo: { title: "SEO", show: true } }),
151
+ ...((isStructuredDataFromPage || currentStructuredData === null) && { seo: { title: "SEO", show: true } }),
152
152
  };
153
153
 
154
154
  const extraColumns = categoryColumns.reduce((acc: Record<string, IColumn>, cur: any) => {
@@ -193,19 +193,19 @@ const StructuredDataList = (props: IProps): JSX.Element => {
193
193
  }, [page, searchQuery]);
194
194
 
195
195
  const getStructuredData = useCallback(
196
- (id: string, filterQuery?: string) => {
196
+ async (id: string, filterQuery?: string) => {
197
197
  const params: IGetStructuredDataParams = { ...getParams(), dataID: null };
198
198
  params.dataID = id;
199
199
  params.filterQuery = filterQuery;
200
- getStructuredDataContents(params);
200
+ await getStructuredDataContents(params);
201
201
  },
202
202
  [getParams, getStructuredDataContents]
203
203
  );
204
204
 
205
- const handleGetGlobalPages = () => {
205
+ const handleGetGlobalPages = async () => {
206
206
  const params: IGetGlobalPagesParams = getParams();
207
207
  params.filterStructuredData = isStructuredDataFromPage && !isAllPages && filter;
208
- getGlobalPages(params, currentFilterQuery);
208
+ await getGlobalPages(params, currentFilterQuery);
209
209
  };
210
210
 
211
211
  const handleGetStructuredData = () => getStructuredData(filter, currentFilterQuery);
@@ -233,17 +233,15 @@ const StructuredDataList = (props: IProps): JSX.Element => {
233
233
  // eslint-disable-next-line react-hooks/exhaustive-deps
234
234
  }, []);
235
235
 
236
- useEffect(() => {
236
+ useLayoutEffect(() => {
237
237
  const isGlobalData = structuredData.global.some((data) => data.id === currentStructuredData?.id);
238
238
  const type = currentStructuredData && isGlobalData ? currentStructuredData.id : "all";
239
239
  setStructuredDataType(type);
240
- // eslint-disable-next-line react-hooks/exhaustive-deps
241
- }, [currentStructuredData]);
242
240
 
243
- useEffect(() => {
244
241
  filterItems("types", "all");
245
242
  if (filter === "all-pages" || !isStructuredDataFromPage) {
246
243
  filterItems("filterSites", "all");
244
+ setColumnsState({ all: allColumns });
247
245
  }
248
246
  unselectAllItems();
249
247
  if (filter !== "all-pages" && currentStructuredData) {
@@ -257,7 +255,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
257
255
  }));
258
256
  }
259
257
  // eslint-disable-next-line react-hooks/exhaustive-deps
260
- }, [filter]);
258
+ }, [currentStructuredData]);
261
259
 
262
260
  useEffect(() => {
263
261
  if (!isFirstRender) {
@@ -289,9 +287,9 @@ const StructuredDataList = (props: IProps): JSX.Element => {
289
287
  } else {
290
288
  emptyState.message = isAllPages
291
289
  ? "You don’t have pages with this content type yet."
292
- : "To start using pages in your site, create as many pages as you need.";
293
- emptyState.button = isAllPages ? "View all content" : "Create the first page";
294
- emptyState.action = isAllPages ? resetFilterValues : addNewAction;
290
+ : isDataEditable ? "To start using pages in your site, create as many pages as you need." : undefined;
291
+ emptyState.button = isAllPages ? "View all content" : isDataEditable ? "Create the first page" : undefined;
292
+ emptyState.action = isAllPages ? resetFilterValues : isDataEditable ? addNewAction : undefined;
295
293
  }
296
294
  const isEmpty = isStructuredDataFromPage ? !currentSitePages.length : !currentDataContent.length;
297
295
  setIsEmpty(isEmpty);
@@ -320,7 +318,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
320
318
  setHistoryPush(path, isFromPage);
321
319
  };
322
320
 
323
- const handleMenuClick = (dataID: string) => {
321
+ const handleMenuClick = async (dataID: string) => {
324
322
  setPage(firstPage);
325
323
  setSelectedStructuredData(dataID, scope);
326
324
  };
@@ -702,7 +700,7 @@ interface IDispatchProps {
702
700
  setHistoryPush(route: string, isEditor?: boolean): void;
703
701
  resetForm(setDefault?: boolean): void;
704
702
  setLanguage(lang: { locale: string; id: number }): void;
705
- getStructuredDataContents(params: IGetStructuredDataParams): void;
703
+ getStructuredDataContents(params: IGetStructuredDataParams): Promise<void>;
706
704
  setSelectedStructuredData(id: string, scope: string): void;
707
705
  deleteDataContent(dataID: number[]): Promise<boolean>;
708
706
  restoreDataContent(dataID: number | number[]): void;
@@ -68,8 +68,11 @@ const UserList = (props: IProps): JSX.Element => {
68
68
  } = useBulkSelection(usersIds);
69
69
 
70
70
  useEffect(() => {
71
- const query = searchQuery ? (currentFilterQuery ? `&query=${searchQuery}` : `?query=${searchQuery}`) : "";
72
- getUsers({ filterQuery: currentFilterQuery, query });
71
+ const handleGetUsers = async () => {
72
+ const query = searchQuery ? (currentFilterQuery ? `&query=${searchQuery}` : `?query=${searchQuery}`) : "";
73
+ await getUsers({ filterQuery: currentFilterQuery, query });
74
+ }
75
+ handleGetUsers();
73
76
  // eslint-disable-next-line react-hooks/exhaustive-deps
74
77
  }, [currentFilterQuery, searchQuery]);
75
78
 
@@ -241,7 +244,7 @@ const mapDispatchToProps = {
241
244
  };
242
245
 
243
246
  interface IDispatchProps {
244
- getUsers(params?: any): void;
247
+ getUsers(params?: any): Promise<void>;
245
248
  getUser(id: number): Promise<void>;
246
249
  resetUserData(): void;
247
250
  setHistoryPush(route: string): void;