@gravity-ui/page-constructor 2.10.0 → 2.12.0-alpha

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 (47) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/cjs/blocks/Questions/Questions.js +1 -1
  3. package/build/cjs/blocks/Security/Security.js +1 -1
  4. package/build/cjs/components/Anchor/Anchor.d.ts +1 -0
  5. package/build/cjs/components/Anchor/Anchor.js +3 -1
  6. package/build/cjs/components/BlockBase/BlockBase.js +2 -2
  7. package/build/cjs/components/BlockBase/__tests__/BlockBase.test.d.ts +1 -0
  8. package/build/cjs/components/BlockBase/__tests__/BlockBase.test.js +44 -0
  9. package/build/cjs/components/Button/Button.js +2 -1
  10. package/build/cjs/components/Image/Image.js +7 -6
  11. package/build/cjs/components/ImageBase/ImageBase.d.ts +10 -0
  12. package/build/cjs/components/ImageBase/ImageBase.js +12 -0
  13. package/build/cjs/components/OverflowScroller/OverflowScroller.d.ts +2 -0
  14. package/build/cjs/components/OverflowScroller/OverflowScroller.js +4 -3
  15. package/build/cjs/components/index.d.ts +2 -0
  16. package/build/cjs/components/index.js +3 -1
  17. package/build/cjs/containers/PageConstructor/Provider.d.ts +2 -0
  18. package/build/cjs/containers/PageConstructor/Provider.js +3 -1
  19. package/build/cjs/context/imageContext/imageContext.d.ts +7 -0
  20. package/build/cjs/context/imageContext/imageContext.js +6 -0
  21. package/build/cjs/context/imageContext/index.d.ts +1 -0
  22. package/build/cjs/context/imageContext/index.js +4 -0
  23. package/build/cjs/models/constructor-items/blocks.d.ts +1 -0
  24. package/build/esm/blocks/Questions/Questions.js +1 -1
  25. package/build/esm/blocks/Security/Security.js +2 -2
  26. package/build/esm/components/Anchor/Anchor.d.ts +1 -0
  27. package/build/esm/components/Anchor/Anchor.js +2 -1
  28. package/build/esm/components/BlockBase/BlockBase.js +2 -2
  29. package/build/esm/components/BlockBase/__tests__/BlockBase.test.d.ts +1 -0
  30. package/build/esm/components/BlockBase/__tests__/BlockBase.test.js +41 -0
  31. package/build/esm/components/Button/Button.js +2 -1
  32. package/build/esm/components/Image/Image.js +7 -6
  33. package/build/esm/components/ImageBase/ImageBase.d.ts +10 -0
  34. package/build/esm/components/ImageBase/ImageBase.js +7 -0
  35. package/build/esm/components/OverflowScroller/OverflowScroller.d.ts +2 -0
  36. package/build/esm/components/OverflowScroller/OverflowScroller.js +4 -3
  37. package/build/esm/components/index.d.ts +2 -0
  38. package/build/esm/components/index.js +1 -0
  39. package/build/esm/containers/PageConstructor/Provider.d.ts +2 -0
  40. package/build/esm/containers/PageConstructor/Provider.js +3 -1
  41. package/build/esm/context/imageContext/imageContext.d.ts +7 -0
  42. package/build/esm/context/imageContext/imageContext.js +2 -0
  43. package/build/esm/context/imageContext/index.d.ts +1 -0
  44. package/build/esm/context/imageContext/index.js +1 -0
  45. package/build/esm/models/constructor-items/blocks.d.ts +1 -0
  46. package/package.json +1 -1
  47. package/server/models/constructor-items/blocks.d.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.11.0](https://github.com/gravity-ui/page-constructor/compare/v2.10.0...v2.11.0) (2023-04-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * add image context to use NextImage instead of normal img tag ([#313](https://github.com/gravity-ui/page-constructor/issues/313)) ([10841dc](https://github.com/gravity-ui/page-constructor/commit/10841dcbcc9bb16255dff57f4d79de94533a06c3))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **QuestionBlock:** fix itemprop parameter value for FaqPage microdata ([#316](https://github.com/gravity-ui/page-constructor/issues/316)) ([4395f8a](https://github.com/gravity-ui/page-constructor/commit/4395f8a78489ee254dfa25051d8f812a7ed0bf4b))
14
+
3
15
  ## [2.10.0](https://github.com/gravity-ui/page-constructor/compare/v2.9.0...v2.10.0) (2023-04-24)
4
16
 
5
17
 
@@ -12,7 +12,7 @@ const FaqMicrodataValues = {
12
12
  PageType: 'https://schema.org/FAQPage',
13
13
  QuestionType: 'https://schema.org/Question',
14
14
  QuestionProp: 'mainEntity',
15
- QuestionTextProp: 'name',
15
+ QuestionTextProp: 'text',
16
16
  AnswerType: 'https://schema.org/Answer',
17
17
  AnswerProp: 'acceptedAnswer',
18
18
  AnswerTextProp: 'text',
@@ -17,7 +17,7 @@ const SecurityBlock = (props) => {
17
17
  react_1.default.createElement(grid_1.Col, null,
18
18
  react_1.default.createElement("h2", { className: b('title') }, title))),
19
19
  points && (react_1.default.createElement(grid_1.Row, { className: b('points') }, points.map(({ text, link, img }, index) => (react_1.default.createElement(grid_1.Col, { key: index, className: b('point'), sizes: { sm: 4, all: 12 } },
20
- react_1.default.createElement("img", { className: b('point-icon'), src: img }),
20
+ react_1.default.createElement(components_1.ImageBase, { className: b('point-icon'), src: img }),
21
21
  react_1.default.createElement(components_1.HTML, { className: b('point-text'), block: true }, text),
22
22
  link && (react_1.default.createElement(components_1.Link, { className: b('point-link'), text: link.text, url: link.url, theme: 'normal', arrow: true }))))))),
23
23
  media && (react_1.default.createElement(grid_1.Row, { className: b('media') },
@@ -1,4 +1,5 @@
1
1
  import { ClassNameProps } from '../../models';
2
+ export declare const qaIdByDefault = "qa-anchor";
2
3
  export interface AnchorProps extends ClassNameProps {
3
4
  id: string;
4
5
  dataQa?: string;
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.qaIdByDefault = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  //TODO move to cloud components
5
6
  const react_1 = tslib_1.__importDefault(require("react"));
6
7
  const utils_1 = require("../../utils");
7
8
  const b = (0, utils_1.block)('Anchor');
8
- const Anchor = ({ id, className, dataQa }) => (react_1.default.createElement("div", { id: id, className: b(null, className), "data-qa": dataQa }));
9
+ exports.qaIdByDefault = 'qa-anchor';
10
+ const Anchor = ({ id, className, dataQa }) => (react_1.default.createElement("div", { id: id, className: b(null, className), "data-qa": dataQa || exports.qaIdByDefault }));
9
11
  exports.default = Anchor;
@@ -7,8 +7,8 @@ const utils_1 = require("../../utils");
7
7
  const Anchor_1 = tslib_1.__importDefault(require("../Anchor/Anchor"));
8
8
  const b = (0, utils_1.block)('block-base');
9
9
  const BlockBase = (props) => {
10
- const { anchor, visible, children, className, resetPaddings } = props;
11
- return (react_1.default.createElement(grid_1.Col, { className: b({ ['reset-paddings']: resetPaddings }, className), visible: visible, reset: true },
10
+ const { anchor, visible, children, className, resetPaddings, qa } = props;
11
+ return (react_1.default.createElement(grid_1.Col, { className: b({ ['reset-paddings']: resetPaddings }, className), visible: visible, reset: true, dataQa: qa },
12
12
  anchor && react_1.default.createElement(Anchor_1.default, { id: anchor.url, className: b('anchor') }),
13
13
  children));
14
14
  };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const react_1 = tslib_1.__importDefault(require("react"));
5
+ const react_2 = require("@testing-library/react");
6
+ const Anchor_1 = require("../../../components/Anchor/Anchor");
7
+ const grid_1 = require("../../../grid");
8
+ const BlockBase_1 = tslib_1.__importDefault(require("../BlockBase"));
9
+ const qaId = 'block-base-component';
10
+ describe('BlockBase', () => {
11
+ test('render component by default', async () => {
12
+ (0, react_2.render)(react_1.default.createElement(BlockBase_1.default, { qa: qaId }));
13
+ const component = react_2.screen.getByTestId(qaId);
14
+ expect(component).toBeInTheDocument();
15
+ expect(component).toBeVisible();
16
+ expect(component).not.toBeDisabled();
17
+ });
18
+ test('add className', () => {
19
+ const className = 'my-class';
20
+ (0, react_2.render)(react_1.default.createElement(BlockBase_1.default, { qa: qaId, className: className }));
21
+ const component = react_2.screen.getByTestId(qaId);
22
+ expect(component).toHaveClass(className);
23
+ });
24
+ test('should reset paddings', () => {
25
+ (0, react_2.render)(react_1.default.createElement(BlockBase_1.default, { qa: qaId, resetPaddings: true }));
26
+ const component = react_2.screen.getByTestId(qaId);
27
+ expect(component).toHaveClass('pc-block-base_reset-paddings');
28
+ });
29
+ test.each(new Array(...Object.values(grid_1.GridColumnSize)))('render with given "%s" size', (size) => {
30
+ (0, react_2.render)(react_1.default.createElement(BlockBase_1.default, { qa: qaId, visible: size }));
31
+ const component = react_2.screen.getByTestId(qaId);
32
+ expect(component).toHaveClass(`d-${size}-block`);
33
+ });
34
+ test('should have anchor', () => {
35
+ const anchor = {
36
+ text: 'anchor',
37
+ url: 'https://github.com/gravity-ui/',
38
+ };
39
+ (0, react_2.render)(react_1.default.createElement(BlockBase_1.default, { anchor: anchor }));
40
+ const component = react_2.screen.getByTestId(Anchor_1.qaIdByDefault);
41
+ expect(component).toBeInTheDocument();
42
+ expect(component).toHaveAttribute('id', anchor.url);
43
+ });
44
+ });
@@ -9,6 +9,7 @@ const useMetrika_1 = require("../../hooks/useMetrika");
9
9
  const icons_1 = require("../../icons");
10
10
  const models_1 = require("../../models");
11
11
  const utils_1 = require("../../utils");
12
+ const ImageBase_1 = tslib_1.__importDefault(require("../ImageBase/ImageBase"));
12
13
  const utils_2 = require("./utils");
13
14
  const b = (0, utils_1.block)('button-block');
14
15
  const Button = (props) => {
@@ -36,7 +37,7 @@ const Button = (props) => {
36
37
  return react_1.default.createElement(uikit_1.StoreBadge, { className: buttonClass, platform: platform, lang: lang, url: url });
37
38
  }
38
39
  let icon;
39
- let image = img && react_1.default.createElement("img", { className: b('image'), src: buttonImg.url, alt: buttonImg.alt });
40
+ let image = img && react_1.default.createElement(ImageBase_1.default, { className: b('image'), src: buttonImg.url, alt: buttonImg.alt });
40
41
  if (theme === 'github') {
41
42
  icon = react_1.default.createElement(uikit_1.Icon, { className: b('icon'), data: icons_1.Github, size: 24, qa: utils_2.ICON_QA });
42
43
  image = undefined;
@@ -5,20 +5,21 @@ const react_1 = tslib_1.__importStar(require("react"));
5
5
  const constants_1 = require("../../constants");
6
6
  const projectSettingsContext_1 = require("../../context/projectSettingsContext");
7
7
  const imageCompress_1 = require("../../utils/imageCompress");
8
+ const ImageBase_1 = tslib_1.__importDefault(require("../ImageBase/ImageBase"));
8
9
  const checkWebP = (src) => {
9
10
  return src.endsWith('.webp') ? src : src + '.webp';
10
11
  };
11
12
  const Image = (props) => {
12
13
  const projectSettings = (0, react_1.useContext)(projectSettingsContext_1.ProjectSettingsContext);
13
- const { src, alt, disableCompress, tablet, desktop, mobile, style, className, onClick, containerClassName, } = props;
14
+ const { src: imageSrc, alt, disableCompress, tablet, desktop, mobile, style, className, onClick, containerClassName, } = props;
14
15
  const [imgLoadingError, setImgLoadingError] = (0, react_1.useState)(false);
15
- const imageSrc = src || desktop;
16
- if (!imageSrc) {
16
+ const src = imageSrc || desktop;
17
+ if (!src) {
17
18
  return null;
18
19
  }
19
20
  const disableWebp = projectSettings.disableCompress ||
20
21
  disableCompress ||
21
- !(0, imageCompress_1.isCompressible)(imageSrc) ||
22
+ !(0, imageCompress_1.isCompressible)(src) ||
22
23
  imgLoadingError;
23
24
  return (react_1.default.createElement("picture", { className: containerClassName },
24
25
  mobile && (react_1.default.createElement(react_1.Fragment, null,
@@ -27,7 +28,7 @@ const Image = (props) => {
27
28
  tablet && (react_1.default.createElement(react_1.Fragment, null,
28
29
  !disableWebp && (react_1.default.createElement("source", { srcSet: checkWebP(tablet), type: "image/webp", media: `(max-width: ${constants_1.BREAKPOINTS.md}px)` })),
29
30
  react_1.default.createElement("source", { srcSet: tablet, media: `(max-width: ${constants_1.BREAKPOINTS.md}px)` }))),
30
- imageSrc && !disableWebp && react_1.default.createElement("source", { srcSet: checkWebP(imageSrc), type: "image/webp" }),
31
- react_1.default.createElement("img", { className: className, src: imageSrc, alt: alt, style: style, onClick: onClick, onError: () => setImgLoadingError(true) })));
31
+ src && !disableWebp && react_1.default.createElement("source", { srcSet: checkWebP(src), type: "image/webp" }),
32
+ react_1.default.createElement(ImageBase_1.default, { className: className, alt: alt, src: src, style: style, onClick: onClick, onError: () => setImgLoadingError(true) })));
32
33
  };
33
34
  exports.default = Image;
@@ -0,0 +1,10 @@
1
+ import { CSSProperties, MouseEventHandler } from 'react';
2
+ import { ImageObjectProps } from '../../models';
3
+ export interface ImageBaseProps extends Partial<ImageObjectProps> {
4
+ style?: CSSProperties;
5
+ className?: string;
6
+ onClick?: MouseEventHandler;
7
+ onError?: () => void;
8
+ }
9
+ export declare const ImageBase: (props: ImageBaseProps) => JSX.Element;
10
+ export default ImageBase;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ImageBase = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const imageContext_1 = require("../../context/imageContext/imageContext");
7
+ const ImageBase = (props) => {
8
+ const { Image } = react_1.default.useContext(imageContext_1.ImageContext);
9
+ return Image ? react_1.default.createElement(Image, Object.assign({}, props)) : react_1.default.createElement("img", Object.assign({}, props));
10
+ };
11
+ exports.ImageBase = ImageBase;
12
+ exports.default = exports.ImageBase;
@@ -4,6 +4,8 @@ export interface OverflowScrollerProps {
4
4
  className?: string;
5
5
  onScrollStart?: () => void;
6
6
  onScrollEnd?: () => void;
7
+ arrowSize?: number;
8
+ arrowClassName?: string;
7
9
  }
8
10
  export interface OverflowScrollerState {
9
11
  arrows: Arrow[];
@@ -8,6 +8,7 @@ const utils_1 = require("../../utils");
8
8
  const b = (0, utils_1.block)('overflow-scroller');
9
9
  const TRANSITION_TIME = 300;
10
10
  const PADDING_SIZE = 24;
11
+ const DEFAULT_ARROW_SIZE = 18;
11
12
  class OverflowScroller extends react_1.default.Component {
12
13
  constructor() {
13
14
  super(...arguments);
@@ -72,7 +73,7 @@ class OverflowScroller extends react_1.default.Component {
72
73
  window.removeEventListener('resize', this.checkOverflow);
73
74
  }
74
75
  render() {
75
- const { className, children } = this.props;
76
+ const { className, arrowClassName, children, arrowSize = DEFAULT_ARROW_SIZE } = this.props;
76
77
  const { arrows, scrollValue } = this.state;
77
78
  const wrapperStyle = arrows.length ? { left: -scrollValue } : { left: 0 };
78
79
  const paddingLeft = arrows.includes('left');
@@ -83,8 +84,8 @@ class OverflowScroller extends react_1.default.Component {
83
84
  }) },
84
85
  react_1.default.createElement("div", { className: b(null, className), ref: this.containerRef },
85
86
  react_1.default.createElement("div", { className: b('wrapper'), style: wrapperStyle, ref: this.wrapperRef }, children)),
86
- arrows.map((direction) => (react_1.default.createElement("div", { key: direction, className: b('arrow', { type: direction }), onClick: (e) => this.handleScrollClick(e, direction) },
87
- react_1.default.createElement(__1.ToggleArrow, { size: 18, type: 'horizontal', iconType: "navigation" }))))));
87
+ arrows.map((direction) => (react_1.default.createElement("div", { key: direction, className: b('arrow', { type: direction }, arrowClassName), onClick: (e) => this.handleScrollClick(e, direction) },
88
+ react_1.default.createElement(__1.ToggleArrow, { size: arrowSize, type: 'horizontal', iconType: "navigation" }))))));
88
89
  }
89
90
  }
90
91
  exports.default = OverflowScroller;
@@ -16,6 +16,7 @@ export { default as FullWidthBackground } from './FullWidthBackground/FullWidthB
16
16
  export { default as HeaderBreadcrumbs } from './HeaderBreadcrumbs/HeaderBreadcrumbs';
17
17
  export { default as HeightCalculator } from './HeightCalculator/HeightCalculator';
18
18
  export { default as Image } from './Image/Image';
19
+ export { default as ImageBase } from './ImageBase/ImageBase';
19
20
  export { default as Link } from './Link/Link';
20
21
  export { default as Links } from './Link/Links';
21
22
  export { default as Media } from './Media/Media';
@@ -36,3 +37,4 @@ export { default as HTML } from './HTML/HTML';
36
37
  export { default as MetaInfo } from './MetaInfo/MetaInfo';
37
38
  export { default as FullScreenMedia } from './FullscreenMedia/FullScreenMedia';
38
39
  export type { RouterLinkProps } from './RouterLink/RouterLink';
40
+ export type { ImageBaseProps } from './ImageBase/ImageBase';
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.FullScreenMedia = exports.MetaInfo = exports.HTML = exports.RouterLink = exports.Author = exports.OverflowScroller = exports.Control = exports.YandexForm = exports.YFMWrapper = exports.VideoBlock = exports.UnpublishedLabel = exports.ToggleArrow = exports.Title = exports.Table = exports.ReactPlayer = exports.OutsideClick = exports.Media = exports.Links = exports.Link = exports.Image = exports.HeightCalculator = exports.HeaderBreadcrumbs = exports.FullWidthBackground = exports.FullscreenImage = exports.Foldable = exports.FileLink = exports.ErrorWrapper = exports.CardBase = exports.Button = exports.BlockHeader = exports.BlockBase = exports.BalancedMasonry = exports.BackLink = exports.BackgroundMedia = exports.BackgroundImage = exports.AnimateBlock = exports.Anchor = void 0;
6
+ exports.FullScreenMedia = exports.MetaInfo = exports.HTML = exports.RouterLink = exports.Author = exports.OverflowScroller = exports.Control = exports.YandexForm = exports.YFMWrapper = exports.VideoBlock = exports.UnpublishedLabel = exports.ToggleArrow = exports.Title = exports.Table = exports.ReactPlayer = exports.OutsideClick = exports.Media = exports.Links = exports.Link = exports.ImageBase = exports.Image = exports.HeightCalculator = exports.HeaderBreadcrumbs = exports.FullWidthBackground = exports.FullscreenImage = exports.Foldable = exports.FileLink = exports.ErrorWrapper = exports.CardBase = exports.Button = exports.BlockHeader = exports.BlockBase = exports.BalancedMasonry = exports.BackLink = exports.BackgroundMedia = exports.BackgroundImage = exports.AnimateBlock = exports.Anchor = void 0;
7
7
  var Anchor_1 = require("./Anchor/Anchor");
8
8
  Object.defineProperty(exports, "Anchor", { enumerable: true, get: function () { return __importDefault(Anchor_1).default; } });
9
9
  var AnimateBlock_1 = require("./AnimateBlock/AnimateBlock");
@@ -40,6 +40,8 @@ var HeightCalculator_1 = require("./HeightCalculator/HeightCalculator");
40
40
  Object.defineProperty(exports, "HeightCalculator", { enumerable: true, get: function () { return __importDefault(HeightCalculator_1).default; } });
41
41
  var Image_1 = require("./Image/Image");
42
42
  Object.defineProperty(exports, "Image", { enumerable: true, get: function () { return __importDefault(Image_1).default; } });
43
+ var ImageBase_1 = require("./ImageBase/ImageBase");
44
+ Object.defineProperty(exports, "ImageBase", { enumerable: true, get: function () { return __importDefault(ImageBase_1).default; } });
43
45
  var Link_1 = require("./Link/Link");
44
46
  Object.defineProperty(exports, "Link", { enumerable: true, get: function () { return __importDefault(Link_1).default; } });
45
47
  var Links_1 = require("./Link/Links");
@@ -1,4 +1,5 @@
1
1
  import { AnalyticsContextProps } from '../../context/analyticsContext';
2
+ import { ImageContextProps } from '../../context/imageContext';
2
3
  import { LocaleContextProps } from '../../context/localeContext';
3
4
  import { LocationContextProps } from '../../context/locationContext';
4
5
  import { MapsContextType } from '../../context/mapsContext/mapsContext';
@@ -17,5 +18,6 @@ export interface PageConstructorProviderProps {
17
18
  mapsContext?: MapsContextType;
18
19
  projectSettings?: ProjectSettingsContextProps;
19
20
  analytics?: AnalyticsContextProps;
21
+ image?: ImageContextProps;
20
22
  }
21
23
  export declare const PageConstructorProvider: (props: WithChildren<PageConstructorProviderProps>) => JSX.Element;
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importStar(require("react"));
6
6
  const constants_1 = require("../../components/constants");
7
7
  const analyticsContext_1 = require("../../context/analyticsContext");
8
+ const imageContext_1 = require("../../context/imageContext");
8
9
  const localeContext_1 = require("../../context/localeContext");
9
10
  const locationContext_1 = require("../../context/locationContext");
10
11
  const mapsContext_1 = require("../../context/mapsContext/mapsContext");
@@ -14,12 +15,13 @@ const projectSettingsContext_1 = require("../../context/projectSettingsContext")
14
15
  const ssrContext_1 = require("../../context/ssrContext");
15
16
  const ThemeValueContext_1 = require("../../context/theme/ThemeValueContext");
16
17
  const PageConstructorProvider = (props) => {
17
- const { isMobile, mapsContext = mapsContext_1.initialMapValue, locale = {}, location = {}, metrika = {}, analytics = {}, ssrConfig = {}, projectSettings = {}, theme = constants_1.DEFAULT_THEME, children, } = props;
18
+ const { isMobile, mapsContext = mapsContext_1.initialMapValue, locale = {}, location = {}, metrika = {}, analytics = {}, ssrConfig = {}, projectSettings = {}, theme = constants_1.DEFAULT_THEME, children, image = {}, } = props;
18
19
  /* eslint-disable react/jsx-key */
19
20
  const context = [
20
21
  react_1.default.createElement(ThemeValueContext_1.ThemeValueContext.Provider, { value: { themeValue: theme } }),
21
22
  react_1.default.createElement(projectSettingsContext_1.ProjectSettingsContext.Provider, { value: projectSettings }),
22
23
  react_1.default.createElement(localeContext_1.LocaleContext.Provider, { value: locale }),
24
+ react_1.default.createElement(imageContext_1.ImageContext.Provider, { value: image }),
23
25
  react_1.default.createElement(locationContext_1.LocationContext.Provider, { value: location }),
24
26
  react_1.default.createElement(mobileContext_1.MobileContext.Provider, { value: Boolean(isMobile) }),
25
27
  react_1.default.createElement(mapsContext_1.MapsContext.Provider, { value: mapsContext }),
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { ImageBaseProps } from '../../components';
3
+ export type Image = React.ComponentClass<ImageBaseProps> | React.FC<ImageBaseProps>;
4
+ export type ImageContextProps = {
5
+ Image?: Image;
6
+ };
7
+ export declare const ImageContext: React.Context<ImageContextProps>;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ImageContext = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ exports.ImageContext = react_1.default.createContext({});
@@ -0,0 +1 @@
1
+ export * from './imageContext';
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./imageContext"), exports);
@@ -39,6 +39,7 @@ export interface BlockBaseProps {
39
39
  anchor?: AnchorProps;
40
40
  visible?: GridColumnSize;
41
41
  resetPaddings?: boolean;
42
+ qa?: string;
42
43
  }
43
44
  export interface LoadableProps {
44
45
  source: string;
@@ -10,7 +10,7 @@ const FaqMicrodataValues = {
10
10
  PageType: 'https://schema.org/FAQPage',
11
11
  QuestionType: 'https://schema.org/Question',
12
12
  QuestionProp: 'mainEntity',
13
- QuestionTextProp: 'name',
13
+ QuestionTextProp: 'text',
14
14
  AnswerType: 'https://schema.org/Answer',
15
15
  AnswerProp: 'acceptedAnswer',
16
16
  AnswerTextProp: 'text',
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { AnimateBlock, FullWidthBackground, HTML, Link, Media } from '../../components';
2
+ import { AnimateBlock, FullWidthBackground, HTML, ImageBase, Link, Media } from '../../components';
3
3
  import { Col, Grid, Row } from '../../grid';
4
4
  import { block } from '../../utils';
5
5
  import './Security.css';
@@ -14,7 +14,7 @@ export const SecurityBlock = (props) => {
14
14
  React.createElement(Col, null,
15
15
  React.createElement("h2", { className: b('title') }, title))),
16
16
  points && (React.createElement(Row, { className: b('points') }, points.map(({ text, link, img }, index) => (React.createElement(Col, { key: index, className: b('point'), sizes: { sm: 4, all: 12 } },
17
- React.createElement("img", { className: b('point-icon'), src: img }),
17
+ React.createElement(ImageBase, { className: b('point-icon'), src: img }),
18
18
  React.createElement(HTML, { className: b('point-text'), block: true }, text),
19
19
  link && (React.createElement(Link, { className: b('point-link'), text: link.text, url: link.url, theme: 'normal', arrow: true }))))))),
20
20
  media && (React.createElement(Row, { className: b('media') },
@@ -1,5 +1,6 @@
1
1
  import { ClassNameProps } from '../../models';
2
2
  import './Anchor.css';
3
+ export declare const qaIdByDefault = "qa-anchor";
3
4
  export interface AnchorProps extends ClassNameProps {
4
5
  id: string;
5
6
  dataQa?: string;
@@ -3,5 +3,6 @@ import React from 'react';
3
3
  import { block } from '../../utils';
4
4
  import './Anchor.css';
5
5
  const b = block('Anchor');
6
- const Anchor = ({ id, className, dataQa }) => (React.createElement("div", { id: id, className: b(null, className), "data-qa": dataQa }));
6
+ export const qaIdByDefault = 'qa-anchor';
7
+ const Anchor = ({ id, className, dataQa }) => (React.createElement("div", { id: id, className: b(null, className), "data-qa": dataQa || qaIdByDefault }));
7
8
  export default Anchor;
@@ -5,8 +5,8 @@ import Anchor from '../Anchor/Anchor';
5
5
  import './BlockBase.css';
6
6
  const b = block('block-base');
7
7
  const BlockBase = (props) => {
8
- const { anchor, visible, children, className, resetPaddings } = props;
9
- return (React.createElement(Col, { className: b({ ['reset-paddings']: resetPaddings }, className), visible: visible, reset: true },
8
+ const { anchor, visible, children, className, resetPaddings, qa } = props;
9
+ return (React.createElement(Col, { className: b({ ['reset-paddings']: resetPaddings }, className), visible: visible, reset: true, dataQa: qa },
10
10
  anchor && React.createElement(Anchor, { id: anchor.url, className: b('anchor') }),
11
11
  children));
12
12
  };
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { qaIdByDefault } from '../../../components/Anchor/Anchor';
4
+ import { GridColumnSize } from '../../../grid';
5
+ import BlockBase from '../BlockBase';
6
+ const qaId = 'block-base-component';
7
+ describe('BlockBase', () => {
8
+ test('render component by default', async () => {
9
+ render(React.createElement(BlockBase, { qa: qaId }));
10
+ const component = screen.getByTestId(qaId);
11
+ expect(component).toBeInTheDocument();
12
+ expect(component).toBeVisible();
13
+ expect(component).not.toBeDisabled();
14
+ });
15
+ test('add className', () => {
16
+ const className = 'my-class';
17
+ render(React.createElement(BlockBase, { qa: qaId, className: className }));
18
+ const component = screen.getByTestId(qaId);
19
+ expect(component).toHaveClass(className);
20
+ });
21
+ test('should reset paddings', () => {
22
+ render(React.createElement(BlockBase, { qa: qaId, resetPaddings: true }));
23
+ const component = screen.getByTestId(qaId);
24
+ expect(component).toHaveClass('pc-block-base_reset-paddings');
25
+ });
26
+ test.each(new Array(...Object.values(GridColumnSize)))('render with given "%s" size', (size) => {
27
+ render(React.createElement(BlockBase, { qa: qaId, visible: size }));
28
+ const component = screen.getByTestId(qaId);
29
+ expect(component).toHaveClass(`d-${size}-block`);
30
+ });
31
+ test('should have anchor', () => {
32
+ const anchor = {
33
+ text: 'anchor',
34
+ url: 'https://github.com/gravity-ui/',
35
+ };
36
+ render(React.createElement(BlockBase, { anchor: anchor }));
37
+ const component = screen.getByTestId(qaIdByDefault);
38
+ expect(component).toBeInTheDocument();
39
+ expect(component).toHaveAttribute('id', anchor.url);
40
+ });
41
+ });
@@ -7,6 +7,7 @@ import { useMetrika } from '../../hooks/useMetrika';
7
7
  import { Github } from '../../icons';
8
8
  import { DefaultEventNames } from '../../models';
9
9
  import { block, setUrlTld } from '../../utils';
10
+ import ImageBase from '../ImageBase/ImageBase';
10
11
  import { ICON_QA, toCommonSize, toCommonView } from './utils';
11
12
  import './Button.css';
12
13
  const b = block('button-block');
@@ -35,7 +36,7 @@ const Button = (props) => {
35
36
  return React.createElement(StoreBadge, { className: buttonClass, platform: platform, lang: lang, url: url });
36
37
  }
37
38
  let icon;
38
- let image = img && React.createElement("img", { className: b('image'), src: buttonImg.url, alt: buttonImg.alt });
39
+ let image = img && React.createElement(ImageBase, { className: b('image'), src: buttonImg.url, alt: buttonImg.alt });
39
40
  if (theme === 'github') {
40
41
  icon = React.createElement(Icon, { className: b('icon'), data: Github, size: 24, qa: ICON_QA });
41
42
  image = undefined;
@@ -2,20 +2,21 @@ import React, { Fragment, useContext, useState } from 'react';
2
2
  import { BREAKPOINTS } from '../../constants';
3
3
  import { ProjectSettingsContext } from '../../context/projectSettingsContext';
4
4
  import { isCompressible } from '../../utils/imageCompress';
5
+ import ImageBase from '../ImageBase/ImageBase';
5
6
  const checkWebP = (src) => {
6
7
  return src.endsWith('.webp') ? src : src + '.webp';
7
8
  };
8
9
  const Image = (props) => {
9
10
  const projectSettings = useContext(ProjectSettingsContext);
10
- const { src, alt, disableCompress, tablet, desktop, mobile, style, className, onClick, containerClassName, } = props;
11
+ const { src: imageSrc, alt, disableCompress, tablet, desktop, mobile, style, className, onClick, containerClassName, } = props;
11
12
  const [imgLoadingError, setImgLoadingError] = useState(false);
12
- const imageSrc = src || desktop;
13
- if (!imageSrc) {
13
+ const src = imageSrc || desktop;
14
+ if (!src) {
14
15
  return null;
15
16
  }
16
17
  const disableWebp = projectSettings.disableCompress ||
17
18
  disableCompress ||
18
- !isCompressible(imageSrc) ||
19
+ !isCompressible(src) ||
19
20
  imgLoadingError;
20
21
  return (React.createElement("picture", { className: containerClassName },
21
22
  mobile && (React.createElement(Fragment, null,
@@ -24,7 +25,7 @@ const Image = (props) => {
24
25
  tablet && (React.createElement(Fragment, null,
25
26
  !disableWebp && (React.createElement("source", { srcSet: checkWebP(tablet), type: "image/webp", media: `(max-width: ${BREAKPOINTS.md}px)` })),
26
27
  React.createElement("source", { srcSet: tablet, media: `(max-width: ${BREAKPOINTS.md}px)` }))),
27
- imageSrc && !disableWebp && React.createElement("source", { srcSet: checkWebP(imageSrc), type: "image/webp" }),
28
- React.createElement("img", { className: className, src: imageSrc, alt: alt, style: style, onClick: onClick, onError: () => setImgLoadingError(true) })));
28
+ src && !disableWebp && React.createElement("source", { srcSet: checkWebP(src), type: "image/webp" }),
29
+ React.createElement(ImageBase, { className: className, alt: alt, src: src, style: style, onClick: onClick, onError: () => setImgLoadingError(true) })));
29
30
  };
30
31
  export default Image;
@@ -0,0 +1,10 @@
1
+ import { CSSProperties, MouseEventHandler } from 'react';
2
+ import { ImageObjectProps } from '../../models';
3
+ export interface ImageBaseProps extends Partial<ImageObjectProps> {
4
+ style?: CSSProperties;
5
+ className?: string;
6
+ onClick?: MouseEventHandler;
7
+ onError?: () => void;
8
+ }
9
+ export declare const ImageBase: (props: ImageBaseProps) => JSX.Element;
10
+ export default ImageBase;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { ImageContext } from '../../context/imageContext/imageContext';
3
+ export const ImageBase = (props) => {
4
+ const { Image } = React.useContext(ImageContext);
5
+ return Image ? React.createElement(Image, Object.assign({}, props)) : React.createElement("img", Object.assign({}, props));
6
+ };
7
+ export default ImageBase;
@@ -5,6 +5,8 @@ export interface OverflowScrollerProps {
5
5
  className?: string;
6
6
  onScrollStart?: () => void;
7
7
  onScrollEnd?: () => void;
8
+ arrowSize?: number;
9
+ arrowClassName?: string;
8
10
  }
9
11
  export interface OverflowScrollerState {
10
12
  arrows: Arrow[];
@@ -6,6 +6,7 @@ import './OverflowScroller.css';
6
6
  const b = block('overflow-scroller');
7
7
  const TRANSITION_TIME = 300;
8
8
  const PADDING_SIZE = 24;
9
+ const DEFAULT_ARROW_SIZE = 18;
9
10
  export default class OverflowScroller extends React.Component {
10
11
  constructor() {
11
12
  super(...arguments);
@@ -70,7 +71,7 @@ export default class OverflowScroller extends React.Component {
70
71
  window.removeEventListener('resize', this.checkOverflow);
71
72
  }
72
73
  render() {
73
- const { className, children } = this.props;
74
+ const { className, arrowClassName, children, arrowSize = DEFAULT_ARROW_SIZE } = this.props;
74
75
  const { arrows, scrollValue } = this.state;
75
76
  const wrapperStyle = arrows.length ? { left: -scrollValue } : { left: 0 };
76
77
  const paddingLeft = arrows.includes('left');
@@ -81,7 +82,7 @@ export default class OverflowScroller extends React.Component {
81
82
  }) },
82
83
  React.createElement("div", { className: b(null, className), ref: this.containerRef },
83
84
  React.createElement("div", { className: b('wrapper'), style: wrapperStyle, ref: this.wrapperRef }, children)),
84
- arrows.map((direction) => (React.createElement("div", { key: direction, className: b('arrow', { type: direction }), onClick: (e) => this.handleScrollClick(e, direction) },
85
- React.createElement(ToggleArrow, { size: 18, type: 'horizontal', iconType: "navigation" }))))));
85
+ arrows.map((direction) => (React.createElement("div", { key: direction, className: b('arrow', { type: direction }, arrowClassName), onClick: (e) => this.handleScrollClick(e, direction) },
86
+ React.createElement(ToggleArrow, { size: arrowSize, type: 'horizontal', iconType: "navigation" }))))));
86
87
  }
87
88
  }
@@ -16,6 +16,7 @@ export { default as FullWidthBackground } from './FullWidthBackground/FullWidthB
16
16
  export { default as HeaderBreadcrumbs } from './HeaderBreadcrumbs/HeaderBreadcrumbs';
17
17
  export { default as HeightCalculator } from './HeightCalculator/HeightCalculator';
18
18
  export { default as Image } from './Image/Image';
19
+ export { default as ImageBase } from './ImageBase/ImageBase';
19
20
  export { default as Link } from './Link/Link';
20
21
  export { default as Links } from './Link/Links';
21
22
  export { default as Media } from './Media/Media';
@@ -36,3 +37,4 @@ export { default as HTML } from './HTML/HTML';
36
37
  export { default as MetaInfo } from './MetaInfo/MetaInfo';
37
38
  export { default as FullScreenMedia } from './FullscreenMedia/FullScreenMedia';
38
39
  export type { RouterLinkProps } from './RouterLink/RouterLink';
40
+ export type { ImageBaseProps } from './ImageBase/ImageBase';
@@ -16,6 +16,7 @@ export { default as FullWidthBackground } from './FullWidthBackground/FullWidthB
16
16
  export { default as HeaderBreadcrumbs } from './HeaderBreadcrumbs/HeaderBreadcrumbs';
17
17
  export { default as HeightCalculator } from './HeightCalculator/HeightCalculator';
18
18
  export { default as Image } from './Image/Image';
19
+ export { default as ImageBase } from './ImageBase/ImageBase';
19
20
  export { default as Link } from './Link/Link';
20
21
  export { default as Links } from './Link/Links';
21
22
  export { default as Media } from './Media/Media';
@@ -1,4 +1,5 @@
1
1
  import { AnalyticsContextProps } from '../../context/analyticsContext';
2
+ import { ImageContextProps } from '../../context/imageContext';
2
3
  import { LocaleContextProps } from '../../context/localeContext';
3
4
  import { LocationContextProps } from '../../context/locationContext';
4
5
  import { MapsContextType } from '../../context/mapsContext/mapsContext';
@@ -17,5 +18,6 @@ export interface PageConstructorProviderProps {
17
18
  mapsContext?: MapsContextType;
18
19
  projectSettings?: ProjectSettingsContextProps;
19
20
  analytics?: AnalyticsContextProps;
21
+ image?: ImageContextProps;
20
22
  }
21
23
  export declare const PageConstructorProvider: (props: WithChildren<PageConstructorProviderProps>) => JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import React, { Fragment } from 'react';
2
2
  import { DEFAULT_THEME } from '../../components/constants';
3
3
  import { AnalyticsContext } from '../../context/analyticsContext';
4
+ import { ImageContext } from '../../context/imageContext';
4
5
  import { LocaleContext } from '../../context/localeContext';
5
6
  import { LocationContext } from '../../context/locationContext';
6
7
  import { MapsContext, initialMapValue } from '../../context/mapsContext/mapsContext';
@@ -10,12 +11,13 @@ import { ProjectSettingsContext, } from '../../context/projectSettingsContext';
10
11
  import { SSRContext } from '../../context/ssrContext';
11
12
  import { ThemeValueContext } from '../../context/theme/ThemeValueContext';
12
13
  export const PageConstructorProvider = (props) => {
13
- const { isMobile, mapsContext = initialMapValue, locale = {}, location = {}, metrika = {}, analytics = {}, ssrConfig = {}, projectSettings = {}, theme = DEFAULT_THEME, children, } = props;
14
+ const { isMobile, mapsContext = initialMapValue, locale = {}, location = {}, metrika = {}, analytics = {}, ssrConfig = {}, projectSettings = {}, theme = DEFAULT_THEME, children, image = {}, } = props;
14
15
  /* eslint-disable react/jsx-key */
15
16
  const context = [
16
17
  React.createElement(ThemeValueContext.Provider, { value: { themeValue: theme } }),
17
18
  React.createElement(ProjectSettingsContext.Provider, { value: projectSettings }),
18
19
  React.createElement(LocaleContext.Provider, { value: locale }),
20
+ React.createElement(ImageContext.Provider, { value: image }),
19
21
  React.createElement(LocationContext.Provider, { value: location }),
20
22
  React.createElement(MobileContext.Provider, { value: Boolean(isMobile) }),
21
23
  React.createElement(MapsContext.Provider, { value: mapsContext }),
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { ImageBaseProps } from '../../components';
3
+ export type Image = React.ComponentClass<ImageBaseProps> | React.FC<ImageBaseProps>;
4
+ export type ImageContextProps = {
5
+ Image?: Image;
6
+ };
7
+ export declare const ImageContext: React.Context<ImageContextProps>;
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export const ImageContext = React.createContext({});
@@ -0,0 +1 @@
1
+ export * from './imageContext';
@@ -0,0 +1 @@
1
+ export * from './imageContext';
@@ -39,6 +39,7 @@ export interface BlockBaseProps {
39
39
  anchor?: AnchorProps;
40
40
  visible?: GridColumnSize;
41
41
  resetPaddings?: boolean;
42
+ qa?: string;
42
43
  }
43
44
  export interface LoadableProps {
44
45
  source: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/page-constructor",
3
- "version": "2.10.0",
3
+ "version": "2.12.0-alpha",
4
4
  "description": "Gravity UI Page Constructor",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -39,6 +39,7 @@ export interface BlockBaseProps {
39
39
  anchor?: AnchorProps;
40
40
  visible?: GridColumnSize;
41
41
  resetPaddings?: boolean;
42
+ qa?: string;
42
43
  }
43
44
  export interface LoadableProps {
44
45
  source: string;