@griddo/ax 11.12.0 → 11.12.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/config/jest/componentsMock.js +7 -5
  2. package/package.json +2 -2
  3. package/src/__tests__/components/Browser/Browser.test.tsx +438 -87
  4. package/src/__tests__/components/Browser/Browser.utils.test.ts +55 -0
  5. package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +1 -3
  6. package/src/__tests__/components/Fields/Button/Button.test.tsx +29 -27
  7. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +158 -0
  8. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorsBanner.test.tsx +90 -0
  9. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.test.tsx +178 -0
  10. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +150 -0
  11. package/src/__tests__/components/KeywordsPreviewModal/KeywordItem/KeywordItem.test.tsx +91 -0
  12. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +122 -0
  13. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.utils.test.ts +15 -0
  14. package/src/__tests__/components/KeywordsPreviewModal/atoms.test.tsx +101 -0
  15. package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +1 -1
  16. package/src/__tests__/modules/FramePreview/FramePreview.test.tsx +318 -0
  17. package/src/__tests__/modules/FramePreview/FramePreview.utils.test.ts +242 -0
  18. package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +185 -0
  19. package/src/components/Browser/index.tsx +294 -149
  20. package/src/components/Browser/style.tsx +75 -6
  21. package/src/components/Browser/utils.tsx +13 -0
  22. package/src/components/Button/index.tsx +2 -1
  23. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +2 -4
  24. package/src/components/Fields/AsyncSelect/style.tsx +13 -0
  25. package/src/components/Fields/FieldGroup/index.tsx +5 -2
  26. package/src/components/Fields/FieldGroup/style.tsx +32 -7
  27. package/src/components/Fields/HeadingField/index.tsx +22 -22
  28. package/src/components/Fields/HiddenField/style.tsx +1 -1
  29. package/src/components/Fields/NumberField/index.tsx +15 -16
  30. package/src/components/Fields/NumberField/style.tsx +2 -0
  31. package/src/components/Fields/ReferenceField/index.tsx +1 -1
  32. package/src/components/Fields/SEOPreview/index.tsx +36 -0
  33. package/src/components/Fields/SEOPreview/style.tsx +24 -0
  34. package/src/components/Fields/Select/index.tsx +5 -1
  35. package/src/components/Fields/Select/style.tsx +56 -0
  36. package/src/components/Fields/SummaryButton/index.tsx +18 -9
  37. package/src/components/Fields/SummaryButton/style.tsx +1 -2
  38. package/src/components/Fields/TagsField/index.tsx +8 -9
  39. package/src/components/Fields/UrlField/index.tsx +26 -27
  40. package/src/components/Fields/index.tsx +2 -0
  41. package/src/components/FloatingNote/index.tsx +35 -0
  42. package/src/components/FloatingNote/style.tsx +26 -0
  43. package/src/components/FloatingPanel/index.tsx +5 -2
  44. package/src/components/FloatingPanel/style.tsx +2 -1
  45. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +85 -0
  46. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/style.tsx +80 -0
  47. package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +57 -0
  48. package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +82 -0
  49. package/src/components/HeadingsPreviewModal/HeadingItem/index.tsx +71 -0
  50. package/src/components/HeadingsPreviewModal/HeadingItem/style.tsx +77 -0
  51. package/src/components/HeadingsPreviewModal/index.tsx +148 -0
  52. package/src/components/HeadingsPreviewModal/style.tsx +82 -0
  53. package/src/components/HeadingsPreviewModal/utils.tsx +329 -0
  54. package/src/components/Icon/index.tsx +1 -2
  55. package/src/components/IconAction/index.tsx +1 -1
  56. package/src/components/KeywordsPreviewModal/KeywordItem/index.tsx +46 -0
  57. package/src/components/KeywordsPreviewModal/KeywordItem/style.tsx +64 -0
  58. package/src/components/KeywordsPreviewModal/atoms.tsx +96 -0
  59. package/src/components/KeywordsPreviewModal/index.tsx +99 -0
  60. package/src/components/KeywordsPreviewModal/style.tsx +87 -0
  61. package/src/components/KeywordsPreviewModal/utils.tsx +22 -0
  62. package/src/components/MainWrapper/AppBar/index.tsx +8 -1
  63. package/src/components/MainWrapper/index.tsx +7 -1
  64. package/src/components/Notification/index.tsx +2 -2
  65. package/src/components/PageFinder/index.tsx +1 -1
  66. package/src/components/ResizePanel/index.tsx +4 -3
  67. package/src/components/ResizePanel/style.tsx +1 -1
  68. package/src/components/SearchField/style.tsx +2 -2
  69. package/src/components/SideModal/index.tsx +2 -1
  70. package/src/components/Tabs/index.tsx +13 -4
  71. package/src/components/Tabs/style.tsx +7 -8
  72. package/src/components/Toast/index.tsx +4 -2
  73. package/src/components/Tooltip/index.tsx +4 -3
  74. package/src/components/index.tsx +8 -0
  75. package/src/forms/fields.tsx +70 -68
  76. package/src/hooks/forms.tsx +22 -1
  77. package/src/hooks/index.tsx +13 -3
  78. package/src/hooks/modals.tsx +103 -15
  79. package/src/hooks/users.tsx +25 -8
  80. package/src/modules/Forms/atoms.tsx +2 -2
  81. package/src/modules/FramePreview/HeadingsOverlay/index.tsx +116 -0
  82. package/src/modules/FramePreview/HeadingsOverlay/style.tsx +34 -0
  83. package/src/modules/FramePreview/index.tsx +55 -16
  84. package/src/modules/FramePreview/style.tsx +34 -2
  85. package/src/modules/FramePreview/utils.tsx +140 -0
  86. package/src/modules/GlobalEditor/Editor/index.tsx +37 -3
  87. package/src/modules/GlobalEditor/PageBrowser/index.tsx +19 -2
  88. package/src/modules/GlobalEditor/Preview/index.tsx +0 -2
  89. package/src/modules/GlobalEditor/Preview/style.tsx +1 -1
  90. package/src/modules/GlobalEditor/index.tsx +119 -57
  91. package/src/modules/PageEditor/Editor/index.tsx +33 -2
  92. package/src/modules/PageEditor/PageBrowser/index.tsx +20 -2
  93. package/src/modules/PageEditor/Preview/index.tsx +0 -2
  94. package/src/modules/PageEditor/Preview/style.tsx +1 -1
  95. package/src/modules/PageEditor/atoms.tsx +1 -1
  96. package/src/modules/PageEditor/index.tsx +130 -66
  97. package/src/modules/PublicPreview/index.tsx +5 -2
  98. package/src/schemas/pages/GlobalPage.ts +87 -70
  99. package/src/schemas/pages/Page.ts +87 -70
  100. package/src/types/index.tsx +12 -0
@@ -1,9 +1,15 @@
1
+ import { FloatingNote } from "@ax/components";
2
+
1
3
  import styled, { css } from "styled-components";
2
4
 
3
- const BrowserWrapper = styled.div`
5
+ const BrowserWrapper = styled.div<{ scaledWidth?: number; isPreview?: boolean }>`
4
6
  background-color: ${(p) => p.theme.color.uiBackground01};
5
- height: 100%;
6
- width: 100%;
7
+ display: flex;
8
+ flex-direction: column;
9
+ height: ${(p) => (p.isPreview ? "100%" : `calc(100vh - ${p.theme.spacing.xl} - (${p.theme.spacing.m} * 2))`)};
10
+ min-width: 360px;
11
+ width: ${(p) => (p.scaledWidth !== undefined ? `${p.scaledWidth}px` : "100%")};
12
+ margin: 0 auto;
7
13
  `;
8
14
 
9
15
  const NavBar = styled.div`
@@ -22,6 +28,9 @@ const NavUrl = styled.div`
22
28
  height: ${(p) => p.theme.spacing.m};
23
29
  padding: ${(p) => p.theme.spacing.xxs} ${(p) => p.theme.spacing.s};
24
30
  width: 100%;
31
+ overflow: hidden;
32
+ white-space: nowrap;
33
+ text-overflow: ellipsis;
25
34
  `;
26
35
 
27
36
  const NavActions = styled.div`
@@ -59,17 +68,24 @@ const FrameWrapper = styled.div<{ hasBorder: boolean; isFormEditor: boolean }>`
59
68
  border-bottom: ${(p) => (p.hasBorder ? `1px solid ${p.theme.color.uiLine}` : "none")};
60
69
  display: flex;
61
70
  justify-content: center;
62
- height: 100%;
71
+ flex: 1;
72
+ min-height: 0;
63
73
  padding: ${(p) => (p.isFormEditor ? p.theme.spacing.m : "0")};
64
74
  `;
65
75
 
76
+ const OuterContainer = styled.div`
77
+ width: 100%;
78
+ height: 100%;
79
+ `;
80
+
66
81
  const Wrapper = styled.div`
67
82
  border-left: 1px solid ${(p) => p.theme.color.uiLine};
68
83
  border-right: 1px solid ${(p) => p.theme.color.uiLine};
69
84
  border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
70
85
  overflow: auto;
71
86
  scroll-behavior: smooth;
72
- height: 100%;
87
+ flex: 1;
88
+ min-height: 0;
73
89
  position: relative;
74
90
 
75
91
  .headroom {
@@ -77,4 +93,57 @@ const Wrapper = styled.div`
77
93
  }
78
94
  `;
79
95
 
80
- export { BrowserWrapper, FrameWrapper, IconWrapper, NavActions, NavBar, NavUrl, Wrapper };
96
+ const ContentWrapper = styled.div`
97
+ display: flex;
98
+ flex-direction: column;
99
+ position: relative;
100
+ width: 100%;
101
+ flex: 1;
102
+ min-height: 0;
103
+ `;
104
+
105
+ const StyledFloatingNote = styled(({ $compact: _, ...props }) => <FloatingNote {...props} />)<{ $compact?: boolean }>`
106
+ position: absolute;
107
+ left: ${(p) => p.theme.spacing.s};
108
+ top: ${(p) => p.theme.spacing.s};
109
+ right: ${(p) => p.theme.spacing.s};
110
+ ${(p) => p.$compact && `&& { padding: ${p.theme.spacing.xs} ${p.theme.spacing.s}; }`}
111
+ `;
112
+
113
+ const ResolutionWrapper = styled.div`
114
+ display: flex;
115
+ margin-left: ${(p) => p.theme.spacing.xs};
116
+ min-width: 115px;
117
+ background-color: ${(p) => p.theme.color.uiBackground02};
118
+ border-radius: 10px;
119
+ align-items: center;
120
+ height: 24px;
121
+ `;
122
+
123
+ const SelectLabel = styled.div`
124
+ ${(p) => p.theme.textStyle.uiXS};
125
+ flex-shrink: 0;
126
+ padding-left: ${(p) => p.theme.spacing.xs};
127
+ color: ${(p) => p.theme.color.textMediumEmphasis};
128
+ `;
129
+
130
+ const ZoomWrapper = styled.div`
131
+ margin-left: ${(p) => p.theme.spacing.s};
132
+ min-width: 65px;
133
+ `;
134
+
135
+ export {
136
+ OuterContainer,
137
+ BrowserWrapper,
138
+ NavBar,
139
+ NavUrl,
140
+ NavActions,
141
+ IconWrapper,
142
+ FrameWrapper,
143
+ Wrapper,
144
+ ContentWrapper,
145
+ StyledFloatingNote,
146
+ ResolutionWrapper,
147
+ ZoomWrapper,
148
+ SelectLabel,
149
+ };
@@ -0,0 +1,13 @@
1
+ const calcAutoZoom = (containerWidth: number, resolution: string): string => {
2
+ const resolutionWidth = parseInt(resolution);
3
+ if (!containerWidth || !resolutionWidth) return "100";
4
+ return String(Math.min(100, Math.floor((containerWidth / resolutionWidth) * 100)));
5
+ };
6
+
7
+ const calcDefaultResolution = (containerWidth: number, options: Array<{ value: string }>): string => {
8
+ const sorted = [...options].sort((a, b) => parseInt(a.value) - parseInt(b.value));
9
+ const best = sorted.find((opt) => parseInt(opt.value) >= containerWidth);
10
+ return best ? best.value : sorted[sorted.length - 1].value;
11
+ };
12
+
13
+ export { calcAutoZoom, calcDefaultResolution };
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
- import { Icon, Loader } from "@ax/components";
2
+ import Icon from "../Icon";
3
+ import Loader from "../Loader";
3
4
 
4
5
  import * as S from "./style";
5
6
 
@@ -1,8 +1,6 @@
1
- import React from "react";
2
-
3
1
  import { getInnerFields } from "@ax/forms";
4
2
  import { FieldContainer, FieldGroup } from "@ax/components";
5
- import { IErrorItem, IPage, ISite, ILanguage } from "@ax/types";
3
+ import type { IErrorItem, IPage, ISite, ILanguage } from "@ax/types";
6
4
 
7
5
  const Field = (props: IFieldProps): JSX.Element => {
8
6
  const {
@@ -56,7 +54,7 @@ const Field = (props: IFieldProps): JSX.Element => {
56
54
  }
57
55
 
58
56
  return isGroup ? (
59
- <FieldGroup title={field.title} collapsed={isCollapsed}>
57
+ <FieldGroup title={field.title} collapsed={isCollapsed} solid={field.solid}>
60
58
  {innerFields}
61
59
  </FieldGroup>
62
60
  ) : (
@@ -56,12 +56,25 @@ export const StyledSelect = styled(AsyncSelect)<{
56
56
  max-width: calc(${(p) => p.theme.spacing.xl} * 6);
57
57
  margin-top: 0;
58
58
  z-index: 99;
59
+
59
60
  .react-select__menu-list {
60
61
  border: 1px solid ${(p) => p.theme.color.uiLine};
61
62
  border-radius: 4px;
62
63
  max-height: calc(${(p) => p.theme.spacing.l} * 3);
63
64
  overflow: auto;
64
65
  padding: 0;
66
+
67
+ &::-webkit-scrollbar {
68
+ -webkit-appearance: none;
69
+ width: 4px;
70
+ height: 100%;
71
+ }
72
+
73
+ &::-webkit-scrollbar-thumb {
74
+ border-radius: 4px;
75
+ background-color: ${(p) => p.theme.colors.iconNonActive};
76
+ }
77
+
65
78
  .react-select__option {
66
79
  background-color: ${(p) => p.theme.color.interactiveBackground};
67
80
  min-height: ${(p) => p.theme.spacing.l};
@@ -3,15 +3,17 @@ import React from "react";
3
3
  import * as S from "./style";
4
4
 
5
5
  const FieldGroup = (props: IFieldsGroupProps): React.ReactElement => {
6
- const { title, children, collapsed } = props;
6
+ const { title, children, collapsed, solid } = props;
7
7
  const [isOpen, setIsOpen] = React.useState(!collapsed);
8
8
 
9
9
  const handleClick = () => {
10
10
  setIsOpen(!isOpen);
11
11
  };
12
12
 
13
+ const className = solid ? "filled" : "";
14
+
13
15
  return (
14
- <S.Wrapper data-testid="field-group-wrapper">
16
+ <S.Wrapper data-testid="field-group-wrapper" className={className} isOpen={isOpen}>
15
17
  <S.Label data-testid="field-group-label" onClick={handleClick} isOpen={isOpen}>
16
18
  {title}
17
19
  </S.Label>
@@ -28,4 +30,5 @@ interface IFieldsGroupProps {
28
30
  title: string;
29
31
  children: any;
30
32
  collapsed?: boolean;
33
+ solid?: boolean;
31
34
  }
@@ -1,10 +1,6 @@
1
1
  import styled from "styled-components";
2
2
 
3
- export const Wrapper = styled.div`
4
- margin: ${(p) => `${p.theme.spacing.m} 0 ${p.theme.spacing.m} 0`};
5
- `;
6
-
7
- export const Label = styled.div<{ isOpen: boolean }>`
3
+ const Label = styled.div<{ isOpen: boolean }>`
8
4
  position: relative;
9
5
  ${(p) => p.theme.textStyle.headingXXS};
10
6
  color: ${(p) => p.theme.colors.textMediumEmphasis};
@@ -25,8 +21,37 @@ export const Label = styled.div<{ isOpen: boolean }>`
25
21
  }
26
22
  `;
27
23
 
28
- export const Content = styled.div<{ isOpen: boolean }>`
24
+ const Content = styled.div<{ isOpen: boolean }>`
29
25
  overflow-y: ${(p) => (p.isOpen ? "visible" : "hidden")};
30
- max-height: ${(p) => (p.isOpen ? `auto` : 0)};
26
+ max-height: ${(p) => (p.isOpen ? "auto" : 0)};
31
27
  transition: all 0.5s ease-in-out;
32
28
  `;
29
+
30
+ const Wrapper = styled.div<{ isOpen: boolean }>`
31
+ margin: ${(p) => `${p.theme.spacing.m} 0 ${p.theme.spacing.m} 0`};
32
+
33
+ &.filled {
34
+ margin: ${(p) => `${p.theme.spacing.xs} 0`};
35
+ background-color: ${(p) => p.theme.colors.uiBackground02};
36
+ border: 1px solid ${(p) => (p.isOpen ? p.theme.color.interactive01 : p.theme.color.uiLine)};
37
+ border-radius: ${(p) => p.theme.radii.s};
38
+ padding: ${(p) => p.theme.spacing.s};
39
+
40
+ ${Label} {
41
+ ${(p) => (p.isOpen ? p.theme.textStyle.uiL : p.theme.textStyle.fieldContent)};
42
+ text-transform: none;
43
+ letter-spacing: normal;
44
+ color: ${(p) => p.theme.colors.textHighEmphasis};
45
+ border-bottom: none;
46
+ margin-bottom: 0;
47
+ padding-bottom: 0;
48
+ }
49
+
50
+ ${Content} {
51
+ padding-top: ${(p) => p.theme.spacing.s};
52
+ display: ${(p) => (p.isOpen ? "block" : "none")};
53
+ }
54
+ }
55
+ `;
56
+
57
+ export { Wrapper, Label, Content };
@@ -1,35 +1,40 @@
1
- import React, { memo } from "react";
2
- import { IHeadingField, ISite } from "@ax/types";
1
+ import { memo, useCallback } from "react";
2
+
3
3
  import { FieldsBehavior, TextField, Wysiwyg } from "@ax/components";
4
+ import type { IHeadingField, ISelectOption, ISite } from "@ax/types";
4
5
 
5
6
  import * as S from "./style";
6
7
 
7
8
  const HeadingField = (props: IHeadingFieldProps): JSX.Element => {
8
- const { value, onChange, options, showAdvanced, toolbar = false } = props;
9
+ const { value, onChange, options, showAdvanced, default: defaultValue, toolbar = false, title, site } = props;
9
10
 
10
- const getContentValue = () => value?.content || props.default?.content || "";
11
- const getTagValue = () => value?.tag || props.default?.tag || "";
11
+ const contentValue = value?.content ?? defaultValue?.content ?? "";
12
+ const tagValue = value?.tag ?? defaultValue?.tag ?? "";
12
13
 
13
- const handleChange = (newValue: string) => {
14
- const tag = getTagValue();
15
- onChange({ content: newValue, tag });
16
- };
14
+ const handleChange = useCallback(
15
+ (newValue: string) => {
16
+ onChange({ content: newValue, tag: tagValue });
17
+ },
18
+ [onChange, tagValue],
19
+ );
17
20
 
18
- const handleSelectChange = (newValue: string) => {
19
- const content = getContentValue();
20
- onChange({ content, tag: newValue });
21
- };
21
+ const handleSelectChange = useCallback(
22
+ (newValue: string) => {
23
+ onChange({ content: contentValue, tag: newValue });
24
+ },
25
+ [onChange, contentValue],
26
+ );
22
27
 
23
28
  return (
24
29
  <>
25
30
  {toolbar ? (
26
- <Wysiwyg {...props} inline={true} value={getContentValue()} onChange={handleChange} />
31
+ <Wysiwyg title={title} site={site} inline={true} value={contentValue} onChange={handleChange} />
27
32
  ) : (
28
- <TextField {...props} value={getContentValue()} onChange={handleChange} />
33
+ <TextField value={contentValue} onChange={handleChange} />
29
34
  )}
30
35
  {showAdvanced && (
31
36
  <S.AdvancedWrapper data-testid="text-field-advanced-wrapper">
32
- <FieldsBehavior fieldType="Select" options={options} value={getTagValue()} onChange={handleSelectChange} />
37
+ <FieldsBehavior fieldType="Select" options={options} value={tagValue} onChange={handleSelectChange} />
33
38
  </S.AdvancedWrapper>
34
39
  )}
35
40
  </>
@@ -40,16 +45,11 @@ interface IHeadingFieldProps {
40
45
  value: IHeadingField;
41
46
  title: string;
42
47
  onChange: (value: IHeadingField) => void;
43
- options: IOption[];
48
+ options: ISelectOption[];
44
49
  showAdvanced: boolean;
45
50
  default: IHeadingField;
46
51
  site: ISite;
47
52
  toolbar?: boolean;
48
53
  }
49
54
 
50
- interface IOption {
51
- value: string;
52
- label: string;
53
- }
54
-
55
55
  export default memo(HeadingField);
@@ -1,5 +1,5 @@
1
1
  import styled from "styled-components";
2
- import { IStyledProps } from "@ax/types";
2
+ import type { IStyledProps } from "@ax/types";
3
3
 
4
4
  const Wrapper = styled.button<{ disabled?: boolean }>`
5
5
  ${(p: IStyledProps) => p.theme.textStyle.fieldLabel};
@@ -1,15 +1,18 @@
1
- import React, { memo, useState, useEffect } from "react";
1
+ import { memo, useState, useEffect } from "react";
2
2
  import { Icon } from "@ax/components";
3
3
 
4
4
  import * as S from "./style";
5
5
 
6
- const NumberField = (props: INumberFieldProps): JSX.Element => {
6
+ const NumberField = (props: INumberFieldProps) => {
7
7
  const { name, value, error, onChange, onBlur, maxValue, minValue, disabled, handleValidation, min, max } = props;
8
8
 
9
9
  const strValue = value || value === 0 ? value.toString() : "";
10
10
  const safeMax = typeof max !== "undefined" ? max : maxValue;
11
11
  const safeMin = typeof min !== "undefined" ? min : minValue;
12
12
 
13
+ let validators: Record<string, unknown> = safeMax !== undefined ? { maxValue: safeMax } : {};
14
+ validators = safeMin !== undefined ? { ...validators, minValue: safeMin } : validators;
15
+
13
16
  const [inputValue, setInputValue] = useState(strValue);
14
17
 
15
18
  useEffect(() => {
@@ -17,18 +20,18 @@ const NumberField = (props: INumberFieldProps): JSX.Element => {
17
20
  }, [strValue]);
18
21
 
19
22
  const setValue = (value: string, eventType: string) => {
20
- const floatValue = value || value === "0" ? parseFloat(value) : undefined;
23
+ const floatValue = value.length > 0 ? parseFloat(value) : undefined;
21
24
  setInputValue(value);
22
25
 
23
- eventType === "blur" ? onBlur && onBlur(floatValue) : onChange(floatValue);
26
+ eventType === "blur" ? onBlur?.(floatValue) : onChange(floatValue);
24
27
  };
25
28
 
26
29
  const handleClick = (pressedKey: string) => {
27
30
  const currentValue = parseFloat(inputValue);
28
31
 
29
- let updatedValue = currentValue ? currentValue : 0;
30
- const isMin = updatedValue === safeMin;
31
- const isMax = updatedValue === safeMax;
32
+ let updatedValue = !Number.isNaN(currentValue) ? currentValue : 0;
33
+ const isMin = safeMin !== undefined && updatedValue === safeMin;
34
+ const isMax = safeMax !== undefined && updatedValue === safeMax;
32
35
 
33
36
  if (pressedKey === "ArrowUp" && !isMax) {
34
37
  updatedValue++;
@@ -38,20 +41,17 @@ const NumberField = (props: INumberFieldProps): JSX.Element => {
38
41
 
39
42
  setInputValue(updatedValue.toString());
40
43
  onChange(updatedValue);
41
- handleValidation && handleValidation(updatedValue.toString(), validators);
44
+ handleValidation?.(updatedValue.toString(), validators);
42
45
  };
43
46
 
44
- let validators: Record<string, unknown> = safeMax ? { maxValue: safeMax } : {};
45
- validators = safeMin ? { ...validators, minValue: safeMin } : validators;
46
-
47
47
  const handleOnBlur = (e: React.FocusEvent<HTMLInputElement>) => {
48
48
  setValue(e.target.value, e.type);
49
- handleValidation && handleValidation(e.target.value, validators);
49
+ handleValidation?.(e.target.value, validators);
50
50
  };
51
51
 
52
52
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
53
53
  setValue(e.target.value, e.type);
54
- error && handleValidation && handleValidation(e.target.value, validators);
54
+ error && handleValidation?.(e.target.value, validators);
55
55
  };
56
56
 
57
57
  const increaseValue = () => handleClick("ArrowUp");
@@ -72,10 +72,10 @@ const NumberField = (props: INumberFieldProps): JSX.Element => {
72
72
  data-testid="input"
73
73
  />
74
74
  <S.QuantityNav error={error} disabled={disabled}>
75
- <S.ArrowUp onClick={increaseValue} data-testid="arrow-up">
75
+ <S.ArrowUp onClick={disabled ? undefined : increaseValue} data-testid="arrow-up">
76
76
  <Icon name="UpArrow" />
77
77
  </S.ArrowUp>
78
- <S.ArrowDown onClick={decreaseValue} data-testid="arrow-down">
78
+ <S.ArrowDown onClick={disabled ? undefined : decreaseValue} data-testid="arrow-down">
79
79
  <Icon name="DownArrow" />
80
80
  </S.ArrowDown>
81
81
  </S.QuantityNav>
@@ -92,7 +92,6 @@ export interface INumberFieldProps {
92
92
  maxValue?: number; // to deprecate
93
93
  min?: number;
94
94
  max?: number;
95
- onClickIcon?: () => void;
96
95
  onBlur?: (value: number | null | undefined) => void;
97
96
  disabled?: boolean;
98
97
  handleValidation?: (value: string, validators: Record<string, unknown>) => void;
@@ -1,6 +1,8 @@
1
1
  import styled from "styled-components";
2
2
 
3
3
  const QuantityButton = styled.div`
4
+ display: flex;
5
+ justify-content: center;
4
6
  background-color: ${(p) => p.theme.color.uiBackground03};
5
7
  position: absolute;
6
8
  cursor: pointer;
@@ -91,7 +91,7 @@ const ReferenceField = (props: IReferenceFieldProps) => {
91
91
  if (isReqOk(response?.status)) {
92
92
  return response.data;
93
93
  } else {
94
- console.log("Error en getSourcesTitles");
94
+ console.error("Error en getSourcesTitles");
95
95
  return [];
96
96
  }
97
97
  };
@@ -0,0 +1,36 @@
1
+ import * as S from "./style";
2
+
3
+ const SEOPreview = (props: IHeadingsPreviewProps) => {
4
+ const { actions } = props;
5
+ const { toggleHeadingsPreviewAction, toggleKeywordsPreviewAction } = actions || {};
6
+
7
+ return (
8
+ <S.Wrapper>
9
+ <S.TextWrapper>
10
+ Quickly review page <strong>headings</strong> for correctness it before publishing. You can also edit the text.
11
+ </S.TextWrapper>
12
+ <S.ButtonWrapper>
13
+ <S.StyledButton type="button" onClick={toggleHeadingsPreviewAction}>
14
+ Open Headings preview
15
+ </S.StyledButton>
16
+ </S.ButtonWrapper>
17
+ <S.TextWrapper margin>
18
+ Review the <strong>Keywords</strong> of the page in a quick way before publishing.
19
+ </S.TextWrapper>
20
+ <S.ButtonWrapper>
21
+ <S.StyledButton type="button" onClick={toggleKeywordsPreviewAction}>
22
+ Open Keywords preview
23
+ </S.StyledButton>
24
+ </S.ButtonWrapper>
25
+ </S.Wrapper>
26
+ );
27
+ };
28
+
29
+ interface IHeadingsPreviewProps {
30
+ actions?: {
31
+ toggleHeadingsPreviewAction?: () => void;
32
+ toggleKeywordsPreviewAction?: () => void;
33
+ };
34
+ }
35
+
36
+ export default SEOPreview;
@@ -0,0 +1,24 @@
1
+ import { Button } from "@ax/components";
2
+
3
+ import styled from "styled-components";
4
+
5
+ const Wrapper = styled.div``;
6
+
7
+ const TextWrapper = styled.div<{ margin?: boolean }>`
8
+ ${(p) => p.theme.textStyle.uiM};
9
+ color: ${(p) => p.theme.colors.textHighEmphasis};
10
+ margin-top: ${(p) => (p.margin ? p.theme.spacing.m : 0)};
11
+ `;
12
+
13
+ const ButtonWrapper = styled.div`
14
+ display: flex;
15
+ justify-content: center;
16
+ margin-top: ${(p) => p.theme.spacing.xs};
17
+ `;
18
+
19
+ const StyledButton = styled((props) => <Button {...props} />)`
20
+ width: 100%;
21
+ max-width: 286px;
22
+ `;
23
+
24
+ export { Wrapper, TextWrapper, ButtonWrapper, StyledButton };
@@ -19,11 +19,12 @@ const Select = (props: ISelectProps): JSX.Element => {
19
19
  onChange,
20
20
  alignRight,
21
21
  maxWidth,
22
+ offSet,
22
23
  } = props;
23
24
 
24
25
  const className = error ? `react-select-error ${type}` : type;
25
26
  const emptyOption = { value: "", label: placeholder || "Empty" };
26
- const isSearchable = !(type === "inline" || type === "mini");
27
+ const isSearchable = !(type === "inline" || type === "mini" || type === "round");
27
28
 
28
29
  const [hasEmptyOption, setHasEmptyOption] = useState(!mandatory);
29
30
  const [inputText, setInputText] = useState<string>("");
@@ -90,6 +91,7 @@ const Select = (props: ISelectProps): JSX.Element => {
90
91
  inputValue={inputText}
91
92
  components={{ Menu }}
92
93
  onKeyDown={handleKeyDown}
94
+ offSet={offSet}
93
95
  />
94
96
  </div>
95
97
  );
@@ -109,11 +111,13 @@ export interface ISelectProps {
109
111
  alignRight?: boolean;
110
112
  onChange: (value: string) => void;
111
113
  maxWidth?: number;
114
+ offSet?: string;
112
115
  }
113
116
 
114
117
  interface IOptionProps {
115
118
  value: string;
116
119
  label: string;
120
+ isDisabled?: boolean;
117
121
  }
118
122
 
119
123
  export default Select;
@@ -6,6 +6,7 @@ export const StyledSelect = styled(Select)<{
6
6
  error: boolean | undefined;
7
7
  alignRight: boolean | undefined;
8
8
  maxWidth?: number;
9
+ offSet?: string;
9
10
  }>`
10
11
  ${(p) => p.theme.textStyle.fieldContent};
11
12
  color: ${(p) => p.theme.color.textHighEmphasis};
@@ -56,12 +57,25 @@ export const StyledSelect = styled(Select)<{
56
57
  max-width: calc(${(p) => p.theme.spacing.xl} * 6);
57
58
  margin-top: 0;
58
59
  z-index: 99;
60
+
59
61
  .react-select__menu-list {
60
62
  border: 1px solid ${(p) => p.theme.color.uiLine};
61
63
  border-radius: 4px;
62
64
  max-height: calc(${(p) => p.theme.spacing.l} * 3);
63
65
  overflow: auto;
64
66
  padding: 0;
67
+
68
+ &::-webkit-scrollbar {
69
+ -webkit-appearance: none;
70
+ width: 4px;
71
+ height: 100%;
72
+ }
73
+
74
+ &::-webkit-scrollbar-thumb {
75
+ border-radius: 4px;
76
+ background-color: ${(p) => p.theme.colors.iconNonActive};
77
+ }
78
+
65
79
  .react-select__option {
66
80
  background-color: ${(p) => p.theme.color.interactiveBackground};
67
81
  min-height: ${(p) => p.theme.spacing.l};
@@ -200,4 +214,46 @@ export const StyledSelect = styled(Select)<{
200
214
  }
201
215
  }
202
216
  }
217
+
218
+ &.round {
219
+ ${(p) => p.theme.textStyle.uiXS};
220
+ margin-bottom: 0;
221
+
222
+ .react-select__control {
223
+ height: 24px;
224
+ min-height: 24px;
225
+ border-radius: 10px;
226
+ border: none;
227
+ padding-left: ${(p) => p.theme.spacing.xs};
228
+
229
+ .react-select__value-container {
230
+ padding: 0;
231
+ }
232
+
233
+ .react-select__indicator {
234
+ padding: 0 4px;
235
+ svg {
236
+ width: ${(p) => p.theme.spacing.s};
237
+ height: ${(p) => p.theme.spacing.s};
238
+ }
239
+ }
240
+ }
241
+
242
+ .react-select__menu {
243
+ min-width: 80px;
244
+ width: fit-content;
245
+ left: ${(p) => (p.offSet ? p.offSet : "50%")};
246
+ transform: translateX(-50%);
247
+ margin-top: ${(p) => p.theme.spacing.xxs};
248
+
249
+ .react-select__option {
250
+ ${(p) => p.theme.textStyle.uiM};
251
+ padding: ${(p) => `${p.theme.spacing.xs} ${p.theme.spacing.m}`};
252
+ min-height: auto;
253
+ text-align: center;
254
+ white-space: nowrap;
255
+ }
256
+ }
257
+
258
+ }
203
259
  `;