@griddo/ax 11.13.2 → 11.13.3-rc.1

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 (59) hide show
  1. package/config/jest/setup.js +10 -0
  2. package/package.json +2 -2
  3. package/src/GlobalStore.tsx +1 -1
  4. package/src/__tests__/components/Fields/AsyncCheckGroup/AsyncCheckGroup.test.tsx +276 -66
  5. package/src/__tests__/components/FloatingMenu/FloatingMenu.test.tsx +300 -99
  6. package/src/__tests__/modules/Settings/Social/Social.test.tsx +12 -4
  7. package/src/api/checkgroups.tsx +4 -3
  8. package/src/api/selects.tsx +12 -5
  9. package/src/components/ActionMenu/index.tsx +1 -3
  10. package/src/components/Browser/index.tsx +12 -3
  11. package/src/components/Browser/style.tsx +7 -0
  12. package/src/components/ConfigPanel/Form/index.tsx +47 -53
  13. package/src/components/Fields/AnalyticsField/PageAnalytics/atoms.tsx +9 -13
  14. package/src/components/Fields/AnalyticsField/PageAnalytics/index.tsx +37 -29
  15. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/atoms.tsx +9 -13
  16. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/index.tsx +17 -11
  17. package/src/components/Fields/AnalyticsField/index.tsx +1 -2
  18. package/src/components/Fields/AnalyticsField/utils.tsx +4 -4
  19. package/src/components/Fields/AsyncCheckGroup/index.tsx +97 -79
  20. package/src/components/Fields/AsyncSelect/index.tsx +33 -22
  21. package/src/components/Fields/DateField/DatePickerInput/index.tsx +2 -2
  22. package/src/components/Fields/DateField/index.tsx +3 -3
  23. package/src/components/Fields/IntegrationsField/SideModal/index.tsx +2 -2
  24. package/src/components/Fields/IntegrationsField/index.tsx +14 -10
  25. package/src/components/Fields/MultiCheckSelect/index.tsx +6 -6
  26. package/src/components/Fields/MultiCheckSelectGroup/index.tsx +39 -37
  27. package/src/components/Fields/MultiCheckSelectGroup/style.tsx +1 -1
  28. package/src/components/Fields/ReferenceField/ManualPanel/index.tsx +0 -2
  29. package/src/components/Fields/RichText/index.tsx +15 -7
  30. package/src/components/Fields/TextArea/index.tsx +9 -6
  31. package/src/components/FloatingMenu/index.tsx +32 -31
  32. package/src/components/FloatingMenu/style.tsx +23 -5
  33. package/src/components/Loader/components/SmallCircle.js +3 -3
  34. package/src/components/MainWrapper/AppBar/style.tsx +1 -0
  35. package/src/components/SideModal/index.tsx +1 -1
  36. package/src/components/TableFilters/CategoryFilter/index.tsx +14 -15
  37. package/src/containers/App/actions.tsx +7 -1
  38. package/src/containers/App/constants.tsx +2 -0
  39. package/src/containers/App/interfaces.tsx +5 -0
  40. package/src/containers/App/reducer.tsx +11 -2
  41. package/src/containers/Forms/actions.tsx +5 -7
  42. package/src/containers/Integrations/actions.tsx +1 -3
  43. package/src/containers/Navigation/Menu/actions.tsx +2 -2
  44. package/src/containers/PageEditor/actions.tsx +3 -2
  45. package/src/containers/Settings/DataPacks/actions.tsx +35 -29
  46. package/src/containers/Sites/actions.tsx +40 -33
  47. package/src/containers/StructuredData/actions.tsx +3 -9
  48. package/src/modules/ActivityLog/LogFilters/DateFilter/index.tsx +5 -4
  49. package/src/modules/Content/NewContentModal/PageImporter/index.tsx +1 -2
  50. package/src/modules/Content/index.tsx +8 -3
  51. package/src/modules/Content/style.tsx +7 -0
  52. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +58 -45
  53. package/src/modules/Navigation/Defaults/index.tsx +103 -104
  54. package/src/modules/PageEditor/index.tsx +9 -1
  55. package/src/modules/PublicPreview/index.tsx +2 -1
  56. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +60 -44
  57. package/src/modules/Settings/ContentTypes/DataPacks/Config/index.tsx +32 -37
  58. package/src/modules/Sites/index.tsx +3 -3
  59. package/src/modules/Users/UserList/index.tsx +1 -1
@@ -1,8 +1,8 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
 
3
3
  import { checkgroups } from "@ax/api";
4
4
  import { isReqOk } from "@ax/helpers";
5
- import { ISite, ICheck, ILanguage } from "@ax/types";
5
+ import type { ICheck, ILanguage, ISite } from "@ax/types";
6
6
 
7
7
  import * as S from "./style";
8
8
 
@@ -23,30 +23,39 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
23
23
  allOption = false,
24
24
  } = props;
25
25
 
26
- const safeValue = value && Array.isArray(value) ? value : [];
26
+ const safeValue = useMemo(() => (value && Array.isArray(value) ? value : []), [value]);
27
27
  const [options, setOptions] = useState<ICheckValue[]>([]);
28
28
  const [allSelected, setAllSelected] = useState<boolean>(false);
29
29
 
30
- const languagesIds = Array.isArray(contentLanguages)
31
- ? languages?.filter((lang) => contentLanguages.includes(lang.locale)).map((lang) => lang.id)
32
- : contentLanguages === "all"
33
- ? languages?.map((lang) => lang.id)
34
- : [];
30
+ const onChangeRef = useRef(onChange);
31
+ useEffect(() => {
32
+ onChangeRef.current = onChange;
33
+ }, [onChange]);
34
+
35
+ const languagesIds = useMemo(
36
+ () =>
37
+ Array.isArray(contentLanguages)
38
+ ? languages?.filter((lang) => contentLanguages.includes(lang.locale)).map((lang) => lang.id)
39
+ : contentLanguages === "all"
40
+ ? languages?.map((lang) => lang.id)
41
+ : [],
42
+ [contentLanguages, languages],
43
+ );
35
44
 
36
- const isCheckValue = (object: any): object is ICheckValue => "value" in object;
45
+ const isCheckValue = useCallback((object: any): object is ICheckValue => "value" in object, []);
37
46
 
38
- const getAllIds = (results: ICheckValue[]) => {
47
+ const getAllIds = useCallback((results: ICheckValue[]): number[] => {
39
48
  return results.reduce((acc: number[], current) => {
40
49
  acc.push(current.value);
41
50
  const children = getAllIds(current.children || []);
42
51
  if (children.length) {
43
- acc = [...acc, ...children];
52
+ acc.push(...children);
44
53
  }
45
54
  return acc;
46
55
  }, []);
47
- };
56
+ }, []);
48
57
 
49
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
58
+ // biome-ignore lint/correctness/useExhaustiveDependencies: safeValue, getAllIds, isCheckValue captured in closure; only refetch when API params change
50
59
  useEffect((): any => {
51
60
  let isSubscribed = true;
52
61
 
@@ -71,7 +80,7 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
71
80
  .then((result) => {
72
81
  if (isSubscribed) {
73
82
  setOptions(result);
74
- if (!!safeValue.length && safeValue.length === result.length) {
83
+ if (safeValue.length > 0 && safeValue.length === result.length) {
75
84
  setAllSelected(true);
76
85
  }
77
86
  if (safeValue.length > 0 && isCheckValue(safeValue[0])) {
@@ -79,104 +88,113 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
79
88
  const resultIDs = result && getAllIds(result);
80
89
  const fixedState = safeValue.reduce((acc: ICheckValue[], current: ICheckValue) => {
81
90
  if (resultIDs.includes(current.value)) {
82
- return [...acc, current];
83
- } else {
84
- const trad = result.find(
85
- (value: ICheckValue) =>
86
- value.dataLanguages && value.dataLanguages.find((data: any) => data.id === current.value),
87
- );
88
- if (trad) {
89
- return [...acc, trad];
90
- }
91
- return acc;
91
+ return acc.concat(current);
92
+ }
93
+ const trad = result.find((value: ICheckValue) =>
94
+ value.dataLanguages?.find((data: any) => data.id === current.value),
95
+ );
96
+ if (trad) {
97
+ return acc.concat(trad);
92
98
  }
99
+ return acc;
93
100
  }, []);
94
- onChange(fixedState);
101
+ onChangeRef.current(fixedState);
95
102
  }
96
103
  }
97
104
  })
98
105
  .catch((apiError) => console.log(apiError));
99
106
 
100
- return () => (isSubscribed = false);
101
- }, [site, source, allLanguages]);
107
+ return () => {
108
+ isSubscribed = false;
109
+ };
110
+ }, [site, source, allLanguages, contentType, languagesIds]);
102
111
 
103
- const filterOptions = (options: ICheckValue[], ids: number[]) => {
112
+ const filterOptions = useCallback((options: ICheckValue[], ids: number[]) => {
104
113
  return options.reduce((acc: ICheckValue[], current) => {
105
114
  if (ids.includes(current.value)) {
106
115
  acc.push(current);
107
116
  }
108
117
  const children = filterOptions(current.children || [], ids);
109
118
  if (children.length) {
110
- acc = [...acc, ...children];
119
+ acc.push(...children);
111
120
  }
112
121
  return acc;
113
122
  }, []);
114
- };
123
+ }, []);
115
124
 
116
- const handleChange = (newValue: ICheck) => {
117
- const arrayIds = safeValue.map((e: any) => (typeof e === "number" ? e : e.value));
125
+ const handleChange = useCallback(
126
+ (newValue: ICheck) => {
127
+ const arrayIds = safeValue.map((e: any) => (typeof e === "number" ? e : e.value));
118
128
 
119
- let newArray: number[] = [];
120
- const isInArray = arrayIds.find((e: any) => e === newValue.value);
129
+ let newArray: number[] = [];
130
+ const isInArray = arrayIds.find((e: any) => e === newValue.value);
121
131
 
122
- if (isInArray && !newValue.isChecked) {
123
- newArray = arrayIds.filter((e: any) => e !== newValue.value);
124
- }
132
+ if (isInArray && !newValue.isChecked) {
133
+ newArray = arrayIds.filter((e: any) => e !== newValue.value);
134
+ }
125
135
 
126
- if (!isInArray && newValue.isChecked) {
127
- newArray = [...arrayIds, newValue.value];
128
- }
136
+ if (!isInArray && newValue.isChecked) {
137
+ newArray = [...arrayIds, newValue.value];
138
+ }
129
139
 
130
- const newArrayObject = filterOptions(options, newArray);
140
+ const newArrayObject = filterOptions(options, newArray);
131
141
 
132
- if (!!newArrayObject.length && newArrayObject.length === options.length) {
133
- setAllSelected(true);
134
- } else {
135
- setAllSelected(false);
136
- }
142
+ if (newArrayObject.length > 0 && newArrayObject.length === options.length) {
143
+ setAllSelected(true);
144
+ } else {
145
+ setAllSelected(false);
146
+ }
137
147
 
138
- onChange(newArrayObject);
139
- error && handleValidation && handleValidation(newArrayObject);
140
- };
148
+ onChangeRef.current(newArrayObject);
149
+ error && handleValidation && handleValidation(newArrayObject);
150
+ },
151
+ [safeValue, options, filterOptions, error, handleValidation],
152
+ );
141
153
 
142
- const handleCheckAll = () => {
154
+ const handleCheckAll = useCallback(() => {
143
155
  setAllSelected((state) => !state);
144
156
 
145
157
  if (!allSelected) {
146
158
  const values = options.map((option) => {
147
159
  return { ...option, isChecked: true };
148
160
  });
149
- onChange(values);
161
+ onChangeRef.current(values);
150
162
  } else {
151
- onChange([]);
163
+ onChangeRef.current([]);
152
164
  }
153
- };
154
-
155
- const isChecked = (id: number): boolean =>
156
- !!safeValue.find((e: ICheckValue | number) => (typeof e === "number" ? e === id : e.value === id));
157
-
158
- const renderChecks = (options: ICheckValue[]): JSX.Element[] => {
159
- return options.map((item) => {
160
- return (
161
- <React.Fragment key={`${item.name}${item.value}`}>
162
- {item.type === "group" ? (
163
- <S.GroupTitle>{item.title}</S.GroupTitle>
164
- ) : (
165
- <S.StyledCheckField
166
- onChange={handleChange}
167
- checked={isChecked(item.value)}
168
- value={item.value}
169
- title={item.title}
170
- name={item.name}
171
- disabled={item.disabled || disabled}
172
- error={item.error}
173
- />
174
- )}
175
- {item.children && <S.GroupWrapper>{renderChecks(item.children)}</S.GroupWrapper>}
176
- </React.Fragment>
177
- );
178
- });
179
- };
165
+ }, [allSelected, options]);
166
+
167
+ const isChecked = useCallback(
168
+ (id: number): boolean =>
169
+ !!safeValue.find((e: ICheckValue | number) => (typeof e === "number" ? e === id : e.value === id)),
170
+ [safeValue],
171
+ );
172
+
173
+ const renderChecks = useCallback(
174
+ (options: ICheckValue[]): JSX.Element[] => {
175
+ return options.map((item) => {
176
+ return (
177
+ <React.Fragment key={`${item.name}${item.value}`}>
178
+ {item.type === "group" ? (
179
+ <S.GroupTitle>{item.title}</S.GroupTitle>
180
+ ) : (
181
+ <S.StyledCheckField
182
+ onChange={handleChange}
183
+ checked={isChecked(item.value)}
184
+ value={item.value}
185
+ title={item.title}
186
+ name={item.name}
187
+ disabled={item.disabled || disabled}
188
+ error={item.error}
189
+ />
190
+ )}
191
+ {item.children && <S.GroupWrapper>{renderChecks(item.children)}</S.GroupWrapper>}
192
+ </React.Fragment>
193
+ );
194
+ });
195
+ },
196
+ [handleChange, isChecked, disabled],
197
+ );
180
198
 
181
199
  return (
182
200
  <S.FieldGroup full={fullHeight}>
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
- import type { GroupBase, InputActionMeta, OptionsOrGroups } from "react-select";
2
+ import type { GroupBase, InputActionMeta, MenuProps, OptionsOrGroups } from "react-select";
3
3
  import { components } from "react-select";
4
4
 
5
5
  import { checkgroups, selects } from "@ax/api";
@@ -30,30 +30,30 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
30
30
  filterLang,
31
31
  languages,
32
32
  contentLanguages,
33
+ forceLanguage,
33
34
  } = props;
34
35
 
35
36
  const initialState: IState = {
36
37
  items: [],
37
- hasEmptyOption: true,
38
38
  inputText: "",
39
39
  };
40
40
 
41
41
  const [state, setState] = useState(initialState);
42
42
  const menuRef = useRef<HTMLDivElement | null>(null);
43
43
 
44
- const isPage = entity === "pages";
45
- const isCategories = entity === "categories";
46
44
  const className = error ? `react-select-error ${type}` : type;
47
45
  const isSearchable = type !== "inline";
48
46
 
49
- const languagesIds = Array.isArray(contentLanguages)
50
- ? languages?.filter((lang) => contentLanguages.includes(lang.locale)).map((lang) => lang.id)
51
- : contentLanguages === "all"
52
- ? languages?.map((lang) => lang.id)
53
- : [];
54
-
55
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
47
+ // biome-ignore lint/correctness/useExhaustiveDependencies: lang es necesaria para recargar opciones al cambiar idioma
56
48
  useEffect(() => {
49
+ const isPage = entity === "pages";
50
+ const isCategories = entity === "categories";
51
+ const languagesIds = Array.isArray(contentLanguages)
52
+ ? languages?.filter((language) => contentLanguages.includes(language.locale)).map((language) => language.id)
53
+ : contentLanguages === "all"
54
+ ? languages?.map((language) => language.id)
55
+ : [];
56
+
57
57
  const getItems = async () => {
58
58
  let data = [];
59
59
  try {
@@ -63,8 +63,8 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
63
63
  const fullOptions = languagesIds?.length ? { ...options, languagesIds } : options;
64
64
  result =
65
65
  isPage && selectedContent
66
- ? await selects.getSelectSiteItems(site.id, entity, fullOptions, selectedContent.id)
67
- : await selects.getSelectSiteItems(site.id, entity, fullOptions);
66
+ ? await selects.getSelectSiteItems(site.id, entity, fullOptions, selectedContent.id, forceLanguage)
67
+ : await selects.getSelectSiteItems(site.id, entity, fullOptions, undefined, forceLanguage);
68
68
  } else {
69
69
  result = await selects.getSelectItems(entity, entityId, filterLang);
70
70
  }
@@ -104,7 +104,22 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
104
104
  return () => {
105
105
  isMounted = false;
106
106
  };
107
- }, [entity, site, lang, selectedContent, entityId, mandatory, placeholder, filter, isCategories, isPage, options]);
107
+ }, [
108
+ entity,
109
+ site,
110
+ lang,
111
+ selectedContent,
112
+ entityId,
113
+ mandatory,
114
+ placeholder,
115
+ filter,
116
+ source,
117
+ options,
118
+ contentLanguages,
119
+ languages,
120
+ filterLang,
121
+ forceLanguage,
122
+ ]);
108
123
 
109
124
  const handleChange = (selectedValue: unknown | null) => {
110
125
  const newValue = selectedValue ? (selectedValue as ICheck | IOption).value : null;
@@ -117,9 +132,7 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
117
132
 
118
133
  const handleInputChange = (inputText: string, meta: InputActionMeta) => {
119
134
  if (meta.action !== "input-blur" && meta.action !== "menu-close") {
120
- const inputValue = inputText.replace(/\W/g, "");
121
- const hasEmptyOption = inputValue.length === 0;
122
- setState((state) => ({ ...state, hasEmptyOption, inputText }));
135
+ setState((state) => ({ ...state, inputText }));
123
136
  }
124
137
  };
125
138
 
@@ -127,9 +140,7 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
127
140
  inputValue: string,
128
141
  callback: (options: OptionsOrGroups<unknown, GroupBase<unknown>>) => void,
129
142
  ) => {
130
- setTimeout(() => {
131
- callback(filterOptions(inputValue));
132
- }, 0);
143
+ callback(filterOptions(inputValue));
133
144
  };
134
145
 
135
146
  const getObjectValue = (val: string | number | null | undefined, options: IOption[]) => {
@@ -156,7 +167,7 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
156
167
  }
157
168
  };
158
169
 
159
- const Menu = (menuProps: any) => (
170
+ const Menu = (menuProps: MenuProps<unknown, false, GroupBase<unknown>>) => (
160
171
  <div ref={menuRef}>
161
172
  <components.Menu {...menuProps} />
162
173
  </div>
@@ -190,7 +201,6 @@ const AsyncSelect = (props: IAsyncSelectProps): JSX.Element => {
190
201
 
191
202
  interface IState {
192
203
  items: IOption[];
193
- hasEmptyOption: boolean;
194
204
  inputText: string;
195
205
  }
196
206
 
@@ -226,6 +236,7 @@ export interface IAsyncSelectProps {
226
236
  filterLang?: number;
227
237
  languages?: ILanguage[];
228
238
  contentLanguages?: "current" | "all" | string[];
239
+ forceLanguage?: number;
229
240
  }
230
241
 
231
242
  export default AsyncSelect;
@@ -6,7 +6,7 @@ import { getRange, getStringifyDateRange, isValidDate, isValidDateRange, stringT
6
6
 
7
7
  import * as S from "./style";
8
8
 
9
- const DatePickerInput = (props: IDatePickerProps): JSX.Element => {
9
+ const DatePickerInput = (props: IDatePickerProps, ref: React.ForwardedRef<HTMLDivElement>): JSX.Element => {
10
10
  const { dates, onClick, handleChange, isOpen, error, disabled, handleValidation, validators } = props;
11
11
  const { start, end } = dates;
12
12
  const placeholder = "dd/mm/yyyy";
@@ -63,7 +63,7 @@ const DatePickerInput = (props: IDatePickerProps): JSX.Element => {
63
63
  );
64
64
 
65
65
  return (
66
- <S.InputWrapper error={error}>
66
+ <S.InputWrapper ref={ref} error={error}>
67
67
  <S.Input
68
68
  value={value || ""}
69
69
  placeholder={placeholder}
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useRef, useState } from "react";
2
2
  import DatePicker, { registerLocale } from "react-datepicker";
3
3
  import "react-datepicker/dist/react-datepicker.css";
4
4
  import en from "date-fns/locale/en-GB";
@@ -87,12 +87,12 @@ const DateField = (props: IDateFieldProps): JSX.Element => {
87
87
  setDates(rangeDates);
88
88
  onChange(selectedDate);
89
89
 
90
- handleValidation && handleValidation(selectedDate, dateValidators);
90
+ handleValidation?.(selectedDate, dateValidators);
91
91
  };
92
92
 
93
93
  const { start, end } = dates;
94
94
 
95
- const ref = React.createRef();
95
+ const ref = useRef(null);
96
96
  const CustomInput = (
97
97
  <DatePickerInput
98
98
  dates={dates}
@@ -1,9 +1,9 @@
1
- import React, { useRef } from "react";
1
+ import { useRef } from "react";
2
2
  import { createPortal } from "react-dom";
3
3
 
4
4
  import { useHandleClickOutside } from "@ax/hooks";
5
5
  import { IconAction } from "@ax/components";
6
- import { IIntegration } from "@ax/types";
6
+ import type { IIntegration } from "@ax/types";
7
7
 
8
8
  import SideModalOption from "./SideModalOption";
9
9
  import * as S from "./style";
@@ -1,8 +1,8 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useState, useEffect } from "react";
2
2
  import { connect } from "react-redux";
3
3
  import { differenceInSeconds } from "date-fns";
4
4
 
5
- import { IIntegration, IRootState, ISite, ILanguage } from "@ax/types";
5
+ import type { IIntegration, IRootState, ISite, ILanguage } from "@ax/types";
6
6
  import { useModal } from "@ax/hooks";
7
7
  import { Tooltip, IconAction } from "@ax/components";
8
8
  import { integrationsActions } from "@ax/containers/Integrations";
@@ -25,9 +25,13 @@ const IntegrationsField = (props: IIntegrationsFieldProps): JSX.Element => {
25
25
  integrationCopy,
26
26
  } = props;
27
27
 
28
- const [state, setState] = useState<Partial<IIntegration>[]>(value || null);
28
+ const [state, setState] = useState<Partial<IIntegration>[]>(value || []);
29
29
  const { isOpen, toggleModal } = useModal();
30
30
 
31
+ useEffect(() => {
32
+ setState(value || []);
33
+ }, [value]);
34
+
31
35
  useEffect(() => {
32
36
  const params = {
33
37
  pagination: false,
@@ -36,25 +40,25 @@ const IntegrationsField = (props: IIntegrationsFieldProps): JSX.Element => {
36
40
  site && getIntegrations(site.id, params, true);
37
41
  }, [getIntegrations, site]);
38
42
 
39
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
40
- useEffect(() => {
41
- onChange && state && onChange(state);
42
- }, [state]);
43
+ const updateState = (newState: Partial<IIntegration>[]) => {
44
+ setState(newState);
45
+ onChange?.(newState);
46
+ };
43
47
 
44
48
  const handleAdd = (integration: Partial<IIntegration>) => {
45
- setState((state) => (state ? [...state, integration] : [integration]));
49
+ updateState([...state, integration]);
46
50
  };
47
51
 
48
52
  const handleEditIntegration = (modifiedIntegration: Partial<IIntegration>, index: number) => {
49
53
  const newIntegrations = [...state];
50
54
  newIntegrations.splice(index, 1, modifiedIntegration);
51
- setState(newIntegrations);
55
+ updateState(newIntegrations);
52
56
  };
53
57
 
54
58
  const removeItem = (index: number) => {
55
59
  const newValue = [...state];
56
60
  newValue.splice(index, 1);
57
- setState(newValue);
61
+ updateState(newValue);
58
62
  };
59
63
 
60
64
  const availableIntegrations = integrations.filter(
@@ -1,9 +1,9 @@
1
- import React, { useState, memo, useRef } from "react";
1
+ import { memo, useCallback, useRef, useState } from "react";
2
2
 
3
- import { ILanguage, ISite } from "@ax/types";
4
3
  import AsyncCheckGroup from "@ax/components/Fields/AsyncCheckGroup";
5
4
  import CheckGroup from "@ax/components/Fields/CheckGroup";
6
5
  import { useHandleClickOutside } from "@ax/hooks";
6
+ import type { ILanguage, ISite } from "@ax/types";
7
7
 
8
8
  import * as S from "./style";
9
9
 
@@ -29,19 +29,19 @@ const MultiCheckSelect = (props: IMultiCheckSelectProps) => {
29
29
  const [isOpen, setIsOpen] = useState(false);
30
30
  const wrapperRef = useRef<HTMLDivElement | null>(null);
31
31
 
32
- const handleClick = () => setIsOpen(!isOpen);
32
+ const handleClick = useCallback(() => setIsOpen((prev) => !prev), []);
33
33
 
34
- const handleClickOutside = (e: MouseEvent) => {
34
+ const handleClickOutside = useCallback((e: MouseEvent) => {
35
35
  if (wrapperRef.current?.contains(e.target as HTMLElement)) {
36
36
  return;
37
37
  }
38
38
 
39
39
  setIsOpen(false);
40
- };
40
+ }, []);
41
41
 
42
42
  useHandleClickOutside(isOpen, handleClickOutside);
43
43
 
44
- const Asterisk = () => (mandatory ? <S.Asterisk>*</S.Asterisk> : null);
44
+ const Asterisk = useCallback(() => (mandatory ? <S.Asterisk>*</S.Asterisk> : null), [mandatory]);
45
45
 
46
46
  return (
47
47
  <S.Wrapper ref={wrapperRef} className={className} data-testid="multi-check-select-wrapper">
@@ -1,6 +1,6 @@
1
- import React, { memo } from "react";
1
+ import { memo, useCallback } from "react";
2
2
 
3
- import { ILanguage, ISite } from "@ax/types";
3
+ import type { ILanguage, ISite } from "@ax/types";
4
4
 
5
5
  import * as S from "./style";
6
6
 
@@ -19,47 +19,49 @@ const MultiCheckSelectGroup = (props: IMultiCheckSelectGroupProps) => {
19
19
  contentType = "data",
20
20
  } = props;
21
21
 
22
- const completedValue = value ? value : {};
22
+ const completedValue = value ?? {};
23
+
24
+ const handleElementChange = useCallback(
25
+ (elementKey: string, val: any[]) => {
26
+ const newValue = { ...completedValue };
27
+ if (val.length > 0) {
28
+ newValue[elementKey] = val;
29
+ } else {
30
+ delete newValue[elementKey];
31
+ }
32
+
33
+ onChange(newValue);
34
+ error && handleValidation && handleValidation(newValue);
35
+ },
36
+ [completedValue, onChange, error, handleValidation],
37
+ );
23
38
 
24
39
  return (
25
40
  <>
26
41
  {note && <S.StyledNoteField value={{ text: note }} />}
27
- {elements &&
28
- elements.map((element) => {
29
- const { placeholder, source, key, mandatory, isGlobal, allOption } = element;
30
-
31
- const handleChange = (val: any[]) => {
32
- let newValue = { ...completedValue };
33
- if (val.length > 0) {
34
- newValue = { ...newValue, [key]: val };
35
- } else {
36
- delete newValue[key];
37
- }
38
-
39
- onChange(newValue);
40
- error && handleValidation && handleValidation(newValue);
41
- };
42
+ {elements?.map((element) => {
43
+ const { placeholder, source, key, mandatory, isGlobal, allOption } = element;
42
44
 
43
- const val = completedValue[key] ? completedValue[key] : [];
44
- const fromSite = isGlobal ? null : site;
45
+ const val = (completedValue[key] as any[]) ?? [];
46
+ const fromSite = isGlobal ? null : site;
45
47
 
46
- return (
47
- <S.StyledMultiCheckSelect
48
- key={key}
49
- mandatory={mandatory}
50
- value={val}
51
- onChange={handleChange}
52
- site={fromSite}
53
- placeholder={placeholder}
54
- source={source}
55
- disabled={disabled}
56
- contentType={contentType}
57
- languages={languages}
58
- contentLanguages={contentLanguages}
59
- allOption={allOption}
60
- />
61
- );
62
- })}
48
+ return (
49
+ <S.StyledMultiCheckSelect
50
+ key={key}
51
+ mandatory={mandatory}
52
+ value={val}
53
+ onChange={(newVal: string | any[]) => handleElementChange(key, newVal as any[])}
54
+ site={fromSite}
55
+ placeholder={placeholder}
56
+ source={source}
57
+ disabled={disabled}
58
+ contentType={contentType}
59
+ languages={languages}
60
+ contentLanguages={contentLanguages}
61
+ allOption={allOption}
62
+ />
63
+ );
64
+ })}
63
65
  </>
64
66
  );
65
67
  };
@@ -1,5 +1,5 @@
1
- import React from "react";
2
1
  import styled from "styled-components";
2
+
3
3
  import MultiCheckSelect from "../MultiCheckSelect";
4
4
  import NoteField from "../NoteField";
5
5
 
@@ -120,8 +120,6 @@ const ManualPanel = (props: IProps) => {
120
120
  setState((state) => ({ ...state, site: siteID }));
121
121
  };
122
122
 
123
- console.log(form.site);
124
-
125
123
  return (
126
124
  <S.Wrapper>
127
125
  {state.fixed.length > 0 && (