@griddo/ax 11.12.1-rc.2 → 11.12.1-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "11.12.1-rc.2",
4
+ "version": "11.12.1-rc.4",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Diego M. Béjar <diego.bejar@secuoyas.com>",
@@ -217,5 +217,5 @@
217
217
  "publishConfig": {
218
218
  "access": "public"
219
219
  },
220
- "gitHead": "edc26c065f085f6c1b2665d4a2f1bd3067fdc24d"
220
+ "gitHead": "da2037760c929e2ba660fda8abb67611be1bea0a"
221
221
  }
@@ -70,7 +70,7 @@ describe("Browser component rendering", () => {
70
70
 
71
71
  renderBrowser(defaultProps);
72
72
 
73
- expect(screen.getByTestId("nav-actions-wrapper")).toBeInTheDocument();
73
+ expect(screen.queryByTestId("nav-actions-wrapper")).not.toBeInTheDocument();
74
74
  expect(screen.queryByTestId("navbar-iframe-wrapper")).not.toBeInTheDocument();
75
75
  expect(screen.getByTestId("browser-content-wrapper")).toBeInTheDocument();
76
76
  });
@@ -63,6 +63,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
63
63
  const isFormEditor = editorType === "form";
64
64
  const isHeadingsEditor = editorType === "headings";
65
65
  const isKeywordsEditor = editorType === "keywords";
66
+ const isZoomEditor = isPageEditor || isHeadingsEditor || isKeywordsEditor;
66
67
 
67
68
  const frameWrapperRef = useRef<HTMLDivElement>(null);
68
69
 
@@ -82,8 +83,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
82
83
  localStorage.setItem("selectedID", "0");
83
84
  (window as any).browserRef = null;
84
85
 
85
- // Only setup ResizeObserver for pageEditor
86
- if (!isPageEditor) return;
86
+ if (!isZoomEditor) return;
87
87
 
88
88
  const el = frameWrapperRef.current;
89
89
  if (!el) return;
@@ -102,7 +102,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
102
102
  observer.observe(el);
103
103
 
104
104
  return () => observer.disconnect();
105
- }, [isPageEditor]);
105
+ }, [isZoomEditor]);
106
106
 
107
107
  // Fetch share data when in preview mode
108
108
  useEffect(() => {
@@ -191,7 +191,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
191
191
  }
192
192
  };
193
193
 
194
- const scaledWidth = isPageEditor
194
+ const scaledWidth = isZoomEditor
195
195
  ? Math.floor(parseInt(dimensions.resolution) * (parseInt(dimensions.zoom) / 100))
196
196
  : undefined;
197
197
 
@@ -208,9 +208,9 @@ const Browser = (props: IBrowserProps): JSX.Element => {
208
208
  data-testid="browser-wrapper"
209
209
  ref={browserRef}
210
210
  scaledWidth={scaledWidth}
211
- isPreview={!isPageEditor}
211
+ isPreview={!isZoomEditor}
212
212
  >
213
- {(isPageEditor || isHeadingsEditor || isKeywordsEditor) && (
213
+ {isZoomEditor && (
214
214
  <S.NavBar>
215
215
  <S.NavUrl>{url}</S.NavUrl>
216
216
  {isPreview ? (
@@ -296,11 +296,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
296
296
  $compact={isCompact}
297
297
  />
298
298
  )}
299
- <S.FrameWrapper
300
- hasBorder={isPageEditor || isHeadingsEditor || isKeywordsEditor}
301
- isFormEditor={isFormEditor}
302
- data-testid="navbar-iframe-wrapper"
303
- >
299
+ <S.FrameWrapper hasBorder={isZoomEditor} isFormEditor={isFormEditor} data-testid="navbar-iframe-wrapper">
304
300
  {isPreview ? (
305
301
  <iframe
306
302
  title="Preview"
@@ -316,20 +312,20 @@ const Browser = (props: IBrowserProps): JSX.Element => {
316
312
  width: scaledWidth !== undefined ? `${scaledWidth}px` : "100%",
317
313
  height: "100%",
318
314
  overflow: "hidden",
319
- flexShrink: isPageEditor ? 0 : "unset",
315
+ flexShrink: isZoomEditor ? 0 : "unset",
320
316
  }}
321
317
  >
322
318
  <iframe
323
319
  title="Preview"
324
- width={isPageEditor ? dimensions.resolution : "100%"}
320
+ width={isZoomEditor ? dimensions.resolution : "100%"}
325
321
  src={urlPreview}
326
322
  loading="lazy"
327
323
  className="frame-content"
328
324
  style={{
329
325
  display: "block",
330
- transform: isPageEditor ? `scale(${parseInt(dimensions.zoom) / 100})` : "scale(1)",
326
+ transform: isZoomEditor ? `scale(${parseInt(dimensions.zoom) / 100})` : "scale(1)",
331
327
  transformOrigin: "0 0",
332
- height: isPageEditor ? `${Math.round(100 / (parseInt(dimensions.zoom) / 100))}%` : "100%",
328
+ height: isZoomEditor ? `${Math.round(100 / (parseInt(dimensions.zoom) / 100))}%` : "100%",
333
329
  }}
334
330
  />
335
331
  </div>
@@ -7,7 +7,7 @@ import type { IHeadingError } from "../../utils";
7
7
  import * as S from "./style";
8
8
 
9
9
  const ErrorsItem = (props: IErrorsItemProps) => {
10
- const { error, onSelectHeading } = props;
10
+ const { error, onSelectHeading, onDelete } = props;
11
11
  const { message, description, headingIds } = error;
12
12
 
13
13
  const [isOpen, setIsOpen] = useState(false);
@@ -54,7 +54,12 @@ const ErrorsItem = (props: IErrorsItemProps) => {
54
54
  <Icon name={isOpen ? "UpArrow" : "DownArrow"} size="16" />
55
55
  </S.IconWrapper>
56
56
  )}
57
- <S.IconWrapper onClick={() => setIsDeleted(true)}>
57
+ <S.IconWrapper
58
+ onClick={() => {
59
+ setIsDeleted(true);
60
+ onDelete();
61
+ }}
62
+ >
58
63
  <Icon name="close" size="16" />
59
64
  </S.IconWrapper>
60
65
  </S.ErrorActions>
@@ -80,6 +85,7 @@ const ErrorsItem = (props: IErrorsItemProps) => {
80
85
  interface IErrorsItemProps {
81
86
  error: IHeadingError;
82
87
  onSelectHeading: (id: number) => () => void;
88
+ onDelete: () => void;
83
89
  }
84
90
 
85
91
  export default ErrorsItem;
@@ -1,4 +1,4 @@
1
- import { useState } from "react";
1
+ import { useMemo, useState } from "react";
2
2
 
3
3
  import { Icon } from "@ax/components";
4
4
 
@@ -11,8 +11,18 @@ const ErrorsBanner = (props: IErrorsBannerProps) => {
11
11
  const { errors, onSelectHeading, isOpen, setIsOpen, resetKey } = props;
12
12
 
13
13
  const [isDeleted, setIsDeleted] = useState(false);
14
+ const [deletedErrorIndices, setDeletedErrorIndices] = useState<Set<number>>(new Set());
14
15
 
15
- if (isDeleted) {
16
+ const allErrorsDeleted = useMemo(
17
+ () => deletedErrorIndices.size === errors.length && errors.length > 0,
18
+ [deletedErrorIndices.size, errors.length],
19
+ );
20
+
21
+ const handleErrorDelete = (index: number) => {
22
+ setDeletedErrorIndices((prev) => new Set([...prev, index]));
23
+ };
24
+
25
+ if (isDeleted || allErrorsDeleted) {
16
26
  return <></>;
17
27
  }
18
28
 
@@ -37,8 +47,13 @@ const ErrorsBanner = (props: IErrorsBannerProps) => {
37
47
  Review <strong>suggestions and warnings</strong> to enhance your page's search engine optimization.
38
48
  </S.Description>
39
49
  <S.ErrorListWrapper>
40
- {errors.map((error) => (
41
- <ErrorItem key={`${error.message}-${resetKey}`} error={error} onSelectHeading={onSelectHeading} />
50
+ {errors.map((error, index) => (
51
+ <ErrorItem
52
+ key={`${error.message}-${resetKey}`}
53
+ error={error}
54
+ onSelectHeading={onSelectHeading}
55
+ onDelete={() => handleErrorDelete(index)}
56
+ />
42
57
  ))}
43
58
  </S.ErrorListWrapper>
44
59
  </S.ErrorsContent>
@@ -67,17 +67,16 @@ const analyzeHeadings = (headings: HeadingNode[], isFiltering = false): IHeading
67
67
 
68
68
  // 2. Check for incorrect nesting (skipped levels)
69
69
  const nestingErrorIds: number[] = [];
70
- let expectedMaxLevel = 2; // Start: we expect H1 or H2
71
- for (let i = 0; i < flatHeadings.length; i++) {
70
+ let lastValidLevel = flatHeadings[0].level;
71
+ for (let i = 1; i < flatHeadings.length; i++) {
72
72
  const currLevel = flatHeadings[i].level;
73
73
 
74
- if (currLevel > expectedMaxLevel) {
74
+ if (currLevel > lastValidLevel + 1) {
75
75
  // Heading skips levels - mark as error
76
76
  nestingErrorIds.push(flatHeadings[i].id);
77
- // Don't update expectedMaxLevel - stay strict on what we expect next
78
77
  } else {
79
- // Correct nesting - update expected max level for next heading
80
- expectedMaxLevel = currLevel + 1;
78
+ // Correct nesting - update last valid level
79
+ lastValidLevel = currLevel;
81
80
  }
82
81
  }
83
82
  if (nestingErrorIds.length > 0) {
@@ -39,11 +39,22 @@ const KeywordsPreviewModal = (props: IKeywordsPreviewProps) => {
39
39
  const handleDeleteKeyword = (value: string) => {
40
40
  setDeletedKeyword(value);
41
41
  deleteKeyword(value);
42
+ setKeywordsFilter([]);
43
+ };
44
+
45
+ const handleCloseModal = () => {
46
+ setKeywordsFilter([]);
47
+ toggleModal();
48
+ };
49
+
50
+ const handleAddKeywords = (value: string[]) => {
51
+ setKeywordsFilter([]);
52
+ addKeywords(value);
42
53
  };
43
54
 
44
55
  return (
45
56
  <S.Wrapper>
46
- <FloatingPanel title="Keywords" toggleModal={toggleModal} closeOnOutsideClick={false} isOpen={isOpen} width={358}>
57
+ <FloatingPanel title="Keywords" toggleModal={handleCloseModal} closeOnOutsideClick={false} isOpen={isOpen} width={358}>
47
58
  {isOpen && (
48
59
  <S.KeywordsWrapper>
49
60
  {keywordsFilter.length > 0 && (
@@ -79,7 +90,7 @@ const KeywordsPreviewModal = (props: IKeywordsPreviewProps) => {
79
90
  </S.KeywordsWrapper>
80
91
  )}
81
92
  </FloatingPanel>
82
- <AddKeywordsModal isOpen={isAddOpen} toggleModal={toggleAddModal} addNewKeyword={addKeywords} />
93
+ <AddKeywordsModal isOpen={isAddOpen} toggleModal={toggleAddModal} addNewKeyword={handleAddKeywords} />
83
94
  {isVisible && <Toast message={toastState} setIsVisible={setIsVisible} />}
84
95
  </S.Wrapper>
85
96
  );
@@ -13,14 +13,14 @@ const useModal = (initialState?: boolean, bodyBlock = true) => {
13
13
  }
14
14
  return () => {
15
15
  // Solo eliminar si no hay otros modales abiertos
16
- const modals = document.querySelectorAll('[data-testid="modal-wrapper"]');
16
+ const modals = document.querySelectorAll('[data-testid="modal-wrapper"], [data-testid="floating-panel"]');
17
17
  if (modals.length <= 1) {
18
18
  document.body.classList.remove("modal-open");
19
19
  }
20
20
  };
21
21
  } else if (!bodyBlock || !isOpen) {
22
22
  // Solo eliminar si no hay modales abiertos
23
- const modals = document.querySelectorAll('[data-testid="modal-wrapper"]');
23
+ const modals = document.querySelectorAll('[data-testid="modal-wrapper"], [data-testid="floating-panel"]');
24
24
  if (modals.length === 0) {
25
25
  document.body.classList.remove("modal-open");
26
26
  }
@@ -72,14 +72,14 @@ const useModals = <T extends string>(modalKeys: readonly T[], bodyBlock = true)
72
72
  }
73
73
  return () => {
74
74
  // Solo eliminar si no hay otros modales abiertos
75
- const modals = document.querySelectorAll('[data-testid="modal-wrapper"]');
75
+ const modals = document.querySelectorAll('[data-testid="modal-wrapper"], [data-testid="floating-panel"]');
76
76
  if (modals.length <= 1) {
77
77
  document.body.classList.remove("modal-open");
78
78
  }
79
79
  };
80
80
  }
81
81
  // Solo eliminar si no hay modales abiertos
82
- const modals = document.querySelectorAll('[data-testid="modal-wrapper"]');
82
+ const modals = document.querySelectorAll('[data-testid="modal-wrapper"], [data-testid="floating-panel"]');
83
83
  if (modals.length === 0) {
84
84
  document.body.classList.remove("modal-open");
85
85
  }