@griddo/ax 11.12.0 → 11.12.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.
Files changed (100) hide show
  1. package/config/jest/componentsMock.js +7 -5
  2. package/package.json +2 -2
  3. package/src/__tests__/components/Browser/Browser.test.tsx +438 -87
  4. package/src/__tests__/components/Browser/Browser.utils.test.ts +55 -0
  5. package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +1 -3
  6. package/src/__tests__/components/Fields/Button/Button.test.tsx +29 -27
  7. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +158 -0
  8. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorsBanner.test.tsx +90 -0
  9. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.test.tsx +178 -0
  10. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +150 -0
  11. package/src/__tests__/components/KeywordsPreviewModal/KeywordItem/KeywordItem.test.tsx +91 -0
  12. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +122 -0
  13. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.utils.test.ts +15 -0
  14. package/src/__tests__/components/KeywordsPreviewModal/atoms.test.tsx +101 -0
  15. package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +1 -1
  16. package/src/__tests__/modules/FramePreview/FramePreview.test.tsx +318 -0
  17. package/src/__tests__/modules/FramePreview/FramePreview.utils.test.ts +242 -0
  18. package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +185 -0
  19. package/src/components/Browser/index.tsx +294 -149
  20. package/src/components/Browser/style.tsx +75 -6
  21. package/src/components/Browser/utils.tsx +13 -0
  22. package/src/components/Button/index.tsx +2 -1
  23. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +2 -4
  24. package/src/components/Fields/AsyncSelect/style.tsx +13 -0
  25. package/src/components/Fields/FieldGroup/index.tsx +5 -2
  26. package/src/components/Fields/FieldGroup/style.tsx +32 -7
  27. package/src/components/Fields/HeadingField/index.tsx +22 -22
  28. package/src/components/Fields/HiddenField/style.tsx +1 -1
  29. package/src/components/Fields/NumberField/index.tsx +15 -16
  30. package/src/components/Fields/NumberField/style.tsx +2 -0
  31. package/src/components/Fields/ReferenceField/index.tsx +1 -1
  32. package/src/components/Fields/SEOPreview/index.tsx +36 -0
  33. package/src/components/Fields/SEOPreview/style.tsx +24 -0
  34. package/src/components/Fields/Select/index.tsx +5 -1
  35. package/src/components/Fields/Select/style.tsx +56 -0
  36. package/src/components/Fields/SummaryButton/index.tsx +18 -9
  37. package/src/components/Fields/SummaryButton/style.tsx +1 -2
  38. package/src/components/Fields/TagsField/index.tsx +8 -9
  39. package/src/components/Fields/UrlField/index.tsx +26 -27
  40. package/src/components/Fields/index.tsx +2 -0
  41. package/src/components/FloatingNote/index.tsx +35 -0
  42. package/src/components/FloatingNote/style.tsx +26 -0
  43. package/src/components/FloatingPanel/index.tsx +5 -2
  44. package/src/components/FloatingPanel/style.tsx +2 -1
  45. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +85 -0
  46. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/style.tsx +80 -0
  47. package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +57 -0
  48. package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +82 -0
  49. package/src/components/HeadingsPreviewModal/HeadingItem/index.tsx +71 -0
  50. package/src/components/HeadingsPreviewModal/HeadingItem/style.tsx +77 -0
  51. package/src/components/HeadingsPreviewModal/index.tsx +148 -0
  52. package/src/components/HeadingsPreviewModal/style.tsx +82 -0
  53. package/src/components/HeadingsPreviewModal/utils.tsx +329 -0
  54. package/src/components/Icon/index.tsx +1 -2
  55. package/src/components/IconAction/index.tsx +1 -1
  56. package/src/components/KeywordsPreviewModal/KeywordItem/index.tsx +46 -0
  57. package/src/components/KeywordsPreviewModal/KeywordItem/style.tsx +64 -0
  58. package/src/components/KeywordsPreviewModal/atoms.tsx +96 -0
  59. package/src/components/KeywordsPreviewModal/index.tsx +99 -0
  60. package/src/components/KeywordsPreviewModal/style.tsx +87 -0
  61. package/src/components/KeywordsPreviewModal/utils.tsx +22 -0
  62. package/src/components/MainWrapper/AppBar/index.tsx +8 -1
  63. package/src/components/MainWrapper/index.tsx +7 -1
  64. package/src/components/Notification/index.tsx +2 -2
  65. package/src/components/PageFinder/index.tsx +1 -1
  66. package/src/components/ResizePanel/index.tsx +4 -3
  67. package/src/components/ResizePanel/style.tsx +1 -1
  68. package/src/components/SearchField/style.tsx +2 -2
  69. package/src/components/SideModal/index.tsx +2 -1
  70. package/src/components/Tabs/index.tsx +13 -4
  71. package/src/components/Tabs/style.tsx +7 -8
  72. package/src/components/Toast/index.tsx +4 -2
  73. package/src/components/Tooltip/index.tsx +4 -3
  74. package/src/components/index.tsx +8 -0
  75. package/src/forms/fields.tsx +70 -68
  76. package/src/hooks/forms.tsx +22 -1
  77. package/src/hooks/index.tsx +13 -3
  78. package/src/hooks/modals.tsx +103 -15
  79. package/src/hooks/users.tsx +25 -8
  80. package/src/modules/Forms/atoms.tsx +2 -2
  81. package/src/modules/FramePreview/HeadingsOverlay/index.tsx +116 -0
  82. package/src/modules/FramePreview/HeadingsOverlay/style.tsx +34 -0
  83. package/src/modules/FramePreview/index.tsx +55 -16
  84. package/src/modules/FramePreview/style.tsx +34 -2
  85. package/src/modules/FramePreview/utils.tsx +140 -0
  86. package/src/modules/GlobalEditor/Editor/index.tsx +37 -3
  87. package/src/modules/GlobalEditor/PageBrowser/index.tsx +19 -2
  88. package/src/modules/GlobalEditor/Preview/index.tsx +0 -2
  89. package/src/modules/GlobalEditor/Preview/style.tsx +1 -1
  90. package/src/modules/GlobalEditor/index.tsx +119 -57
  91. package/src/modules/PageEditor/Editor/index.tsx +33 -2
  92. package/src/modules/PageEditor/PageBrowser/index.tsx +20 -2
  93. package/src/modules/PageEditor/Preview/index.tsx +0 -2
  94. package/src/modules/PageEditor/Preview/style.tsx +1 -1
  95. package/src/modules/PageEditor/atoms.tsx +1 -1
  96. package/src/modules/PageEditor/index.tsx +130 -66
  97. package/src/modules/PublicPreview/index.tsx +5 -2
  98. package/src/schemas/pages/GlobalPage.ts +87 -70
  99. package/src/schemas/pages/Page.ts +87 -70
  100. package/src/types/index.tsx +12 -0
@@ -7,6 +7,8 @@ import {
7
7
  CancelScheduleModal,
8
8
  ErrorPage,
9
9
  ErrorToast,
10
+ HeadingsPreviewModal,
11
+ KeywordsPreviewModal,
10
12
  Loading,
11
13
  MainWrapper,
12
14
  Modal,
@@ -22,8 +24,9 @@ import { pageStatus } from "@ax/containers/PageEditor/interfaces";
22
24
  import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
23
25
  import { RouteLeavingGuard } from "@ax/guards";
24
26
  import { dateToString, getDeactivatedModules, isModuleDisabled } from "@ax/helpers";
25
- import { useIsDirty, useModal, usePermission } from "@ax/hooks";
27
+ import { useIsDirty, useModals, usePermissions } from "@ax/hooks";
26
28
  import type {
29
+ HeadingFilter,
27
30
  IErrorItem,
28
31
  ILanguage,
29
32
  INotification,
@@ -70,15 +73,18 @@ const PageEditor = (props: IProps) => {
70
73
  setCurrentPageID,
71
74
  restorePage,
72
75
  schemaVersion,
76
+ updateEditorContent,
73
77
  } = props;
74
78
 
75
- const isAllowedToPublishPages = usePermission("content.publishUnpublishPages");
76
- const isAllowedToCreatePages = usePermission("content.createPages");
77
- const isAllowedToDeletePage = usePermission("content.deletePages");
78
- const isAllowedToDeletePublishedPage = usePermission("content.deletePublishedPages");
79
- const isAllowedToEditContentPage = usePermission("content.editContentPages");
79
+ const isAllowedTo = usePermissions({
80
+ publishPages: "content.publishUnpublishPages",
81
+ createPages: "content.createPages",
82
+ deletePages: "content.deletePages",
83
+ deletePublishedPages: "content.deletePublishedPages",
84
+ editContentPages: "content.editContentPages",
85
+ });
80
86
 
81
- const defaultTab = isAllowedToEditContentPage ? "edit" : "view";
87
+ const defaultTab = isAllowedTo.editContentPages ? "edit" : "view";
82
88
 
83
89
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
84
90
  const [isReadOnly, setIsReadOnly] = useState(false);
@@ -86,18 +92,26 @@ const PageEditor = (props: IProps) => {
86
92
  const [notification, setNotification] = useState<INotification | null>(null);
87
93
  const [toastMsg, setToastMsg] = useState<string | null>(null);
88
94
  const [scheduleDate, setScheduleDate] = useState({ date: dateToString(new Date(), "yyy/MM/dd"), time: "12:00 am" });
89
- const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
90
- const { isOpen, toggleModal } = useModal();
91
- const { isOpen: isUnpublishOpen, toggleModal: toggleUnpublishModal } = useModal();
92
- const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
93
- const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
94
- const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
95
- const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
96
- const { isOpen: isDirtyNavigateOpen, toggleModal: toggleDirtyNavigateModal } = useModal();
97
95
  const [pendingNavigateCallback, setPendingNavigateCallback] = useState<(() => void) | null>(null);
98
96
  const [errorPagesChecked, setErrorPagesChecked] = useState(false);
97
+ const [headingsFilter, setHeadingsFilter] = useState<HeadingFilter>("all");
98
+ const [keywordsFilter, setKeywordsFilter] = useState<string[]>([]);
99
99
  const browserRef = useRef<HTMLDivElement>(null);
100
100
 
101
+ const { isOpen, toggleModal } = useModals([
102
+ "userEditing",
103
+ "unpublish",
104
+ "delete",
105
+ "schedule",
106
+ "cancelSchedule",
107
+ "restore",
108
+ "headingsPreview",
109
+ "keywordsPreview",
110
+ "dirtyNavigate",
111
+ ]);
112
+
113
+ const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
114
+
101
115
  const isGlobal = editorContent && editorContent.origin === "GLOBAL";
102
116
  const isEditable = editorContent?.editable;
103
117
  const isPublished = props.pageStatus === pageStatus.PUBLISHED || props.pageStatus === pageStatus.UPLOAD_PENDING;
@@ -107,11 +121,13 @@ const PageEditor = (props: IProps) => {
107
121
  const isTranslated = pageLanguages.length > 1;
108
122
  const structuredData = editorContent ? editorContent.structuredData : "";
109
123
  const isEditLive = isPublished && hasDraft;
110
- const isAllowedToDelete = (isPublished && isAllowedToDeletePublishedPage) || (!isPublished && isAllowedToDeletePage);
124
+ const isAllowedToDelete =
125
+ (isPublished && isAllowedTo.deletePublishedPages) || (!isPublished && isAllowedTo.deletePages);
111
126
  const canBeUnpublished = editorContent?.canBeUnpublished;
112
127
  const deleteHelpText = !canBeUnpublished ? "This is the canonical site of the page. You cannot unpublish it." : null;
113
128
  const isScheduled = !!editorContent && !!editorContent.publicationScheduled;
114
129
  const isDeleted = editorContent.deleted;
130
+ const defaultContentTab = "content";
115
131
 
116
132
  const errorNotificationText =
117
133
  "There are some errors on the page so you can not publish yet. Please review them in the error panel.";
@@ -126,13 +142,13 @@ const PageEditor = (props: IProps) => {
126
142
 
127
143
  // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
128
144
  useEffect(() => {
129
- const { pageID, getPage, setTab, sendPagePing } = props;
130
- const defaultTab = "content";
145
+ const { pageID, getPage, sendPagePing } = props;
146
+
131
147
  const handleGetPage = async () => {
132
148
  await getPage(pageID);
133
149
  };
134
150
 
135
- setTab(defaultTab);
151
+ setTab(defaultContentTab);
136
152
  resetDirty();
137
153
  setToastMsg(null);
138
154
  handleGetPage();
@@ -152,11 +168,11 @@ const PageEditor = (props: IProps) => {
152
168
  const { pageID, sendPagePing, currentUserID } = props;
153
169
  if (userEditing && userEditing.id !== currentUserID) {
154
170
  setIsReadOnly(true);
155
- !isOpen && toggleModal();
171
+ !isOpen("userEditing") && toggleModal("userEditing");
156
172
  } else {
157
173
  setIsReadOnly(false);
158
174
  pageID && sendPagePing(pageID);
159
- isOpen && toggleModal();
175
+ isOpen("userEditing") && toggleModal("userEditing");
160
176
  }
161
177
  }, [userEditing]);
162
178
 
@@ -179,7 +195,7 @@ const PageEditor = (props: IProps) => {
179
195
  const allPageVersions = pageLanguages.map((lang: IPageLanguage) => lang.pageId);
180
196
  const isDeleted = deleteAllVersions ? await deleteBulk(allPageVersions) : await deletePage();
181
197
 
182
- toggleDeleteModal();
198
+ toggleModal("delete");
183
199
  if (isDeleted) {
184
200
  setRoute(path);
185
201
  }
@@ -265,7 +281,7 @@ const PageEditor = (props: IProps) => {
265
281
  discardDraft();
266
282
  };
267
283
 
268
- const handleDiscarChanges = () => {
284
+ const handleDiscardChanges = () => {
269
285
  const { getPage, pageID } = props;
270
286
  resetDirty();
271
287
 
@@ -285,7 +301,7 @@ const PageEditor = (props: IProps) => {
285
301
  const saved = await schedulePublication(dateString, isDraft);
286
302
  if (saved) {
287
303
  resetDirty();
288
- toggleScheduleModal();
304
+ toggleModal("schedule");
289
305
  }
290
306
  };
291
307
 
@@ -294,7 +310,7 @@ const PageEditor = (props: IProps) => {
294
310
  if (saved) {
295
311
  setScheduleDate({ date: "", time: "12:00 am" });
296
312
  resetDirty();
297
- toggleCancelScheduleModal();
313
+ toggleModal("cancelSchedule");
298
314
  }
299
315
  };
300
316
 
@@ -315,7 +331,7 @@ const PageEditor = (props: IProps) => {
315
331
  }
316
332
  : {
317
333
  label: status === pageStatus.UPLOAD_PENDING ? "Cancel publication" : "Unpublish",
318
- action: hasDraft ? toggleUnpublishModal : unpublishPage,
334
+ action: hasDraft ? () => toggleModal("unpublish") : unpublishPage,
319
335
  disabled: !canBeUnpublished,
320
336
  helpText: deleteHelpText,
321
337
  };
@@ -332,24 +348,24 @@ const PageEditor = (props: IProps) => {
332
348
 
333
349
  const menuOptions = [];
334
350
 
335
- if (isAllowedToPublishPages && !isScheduled && !isPublished && props.pageStatus !== pageStatus.OFFLINE_PENDING) {
351
+ if (isAllowedTo.publishPages && !isScheduled && !isPublished && props.pageStatus !== pageStatus.OFFLINE_PENDING) {
336
352
  menuOptions.push({
337
353
  label: "Schedule",
338
354
  icon: "calendar",
339
- action: toggleScheduleModal,
355
+ action: () => toggleModal("schedule"),
340
356
  });
341
357
  }
342
358
 
343
- if (isAllowedToPublishPages && isScheduled) {
359
+ if (isAllowedTo.publishPages && isScheduled) {
344
360
  menuOptions.push({
345
361
  label: "Cancel Schedule",
346
362
  icon: "cancelEvent",
347
- action: toggleCancelScheduleModal,
363
+ action: () => toggleModal("cancelSchedule"),
348
364
  });
349
365
  }
350
366
 
351
367
  if (
352
- isAllowedToEditContentPage &&
368
+ isAllowedTo.editContentPages &&
353
369
  (props.pageStatus === pageStatus.PUBLISHED ||
354
370
  props.pageStatus === pageStatus.OFFLINE ||
355
371
  props.pageStatus === pageStatus.OFFLINE_PENDING ||
@@ -363,19 +379,19 @@ const PageEditor = (props: IProps) => {
363
379
  });
364
380
  }
365
381
 
366
- if (props.pageStatus === pageStatus.PUBLISHED && !hasDraft && isDirty && isAllowedToPublishPages) {
382
+ if (props.pageStatus === pageStatus.PUBLISHED && !hasDraft && isDirty && isAllowedTo.publishPages) {
367
383
  menuOptions.push({
368
384
  label: "Unpublish",
369
385
  icon: "offline",
370
- action: isDraft ? toggleUnpublishModal : unpublishPage,
386
+ action: isDraft ? () => toggleModal("unpublish") : unpublishPage,
371
387
  });
372
388
  }
373
389
 
374
- if (isAllowedToEditContentPage && !isScheduled && (isDraft || (isPublished && isDirty))) {
390
+ if (isAllowedTo.editContentPages && !isScheduled && (isDraft || (isPublished && isDirty))) {
375
391
  menuOptions.push({
376
392
  label: "Discard changes",
377
393
  icon: "close",
378
- action: isDraft ? handleDiscardDraft : handleDiscarChanges,
394
+ action: isDraft ? handleDiscardDraft : handleDiscardChanges,
379
395
  });
380
396
  }
381
397
 
@@ -383,13 +399,13 @@ const PageEditor = (props: IProps) => {
383
399
  menuOptions.push({
384
400
  label: "Delete page",
385
401
  icon: "delete",
386
- action: toggleDeleteModal,
402
+ action: () => toggleModal("delete"),
387
403
  });
388
404
  }
389
405
 
390
406
  const downArrowMenu = {
391
407
  displayed: !isReadOnly && !isDeleted,
392
- button: isAllowedToPublishPages ? getPublishButton(props.pageStatus) : undefined,
408
+ button: isAllowedTo.publishPages ? getPublishButton(props.pageStatus) : undefined,
393
409
  options: !isGlobal ? menuOptions : [],
394
410
  };
395
411
 
@@ -437,7 +453,7 @@ const PageEditor = (props: IProps) => {
437
453
  }
438
454
 
439
455
  let availableLanguages = siteLanguages;
440
- if (!isTemplateActivated || hasDeactivatedModules || !isAllowedToCreatePages || isDeleted) {
456
+ if (!isTemplateActivated || hasDeactivatedModules || !isAllowedTo.createPages || isDeleted) {
441
457
  const pageLanguagesIDs = pageLanguages.map((language) => language.languageId);
442
458
  availableLanguages = siteLanguages.filter((language) => {
443
459
  return pageLanguagesIDs.includes(language.id);
@@ -462,7 +478,7 @@ const PageEditor = (props: IProps) => {
462
478
  }
463
479
  };
464
480
 
465
- const rightButtonProps = isAllowedToEditContentPage
481
+ const rightButtonProps = isAllowedTo.editContentPages
466
482
  ? {
467
483
  label: isSaving ? "Saving" : getSaveLabel(),
468
484
  disabled:
@@ -539,33 +555,33 @@ const PageEditor = (props: IProps) => {
539
555
  props.setHistoryPush(backLinkRoute, false);
540
556
  };
541
557
 
542
- const mainAction = { title: "Preview Page", onClick: toggleModal };
558
+ const mainAction = { title: "Preview Page", onClick: () => toggleModal("userEditing") };
543
559
  const secondaryAction = { title: "Ok, go back", onClick: handleGoBack };
544
560
 
545
- const mainUnpublishAction = { title: "Ok", onClick: toggleUnpublishModal };
561
+ const mainUnpublishAction = { title: "Ok", onClick: () => toggleModal("unpublish") };
546
562
 
547
563
  const mainDeleteModalAction = {
548
564
  title: "Delete page",
549
565
  onClick: removePage,
550
566
  };
551
567
 
552
- const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
568
+ const secondaryDeleteModalAction = { title: "Cancel", onClick: () => toggleModal("delete") };
553
569
 
554
570
  const mainScheduleModalAction = {
555
571
  title: "Schedule",
556
572
  onClick: handleSchedulePublication,
557
573
  };
558
574
 
559
- const secondaryScheduleModalAction = { title: "Cancel", onClick: toggleScheduleModal };
575
+ const secondaryScheduleModalAction = { title: "Cancel", onClick: () => toggleModal("schedule") };
560
576
 
561
577
  const mainCancelScheduleModalAction = {
562
578
  title: "Cancel Schedule",
563
579
  onClick: handleCancelSchedulePublication,
564
580
  };
565
581
 
566
- const secondaryCancelScheduleModalAction = { title: "Back", onClick: toggleCancelScheduleModal };
582
+ const secondaryCancelScheduleModalAction = { title: "Back", onClick: () => toggleModal("cancelSchedule") };
567
583
 
568
- const tabIcons = isAllowedToEditContentPage
584
+ const tabIcons = isAllowedTo.editContentPages
569
585
  ? [
570
586
  { name: "edit", text: "Edit mode" },
571
587
  { name: "view", text: "Preview mode" },
@@ -583,6 +599,7 @@ const PageEditor = (props: IProps) => {
583
599
  icons: tabIcons,
584
600
  selectedTab,
585
601
  action: (tab: string) => handleSelectedTab(tab),
602
+ disabled: isOpen("headingsPreview") || isOpen("keywordsPreview"),
586
603
  };
587
604
 
588
605
  const contentLanguages: ILanguage[] = [];
@@ -610,7 +627,7 @@ const PageEditor = (props: IProps) => {
610
627
  await getPage(selectedPageLanguage.pageId, false, isDraft);
611
628
  resetDirty();
612
629
  } else {
613
- isAllowedToEditContentPage && setSelectedTab("edit");
630
+ isAllowedTo.editContentPages && setSelectedTab("edit");
614
631
  createNewTranslation(true);
615
632
  await getPage(pageID);
616
633
  }
@@ -619,18 +636,40 @@ const PageEditor = (props: IProps) => {
619
636
  const handleRestorePage = async () => {
620
637
  const isRestored = await restorePage(pageID);
621
638
  if (isRestored) {
622
- isRestoreOpen && toggleRestoreModal();
639
+ isOpen("restore") && toggleModal("restore");
623
640
  await getPage(pageID);
624
641
  }
625
642
  };
626
643
 
644
+ const handleAddKeywords = (newKeywords: string[]) => {
645
+ const keywords: string[] = [...editorContent.metaKeywords, ...newKeywords];
646
+ updateEditorContent(0, "metaKeywords", keywords);
647
+ handleSavePage();
648
+ };
649
+
650
+ const handledeleteKeyword = (keyword: string) => {
651
+ const newKeywords = (editorContent.metaKeywords as string[]).filter((key) => key !== keyword);
652
+ updateEditorContent(0, "metaKeywords", newKeywords);
653
+ handleSavePage();
654
+ };
655
+
656
+ const handleToggleHeadingsEditor = () => {
657
+ toggleModal("headingsPreview");
658
+ setTab(defaultContentTab);
659
+ };
660
+
661
+ const handleToggleKeywordsEditor = () => {
662
+ toggleModal("keywordsPreview");
663
+ setTab(defaultContentTab);
664
+ };
665
+
627
666
  const onNavigateWithDirty = (navigateCallback: () => void) => {
628
667
  setPendingNavigateCallback(() => navigateCallback);
629
- toggleDirtyNavigateModal();
668
+ toggleModal("dirtyNavigate");
630
669
  };
631
670
 
632
671
  const handleConfirmDirtyNavigation = () => {
633
- toggleDirtyNavigateModal();
672
+ toggleModal("dirtyNavigate");
634
673
  if (pendingNavigateCallback) {
635
674
  pendingNavigateCallback();
636
675
  setPendingNavigateCallback(null);
@@ -638,7 +677,7 @@ const PageEditor = (props: IProps) => {
638
677
  };
639
678
 
640
679
  const handleCancelDirtyNavigation = () => {
641
- toggleDirtyNavigateModal();
680
+ toggleModal("dirtyNavigate");
642
681
  setPendingNavigateCallback(null);
643
682
  };
644
683
 
@@ -704,7 +743,7 @@ const PageEditor = (props: IProps) => {
704
743
  type="error"
705
744
  text={deletedNotificationText}
706
745
  btnText="Restore page"
707
- onClick={toggleRestoreModal}
746
+ onClick={() => toggleModal("restore")}
708
747
  />
709
748
  </S.NotificationWrapper>
710
749
  )}
@@ -736,6 +775,12 @@ const PageEditor = (props: IProps) => {
736
775
  setNotification={setNotification}
737
776
  isEditLive={isEditLive}
738
777
  isDisabled={isDeleted}
778
+ toggleHeadingsPreview={handleToggleHeadingsEditor}
779
+ isHeadingsPreviewOpen={isOpen("headingsPreview")}
780
+ headingsFilter={headingsFilter}
781
+ toggleKeywordsPreview={handleToggleKeywordsEditor}
782
+ isKeywordsPreviewOpen={isOpen("keywordsPreview")}
783
+ keywordsFilter={keywordsFilter}
739
784
  isDirty={isDirty}
740
785
  onNavigateWithDirty={onNavigateWithDirty}
741
786
  />
@@ -749,14 +794,14 @@ const PageEditor = (props: IProps) => {
749
794
  )}
750
795
 
751
796
  <Modal
752
- isOpen={isOpen}
753
- hide={toggleModal}
797
+ isOpen={isOpen("userEditing")}
798
+ hide={() => toggleModal("userEditing")}
754
799
  size="S"
755
800
  title="This page is currently being edited"
756
801
  mainAction={mainAction}
757
802
  secondaryAction={secondaryAction}
758
803
  >
759
- {isOpen && (
804
+ {isOpen("userEditing") && (
760
805
  <S.ModalContent>
761
806
  <p>
762
807
  <strong>{userEditing?.name}</strong> is currently working on this page. You can preview the page but{" "}
@@ -766,13 +811,13 @@ const PageEditor = (props: IProps) => {
766
811
  )}
767
812
  </Modal>
768
813
  <Modal
769
- isOpen={isUnpublishOpen}
770
- hide={toggleUnpublishModal}
814
+ isOpen={isOpen("unpublish")}
815
+ hide={() => toggleModal("unpublish")}
771
816
  size="S"
772
817
  title="Unpublish Modified Page"
773
818
  mainAction={mainUnpublishAction}
774
819
  >
775
- {isUnpublishOpen && (
820
+ {isOpen("unpublish") && (
776
821
  <S.ModalContent>
777
822
  <p>
778
823
  There are some saved changes that are not published on this page. To Unpublish this page,{" "}
@@ -782,37 +827,54 @@ const PageEditor = (props: IProps) => {
782
827
  )}
783
828
  </Modal>
784
829
  <DeleteModal
785
- isOpen={isDeleteOpen}
786
- toggleModal={toggleDeleteModal}
830
+ isOpen={isOpen("delete")}
831
+ toggleModal={() => toggleModal("delete")}
787
832
  mainModalAction={mainDeleteModalAction}
788
833
  secondaryModalAction={secondaryDeleteModalAction}
789
834
  {...{ isTranslated, deleteAllVersions, setDeleteAllVersions }}
790
835
  />
791
836
  <ScheduleModal
792
- isOpen={isScheduleOpen}
793
- toggleModal={toggleScheduleModal}
837
+ isOpen={isOpen("schedule")}
838
+ toggleModal={() => toggleModal("schedule")}
794
839
  mainModalAction={mainScheduleModalAction}
795
840
  secondaryModalAction={secondaryScheduleModalAction}
796
841
  scheduleDate={scheduleDate}
797
842
  setScheduleDate={setScheduleDate}
798
843
  />
799
844
  <CancelScheduleModal
800
- isOpen={isCancelScheduleOpen}
801
- toggleModal={toggleCancelScheduleModal}
845
+ isOpen={isOpen("cancelSchedule")}
846
+ toggleModal={() => toggleModal("cancelSchedule")}
802
847
  mainModalAction={mainCancelScheduleModalAction}
803
848
  secondaryModalAction={secondaryCancelScheduleModalAction}
804
849
  />
805
850
  <RestoreModal
806
- isOpen={isRestoreOpen}
807
- toggleModal={toggleRestoreModal}
851
+ isOpen={isOpen("restore")}
852
+ toggleModal={() => toggleModal("restore")}
808
853
  {...{
809
854
  isChild: false,
810
855
  hasIssues: schemaVersion !== editorContent.schemaVersionTimestamp,
811
856
  restorePage: handleRestorePage,
812
857
  }}
813
858
  />
859
+ <HeadingsPreviewModal
860
+ isOpen={isOpen("headingsPreview")}
861
+ toggleModal={handleToggleHeadingsEditor}
862
+ browserRef={browserRef}
863
+ headingsFilter={headingsFilter}
864
+ setHeadingsFilter={setHeadingsFilter}
865
+ />
866
+ <KeywordsPreviewModal
867
+ isOpen={isOpen("keywordsPreview")}
868
+ toggleModal={handleToggleKeywordsEditor}
869
+ browserRef={browserRef}
870
+ keywords={editorContent?.metaKeywords || []}
871
+ keywordsFilter={keywordsFilter}
872
+ setKeywordsFilter={setKeywordsFilter}
873
+ addKeywords={handleAddKeywords}
874
+ deleteKeyword={handledeleteKeyword}
875
+ />
814
876
  <Modal
815
- isOpen={isDirtyNavigateOpen}
877
+ isOpen={isOpen("dirtyNavigate")}
816
878
  hide={handleCancelDirtyNavigation}
817
879
  size="S"
818
880
  title="Unsaved changes"
@@ -895,6 +957,7 @@ const mapDispatchToProps = {
895
957
  schedulePublication: pageEditorActions.schedulePublication,
896
958
  setCurrentPageID: pageEditorActions.setCurrentPageID,
897
959
  restorePage: pageEditorActions.restorePage,
960
+ updateEditorContent: pageEditorActions.updateEditorContent,
898
961
  };
899
962
 
900
963
  interface IPageEditorDispatchProps {
@@ -922,6 +985,7 @@ interface IPageEditorDispatchProps {
922
985
  schedulePublication(date: string | null, isDraft: boolean): Promise<boolean>;
923
986
  setCurrentPageID: (currentPageID: number | null) => void;
924
987
  restorePage(id: number | number[]): Promise<boolean>;
988
+ updateEditorContent(selectedEditorID: number, key: string, value: any): void;
925
989
  }
926
990
 
927
991
  type IProps = IPageEditorStateProps & IPageEditorDispatchProps & RouteComponentProps;
@@ -67,9 +67,12 @@ const PublicPreview = () => {
67
67
 
68
68
  if (isLoading || !state) return <Loading />;
69
69
 
70
+ const setBrowserRef = (ref: any) => {
71
+ (window as any).browserRef = ref;
72
+ };
73
+
70
74
  return (
71
- // biome-ignore lint/suspicious/noAssignInExpressions: TODO: fix this
72
- <S.Wrapper ref={(ref: any) => ((window as any).browserRef = ref)}>
75
+ <S.Wrapper ref={setBrowserRef}>
73
76
  <BrowserContent
74
77
  cloudinaryName={state?.cloudinaryName}
75
78
  theme={theme}