@griddo/ax 11.14.2 → 11.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/config/jest/setup.js +13 -7
  2. package/package.json +2 -2
  3. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +4 -3
  4. package/src/components/CategoryCell/index.tsx +3 -1
  5. package/src/components/ElementsTooltip/index.tsx +8 -4
  6. package/src/components/ErrorPage/index.tsx +3 -1
  7. package/src/components/FilterTagsBar/index.tsx +3 -3
  8. package/src/components/HeadingsPreviewModal/utils.tsx +2 -1
  9. package/src/components/KeywordsPreviewModal/atoms.tsx +2 -2
  10. package/src/components/KeywordsPreviewModal/index.tsx +6 -6
  11. package/src/components/KeywordsPreviewModal/utils.tsx +5 -3
  12. package/src/components/Modal/style.tsx +5 -5
  13. package/src/components/TableFilters/CheckGroupFilter/index.tsx +3 -2
  14. package/src/components/TableFilters/LiveFilter/index.tsx +4 -4
  15. package/src/components/TableFilters/SiteFilter/index.tsx +11 -12
  16. package/src/components/TableFilters/TranslationsFilter/index.tsx +2 -2
  17. package/src/components/TableFilters/TranslationsFilter/style.tsx +2 -2
  18. package/src/components/Tag/index.tsx +15 -7
  19. package/src/components/TruncatedTooltip/index.tsx +48 -0
  20. package/src/components/index.tsx +2 -0
  21. package/src/constants/index.ts +3 -0
  22. package/src/helpers/categoryColumns.tsx +55 -0
  23. package/src/helpers/images.tsx +3 -1
  24. package/src/helpers/index.tsx +2 -0
  25. package/src/modules/ActivityLog/ItemLog/EventItem/index.tsx +17 -10
  26. package/src/modules/ActivityLog/ItemLog/EventItem/style.tsx +18 -1
  27. package/src/modules/ActivityLog/ItemLogUser/UserItem/EventItem/index.tsx +10 -7
  28. package/src/modules/ActivityLog/ItemLogUser/UserItem/index.tsx +9 -3
  29. package/src/modules/ActivityLog/ItemLogUser/UserItem/style.tsx +17 -1
  30. package/src/modules/App/Routing/NavMenu/NavItem/style.tsx +1 -0
  31. package/src/modules/App/Routing/index.tsx +2 -2
  32. package/src/modules/App/Routing/style.tsx +8 -1
  33. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/style.tsx +1 -0
  34. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +7 -3
  35. package/src/modules/Categories/CategoriesList/CategoryItem/style.tsx +38 -5
  36. package/src/modules/Content/BulkHeader/TableHeader/style.tsx +1 -1
  37. package/src/modules/Content/PageItem/index.tsx +80 -204
  38. package/src/modules/Content/PageItem/style.tsx +18 -10
  39. package/src/modules/Content/atoms.tsx +147 -18
  40. package/src/modules/Content/index.tsx +2 -9
  41. package/src/modules/Forms/FormCategoriesList/CategoryItem/index.tsx +7 -3
  42. package/src/modules/Forms/FormCategoriesList/CategoryItem/style.tsx +34 -0
  43. package/src/modules/Forms/FormEditor/index.tsx +28 -50
  44. package/src/modules/Forms/FormList/FormItem/index.tsx +91 -120
  45. package/src/modules/Forms/FormList/FormItem/style.tsx +19 -0
  46. package/src/modules/Forms/FormList/index.tsx +27 -48
  47. package/src/modules/Forms/atoms.tsx +44 -32
  48. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +8 -8
  49. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/style.tsx +2 -2
  50. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +83 -101
  51. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +11 -5
  52. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +31 -54
  53. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/style.tsx +13 -7
  54. package/src/modules/Users/Roles/BulkHeader/TableHeader/style.tsx +6 -6
  55. package/src/modules/Users/Roles/RoleItem/index.tsx +3 -4
  56. package/src/modules/Users/Roles/RoleItem/style.tsx +12 -15
  57. package/src/modules/Users/Roles/index.tsx +1 -2
  58. package/src/modules/Users/UserCreate/SiteItem/index.tsx +10 -4
  59. package/src/modules/Users/UserCreate/SiteItem/style.tsx +33 -1
  60. package/src/modules/Users/UserForm/index.tsx +6 -4
  61. package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +2 -4
  62. package/src/modules/Users/UserList/BulkHeader/TableHeader/style.tsx +1 -1
  63. package/src/modules/Users/UserList/UserItem/index.tsx +18 -10
  64. package/src/modules/Users/UserList/UserItem/style.tsx +60 -4
@@ -16,10 +16,16 @@ module.exports = () => {
16
16
 
17
17
  window.matchMedia =
18
18
  window.matchMedia ||
19
- function () {
20
- return {
21
- matches: false,
22
- addListener: function () {},
23
- removeListener: function () {},
24
- };
25
- };
19
+ (() => ({
20
+ matches: false,
21
+ addListener: function () {},
22
+ removeListener: function () {},
23
+ }));
24
+
25
+ class ResizeObserverMock {
26
+ observe() {}
27
+ unobserve() {}
28
+ disconnect() {}
29
+ }
30
+
31
+ global.ResizeObserver = ResizeObserverMock;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "11.14.2",
4
+ "version": "11.14.3",
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": "92256859b4b631e6fbd04d6ab48804b42b293614"
222
+ "gitHead": "4367bb007b546db463c1a4c03a835af9d8b59709"
223
223
  }
@@ -71,7 +71,8 @@ describe("KeywordsPreviewModal component rendering", () => {
71
71
  renderComponent({ ...defaultProps, isOpen: true, keywordsFilter: ["seo"] });
72
72
 
73
73
  expect(screen.getByText("Show keyword:")).toBeTruthy();
74
- expect(screen.getByText("seo")).toBeTruthy();
74
+ const seoElements = screen.getAllByText("seo");
75
+ expect(seoElements.length).toBeGreaterThan(0);
75
76
  });
76
77
 
77
78
  it("should not render the filter section when keywordsFilter is empty", () => {
@@ -87,8 +88,8 @@ describe("KeywordsPreviewModal component events", () => {
87
88
 
88
89
  renderComponent({ ...defaultProps, isOpen: true, toggleModal: toggleModalMock });
89
90
 
90
- const closeButton = screen.getByTestId("icon-action-component");
91
- fireEvent.click(closeButton);
91
+ const closeButtons = screen.getAllByTestId("icon-action-component");
92
+ fireEvent.click(closeButtons[0]);
92
93
  expect(toggleModalMock).toBeCalled();
93
94
  });
94
95
 
@@ -1,5 +1,7 @@
1
- import React, { useEffect } from "react";
1
+ import { useEffect } from "react";
2
+
2
3
  import { ElementsTooltip } from "@ax/components";
4
+
3
5
  import * as S from "./style";
4
6
 
5
7
  const CategoryCell = (props: ICategoryCellProps): JSX.Element => {
@@ -66,11 +66,15 @@ const ElementsTooltip = (props: IElementsTooltipProps): JSX.Element => {
66
66
 
67
67
  if (!elements) return <></>;
68
68
 
69
- const visibleElements = elements.slice(0, defaultElements);
70
- const remainingElements = elements.length - defaultElements;
69
+ const filteredElements = elements.filter((element) => element !== null && element !== "");
70
+
71
+ if (filteredElements.length === 0) return <></>;
72
+
73
+ const visibleElements = filteredElements.slice(0, defaultElements);
74
+ const remainingElements = filteredElements.length - defaultElements;
71
75
 
72
76
  const elementsRows: string[][] = [];
73
- elements.forEach((element, idx) => {
77
+ filteredElements.forEach((element, idx) => {
74
78
  const row = Math.floor(idx / elementsPerRow);
75
79
  const isNewRow = idx % elementsPerRow === 0;
76
80
  elementsRows[row] = isNewRow ? [element] : [...elementsRows[row], element];
@@ -91,7 +95,7 @@ const ElementsTooltip = (props: IElementsTooltipProps): JSX.Element => {
91
95
  <>
92
96
  <S.Wrapper data-testid="elements-wrapper">
93
97
  {visibleElements.map((fullElement, idx) => {
94
- const element = defaultElements === 1 && maxChar ? trimText(fullElement, maxChar) : fullElement;
98
+ const element = maxChar ? trimText(fullElement, maxChar) : fullElement;
95
99
  const color = defaultColor || colors?.[element];
96
100
 
97
101
  return (
@@ -40,7 +40,9 @@ const ErrorPage = (props: IProps): JSX.Element => {
40
40
  title: "Something went wrong",
41
41
  description: <>Try refreshing the page, or try again later</>,
42
42
  buttonText: "Refresh page",
43
- buttonAction: () => history.push("/logout"),
43
+ buttonAction: () => {
44
+ window.location.href = "/logout";
45
+ },
44
46
  },
45
47
  wrongEditor: {
46
48
  icon: "working",
@@ -1,5 +1,5 @@
1
- import React from "react";
2
- import { IQueryValue } from "@ax/types";
1
+ import type { IQueryValue } from "@ax/types";
2
+
3
3
  import Button from "../Button";
4
4
 
5
5
  import * as S from "./style";
@@ -37,7 +37,7 @@ const FilterTagsBar = (props: IFilterTagsBarProps): JSX.Element => {
37
37
  <S.TagList>
38
38
  {tags.map((tag: string, index: number) => {
39
39
  const handleDeleteTag = () => deleteTag(tag);
40
- return <S.StyledTag key={index} text={tag} color="#FFFFFF" onDeleteAction={handleDeleteTag} />;
40
+ return <S.StyledTag key={index} text={tag} color="#FFFFFF" maxChar={30} onDeleteAction={handleDeleteTag} />;
41
41
  })}
42
42
  </S.TagList>
43
43
  <S.ButtonWrapper>
@@ -1,3 +1,4 @@
1
+ import { GRIDDO_ID } from "@ax/constants";
1
2
  import type { HeadingFilter, HeadingLevel, HeadingNode } from "@ax/types";
2
3
 
3
4
  const MAX_HEADING_LENGTH = 70;
@@ -150,7 +151,7 @@ const analyzeHeadings = (headings: HeadingNode[], isFiltering = false): IHeading
150
151
 
151
152
  const parseHeadingsTree = (html: HTMLDivElement): HeadingNode[] => {
152
153
  const frameObject = html.querySelector<HTMLIFrameElement>(".frame-content");
153
- const frameContent = frameObject?.contentWindow?.document.getElementById("___griddo") as HTMLElement;
154
+ const frameContent = frameObject?.contentWindow?.document.getElementById(GRIDDO_ID) as HTMLElement;
154
155
 
155
156
  if (!frameContent) {
156
157
  return [];
@@ -1,7 +1,7 @@
1
1
  import { useState } from "react";
2
2
 
3
+ import { FieldsBehavior, Modal } from "@ax/components";
3
4
  import type { IModal } from "@ax/types";
4
- import { Modal, FieldsBehavior } from "@ax/components";
5
5
 
6
6
  import * as S from "./style";
7
7
 
@@ -39,7 +39,7 @@ const AddKeywordsModal = (props: IAddKeywordsModal) => {
39
39
  secondaryAction={secondaryModalAction}
40
40
  mainAction={mainModalAction}
41
41
  size="S"
42
- height={282}
42
+ height={288}
43
43
  >
44
44
  <S.ModalContent>
45
45
  <FieldsBehavior
@@ -69,16 +69,16 @@ const KeywordsPreviewModal = (props: IKeywordsPreviewProps) => {
69
69
  )}
70
70
  <S.KeywordsListWrapper>
71
71
  {keywords.length === 0 && <S.StyledSummaryButton />}
72
- {Object.keys(keywordCounts).map((key, index) => {
73
- const isSelected = keywordsFilter.includes(key);
72
+ {keywords.map((keyword, index) => {
73
+ const isSelected = keywordsFilter.includes(keyword);
74
74
  return (
75
75
  <KeywordItem
76
- keyword={key}
77
- count={keywordCounts[key]}
76
+ keyword={keyword}
77
+ count={keywordCounts[keyword] ?? 0}
78
78
  isSelected={isSelected}
79
- onClick={handleAddTag(key)}
79
+ onClick={handleAddTag(keyword)}
80
80
  deleteKeyword={handleDeleteKeyword}
81
- key={`${key}-${index}`}
81
+ key={`${keyword}-${index}`}
82
82
  />
83
83
  );
84
84
  })}
@@ -1,16 +1,18 @@
1
+ import { GRIDDO_ID } from "@ax/constants";
2
+
1
3
  const countKeywords = (html: HTMLDivElement, keywords: string[]) => {
2
4
  const frameObject = html.querySelector<HTMLIFrameElement>(".frame-content");
3
- const frameContent = frameObject?.contentWindow?.document.getElementById("___griddo") as HTMLElement;
5
+ const frameContent = frameObject?.contentWindow?.document.getElementById(GRIDDO_ID) as HTMLElement;
4
6
 
5
7
  if (!frameContent) {
6
8
  return {};
7
9
  }
8
10
 
9
- const htmlContent = frameContent.innerText.toLowerCase();
11
+ const htmlContent = frameContent.innerText.toLowerCase().normalize("NFC");
10
12
  const keywordCounts: Record<string, number> = {};
11
13
 
12
14
  keywords.forEach((keyword) => {
13
- const lowerKeyword = keyword.toLowerCase();
15
+ const lowerKeyword = keyword.toLowerCase().normalize("NFC");
14
16
  const regex = new RegExp(lowerKeyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
15
17
  const matches = htmlContent.match(regex);
16
18
  keywordCounts[keyword] = matches ? matches.length : 0;
@@ -26,6 +26,11 @@ const getHeight = (size: string | undefined) => {
26
26
  }
27
27
  };
28
28
 
29
+ const getModalHeight = (height?: number | string, size?: string) => {
30
+ const heightProp = typeof height === "number" ? `${height}px` : height;
31
+ return height !== undefined ? heightProp : getHeight(size);
32
+ };
33
+
29
34
  const ModalOverlay = styled.div<{ isChild?: boolean }>`
30
35
  position: fixed;
31
36
  top: 0;
@@ -51,11 +56,6 @@ const ModalWrapper = styled.div<{ isChild?: boolean }>`
51
56
  outline: 0;
52
57
  `;
53
58
 
54
- function getModalHeight(height?: number | string, size?: string) {
55
- const heightProp = typeof height === "number" ? `${height}px` : height;
56
- return height !== undefined ? heightProp : getHeight(size);
57
- }
58
-
59
59
  const Modal = styled.div<{ size?: string; height?: number | string }>`
60
60
  z-index: 100;
61
61
  display: flex;
@@ -1,7 +1,8 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useState } from "react";
2
+
2
3
  import { CheckGroup, FloatingMenu, Icon, ListTitle, SearchField } from "@ax/components";
3
4
  import { areEquals } from "@ax/helpers";
4
- import { IFilterValue, IQueryValue } from "@ax/types";
5
+ import type { IFilterValue, IQueryValue } from "@ax/types";
5
6
 
6
7
  import * as S from "./style";
7
8
 
@@ -1,9 +1,9 @@
1
- import React, { useState, useEffect } from "react";
1
+ import { useEffect, useState } from "react";
2
2
 
3
- import { CheckGroup, Icon, FloatingMenu, ListTitle } from "@ax/components";
4
3
  import { checkgroups } from "@ax/api";
4
+ import { CheckGroup, FloatingMenu, Icon, ListTitle } from "@ax/components";
5
5
  import { areEquals, isReqOk } from "@ax/helpers";
6
- import { IFilterValue, IPageLiveStatus, IQueryValue } from "@ax/types";
6
+ import type { IFilterValue, IPageLiveStatus, IQueryValue } from "@ax/types";
7
7
 
8
8
  import * as S from "./style";
9
9
 
@@ -54,7 +54,7 @@ const LiveFilter = (props: ILiveFilterProps): JSX.Element => {
54
54
  title: item.title,
55
55
  icon: item.status,
56
56
  };
57
- if ((filterOptions && filterOptions.includes(newFilter.value)) || !filterOptions) {
57
+ if (filterOptions?.includes(newFilter.value) || !filterOptions) {
58
58
  filters.push(newFilter);
59
59
  }
60
60
  });
@@ -1,9 +1,9 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useState } from "react";
2
2
 
3
+ import { selects } from "@ax/api";
3
4
  import { CheckGroupFilter } from "@ax/components";
4
5
  import { isReqOk } from "@ax/helpers";
5
- import { selects } from "@ax/api";
6
- import { IFilterValue, IQueryValue } from "@ax/types";
6
+ import type { IFilterValue, IQueryValue } from "@ax/types";
7
7
 
8
8
  const SiteFilter = (props: ISiteFilterProps): JSX.Element => {
9
9
  const {
@@ -47,15 +47,14 @@ const SiteFilter = (props: ISiteFilterProps): JSX.Element => {
47
47
  useEffect(() => {
48
48
  getSelectSites()
49
49
  .then((items) => {
50
- items &&
51
- items.forEach((item: { value: string; label: string }) => {
52
- const newFilter = {
53
- name: item.value,
54
- value: item.value,
55
- title: item.label,
56
- };
57
- filters.push(newFilter);
58
- });
50
+ items?.forEach((item: { value: string; label: string }) => {
51
+ const newFilter = {
52
+ name: item.value,
53
+ value: item.value,
54
+ title: item.label,
55
+ };
56
+ filters.push(newFilter);
57
+ });
59
58
 
60
59
  setOptions(filters);
61
60
  })
@@ -1,8 +1,8 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useState } from "react";
2
2
 
3
3
  import { CheckGroup, FloatingMenu, Icon, ListTitle } from "@ax/components";
4
4
  import { areEquals } from "@ax/helpers";
5
- import { IFilterValue, IQueryValue } from "@ax/types";
5
+ import type { IFilterValue, IQueryValue } from "@ax/types";
6
6
 
7
7
  import * as S from "./style";
8
8
 
@@ -1,7 +1,7 @@
1
- import React from "react";
2
- import styled from "styled-components";
3
1
  import { Header } from "@ax/components/TableList/style";
4
2
 
3
+ import styled from "styled-components";
4
+
5
5
  const Translations = styled((props) => <Header {...props} />)<{ isActive: boolean }>`
6
6
  width: 115px;
7
7
  justify-content: center;
@@ -3,7 +3,14 @@ import { Icon } from "@ax/components";
3
3
  import * as S from "./style";
4
4
 
5
5
  const Tag = (props: ITagProps): JSX.Element => {
6
- const { type, text, color, icon, textColor, rounded = true, onDeleteAction, className, small } = props;
6
+ const { type, text, color, icon, textColor, rounded = true, onDeleteAction, className, small, maxChar } = props;
7
+
8
+ const truncateText = (str: string, max: number) => {
9
+ if (str.length <= max) return str;
10
+ return `${str.slice(0, max)}...`;
11
+ };
12
+
13
+ const displayText = maxChar && text ? truncateText(text, maxChar) : text;
7
14
 
8
15
  const handleClick = () => {
9
16
  if (onDeleteAction) {
@@ -22,27 +29,27 @@ const Tag = (props: ITagProps): JSX.Element => {
22
29
  switch (type) {
23
30
  case "status":
24
31
  return (
25
- <S.TagStatus className={className} data-testid="tag-status">
32
+ <S.TagStatus className={className} data-testid="tag-status" title={text || ""}>
26
33
  <S.Bullet color={color} />
27
- {text}
34
+ {displayText}
28
35
  </S.TagStatus>
29
36
  );
30
37
  case "square":
31
38
  return (
32
- <S.TagSquare color={color} textColor={textColor} className={className} data-testid="tag-square">
39
+ <S.TagSquare color={color} textColor={textColor} className={className} data-testid="tag-square" title={text || ""}>
33
40
  {icon && (
34
41
  <S.IconTag>
35
42
  <Icon name={icon} size="16" />
36
43
  </S.IconTag>
37
44
  )}
38
- <div>{text}</div>
45
+ <div>{displayText}</div>
39
46
  </S.TagSquare>
40
47
  );
41
48
  default:
42
49
  return (
43
- <S.TagFixed color={color} rounded={rounded} small={small} className={className} data-testid="tag-fixed">
50
+ <S.TagFixed color={color} rounded={rounded} small={small} className={className} data-testid="tag-fixed" title={text || ""}>
44
51
  <S.TagText>
45
- <S.Title data-testid="tag-fixed-title">{text}</S.Title>
52
+ <S.Title data-testid="tag-fixed-title">{displayText}</S.Title>
46
53
  {deleteIcon}
47
54
  </S.TagText>
48
55
  </S.TagFixed>
@@ -60,6 +67,7 @@ export interface ITagProps {
60
67
  onDeleteAction?: () => void;
61
68
  className?: string;
62
69
  small?: boolean;
70
+ maxChar?: number;
63
71
  }
64
72
 
65
73
  export default Tag;
@@ -0,0 +1,48 @@
1
+ import React from "react";
2
+
3
+ import { Tooltip } from "@ax/components";
4
+
5
+ const TruncatedTooltip = ({ content, children, ...props }: any) => {
6
+ const [isTruncated, setIsTruncated] = React.useState(false);
7
+ const elementRef = React.useRef<HTMLElement | null>(null);
8
+
9
+ const checkTruncation = React.useCallback(() => {
10
+ if (elementRef.current) {
11
+ const isTrunc = elementRef.current.scrollWidth > elementRef.current.clientWidth;
12
+ setIsTruncated(isTrunc);
13
+ }
14
+ }, []);
15
+
16
+ React.useEffect(() => {
17
+ const element = elementRef.current;
18
+ if (!element) return;
19
+
20
+ checkTruncation();
21
+
22
+ const observer = new ResizeObserver(() => {
23
+ checkTruncation();
24
+ });
25
+
26
+ observer.observe(element);
27
+
28
+ return () => {
29
+ observer.disconnect();
30
+ };
31
+ }, [checkTruncation]);
32
+
33
+ const ChildWithRef = React.cloneElement(children, {
34
+ ref: elementRef,
35
+ });
36
+
37
+ if (!isTruncated) {
38
+ return ChildWithRef;
39
+ }
40
+
41
+ return (
42
+ <Tooltip content={content} {...props}>
43
+ {ChildWithRef}
44
+ </Tooltip>
45
+ );
46
+ };
47
+
48
+ export default TruncatedTooltip;
@@ -125,6 +125,7 @@ import Tabs from "./Tabs";
125
125
  import Tag from "./Tag";
126
126
  import Toast from "./Toast";
127
127
  import Tooltip from "./Tooltip";
128
+ import TruncatedTooltip from "./TruncatedTooltip";
128
129
  import UserRolesAndSites from "./UserRolesAndSites";
129
130
 
130
131
  export {
@@ -246,6 +247,7 @@ export {
246
247
  Tooltip,
247
248
  TranslateButton,
248
249
  TranslationsFilter,
250
+ TruncatedTooltip,
249
251
  TypeFilter,
250
252
  UniqueCheck,
251
253
  UrlField,
@@ -29,6 +29,8 @@ const VALID_DOCUMENT_FORMATS = ["pdf", "doc", "docx", "xls", "xlsx", "zip", "csv
29
29
  const VALID_VIDEO_FORMATS = ["mov", "mp4", "wmv", "avi", "webm", "mkv"];
30
30
  const VALID_FILE_FORMATS = [...VALID_DOCUMENT_FORMATS, ...VALID_VIDEO_FORMATS];
31
31
 
32
+ const GRIDDO_ID = "___griddo" as const;
33
+
32
34
  export {
33
35
  itemLabel,
34
36
  type ItemLabel,
@@ -36,4 +38,5 @@ export {
36
38
  VALID_DOCUMENT_FORMATS,
37
39
  VALID_VIDEO_FORMATS,
38
40
  VALID_FILE_FORMATS,
41
+ GRIDDO_ID,
39
42
  };
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+
3
+ import { CategoryCell } from "@ax/components";
4
+ import { Cell } from "@ax/components/TableList/TableItem/style";
5
+ import type { ISchemaField } from "@ax/types";
6
+
7
+ import styled from "styled-components";
8
+
9
+ const ColumnCell = styled(Cell)`
10
+ flex: 0 0 215px;
11
+ position: relative;
12
+ align-items: center;
13
+ `;
14
+
15
+ const buildCategoryColumns = (
16
+ categoryColumns: ISchemaField[],
17
+ activeColumns: string[],
18
+ contentData: Record<string, any> | undefined,
19
+ categoryColors: any,
20
+ addCategoryColors: (cats: string[]) => void,
21
+ onClick: () => void,
22
+ ) => {
23
+ return categoryColumns.map((col) => {
24
+ if (!activeColumns.includes(col.key)) {
25
+ return <React.Fragment key={col.key} />;
26
+ }
27
+
28
+ const type: any = contentData?.[col.key];
29
+
30
+ if (typeof type !== "object") {
31
+ return (
32
+ <ColumnCell key={col.key} onClick={onClick}>
33
+ {type}
34
+ </ColumnCell>
35
+ );
36
+ }
37
+
38
+ const categories: string[] = !type
39
+ ? []
40
+ : Array.isArray(type)
41
+ ? type.map((cat: any) => cat.label || cat.title)
42
+ : [type.label || type.title];
43
+
44
+ return (
45
+ <CategoryCell
46
+ key={col.key}
47
+ categories={categories}
48
+ categoryColors={categoryColors}
49
+ addCategoryColors={addCategoryColors}
50
+ />
51
+ );
52
+ });
53
+ };
54
+
55
+ export { buildCategoryColumns, ColumnCell };
@@ -1,5 +1,7 @@
1
1
  import type { Area } from "react-easy-crop";
2
2
 
3
+ import { GRIDDO_ID } from "@ax/constants";
4
+
3
5
  import { toBlob } from "html-to-image";
4
6
 
5
7
  const formatBytes = (bytes: number | null, decimals = 2) => {
@@ -65,7 +67,7 @@ const getImageFromHtml = async (html: HTMLDivElement, fileName: string): Promise
65
67
 
66
68
  const getImageFromIFrame = async (html: HTMLDivElement, fileName: string): Promise<File | null> => {
67
69
  const frameOBject = html.querySelector<HTMLIFrameElement>(".frame-content");
68
- const frameContentFirst = frameOBject?.contentWindow?.document.getElementById("___griddo")?.firstChild as HTMLElement;
70
+ const frameContentFirst = frameOBject?.contentWindow?.document.getElementById(GRIDDO_ID)?.firstChild as HTMLElement;
69
71
  const frameContentId = frameOBject?.contentWindow?.document.getElementById("griddoFormThumb") as HTMLElement;
70
72
 
71
73
  const frameContent = frameContentId || frameContentFirst;
@@ -1,4 +1,5 @@
1
1
  import { arrayInsert, isEmptyArray, moveArrayElement } from "./arrays";
2
+ import { buildCategoryColumns } from "./categoryColumns";
2
3
  import {
3
4
  areAllComponentsEmpty,
4
5
  areEqual,
@@ -117,6 +118,7 @@ export {
117
118
  areEqualDates,
118
119
  areEquals,
119
120
  arrayInsert,
121
+ buildCategoryColumns,
120
122
  camelize,
121
123
  capitalize,
122
124
  compressImage,
@@ -1,17 +1,19 @@
1
- import React, { useState } from "react";
1
+ import type React from "react";
2
+ import { useState } from "react";
2
3
  import { connect } from "react-redux";
3
4
 
4
- import { LogActivityDTO, LogContentDTO, LogContentTypeDTO } from "@griddo/api-types";
5
- import { Avatar, Tag, RestoreModal } from "@ax/components";
6
- import { useModal } from "@ax/hooks";
7
- import { pageEditorActions } from "@ax/containers/PageEditor";
5
+ import { Avatar, RestoreModal, Tag, TruncatedTooltip } from "@ax/components";
8
6
  import { activityLogActions } from "@ax/containers/ActivityLog";
9
7
  import { navigationActions } from "@ax/containers/Navigation";
8
+ import { pageEditorActions } from "@ax/containers/PageEditor";
10
9
  import { structuredDataActions } from "@ax/containers/StructuredData";
11
- import { IRootState } from "@ax/types";
10
+ import { useModal } from "@ax/hooks";
11
+ import type { IRootState } from "@ax/types";
12
+
13
+ import type { LogActivityDTO, LogContentDTO, LogContentTypeDTO } from "@griddo/api-types";
12
14
 
13
- import DetailModal from "../../DetailModal";
14
15
  import { eventKey } from "../../constants";
16
+ import DetailModal from "../../DetailModal";
15
17
  import { getContentTypeSchemaVersion, getPageSchemaVersion } from "../../utils";
16
18
 
17
19
  import * as S from "./style";
@@ -96,19 +98,24 @@ const EventItem = (props: IEventItemProps) => {
96
98
  <S.AvatarWrapper>
97
99
  <Avatar image={user.image} name={user.name || ""} />
98
100
  </S.AvatarWrapper>
99
- <S.Name>{user.name}</S.Name>
101
+ <S.Name>
102
+ <TruncatedTooltip content={user.name || ""} expanded top={-5}>
103
+ <S.Title>{user.name}</S.Title>
104
+ </TruncatedTooltip>
105
+ </S.Name>
100
106
  </S.User>
101
107
  <S.Time>{created.hour}</S.Time>
102
108
  <S.Site>
103
109
  <Tag
104
- text={site && site.name ? site.name : "Global"}
110
+ text={site?.name ? site.name : "Global"}
105
111
  type="square"
106
112
  color={site ? undefined : "#C3F4FF"}
107
113
  textColor="rgba(32, 34, 76, 0.6)"
114
+ maxChar={30}
108
115
  />
109
116
  </S.Site>
110
117
  <S.ContentType>
111
- {!!contentType && <Tag text={contentType.content?.title || ""} color="#E6E7F8" small={true} />}
118
+ {!!contentType && <Tag text={contentType.content?.title || ""} color="#E6E7F8" small={true} maxChar={30} />}
112
119
  </S.ContentType>
113
120
  <S.Event>
114
121
  {eventType?.name || ""}{" "}
@@ -32,8 +32,25 @@ const User = styled.div`
32
32
  `;
33
33
 
34
34
  const Name = styled.div`
35
+ position: relative;
35
36
  ${(p) => p.theme.textStyle.uiS};
36
37
  color: ${(p) => p.theme.colors.textHighEmphasis};
38
+ min-width: 0;
39
+ > * {
40
+ min-width: 0;
41
+ max-width: 100%;
42
+ }
43
+ `;
44
+
45
+ const Title = styled.div`
46
+ width: 100%;
47
+ ${(p) => p.theme.textStyle.uiS};
48
+ color: ${(p) => p.theme.colors.textHighEmphasis};
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ white-space: nowrap;
52
+ display: block;
53
+ min-width: 0;
37
54
  `;
38
55
 
39
56
  const AvatarWrapper = styled.div`
@@ -78,4 +95,4 @@ const Event = styled.div`
78
95
  }
79
96
  `;
80
97
 
81
- export { Item, User, Name, AvatarWrapper, Time, Site, ContentType, Event };
98
+ export { Item, User, Name, Title, AvatarWrapper, Time, Site, ContentType, Event };