@griddo/ax 11.11.7 → 11.11.8-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 (104) 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 -144
  20. package/src/components/Browser/style.tsx +75 -6
  21. package/src/components/Browser/utils.tsx +13 -0
  22. package/src/components/BrowserContent/index.tsx +2 -2
  23. package/src/components/Button/index.tsx +2 -1
  24. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +2 -4
  25. package/src/components/Fields/AsyncSelect/style.tsx +13 -0
  26. package/src/components/Fields/FieldGroup/index.tsx +5 -2
  27. package/src/components/Fields/FieldGroup/style.tsx +32 -7
  28. package/src/components/Fields/HeadingField/index.tsx +2 -2
  29. package/src/components/Fields/HiddenField/style.tsx +1 -1
  30. package/src/components/Fields/NumberField/index.tsx +15 -16
  31. package/src/components/Fields/NumberField/style.tsx +2 -0
  32. package/src/components/Fields/ReferenceField/index.tsx +1 -1
  33. package/src/components/Fields/SEOPreview/index.tsx +36 -0
  34. package/src/components/Fields/SEOPreview/style.tsx +24 -0
  35. package/src/components/Fields/Select/index.tsx +5 -1
  36. package/src/components/Fields/Select/style.tsx +56 -0
  37. package/src/components/Fields/SummaryButton/index.tsx +18 -9
  38. package/src/components/Fields/SummaryButton/style.tsx +1 -2
  39. package/src/components/Fields/TagsField/index.tsx +8 -9
  40. package/src/components/Fields/UrlField/index.tsx +26 -27
  41. package/src/components/Fields/index.tsx +2 -0
  42. package/src/components/FloatingNote/index.tsx +35 -0
  43. package/src/components/FloatingNote/style.tsx +26 -0
  44. package/src/components/FloatingPanel/index.tsx +5 -2
  45. package/src/components/FloatingPanel/style.tsx +2 -1
  46. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +85 -0
  47. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/style.tsx +80 -0
  48. package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +57 -0
  49. package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +82 -0
  50. package/src/components/HeadingsPreviewModal/HeadingItem/index.tsx +71 -0
  51. package/src/components/HeadingsPreviewModal/HeadingItem/style.tsx +77 -0
  52. package/src/components/HeadingsPreviewModal/index.tsx +146 -0
  53. package/src/components/HeadingsPreviewModal/style.tsx +82 -0
  54. package/src/components/HeadingsPreviewModal/utils.tsx +257 -0
  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/OcassionalToast/index.tsx +8 -1
  66. package/src/components/OcassionalToast/style.tsx +15 -1
  67. package/src/components/PageFinder/index.tsx +1 -1
  68. package/src/components/ResizePanel/index.tsx +4 -3
  69. package/src/components/ResizePanel/style.tsx +1 -1
  70. package/src/components/SearchField/style.tsx +2 -2
  71. package/src/components/SideModal/index.tsx +2 -1
  72. package/src/components/Tabs/index.tsx +13 -4
  73. package/src/components/Tabs/style.tsx +7 -8
  74. package/src/components/Toast/index.tsx +4 -2
  75. package/src/components/Tooltip/index.tsx +4 -3
  76. package/src/components/index.tsx +8 -2
  77. package/src/forms/fields.tsx +70 -68
  78. package/src/hooks/forms.tsx +22 -1
  79. package/src/hooks/index.tsx +13 -3
  80. package/src/hooks/modals.tsx +103 -15
  81. package/src/hooks/users.tsx +25 -8
  82. package/src/modules/Forms/atoms.tsx +2 -2
  83. package/src/modules/FramePreview/HeadingsOverlay/index.tsx +113 -0
  84. package/src/modules/FramePreview/HeadingsOverlay/style.tsx +24 -0
  85. package/src/modules/FramePreview/index.tsx +55 -16
  86. package/src/modules/FramePreview/style.tsx +34 -2
  87. package/src/modules/FramePreview/utils.tsx +140 -0
  88. package/src/modules/GlobalEditor/Editor/index.tsx +37 -3
  89. package/src/modules/GlobalEditor/PageBrowser/index.tsx +19 -2
  90. package/src/modules/GlobalEditor/Preview/index.tsx +0 -2
  91. package/src/modules/GlobalEditor/Preview/style.tsx +1 -1
  92. package/src/modules/GlobalEditor/index.tsx +119 -57
  93. package/src/modules/PageEditor/Editor/index.tsx +33 -2
  94. package/src/modules/PageEditor/PageBrowser/index.tsx +20 -2
  95. package/src/modules/PageEditor/Preview/index.tsx +0 -2
  96. package/src/modules/PageEditor/Preview/style.tsx +1 -1
  97. package/src/modules/PageEditor/atoms.tsx +1 -1
  98. package/src/modules/PageEditor/index.tsx +130 -66
  99. package/src/modules/PublicPreview/index.tsx +8 -5
  100. package/src/schemas/pages/GlobalPage.ts +87 -70
  101. package/src/schemas/pages/Page.ts +87 -70
  102. package/src/types/index.tsx +12 -0
  103. package/src/components/PageInfoBanner/index.tsx +0 -38
  104. package/src/components/PageInfoBanner/styles.tsx +0 -40
@@ -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,6 +1,6 @@
1
1
  import { useCallback, useEffect, useState } from "react";
2
2
 
3
- import { PageInfoBanner } from "@ax/components";
3
+ import { OcassionalToast } from "@ax/components";
4
4
  import { formatDate } from "@ax/helpers";
5
5
  import type { ILanguage, ISocialState } from "@ax/types";
6
6
 
@@ -59,7 +59,7 @@ const BrowserContent = (props: IProps) => {
59
59
  selectHoverEditorID={selectHoverEditorID}
60
60
  >
61
61
  {renderer === "sharedPage" && showBanner && (
62
- <PageInfoBanner
62
+ <OcassionalToast
63
63
  message={`This draft link expires on ${shareEndDate ? formatDate(new Date(shareEndDate)) : "..."}`}
64
64
  icon="scheduled"
65
65
  onDismiss={() => setShowBanner(false)}
@@ -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,5 +1,5 @@
1
- import React, { memo } from "react";
2
- import { IHeadingField, ISite } from "@ax/types";
1
+ import { memo } from "react";
2
+ import type { IHeadingField, ISite } from "@ax/types";
3
3
  import { FieldsBehavior, TextField, Wysiwyg } from "@ax/components";
4
4
 
5
5
  import * as S from "./style";
@@ -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
  `;
@@ -1,12 +1,13 @@
1
- import React, { useState } from "react";
1
+ import { useState } from "react";
2
2
  import { connect } from "react-redux";
3
- import { IRootState } from "@ax/types";
3
+
4
+ import type { IRootState } from "@ax/types";
4
5
  import { pageEditorActions } from "@ax/containers/PageEditor";
5
6
 
6
7
  import * as S from "./style";
7
8
 
8
9
  const SummaryButton = (props: ISummaryButtonProps): JSX.Element => {
9
- const { autoSummary, getPageSummary, disabled } = props;
10
+ const { autoSummary, getPageSummary, disabled, description, className } = props;
10
11
 
11
12
  const initialState = {
12
13
  isLoading: false,
@@ -27,6 +28,15 @@ const SummaryButton = (props: ISummaryButtonProps): JSX.Element => {
27
28
  }
28
29
  };
29
30
 
31
+ const descriptionText = description ? (
32
+ description
33
+ ) : (
34
+ <>
35
+ You can automatically generate <strong>SEO meta descriptions</strong> and <strong>keywords</strong> quickly with{" "}
36
+ <strong>AI</strong>.
37
+ </>
38
+ );
39
+
30
40
  const buttonText = state.isLoading
31
41
  ? "Processing..."
32
42
  : state.isClicked
@@ -36,11 +46,8 @@ const SummaryButton = (props: ISummaryButtonProps): JSX.Element => {
36
46
  return (
37
47
  <>
38
48
  {autoSummary ? (
39
- <S.Wrapper data-testid="summary-button-wrapper">
40
- <S.Text>
41
- You can automatically generate <strong>SEO meta descriptions</strong> and <strong>keywords</strong> quickly
42
- with <strong>AI</strong>.
43
- </S.Text>
49
+ <S.Wrapper data-testid="summary-button-wrapper" className={className}>
50
+ <S.Text>{descriptionText}</S.Text>
44
51
  <S.ButtonWrapper>
45
52
  <S.StyledButton
46
53
  type="button"
@@ -63,7 +70,9 @@ const SummaryButton = (props: ISummaryButtonProps): JSX.Element => {
63
70
 
64
71
  export interface ISummaryButtonProps {
65
72
  autoSummary: boolean;
66
- disabled: boolean;
73
+ disabled?: boolean;
74
+ description?: React.ReactNode;
75
+ className?: string;
67
76
  getPageSummary: () => Promise<boolean>;
68
77
  }
69
78
 
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import styled from "styled-components";
3
2
  import { Button } from "@ax/components";
4
3
 
@@ -21,7 +20,7 @@ const ButtonWrapper = styled.div`
21
20
 
22
21
  const StyledButton = styled((props) => <Button {...props} />)`
23
22
  width: 100%;
24
- max-width: 264px;
23
+ max-width: 286px;
25
24
  `;
26
25
 
27
26
  const ErrorText = styled.div`