@griddo/ax 1.73.28 → 1.74.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 (98) hide show
  1. package/config/griddo-config/index.js +24 -3
  2. package/config/webpackSchemas.config.js +3 -5
  3. package/package.json +3 -2
  4. package/scripts/griddo-sync-schemas.js +4 -3
  5. package/src/__mocks__/axios/SitesList.ts +84 -0
  6. package/src/__mocks__/store/SitesList.ts +714 -0
  7. package/src/__tests__/components/Avatar/Avatar.test.tsx +119 -0
  8. package/src/__tests__/components/Avatar/__snapshots__/Avatar.test.tsx.snap +61 -0
  9. package/src/__tests__/components/Fields/ReferenceField/ReferenceField.test.tsx +10 -10
  10. package/src/__tests__/components/LanguageMenu/LanguageMenu.test.tsx +221 -0
  11. package/src/__tests__/components/Loading/Loading.test.tsx +23 -0
  12. package/src/__tests__/components/Login/Login.test.tsx +247 -0
  13. package/src/__tests__/components/Login/RecoveryModal/RecoveryModal.test.tsx +185 -0
  14. package/src/__tests__/components/TableFilters/LiveFilter/LiveFilter.test.tsx +6 -6
  15. package/src/__tests__/modules/Sites/Sites.test.tsx +259 -0
  16. package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +51 -0
  17. package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +896 -0
  18. package/src/api/sites.tsx +43 -4
  19. package/src/components/ActionMenu/index.tsx +1 -1
  20. package/src/components/Avatar/index.tsx +4 -4
  21. package/src/components/Browser/index.tsx +27 -20
  22. package/src/components/BrowserContent/index.tsx +6 -0
  23. package/src/components/Fields/AsyncSelect/style.tsx +6 -3
  24. package/src/components/Fields/CheckField/index.tsx +1 -0
  25. package/src/components/Fields/DateField/style.tsx +3 -1
  26. package/src/components/Fields/HeadingField/index.tsx +14 -5
  27. package/src/components/Fields/ImageField/index.tsx +3 -0
  28. package/src/components/Fields/ImageField/style.tsx +2 -2
  29. package/src/components/Fields/Select/style.tsx +2 -0
  30. package/src/components/Fields/SliderField/index.tsx +2 -1
  31. package/src/components/Fields/TextField/index.tsx +1 -0
  32. package/src/components/Icon/components/BulletList.js +16 -0
  33. package/src/components/Icon/components/Grid2.js +16 -0
  34. package/src/components/Icon/svgs/Bullet-list.svg +3 -0
  35. package/src/components/Icon/svgs/Grid-2.svg +3 -0
  36. package/src/components/IconAction/index.tsx +4 -2
  37. package/src/components/IconAction/style.tsx +8 -2
  38. package/src/components/LanguageMenu/index.tsx +13 -6
  39. package/src/components/Login/RecoveryModal/index.tsx +5 -4
  40. package/src/components/Login/index.tsx +13 -3
  41. package/src/components/Login/style.tsx +12 -25
  42. package/src/components/Pagination/style.tsx +1 -1
  43. package/src/components/SearchField/index.tsx +9 -1
  44. package/src/components/SideModal/style.tsx +8 -8
  45. package/src/components/TableFilters/LastAccessFilter/index.tsx +52 -0
  46. package/src/components/TableFilters/LastAccessFilter/style.tsx +31 -0
  47. package/src/components/TableFilters/LiveFilter/index.tsx +7 -5
  48. package/src/components/TableFilters/NameFilter/index.tsx +4 -3
  49. package/src/components/TableFilters/index.tsx +2 -0
  50. package/src/components/TableList/index.tsx +2 -1
  51. package/src/components/TableList/style.tsx +2 -2
  52. package/src/components/index.tsx +2 -0
  53. package/src/containers/App/actions.tsx +5 -0
  54. package/src/containers/App/interfaces.tsx +1 -1
  55. package/src/containers/App/reducer.tsx +1 -1
  56. package/src/containers/Navigation/Defaults/actions.tsx +3 -1
  57. package/src/containers/PageEditor/actions.tsx +6 -3
  58. package/src/containers/Sites/actions.tsx +76 -11
  59. package/src/containers/Sites/constants.tsx +2 -0
  60. package/src/containers/Sites/interfaces.tsx +12 -0
  61. package/src/containers/Sites/reducer.tsx +8 -0
  62. package/src/helpers/schemas.tsx +27 -1
  63. package/src/hooks/iframe.ts +56 -0
  64. package/src/hooks/index.tsx +3 -0
  65. package/src/modules/Content/index.tsx +4 -3
  66. package/src/modules/FramePreview/index.tsx +25 -39
  67. package/src/modules/GlobalEditor/Editor/index.tsx +2 -2
  68. package/src/modules/GlobalEditor/PageBrowser/index.tsx +16 -4
  69. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +11 -7
  70. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/index.tsx +4 -3
  71. package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +1 -0
  72. package/src/modules/PageEditor/Editor/index.tsx +2 -2
  73. package/src/modules/PageEditor/PageBrowser/index.tsx +16 -4
  74. package/src/modules/PageEditor/index.tsx +8 -7
  75. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/Field/index.tsx +12 -11
  76. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/index.tsx +3 -17
  77. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/style.tsx +2 -10
  78. package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/index.tsx +72 -0
  79. package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/style.tsx +32 -0
  80. package/src/modules/Sites/SitesList/{SiteItem → GridView/GridSiteItem}/index.tsx +17 -27
  81. package/src/modules/Sites/SitesList/{SiteItem → GridView/GridSiteItem}/style.tsx +14 -25
  82. package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +64 -0
  83. package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/style.tsx +50 -0
  84. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +75 -0
  85. package/src/modules/Sites/SitesList/ListView/BulkHeader/style.tsx +8 -0
  86. package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +200 -0
  87. package/src/modules/Sites/SitesList/ListView/ListSiteItem/style.tsx +112 -0
  88. package/src/modules/Sites/SitesList/RecentSiteItem/index.tsx +50 -0
  89. package/src/modules/Sites/SitesList/RecentSiteItem/style.tsx +28 -0
  90. package/src/modules/Sites/SitesList/SiteModal/index.tsx +4 -3
  91. package/src/modules/Sites/SitesList/atoms.tsx +47 -0
  92. package/src/modules/Sites/SitesList/hooks.tsx +102 -0
  93. package/src/modules/Sites/SitesList/index.tsx +272 -19
  94. package/src/modules/Sites/SitesList/style.tsx +157 -4
  95. package/src/modules/Sites/SitesList/utils.tsx +33 -0
  96. package/src/modules/Sites/index.tsx +6 -11
  97. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +1 -1
  98. package/src/types/index.tsx +25 -2
package/src/api/sites.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AxiosResponse } from "axios";
2
- import { IGetGlobalPagesParams, IGetSitePagesParams } from "@ax/types";
2
+ import { IGetGlobalPagesParams, IGetSitePagesParams, IGetSitesParams } from "@ax/types";
3
3
  import { template } from "./config";
4
4
  import { sendRequest, sendInitialRequest, IServiceConfig } from "./utils";
5
5
 
@@ -69,11 +69,21 @@ const SERVICES: { [key: string]: IServiceConfig } = {
69
69
  endpoint: ["/site/", "/publish"],
70
70
  method: "POST",
71
71
  },
72
+ PUBLISH_SITE_BULK: {
73
+ ...template,
74
+ endpoint: "/site/publish/bulk",
75
+ method: "POST",
76
+ },
72
77
  UNPUBLISH_SITE: {
73
78
  ...template,
74
79
  endpoint: ["/site/", "/unpublish"],
75
80
  method: "POST",
76
81
  },
82
+ UNPUBLISH_SITE_BULK: {
83
+ ...template,
84
+ endpoint: "/site/unpublish/bulk",
85
+ method: "POST",
86
+ },
77
87
  IMPORT_PAGE: {
78
88
  ...template,
79
89
  endpoint: ["/site/", "/pages/global/imports/"],
@@ -101,10 +111,33 @@ const SERVICES: { [key: string]: IServiceConfig } = {
101
111
  },
102
112
  };
103
113
 
104
- const getAllSites = async (language?: number) => {
114
+ const getAllSites = async (params: IGetSitesParams = { recentSitesNumber: 7 }) => {
105
115
  const { host, endpoint } = SERVICES.GET_ALL_SITES;
106
- SERVICES.GET_ALL_SITES.dynamicUrl = language ? `${host}${endpoint}?language=${language}` : "";
107
- return sendRequest(SERVICES.GET_ALL_SITES);
116
+ const { token, language, recentSitesNumber, searchQuery, filterQuery, page, itemsPerPage, pagination } = params;
117
+
118
+ const queryParams =
119
+ language && recentSitesNumber
120
+ ? `?recentSitesNumber=${recentSitesNumber}&language=${language}`
121
+ : (language && `?language=${language}`) || `?recentSitesNumber=${recentSitesNumber}`;
122
+
123
+ SERVICES.GET_ALL_SITES.dynamicUrl = `${host}${endpoint}${queryParams}`;
124
+
125
+ if (pagination) {
126
+ const paginationParams = `&pagination=true&page=${page}&itemsPerPage=${itemsPerPage}`;
127
+ SERVICES.GET_ALL_SITES.dynamicUrl = `${SERVICES.GET_ALL_SITES.dynamicUrl}${paginationParams}`;
128
+ }
129
+ if (filterQuery) {
130
+ SERVICES.GET_ALL_SITES.dynamicUrl = `${SERVICES.GET_ALL_SITES.dynamicUrl}${filterQuery}`;
131
+ }
132
+ if (searchQuery && searchQuery.trim() !== "") {
133
+ SERVICES.GET_ALL_SITES.dynamicUrl = `${SERVICES.GET_ALL_SITES.dynamicUrl}${searchQuery}`;
134
+ }
135
+
136
+ if (token) {
137
+ return sendInitialRequest(SERVICES.GET_ALL_SITES, token, { recentSitesNumber: 7 });
138
+ } else {
139
+ return sendRequest(SERVICES.GET_ALL_SITES);
140
+ }
108
141
  };
109
142
 
110
143
  const getSiteInfo = async (siteID: number) => {
@@ -280,6 +313,7 @@ const getSiteImages = async (
280
313
  host,
281
314
  endpoint: [prefix, suffix],
282
315
  } = SERVICES.GET_SITE_IMAGES;
316
+
283
317
  const items = itemsPerPage ? `&itemsPerPage=${itemsPerPage}` : "";
284
318
  const thumb = thumbWidth && thumbHeight ? `&thumbWidth=${thumbWidth}&thumbHeight=${thumbHeight}` : "";
285
319
  SERVICES.GET_SITE_IMAGES.dynamicUrl = `${host}${prefix}${site}${suffix}?page=${page}${items}${thumb}${query}`;
@@ -318,6 +352,8 @@ const publishSite = (siteID: number) => {
318
352
  return sendRequest(SERVICES.PUBLISH_SITE);
319
353
  };
320
354
 
355
+ const publishSiteBulk = (ids: number[]) => sendRequest(SERVICES.PUBLISH_SITE_BULK, { sites: ids });
356
+
321
357
  const unpublishSite = (siteID: number) => {
322
358
  const {
323
359
  host,
@@ -327,6 +363,7 @@ const unpublishSite = (siteID: number) => {
327
363
  SERVICES.UNPUBLISH_SITE.dynamicUrl = `${host}${prefix}${siteID}${suffix}`;
328
364
  return sendRequest(SERVICES.UNPUBLISH_SITE);
329
365
  };
366
+ const unpublishSiteBulk = (ids: number[]) => sendRequest(SERVICES.UNPUBLISH_SITE_BULK, { sites: ids });
330
367
 
331
368
  export default {
332
369
  getAllSites,
@@ -339,7 +376,9 @@ export default {
339
376
  createSite,
340
377
  updateSite,
341
378
  publishSite,
379
+ publishSiteBulk,
342
380
  unpublishSite,
381
+ unpublishSiteBulk,
343
382
  getGlobalPages,
344
383
  getGlobalPagesLight,
345
384
  importPage,
@@ -8,7 +8,7 @@ const ActionMenu = (props: IProps): JSX.Element => {
8
8
  const { options, icon, className, tooltip } = props;
9
9
 
10
10
  const MoreInfoButton = () => (
11
- <S.IconActionWrapper>
11
+ <S.IconActionWrapper data-testid="more-info-button">
12
12
  <IconAction icon={icon} />
13
13
  </S.IconActionWrapper>
14
14
  );
@@ -2,17 +2,17 @@ import React from "react";
2
2
  import { getInitials } from "@ax/helpers";
3
3
  import * as S from "./style";
4
4
 
5
- const Avatar = (props: IProps) => {
5
+ const Avatar = (props: IAvatarProps): JSX.Element => {
6
6
  const { image, name } = props;
7
7
 
8
8
  return (
9
- <S.AvatarWrapper>
10
- <S.Avatar image={image} initials={getInitials(name)} />
9
+ <S.AvatarWrapper data-testid="avatar-wrapper">
10
+ <S.Avatar image={image} initials={getInitials(name)} data-testid="avatar" />
11
11
  </S.AvatarWrapper>
12
12
  );
13
13
  };
14
14
 
15
- interface IProps {
15
+ export interface IAvatarProps {
16
16
  image?: string | null;
17
17
  name?: string;
18
18
  }
@@ -1,8 +1,9 @@
1
- import React, { useEffect, useState, useCallback } from "react";
1
+ import React, { useEffect, useState } from "react";
2
2
 
3
3
  import { findByEditorID } from "@ax/forms";
4
4
  import { copyTextToClipboard } from "@ax/helpers";
5
- import { useToast } from "@ax/hooks";
5
+ import { useOnMessageReceivedFromIframe, useToast } from "@ax/hooks";
6
+
6
7
  import Icon from "../Icon";
7
8
  import Tooltip from "../Tooltip";
8
9
  import Toast from "../Toast";
@@ -24,9 +25,9 @@ const Browser = (props: IBrowserProps): JSX.Element => {
24
25
  disabled,
25
26
  siteID,
26
27
  isPreview,
27
- setSelectedContent,
28
28
  showIframe,
29
29
  browserRef,
30
+ actions,
30
31
  } = props;
31
32
 
32
33
  const { id, entity } = content;
@@ -36,21 +37,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
36
37
  const [resolution, setResolution] = useState("desktop");
37
38
  const { isVisible, toggleToast, setIsVisible } = useToast();
38
39
 
39
- const onMessageReceivedFromIframe = useCallback(
40
- (ev: MessageEvent<{ type: string; message: string }>) => {
41
- if (typeof ev.data !== "object") return;
42
- if (!ev.data.type) return;
43
- if (ev.data.type !== "module-click") return;
44
- if (!ev.data.message) return;
45
- setSelectedContent(ev.data.message);
46
- },
47
- [setSelectedContent]
48
- );
49
-
50
- useEffect(() => {
51
- window.addEventListener("message", onMessageReceivedFromIframe);
52
- return () => window.removeEventListener("message", onMessageReceivedFromIframe);
53
- }, [onMessageReceivedFromIframe]);
40
+ useOnMessageReceivedFromIframe(actions);
54
41
 
55
42
  useEffect(() => {
56
43
  localStorage.setItem("selectedID", "0");
@@ -70,7 +57,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
70
57
  const isNavigationModule = ["header", "footer"].includes(selectedComponent.type);
71
58
 
72
59
  if (!isPreview && (!disabled || isNavigationModule)) {
73
- setSelectedContent && setSelectedContent(editorID);
60
+ actions?.setSelectedContentAction(editorID);
74
61
  }
75
62
  };
76
63
 
@@ -102,6 +89,21 @@ const Browser = (props: IBrowserProps): JSX.Element => {
102
89
  message: "URL Copied",
103
90
  };
104
91
 
92
+ const deleteModuleSelected = (editorID: number) => {
93
+ actions?.setSelectedContentAction(0);
94
+ actions?.deleteModuleAction(editorID);
95
+ };
96
+
97
+ const duplicateModuleSelected = async (editorID: number) => {
98
+ const duplicatedEditorID = await actions?.duplicateModuleAction(editorID);
99
+ actions?.setSelectedContentAction(duplicatedEditorID);
100
+ };
101
+
102
+ const moduleActions = {
103
+ deleteModuleAction: deleteModuleSelected,
104
+ duplicateModuleAction: duplicateModuleSelected,
105
+ };
106
+
105
107
  return (
106
108
  <S.BrowserWrapper ref={browserRef}>
107
109
  <S.NavBar>
@@ -150,6 +152,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
150
152
  footer={footer}
151
153
  languageID={content.language}
152
154
  pageLanguages={content.pageLanguages}
155
+ moduleActions={moduleActions}
153
156
  renderer="editor"
154
157
  />
155
158
  </S.Wrapper>
@@ -164,7 +167,6 @@ interface IBrowserProps {
164
167
  header?: any;
165
168
  footer?: any;
166
169
  url: string;
167
- setSelectedContent?: any;
168
170
  isPage: boolean;
169
171
  theme: string;
170
172
  socials: any;
@@ -175,6 +177,11 @@ interface IBrowserProps {
175
177
  isPreview?: boolean;
176
178
  showIframe?: boolean;
177
179
  browserRef?: any;
180
+ actions?: {
181
+ setSelectedContentAction: any;
182
+ deleteModuleAction(editorID: number): void;
183
+ duplicateModuleAction(editorID: number): Promise<number>;
184
+ };
178
185
  }
179
186
 
180
187
  export default Browser;
@@ -18,6 +18,7 @@ const BrowserContent = (props: IProps) => {
18
18
  languageID,
19
19
  pageLanguages,
20
20
  selectEditorID,
21
+ moduleActions,
21
22
  renderer,
22
23
  } = props;
23
24
 
@@ -44,6 +45,7 @@ const BrowserContent = (props: IProps) => {
44
45
  publicApiUrl={PUBLIC_API_URL}
45
46
  instance={INSTANCE}
46
47
  siteId={siteID}
48
+ moduleActions={moduleActions}
47
49
  >
48
50
  <Preview
49
51
  isPage={isPage}
@@ -77,6 +79,10 @@ interface IProps {
77
79
  parentComponent: string | undefined | null,
78
80
  e: React.SyntheticEvent
79
81
  ): void;
82
+ moduleActions?: {
83
+ deleteModuleAction(editorID: number): void;
84
+ duplicateModuleAction(editorID: number): void;
85
+ };
80
86
  }
81
87
 
82
88
  export default BrowserContent;
@@ -15,6 +15,8 @@ export const StyledSelect = styled(AsyncSelect)<{ isDisabled: boolean | undefine
15
15
  height: ${(p) => p.theme.spacing.l};
16
16
  min-width: calc(${(p) => p.theme.spacing.xl} * 4);
17
17
  max-width: calc(${(p) => p.theme.spacing.xl} * 6);
18
+ cursor: pointer;
19
+
18
20
  :hover {
19
21
  border-color: ${(p) => (p.error ? p.theme.color.error : p.theme.color.uiLine)};
20
22
  }
@@ -59,13 +61,14 @@ export const StyledSelect = styled(AsyncSelect)<{ isDisabled: boolean | undefine
59
61
  min-height: ${(p) => p.theme.spacing.l};
60
62
  padding: ${(p) => p.theme.spacing.s};
61
63
  &:first-child {
62
- ${(p) => !p.mandatory && p.hasEmptyOption &&
64
+ ${(p) =>
65
+ !p.mandatory &&
66
+ p.hasEmptyOption &&
63
67
  css`
64
68
  font-style: italic;
65
69
  color: ${(p) => p.theme.color.textLowEmphasis};
66
70
  background-color: ${(p) => p.theme.color.uiLineInverse};
67
- `
68
- }
71
+ `}
69
72
  }
70
73
  :hover {
71
74
  cursor: pointer;
@@ -43,6 +43,7 @@ const CheckField = ({
43
43
  checked={checked}
44
44
  disabled={disabled}
45
45
  onChange={handleChange}
46
+ aria-label={`${name}`}
46
47
  />
47
48
  <S.CheckMark
48
49
  data-testid={"checkFieldCheckMark"}
@@ -1,7 +1,9 @@
1
1
  import styled from "styled-components";
2
2
 
3
3
  export const DatePickerWrapper = styled.div`
4
- .react-datepicker-popper[data-placement^="bottom"] {
4
+ .react-datepicker-popper,
5
+ .react-datepicker-popper[data-placement^="bottom"],
6
+ .react-datepicker-popper[data-placement^="bottom-start"] {
5
7
  margin-top: 0
6
8
  z-index: 2;
7
9
  }
@@ -7,17 +7,25 @@ import * as S from "./style";
7
7
  const HeadingField = (props: IHeadingFieldProps): JSX.Element => {
8
8
  const { value, onChange, options, showAdvanced } = props;
9
9
 
10
- const handleChange = (newValue: string) => onChange({ ...value, content: newValue });
11
- const handleSelectChange = (newValue: string) => onChange({ ...value, tag: newValue });
10
+ const getContentValue = () => value?.content || props.default?.content || "";
11
+ const getTagValue = () => value?.tag || props.default?.tag || "";
12
12
 
13
- const contentValue = value && value.content ? value.content : "";
13
+ const handleChange = (newValue: string) => {
14
+ const tag = getTagValue();
15
+ onChange({ content: newValue, tag });
16
+ };
17
+
18
+ const handleSelectChange = (newValue: string) => {
19
+ const content = getContentValue();
20
+ onChange({ content, tag: newValue });
21
+ };
14
22
 
15
23
  return (
16
24
  <>
17
- <TextField {...props} value={contentValue} onChange={handleChange} />
25
+ <TextField {...props} value={getContentValue()} onChange={handleChange} />
18
26
  {showAdvanced && (
19
27
  <S.AdvancedWrapper data-testid="text-field-advanced-wrapper">
20
- <FieldsBehavior fieldType="Select" options={options} value={value.tag} onChange={handleSelectChange} />
28
+ <FieldsBehavior fieldType="Select" options={options} value={getTagValue()} onChange={handleSelectChange} />
21
29
  </S.AdvancedWrapper>
22
30
  )}
23
31
  </>
@@ -30,6 +38,7 @@ interface IHeadingFieldProps {
30
38
  onChange: (value: IHeadingField) => void;
31
39
  options: IOption[];
32
40
  showAdvanced: boolean;
41
+ default: IHeadingField;
33
42
  }
34
43
 
35
44
  interface IOption {
@@ -19,6 +19,7 @@ const ImageField = (props: IImageFieldProps) => {
19
19
  site,
20
20
  setIsGalleryOpened,
21
21
  cropPreview = false,
22
+ fullWidth = false,
22
23
  } = props;
23
24
 
24
25
  const isLinkableImage = selectedContent && selectedContent.component === "LinkableImage";
@@ -73,6 +74,7 @@ const ImageField = (props: IImageFieldProps) => {
73
74
  preview={!!previewSrc}
74
75
  disabled={disabled}
75
76
  onClick={handleClick}
77
+ fullWidth={fullWidth}
76
78
  >
77
79
  <S.IconWrapper>
78
80
  <Icon name="image" size="18" />
@@ -114,6 +116,7 @@ export interface IImageFieldProps {
114
116
  validators?: Record<string, unknown>;
115
117
  site: ISite;
116
118
  cropPreview?: boolean;
119
+ fullWidth?: boolean;
117
120
  }
118
121
 
119
122
  export default memo(ImageField);
@@ -1,14 +1,14 @@
1
1
  import styled from "styled-components";
2
2
  import { Wrapper } from "@ax/components/FieldsBehavior/style";
3
3
 
4
- const FieldWrapper = styled.div<{ error?: boolean; preview: boolean; disabled?: boolean }>`
4
+ const FieldWrapper = styled.div<{ error?: boolean; preview: boolean; disabled?: boolean; fullWidth?: boolean }>`
5
5
  position: relative;
6
6
  display: ${(p) => (p.preview ? `none` : `flex`)};
7
7
  justify-content: center;
8
8
  align-items: center;
9
9
  flex-flow: column nowrap;
10
10
  min-height: ${(p) => `calc(${p.theme.spacing.l} * 2)`};
11
- max-width: ${(p) => `calc(${p.theme.spacing.xl} * 5)`};
11
+ max-width: ${(p) => (!p.fullWidth ? `calc(${p.theme.spacing.xl} * 5) ` : '100%')};
12
12
  width: 100%;
13
13
  background-color: ${(p) => p.theme.colors.uiBackground02};
14
14
  border: ${(p) =>
@@ -18,6 +18,8 @@ export const StyledSelect = styled(Select)<{
18
18
  box-shadow: none;
19
19
  height: ${(p) => p.theme.spacing?.l};
20
20
  max-width: calc(${(p) => p.theme.spacing?.xl} * 6);
21
+ cursor: pointer;
22
+
21
23
  :hover {
22
24
  border-color: ${(p) => (p.error ? p.theme.color?.error : p.theme.color?.uiLine)};
23
25
  }
@@ -17,7 +17,8 @@ const SliderField = (props: ITextFieldProps): JSX.Element => {
17
17
  if (bubbleRef.current) {
18
18
  bubbleRef.current.style.left = setBubbleLeft();
19
19
  }
20
- }, [bubbleRef]);
20
+ // eslint-disable-next-line react-hooks/exhaustive-deps
21
+ }, [bubbleRef, value]);
21
22
 
22
23
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
23
24
  const newValue = e.target.value;
@@ -96,6 +96,7 @@ const TextField = (props: ITextFieldProps): JSX.Element => {
96
96
  icon={hasBackgroundIcon}
97
97
  hasPrefix={!!prefix}
98
98
  prefixWidth={width}
99
+ aria-label={name}
99
100
  />
100
101
  {hasBackgroundIcon && (
101
102
  <S.BackgroundIcon data-testid="backgroundIconComponent" onClick={onClickIcon}>
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+
3
+ function SvgBulletList(props) {
4
+ return (
5
+ <svg width={24} height={24} fill="none" {...props}>
6
+ <path
7
+ fillRule="evenodd"
8
+ clipRule="evenodd"
9
+ d="M2.5 6C2.5 5.17 3.17 4.5 4 4.5C4.83 4.5 5.5 5.17 5.5 6C5.5 6.83 4.83 7.5 4 7.5C3.17 7.5 2.5 6.83 2.5 6ZM2.5 12C2.5 11.17 3.17 10.5 4 10.5C4.83 10.5 5.5 11.17 5.5 12C5.5 12.83 4.83 13.5 4 13.5C3.17 13.5 2.5 12.83 2.5 12ZM4 16.5C3.17 16.5 2.5 17.18 2.5 18C2.5 18.82 3.18 19.5 4 19.5C4.82 19.5 5.5 18.82 5.5 18C5.5 17.18 4.83 16.5 4 16.5ZM8 19C7.44772 19 7 18.5523 7 18C7 17.4477 7.44772 17 8 17H20C20.5523 17 21 17.4477 21 18C21 18.5523 20.5523 19 20 19H8ZM7 12C7 12.5523 7.44772 13 8 13H20C20.5523 13 21 12.5523 21 12C21 11.4477 20.5523 11 20 11H8C7.44772 11 7 11.4477 7 12ZM7 6C7 5.44772 7.44772 5 8 5H20C20.5523 5 21 5.44772 21 6C21 6.55228 20.5523 7 20 7H8C7.44772 7 7 6.55228 7 6Z"
10
+ fill="#5057FF"
11
+ />
12
+ </svg>
13
+ );
14
+ }
15
+
16
+ export default SvgBulletList;
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+
3
+ function SvgGrid2(props) {
4
+ return (
5
+ <svg width={24} height={24} fill="none" {...props}>
6
+ <path
7
+ fillRule="evenodd"
8
+ clipRule="evenodd"
9
+ d="M5 9H9V5H5V9ZM5 11C3.89543 11 3 10.1046 3 9V5C3 3.89543 3.89543 3 5 3H9C10.1046 3 11 3.89543 11 5V9C11 10.1046 10.1046 11 9 11H5ZM5 19H9V15H5V19ZM5 21C3.89543 21 3 20.1046 3 19V15C3 13.8954 3.89543 13 5 13H9C10.1046 13 11 13.8954 11 15V19C11 20.1046 10.1046 21 9 21H5ZM15 19H19V15H15V19ZM15 21C13.8954 21 13 20.1046 13 19V15C13 13.8954 13.8954 13 15 13H19C20.1046 13 21 13.8954 21 15V19C21 20.1046 20.1046 21 19 21H15ZM15 9H19V5H15V9ZM15 11C13.8954 11 13 10.1046 13 9V5C13 3.89543 13.8954 3 15 3H19C20.1046 3 21 3.89543 21 5V9C21 10.1046 20.1046 11 19 11H15Z"
10
+ fill="#5057FF"
11
+ />
12
+ </svg>
13
+ );
14
+ }
15
+
16
+ export default SvgGrid2;
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 6C2.5 5.17 3.17 4.5 4 4.5C4.83 4.5 5.5 5.17 5.5 6C5.5 6.83 4.83 7.5 4 7.5C3.17 7.5 2.5 6.83 2.5 6ZM2.5 12C2.5 11.17 3.17 10.5 4 10.5C4.83 10.5 5.5 11.17 5.5 12C5.5 12.83 4.83 13.5 4 13.5C3.17 13.5 2.5 12.83 2.5 12ZM4 16.5C3.17 16.5 2.5 17.18 2.5 18C2.5 18.82 3.18 19.5 4 19.5C4.82 19.5 5.5 18.82 5.5 18C5.5 17.18 4.83 16.5 4 16.5ZM8 19C7.44772 19 7 18.5523 7 18C7 17.4477 7.44772 17 8 17H20C20.5523 17 21 17.4477 21 18C21 18.5523 20.5523 19 20 19H8ZM7 12C7 12.5523 7.44772 13 8 13H20C20.5523 13 21 12.5523 21 12C21 11.4477 20.5523 11 20 11H8C7.44772 11 7 11.4477 7 12ZM7 6C7 5.44772 7.44772 5 8 5H20C20.5523 5 21 5.44772 21 6C21 6.55228 20.5523 7 20 7H8C7.44772 7 7 6.55228 7 6Z" fill="#5057FF"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M5 9H9V5H5V9ZM5 11C3.89543 11 3 10.1046 3 9V5C3 3.89543 3.89543 3 5 3H9C10.1046 3 11 3.89543 11 5V9C11 10.1046 10.1046 11 9 11H5ZM5 19H9V15H5V19ZM5 21C3.89543 21 3 20.1046 3 19V15C3 13.8954 3.89543 13 5 13H9C10.1046 13 11 13.8954 11 15V19C11 20.1046 10.1046 21 9 21H5ZM15 19H19V15H15V19ZM15 21C13.8954 21 13 20.1046 13 19V15C13 13.8954 13.8954 13 15 13H19C20.1046 13 21 13.8954 21 15V19C21 20.1046 20.1046 21 19 21H15ZM15 9H19V5H15V9ZM15 11C13.8954 11 13 10.1046 13 9V5C13 3.89543 13.8954 3 15 3H19C20.1046 3 21 3.89543 21 5V9C21 10.1046 20.1046 11 19 11H15Z" fill="#5057FF"/>
3
+ </svg>
@@ -4,7 +4,7 @@ import { Icon } from "@ax/components";
4
4
  import * as S from "./style";
5
5
 
6
6
  const IconAction = (props: IIconActionProps): JSX.Element => {
7
- const { icon, onClick, disabled, inversed, size = "m" } = props;
7
+ const { icon, onClick, disabled, inversed, size = "m", active } = props;
8
8
  const handleOnClick = (e: React.MouseEvent<HTMLButtonElement>) => {
9
9
  if (onClick !== undefined) {
10
10
  e.preventDefault();
@@ -19,8 +19,9 @@ const IconAction = (props: IIconActionProps): JSX.Element => {
19
19
  onClick={handleOnClick}
20
20
  size={size}
21
21
  inversed={inversed}
22
+ active={active}
22
23
  >
23
- <S.Icon>
24
+ <S.Icon data-testid={`icon-action-${icon}`}>
24
25
  <Icon name={icon} />
25
26
  </S.Icon>
26
27
  </S.Button>
@@ -33,6 +34,7 @@ export interface IIconActionProps {
33
34
  size?: "s" | "m";
34
35
  onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
35
36
  inversed?: boolean;
37
+ active?: boolean;
36
38
  }
37
39
 
38
40
  export default IconAction;
@@ -1,7 +1,12 @@
1
1
  import styled from "styled-components";
2
2
 
3
- export const Button = styled.button<{ size: string | undefined; inversed?: boolean; disabled?: boolean }>`
4
- background: transparent;
3
+ export const Button = styled.button<{
4
+ size: string | undefined;
5
+ inversed?: boolean;
6
+ disabled?: boolean;
7
+ active?: boolean;
8
+ }>`
9
+ background: ${(p) => (p.active ? p.theme.color?.overlayHoverPrimary : "transparent")};
5
10
  border: none;
6
11
  border-radius: 50%;
7
12
  color: inherit;
@@ -22,6 +27,7 @@ export const Button = styled.button<{ size: string | undefined; inversed?: boole
22
27
  opacity: 0;
23
28
  transition: opacity 0.1s;
24
29
  }
30
+
25
31
  :hover:before {
26
32
  background-color: ${(p) => p.theme.color?.overlayHoverPrimary};
27
33
  opacity: 1;
@@ -5,7 +5,7 @@ import { Flag, Icon } from "@ax/components";
5
5
  import * as S from "./style";
6
6
  import { ILanguage } from "@ax/types";
7
7
 
8
- const LanguageMenu = (props: IProps): JSX.Element => {
8
+ const LanguageMenu = (props: ILanguageMenuProps): JSX.Element => {
9
9
  const { availableLanguages, language, setLanguage, isInAppBar, currentLanguages } = props;
10
10
 
11
11
  const getCurrentLanguage = (lang: string) =>
@@ -30,18 +30,23 @@ const LanguageMenu = (props: IProps): JSX.Element => {
30
30
  }
31
31
 
32
32
  return (
33
- <S.LanguageIcon>
33
+ <S.LanguageIcon data-testid="language-icon">
34
34
  <Icon name={iconName} />
35
35
  </S.LanguageIcon>
36
36
  );
37
37
  };
38
38
 
39
39
  const languageMenuItem = (item: ILanguage) => (
40
- <S.LanguageItem key={item.locale} selected={item.locale === language} onClick={setLanguage(item)} data-testid="language-menu-item">
40
+ <S.LanguageItem
41
+ key={item.locale}
42
+ selected={item.locale === language}
43
+ onClick={setLanguage(item)}
44
+ data-testid="language-menu-item"
45
+ >
41
46
  <S.LanguageItemWrapper>
42
47
  <S.LanguageLabel>
43
48
  <Flag name={item.locale} />
44
- <S.LanguageText>
49
+ <S.LanguageText data-testid="language-text">
45
50
  <S.Locale>{item.label}</S.Locale> - {item.language}
46
51
  </S.LanguageText>
47
52
  </S.LanguageLabel>
@@ -54,11 +59,13 @@ const LanguageMenu = (props: IProps): JSX.Element => {
54
59
  );
55
60
 
56
61
  return (
57
- <S.ActionMenu data-testid="language-menu">{availableLanguages && availableLanguages.map((item: any) => languageMenuItem(item))}</S.ActionMenu>
62
+ <S.ActionMenu data-testid="language-menu">
63
+ {availableLanguages && availableLanguages.map((item: any) => languageMenuItem(item))}
64
+ </S.ActionMenu>
58
65
  );
59
66
  };
60
67
 
61
- interface IProps {
68
+ export interface ILanguageMenuProps {
62
69
  language?: string | null;
63
70
  availableLanguages?: any[] | null;
64
71
  currentLanguages?: any[];
@@ -45,7 +45,7 @@ const RecoveryModal = (props: IRecoveryProps): JSX.Element => {
45
45
 
46
46
  const ModalContent = !modalState.isMailSent ? (
47
47
  <>
48
- <p>Forgot your password? No problem, we'll send you an email with instructions to reset it.</p>
48
+ <p>Forgot your password? No problem, we&apos;ll send you an email with instructions to reset it.</p>
49
49
  <FieldsBehavior
50
50
  fieldType="TextField"
51
51
  placeholder="Type your email"
@@ -55,6 +55,7 @@ const RecoveryModal = (props: IRecoveryProps): JSX.Element => {
55
55
  onChange={handleModalChange}
56
56
  error={modalState.error}
57
57
  helptext={errorText}
58
+ name="email"
58
59
  />
59
60
  </>
60
61
  ) : (
@@ -62,7 +63,7 @@ const RecoveryModal = (props: IRecoveryProps): JSX.Element => {
62
63
  <p>
63
64
  We sent you an email with further instructions to reset your password to <strong>{modalState.input}</strong>.
64
65
  </p>
65
- <p>If you don't receive it, please check your spam folder or contact support.</p>
66
+ <p>If you don&apos;t receive it, please check your spam folder or contact support.</p>
66
67
  </>
67
68
  );
68
69
 
@@ -75,12 +76,12 @@ const RecoveryModal = (props: IRecoveryProps): JSX.Element => {
75
76
  mainAction={mainAction}
76
77
  secondaryAction={secondaryAction}
77
78
  >
78
- {isOpen ? <S.ModalContent>{ModalContent}</S.ModalContent> : null}
79
+ {isOpen ? <S.ModalContent data-testid="recovery-modal-content">{ModalContent}</S.ModalContent> : null}
79
80
  </Modal>
80
81
  );
81
82
  };
82
83
 
83
- interface IRecoveryProps {
84
+ export interface IRecoveryProps {
84
85
  isOpen: boolean;
85
86
  toggleModal(): void;
86
87
  }
@@ -42,7 +42,7 @@ const Login = (props: ILoginProps): JSX.Element => {
42
42
  const icon = viewPass ? "hide" : "view";
43
43
 
44
44
  return (
45
- <S.Wrapper>
45
+ <S.Wrapper data-testid="login-wrapper">
46
46
  <S.Main>
47
47
  <S.Header>
48
48
  <S.Subtitle>{textWelcome}</S.Subtitle>
@@ -56,6 +56,7 @@ const Login = (props: ILoginProps): JSX.Element => {
56
56
  autoComplete="email"
57
57
  value={email}
58
58
  onChange={_handleEmail}
59
+ name="email"
59
60
  />
60
61
  <FieldsBehavior
61
62
  fieldType="TextField"
@@ -67,6 +68,7 @@ const Login = (props: ILoginProps): JSX.Element => {
67
68
  icon={icon}
68
69
  onClickIcon={_togglePassword}
69
70
  iconPosition="in"
71
+ name="password"
70
72
  />
71
73
  <FieldsBehavior
72
74
  fieldType="UniqueCheck"
@@ -74,12 +76,20 @@ const Login = (props: ILoginProps): JSX.Element => {
74
76
  value={rememberMe}
75
77
  onChange={handleRememberMe}
76
78
  options={[{ title: "Remember me" }]}
79
+ name="rememberMe"
77
80
  />
78
81
  <Button type="submit" disabled={isLoggingIn ? true : false}>
79
82
  {btnText}
80
83
  </Button>
81
84
  <S.Password>
82
- <span onClick={toggleModal} onKeyPress={toggleModal} role="checkbox" aria-checked="false" tabIndex={0}>
85
+ <span
86
+ onClick={toggleModal}
87
+ onKeyPress={toggleModal}
88
+ role="checkbox"
89
+ aria-checked="false"
90
+ tabIndex={0}
91
+ data-testid="forgot-button"
92
+ >
83
93
  Forgot your password?
84
94
  </span>
85
95
  </S.Password>
@@ -90,7 +100,7 @@ const Login = (props: ILoginProps): JSX.Element => {
90
100
  );
91
101
  };
92
102
 
93
- interface ILoginProps {
103
+ export interface ILoginProps {
94
104
  handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
95
105
  email: string;
96
106
  handleEmail: (email: string) => void;