represents an inline code.
* It is marked in markdown with single backticks: some `inline code`
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} 'code' type of component
*/
export const Code = React.forwardRef((props, ref) => {
const { className, rootClassName, ...otherProps } = props;
@@ -26,12 +20,17 @@ export const Code = React.forwardRef((props, ref) => {
return
;
});
Code.displayName = 'Code';
-Code.defaultProps = defaultPropsCode;
-Code.propTypes = propTypesCode;
/**
* HTML element represents a preformatted text.
* Codeblock in markdown is rendered with tag.
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} CodeBlock type of component rendered with 'pre' element
*/
export const CodeBlock = React.forwardRef((props, ref) => {
const { className, rootClassName, ...otherProps } = props;
@@ -40,5 +39,3 @@ export const CodeBlock = React.forwardRef((props, ref) => {
return ;
});
CodeBlock.displayName = 'CodeBlock';
-CodeBlock.defaultProps = defaultPropsCode;
-CodeBlock.propTypes = propTypesCode;
diff --git a/src/containers/PageBuilder/Primitives/CustomAppearance/CustomAppearance.js b/src/containers/PageBuilder/Primitives/CustomAppearance/CustomAppearance.js
index 8136caf14..0d1dcd908 100644
--- a/src/containers/PageBuilder/Primitives/CustomAppearance/CustomAppearance.js
+++ b/src/containers/PageBuilder/Primitives/CustomAppearance/CustomAppearance.js
@@ -6,7 +6,35 @@ import { ResponsiveImage } from '../../../../components/index.js';
import css from './CustomAppearance.module.css';
-// BackgroundImage doesn't have enforcable aspectratio
+/**
+ * @typedef {Object} ImageVariant
+ * @property {number} width
+ * @property {number} height
+ * @property {string} url image source
+ */
+
+/**
+ * Render a custom appearance for a section component.
+ * E.g. change the background color or image of the SectionContainer
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.backgroundColor hexadecimal color string ('#ffaa00')
+ * @param {Object} props.backgroundImage
+ * @param {string} props.backgroundImage.id
+ * @param {'imageAsset'} props.backgroundImage.type
+ * @param {Object} props.backgroundImage.attributes
+ * @param {Object} props.backgroundImage.attributes.variants
+ * @param {Object} props.backgroundImageOverlay
+ * @param {string} props.backgroundImageOverlay.preset
+ * @param {string} props.backgroundImageOverlay.color
+ * @param {number} props.backgroundImageOverlay.opacity
+ * @param {string?} props.alt
+ * @param {string?} props.sizes
+ * @returns {JSX.Element} custom appearance for the container of a section component
+ */
export const CustomAppearance = React.forwardRef((props, ref) => {
const {
className,
@@ -14,7 +42,7 @@ export const CustomAppearance = React.forwardRef((props, ref) => {
backgroundColor,
backgroundImage,
backgroundImageOverlay,
- alt,
+ alt = 'background image',
sizes,
} = props;
@@ -52,39 +80,3 @@ export const CustomAppearance = React.forwardRef((props, ref) => {
});
CustomAppearance.displayName = 'CustomAppearance';
-
-CustomAppearance.defaultProps = {
- rootClassName: null,
- className: null,
- alt: 'background image',
- sizes: null,
- backgroundColor: null,
- backgroundImage: null,
- backgroundImageOverlay: null,
-};
-
-CustomAppearance.propTypes = {
- rootClassName: string,
- className: string,
- backgroundColor: string,
- backgroundImage: shape({
- id: string.isRequired,
- type: oneOf(['imageAsset']).isRequired,
- attributes: shape({
- variants: objectOf(
- shape({
- width: number.isRequired,
- height: number.isRequired,
- url: string.isRequired,
- })
- ).isRequired,
- }).isRequired,
- }),
- backgroundImageOverlay: shape({
- preset: string.isRequired,
- color: string.isRequired,
- opacity: number.isRequired,
- }),
- alt: string,
- sizes: string,
-};
diff --git a/src/containers/PageBuilder/Primitives/Heading/Heading.js b/src/containers/PageBuilder/Primitives/Heading/Heading.js
index fb6a4f176..651ad3d07 100644
--- a/src/containers/PageBuilder/Primitives/Heading/Heading.js
+++ b/src/containers/PageBuilder/Primitives/Heading/Heading.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { node, string } from 'prop-types';
import classNames from 'classnames';
import css from './Heading.module.css';
@@ -13,19 +12,17 @@ const Heading = props => {
return ;
};
-const defaultPropsHeading = {
- rootClassName: null,
- className: null,
- as: null,
-};
-
-const propTypesHeading = {
- rootClassName: string,
- className: string,
- children: node.isRequired,
- as: string,
-};
-
+/**
+ * Render a h1 heading element
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.as tag/element to be used. Default 'h1'
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} heading element
+ */
export const H1 = React.forwardRef((props, ref) => {
const { rootClassName: rootClass, as, ...otherProps } = props;
return (
@@ -33,9 +30,18 @@ export const H1 = React.forwardRef((props, ref) => {
);
});
H1.displayName = 'H1';
-H1.defaultProps = defaultPropsHeading;
-H1.propTypes = propTypesHeading;
+/**
+ * Render a h2 heading element
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.as tag/element to be used. Default 'h2'
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} heading element
+ */
export const H2 = React.forwardRef((props, ref) => {
const { rootClassName: rootClass, as, ...otherProps } = props;
return (
@@ -43,9 +49,18 @@ export const H2 = React.forwardRef((props, ref) => {
);
});
H2.displayName = 'H2';
-H2.defaultProps = defaultPropsHeading;
-H2.propTypes = propTypesHeading;
+/**
+ * Render a h3 heading element
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.as tag/element to be used. Default 'h3'
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} heading element
+ */
export const H3 = React.forwardRef((props, ref) => {
const { rootClassName: rootClass, as, ...otherProps } = props;
return (
@@ -53,9 +68,18 @@ export const H3 = React.forwardRef((props, ref) => {
);
});
H3.displayName = 'H3';
-H3.defaultProps = defaultPropsHeading;
-H3.propTypes = propTypesHeading;
+/**
+ * Render a h4 heading element
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.as tag/element to be used. Default 'h4'
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} heading element
+ */
export const H4 = React.forwardRef((props, ref) => {
const { rootClassName: rootClass, as, ...otherProps } = props;
return (
@@ -63,9 +87,18 @@ export const H4 = React.forwardRef((props, ref) => {
);
});
H4.displayName = 'H4';
-H4.defaultProps = defaultPropsHeading;
-H4.propTypes = propTypesHeading;
+/**
+ * Render a h5 heading element
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.as tag/element to be used. Default 'h5'
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} heading element
+ */
export const H5 = React.forwardRef((props, ref) => {
const { rootClassName: rootClass, as, ...otherProps } = props;
return (
@@ -73,9 +106,18 @@ export const H5 = React.forwardRef((props, ref) => {
);
});
H5.displayName = 'H5';
-H5.defaultProps = defaultPropsHeading;
-H5.propTypes = propTypesHeading;
+/**
+ * Render a h6 heading element
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.as tag/element to be used. Default 'h6'
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} heading element
+ */
export const H6 = React.forwardRef((props, ref) => {
const { rootClassName: rootClass, as, ...otherProps } = props;
return (
@@ -83,5 +125,3 @@ export const H6 = React.forwardRef((props, ref) => {
);
});
H6.displayName = 'H6';
-H6.defaultProps = defaultPropsHeading;
-H6.propTypes = propTypesHeading;
diff --git a/src/containers/PageBuilder/Primitives/Image/Image.js b/src/containers/PageBuilder/Primitives/Image/Image.js
index f3b77464c..07de1f748 100644
--- a/src/containers/PageBuilder/Primitives/Image/Image.js
+++ b/src/containers/PageBuilder/Primitives/Image/Image.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { number, objectOf, oneOf, shape, string } from 'prop-types';
import classNames from 'classnames';
import { AspectRatioWrapper, ResponsiveImage } from '../../../../components/index.js';
@@ -7,32 +6,51 @@ import { Link } from '../Link';
import css from './Image.module.css';
-// Images in markdown point to elsewhere (they don't support responsive image variants)
+/**
+ * Images in markdown point to elsewhere (they don't support responsive image variants)
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string} props.src image source
+ * @param {string?} props.alt alt text for the image
+ * @returns {JSX.Element} image element for markdown processor
+ */
export const MarkdownImage = React.forwardRef((props, ref) => {
- const { className, rootClassName, ...otherProps } = props;
+ const { className, rootClassName, alt = 'image', ...otherProps } = props;
const classes = classNames(rootClassName || css.markdownImage, className);
- return ;
+ return ;
});
MarkdownImage.displayName = 'MarkdownImage';
-MarkdownImage.defaultProps = {
- rootClassName: null,
- className: null,
- alt: 'image',
-};
-
-MarkdownImage.propTypes = {
- rootClassName: string,
- className: string,
- src: string.isRequired,
- alt: string,
-};
-
-// Image as a Field (by default these are only allowed inside a block).
+/**
+ * @typedef {Object} ImageVariant
+ * @property {number} width
+ * @property {number} height
+ * @property {string} url image source
+ */
+
+/**
+ * Image as a Field (by default these are only allowed inside a block).
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.alt alt text for the image
+ * @param {Object} props.image
+ * @param {Object} props.image.id
+ * @param {'imageAsset'} props.image.type
+ * @param {Object} props.image.attributes
+ * @param {Object.} props.image.attributes.variants
+ * @param {string?} props.sizes responsive sizes string to be used with srcset
+ * @returns {JSX.Element} image element
+ */
export const FieldImage = React.forwardRef((props, ref) => {
- const { className, rootClassName, alt, image, sizes, link, ...otherProps } = props;
+ const { className, rootClassName, alt = 'image', image, sizes, link, ...otherProps } = props;
const { variants } = image?.attributes || {};
const variantNames = Object.keys(variants);
@@ -75,30 +93,3 @@ export const FieldImage = React.forwardRef((props, ref) => {
});
FieldImage.displayName = 'FieldImage';
-
-FieldImage.defaultProps = {
- rootClassName: null,
- className: null,
- alt: 'image',
- sizes: null,
-};
-
-FieldImage.propTypes = {
- rootClassName: string,
- className: string,
- alt: string,
- image: shape({
- id: string.isRequired,
- type: oneOf(['imageAsset']).isRequired,
- attributes: shape({
- variants: objectOf(
- shape({
- width: number.isRequired,
- height: number.isRequired,
- url: string.isRequired,
- })
- ).isRequired,
- }).isRequired,
- }).isRequired,
- sizes: string,
-};
diff --git a/src/containers/PageBuilder/Primitives/Ingress/Ingress.js b/src/containers/PageBuilder/Primitives/Ingress/Ingress.js
index da7951e5f..e5cb24771 100644
--- a/src/containers/PageBuilder/Primitives/Ingress/Ingress.js
+++ b/src/containers/PageBuilder/Primitives/Ingress/Ingress.js
@@ -1,11 +1,19 @@
import React from 'react';
-import { node, string } from 'prop-types';
import classNames from 'classnames';
import css from './Ingress.module.css';
-// Ingress: a lead paragraph or an opening paragraph
-// It's usually between a headline and the article
+/**
+ * Ingress: a lead paragraph or an opening paragraph
+ * It's usually between a headline and the article
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} ingress (using 'p' element)
+ */
export const Ingress = React.forwardRef((props, ref) => {
const { className, rootClassName, ...otherProps } = props;
const classes = classNames(rootClassName || css.ingress, className);
@@ -14,13 +22,3 @@ export const Ingress = React.forwardRef((props, ref) => {
});
Ingress.displayName = 'Ingress';
-Ingress.defaultProps = {
- rootClassName: null,
- className: null,
-};
-
-Ingress.propTypes = {
- rootClassName: string,
- className: string,
- children: node.isRequired,
-};
diff --git a/src/containers/PageBuilder/Primitives/Link/Link.js b/src/containers/PageBuilder/Primitives/Link/Link.js
index ddc42b6b8..45266e218 100644
--- a/src/containers/PageBuilder/Primitives/Link/Link.js
+++ b/src/containers/PageBuilder/Primitives/Link/Link.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { node, string } from 'prop-types';
import classNames from 'classnames';
import { useLocation } from 'react-router-dom';
@@ -9,6 +8,17 @@ import { matchPathname } from '../../../../util/routes.js';
import { NamedLink, ExternalLink } from '../../../../components/index.js';
import css from './Link.module.css';
+/**
+ * Link element which internally uses NamedLink or ExternalLink
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @param {string} props.href link destination. In-app links need to start with '/'.
+ * @returns {JSX.Element} link element
+ */
export const Link = React.forwardRef((props, ref) => {
const location = useLocation();
const routes = useRouteConfiguration();
@@ -64,15 +74,3 @@ export const Link = React.forwardRef((props, ref) => {
});
Link.displayName = 'Link';
-
-Link.defaultProps = {
- rootClassName: null,
- className: null,
-};
-
-Link.propTypes = {
- rootClassName: string,
- className: string,
- children: node.isRequired,
- href: string.isRequired,
-};
diff --git a/src/containers/PageBuilder/Primitives/Link/SocialMediaLink.js b/src/containers/PageBuilder/Primitives/Link/SocialMediaLink.js
index 006accf67..a03264122 100644
--- a/src/containers/PageBuilder/Primitives/Link/SocialMediaLink.js
+++ b/src/containers/PageBuilder/Primitives/Link/SocialMediaLink.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { node, string } from 'prop-types';
import classNames from 'classnames';
import { ExternalLink } from '../../../../components/index.js';
@@ -43,6 +42,18 @@ export const supportedPlatforms = [
'youtube',
];
+/**
+ * Link element which internally uses NamedLink or ExternalLink
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.title
+ * @param {('facebook' | 'instagram' | 'linkedin' | 'pinterest' | 'tiktok' | 'twitter' | 'youtube')} props.platform social media service platform
+ * @param {string} props.href social media service profile
+ * @returns {JSX.Element} social media link (wraps a platform-specific SVG icon)
+ */
export const SocialMediaLink = React.forwardRef((props, ref) => {
const Icon = getIconConf(props.platform);
@@ -61,17 +72,3 @@ export const SocialMediaLink = React.forwardRef((props, ref) => {
});
SocialMediaLink.displayName = 'SocialMediaLink';
-
-SocialMediaLink.defaultProps = {
- title: null,
- rootClassName: null,
- className: null,
-};
-
-SocialMediaLink.propTypes = {
- title: string,
- rootClassName: string,
- className: string,
- platform: node.isRequired,
- href: string.isRequired,
-};
diff --git a/src/containers/PageBuilder/Primitives/List/List.js b/src/containers/PageBuilder/Primitives/List/List.js
index f338e4fef..695c7aba7 100644
--- a/src/containers/PageBuilder/Primitives/List/List.js
+++ b/src/containers/PageBuilder/Primitives/List/List.js
@@ -1,20 +1,18 @@
import React from 'react';
-import { node, string } from 'prop-types';
import classNames from 'classnames';
import css from './List.module.css';
-const defaultPropsList = {
- rootClassName: null,
- className: null,
-};
-
-const propTypesList = {
- rootClassName: string,
- className: string,
- children: node.isRequired,
-};
-
+/**
+ * Unordered list.
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} element
+ */
export const Ul = React.forwardRef((props, ref) => {
const { className, rootClassName, ...otherProps } = props;
const classes = classNames(rootClassName || css.ul, className);
@@ -22,9 +20,17 @@ export const Ul = React.forwardRef((props, ref) => {
return ;
});
Ul.displayName = 'Ul';
-Ul.defaultProps = defaultPropsList;
-Ul.propTypes = propTypesList;
+/**
+ * Ordered list.
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} element
+ */
export const Ol = React.forwardRef((props, ref) => {
const { className, rootClassName, ...otherProps } = props;
const classes = classNames(rootClassName || css.ol, className);
@@ -32,9 +38,17 @@ export const Ol = React.forwardRef((props, ref) => {
return
;
});
Ol.displayName = 'Ol';
-Ol.defaultProps = defaultPropsList;
-Ol.propTypes = propTypesList;
+/**
+ * List item.
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element} - element
+ */
export const Li = React.forwardRef((props, ref) => {
const { className, rootClassName, ...otherProps } = props;
const classes = classNames(rootClassName || css.li, className);
@@ -42,5 +56,3 @@ export const Li = React.forwardRef((props, ref) => {
return
;
});
Li.displayName = 'Li';
-Li.defaultProps = defaultPropsList;
-Li.propTypes = propTypesList;
diff --git a/src/containers/PageBuilder/Primitives/P/P.js b/src/containers/PageBuilder/Primitives/P/P.js
index 5df9db7c5..5587bfcc3 100644
--- a/src/containers/PageBuilder/Primitives/P/P.js
+++ b/src/containers/PageBuilder/Primitives/P/P.js
@@ -1,9 +1,18 @@
import React from 'react';
-import { node, string } from 'prop-types';
import classNames from 'classnames';
import css from './P.module.css';
+/**
+ * Paragraph
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @returns {JSX.Element}
element
+ */
export const P = React.forwardRef((props, ref) => {
const { className, rootClassName, ...otherProps } = props;
const classes = classNames(rootClassName || css.p, className);
@@ -12,15 +21,3 @@ export const P = React.forwardRef((props, ref) => {
});
P.displayName = 'P';
-
-P.defaultProps = {
- rootClassName: null,
- className: null,
- children: null,
-};
-
-P.propTypes = {
- rootClassName: string,
- className: string,
- children: node,
-};
diff --git a/src/containers/PageBuilder/Primitives/Text/Text.js b/src/containers/PageBuilder/Primitives/Text/Text.js
index 090fe7672..285024920 100644
--- a/src/containers/PageBuilder/Primitives/Text/Text.js
+++ b/src/containers/PageBuilder/Primitives/Text/Text.js
@@ -1,9 +1,19 @@
import React from 'react';
-import { node, string } from 'prop-types';
import classNames from 'classnames';
import css from './Text.module.css';
+/**
+ * Text element (e.g. )
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {ReactNode} props.children
+ * @param {string?} props.as tag/element to be used. Default is 'span'.
+ * @returns {JSX.Element} element
+ */
export const Text = React.forwardRef((props, ref) => {
const { className, rootClassName, as, ...otherProps } = props;
const Tag = as || 'span';
@@ -13,17 +23,3 @@ export const Text = React.forwardRef((props, ref) => {
});
Text.displayName = 'Text';
-
-Text.defaultProps = {
- rootClassName: null,
- className: null,
- children: null,
- as: 'span',
-};
-
-Text.propTypes = {
- rootClassName: string,
- className: string,
- children: node,
- as: string,
-};
diff --git a/src/containers/PageBuilder/Primitives/YoutubeEmbed/YoutubeEmbed.js b/src/containers/PageBuilder/Primitives/YoutubeEmbed/YoutubeEmbed.js
index 9a1649ee8..191417941 100644
--- a/src/containers/PageBuilder/Primitives/YoutubeEmbed/YoutubeEmbed.js
+++ b/src/containers/PageBuilder/Primitives/YoutubeEmbed/YoutubeEmbed.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { string } from 'prop-types';
import classNames from 'classnames';
import { lazyLoadWithDimensions } from '../../../../util/uiHelpers.js';
@@ -17,8 +16,19 @@ const IFrame = props => {
};
const LazyIFrame = lazyLoadWithDimensions(IFrame);
+/**
+ * Embeds a YouTube video inside an iframe that has given aspect ratio.
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string} props.youtubeVideoId video id of a youtube video
+ * @param {string?} props.aspectRatio e.g. '16/9'
+ * @returns {JSX.Element} an element with given aspect ratio that contains iframe showing youtube video
+ */
export const YoutubeEmbed = props => {
- const { className, rootClassName, youtubeVideoId, aspectRatio } = props;
+ const { className, rootClassName, youtubeVideoId, aspectRatio = '16/9' } = props;
const hasSlash = aspectRatio.indexOf('/') > 0;
const [aspectWidth, aspectHeight] = hasSlash ? aspectRatio.split('/') : [16, 9];
const width = Number.parseInt(aspectWidth, RADIX);
@@ -41,16 +51,3 @@ export const YoutubeEmbed = props => {
};
YoutubeEmbed.displayName = 'YoutubeEmbed';
-
-YoutubeEmbed.defaultProps = {
- rootClassName: null,
- className: null,
- aspectRatio: '16/9',
-};
-
-YoutubeEmbed.propTypes = {
- rootClassName: string,
- className: string,
- youtubeVideoId: string.isRequired,
- aspectRatio: string,
-};
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionArticle/SectionArticle.js b/src/containers/PageBuilder/SectionBuilder/SectionArticle/SectionArticle.js
index 49749c34e..d3f47e090 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionArticle/SectionArticle.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionArticle/SectionArticle.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { arrayOf, bool, func, node, object, shape, string } from 'prop-types';
import classNames from 'classnames';
import Field, { hasDataInFields } from '../../Field';
@@ -8,8 +7,44 @@ import BlockBuilder from '../../BlockBuilder';
import SectionContainer from '../SectionContainer';
import css from './SectionArticle.module.css';
-// Section component that's able to show article content
-// The article content is mainly supposed to be inside a block
+/**
+ * @typedef {Object} BlockConfig
+ * @property {string} blockId
+ * @property {string} blockName
+ * @property {'defaultBlock' | 'footerBlock' | 'socialMediaLink'} blockType
+ */
+
+/**
+ * @typedef {Object} FieldComponentConfig
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * Section component that's able to show article content.
+ * The article content is mainly supposed to be inside a block.
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {Object} props.defaultClasses
+ * @param {string} props.defaultClasses.sectionDetails
+ * @param {string} props.defaultClasses.title
+ * @param {string} props.defaultClasses.description
+ * @param {string} props.defaultClasses.ctaButton
+ * @param {string} props.sectionId id of the section
+ * @param {'article'} props.sectionType
+ * @param {Object?} props.title
+ * @param {Object?} props.description
+ * @param {Object?} props.appearance
+ * @param {Object?} props.callToAction
+ * @param {Array?} props.blocks array of block configs
+ * @param {boolean?} props.isInsideContainer
+ * @param {Object} props.options extra options for the section component (e.g. custom fieldComponents)
+ * @param {Object?} props.options.fieldComponents custom fields
+ * @returns {JSX.Element} Section for article content
+ */
const SectionArticle = props => {
const {
sectionId,
@@ -20,8 +55,8 @@ const SectionArticle = props => {
description,
appearance,
callToAction,
- blocks,
- isInsideContainer,
+ blocks = [],
+ isInsideContainer = false,
options,
} = props;
@@ -66,41 +101,4 @@ const SectionArticle = props => {
);
};
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
-});
-
-SectionArticle.defaultProps = {
- className: null,
- rootClassName: null,
- defaultClasses: null,
- textClassName: null,
- title: null,
- description: null,
- appearance: null,
- callToAction: null,
- blocks: [],
- isInsideContainer: false,
- options: null,
-};
-
-SectionArticle.propTypes = {
- sectionId: string.isRequired,
- className: string,
- rootClassName: string,
- defaultClasses: shape({
- sectionDetails: string,
- title: string,
- description: string,
- ctaButton: string,
- }),
- title: object,
- description: object,
- appearance: object,
- callToAction: object,
- blocks: arrayOf(object),
- isInsideContainer: bool,
- options: propTypeOption,
-};
-
export default SectionArticle;
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionBuilder.js b/src/containers/PageBuilder/SectionBuilder/SectionBuilder.js
index 9b94cab37..2016694e8 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionBuilder.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionBuilder.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { arrayOf, bool, func, node, oneOf, shape, string } from 'prop-types';
import classNames from 'classnames';
// Section components
@@ -45,8 +44,44 @@ const defaultSectionComponents = {
// Section builder //
//////////////////////
+/**
+ * @typedef {Object} FieldOption
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * @typedef {Object} BlockOption
+ * @property {ReactNode} component
+ */
+
+/**
+ * @typedef {Object} SectionOption
+ * @property {ReactNode} component
+ */
+
+/**
+ * @typedef {Object} SectionConfig
+ * @property {string} sectionId
+ * @property {string} sectionName
+ * @property {('article' | 'carousel' | 'columns' | 'features' | 'hero')} sectionType
+ */
+
+/**
+ * Build section elements from given section config array.
+ *
+ * @component
+ * @param {Object} props
+ * @param {Array} props.sections
+ * @param {Object} props.options
+ * @param {Object} props.options.fieldComponents
+ * @param {Object} props.options.blockComponents
+ * @param {Object} props.options.sectionComponents
+ * @param {boolean} props.options.isInsideContainer
+ * @returns {JSX.Element} element containing array of sections according from given config array.
+ */
const SectionBuilder = props => {
- const { sections, options } = props;
+ const { sections = [], options } = props;
const { sectionComponents = {}, isInsideContainer, ...otherOption } = options || {};
// If there's no sections, we can't render the correct section component
@@ -114,53 +149,4 @@ const SectionBuilder = props => {
);
};
-const propTypeSection = shape({
- sectionId: string,
- sectionName: string,
- sectionType: oneOf(['article', 'carousel', 'columns', 'features', 'hero']).isRequired,
- // Plus all kind of unknown fields.
- // BlockBuilder doesn't really need to care about those
-});
-
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
- blockComponents: shape({ component: node }),
- sectionComponents: shape({ component: node }),
- // isInsideContainer boolean means that the section is not taking
- // the full viewport width but is run inside some wrapper.
- isInsideContainer: bool,
-});
-
-const defaultSections = shape({
- sections: arrayOf(propTypeSection),
- options: propTypeOption,
-});
-
-const customSection = shape({
- sectionId: string.isRequired,
- sectionType: string.isRequired,
- // Plus all kind of unknown fields.
- // BlockBuilder doesn't really need to care about those
-});
-const propTypeOptionForCustomSections = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
- blockComponents: shape({ component: node }),
- sectionComponents: shape({ component: node }).isRequired,
- // isInsideContainer boolean means that the section is not taking
- // the full viewport width but is run inside some wrapper.
- isInsideContainer: bool,
-});
-
-const customSections = shape({
- sections: arrayOf(customSection),
- options: propTypeOptionForCustomSections.isRequired,
-});
-
-SectionBuilder.defaultProps = {
- sections: [],
- options: null,
-};
-
-SectionBuilder.propTypes = oneOf([defaultSections, customSections]).isRequired;
-
export default SectionBuilder;
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionCarousel/SectionCarousel.js b/src/containers/PageBuilder/SectionBuilder/SectionCarousel/SectionCarousel.js
index ad7fb2053..2f0691847 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionCarousel/SectionCarousel.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionCarousel/SectionCarousel.js
@@ -1,5 +1,4 @@
import React, { useEffect } from 'react';
-import { arrayOf, func, node, number, object, shape, string } from 'prop-types';
import classNames from 'classnames';
import Field, { hasDataInFields } from '../../Field';
@@ -28,20 +27,57 @@ const getResponsiveImageSizes = numColumns => {
return config ? config.responsiveImageSizes : COLUMN_CONFIG[0].responsiveImageSizes;
};
-// Section component that's able to show blocks in a carousel
-// the number blocks visible is defined by "numColumns" prop.
+/**
+ * @typedef {Object} BlockConfig
+ * @property {string} blockId
+ * @property {string} blockName
+ * @property {'defaultBlock' | 'footerBlock' | 'socialMediaLink'} blockType
+ */
+
+/**
+ * @typedef {Object} FieldComponentConfig
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * Section component that's able to show blocks in a carousel.
+ * The number blocks visible is defined by "numColumns" prop.
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {Object} props.defaultClasses
+ * @param {string} props.defaultClasses.sectionDetails
+ * @param {string} props.defaultClasses.title
+ * @param {string} props.defaultClasses.description
+ * @param {string} props.defaultClasses.ctaButton
+ * @param {string} props.sectionId id of the section
+ * @param {'carousel'} props.sectionType
+ * @param {number?} props.numColumns
+ * @param {Object?} props.title
+ * @param {Object?} props.description
+ * @param {Object?} props.appearance
+ * @param {Object?} props.callToAction
+ * @param {Array?} props.blocks array of block configs
+ * @param {boolean?} props.isInsideContainer
+ * @param {Object} props.options extra options for the section component (e.g. custom fieldComponents)
+ * @param {Object?} props.options.fieldComponents custom fields
+ * @returns {JSX.Element} Section for article content
+ */
const SectionCarousel = props => {
const {
sectionId,
className,
rootClassName,
defaultClasses,
- numColumns,
+ numColumns = 1,
title,
description,
appearance,
callToAction,
- blocks,
+ blocks = [],
options,
} = props;
const sliderContainerId = `${props.sectionId}-container`;
@@ -148,41 +184,4 @@ const SectionCarousel = props => {
);
};
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
-});
-
-SectionCarousel.defaultProps = {
- className: null,
- rootClassName: null,
- defaultClasses: null,
- textClassName: null,
- numColumns: 1,
- title: null,
- description: null,
- appearance: null,
- callToAction: null,
- blocks: [],
- options: null,
-};
-
-SectionCarousel.propTypes = {
- sectionId: string.isRequired,
- className: string,
- rootClassName: string,
- defaultClasses: shape({
- sectionDetails: string,
- title: string,
- description: string,
- ctaButton: string,
- }),
- numColumns: number,
- title: object,
- description: object,
- appearance: object,
- callToAction: object,
- blocks: arrayOf(object),
- options: propTypeOption,
-};
-
export default SectionCarousel;
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionColumns/SectionColumns.js b/src/containers/PageBuilder/SectionBuilder/SectionColumns/SectionColumns.js
index 3dc94045a..c3e3ab090 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionColumns/SectionColumns.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionColumns/SectionColumns.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { arrayOf, bool, func, node, number, object, shape, string } from 'prop-types';
import classNames from 'classnames';
import Field, { hasDataInFields } from '../../Field';
@@ -25,7 +24,43 @@ const getResponsiveImageSizes = numColumns => {
return config ? config.responsiveImageSizes : COLUMN_CONFIG[0].responsiveImageSizes;
};
-// Section component that's able to show blocks in multiple different columns (defined by "numColumns" prop)
+/**
+ * @typedef {Object} BlockConfig
+ * @property {string} blockId
+ * @property {string} blockName
+ * @property {'defaultBlock' | 'footerBlock' | 'socialMediaLink'} blockType
+ */
+
+/**
+ * @typedef {Object} FieldComponentConfig
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * Section component that's able to show blocks in multiple different columns (defined by "numColumns" prop)
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {Object} props.defaultClasses
+ * @param {string} props.defaultClasses.sectionDetails
+ * @param {string} props.defaultClasses.title
+ * @param {string} props.defaultClasses.description
+ * @param {string} props.defaultClasses.ctaButton
+ * @param {string} props.sectionId id of the section
+ * @param {'columns'} props.sectionType
+ * @param {Object?} props.title
+ * @param {Object?} props.description
+ * @param {Object?} props.appearance
+ * @param {Object?} props.callToAction
+ * @param {Array?} props.blocks array of block configs
+ * @param {boolean?} props.isInsideContainer
+ * @param {Object} props.options extra options for the section component (e.g. custom fieldComponents)
+ * @param {Object?} props.options.fieldComponents custom fields
+ * @returns {JSX.Element} Section for article content
+ */
const SectionColumns = props => {
const {
sectionId,
@@ -37,8 +72,8 @@ const SectionColumns = props => {
description,
appearance,
callToAction,
- blocks,
- isInsideContainer,
+ blocks = [],
+ isInsideContainer = false,
options,
} = props;
@@ -84,43 +119,4 @@ const SectionColumns = props => {
);
};
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
-});
-
-SectionColumns.defaultProps = {
- className: null,
- rootClassName: null,
- defaultClasses: null,
- textClassName: null,
- numColumns: 1,
- title: null,
- description: null,
- appearance: null,
- callToAction: null,
- blocks: [],
- isInsideContainer: false,
- options: null,
-};
-
-SectionColumns.propTypes = {
- sectionId: string.isRequired,
- className: string,
- rootClassName: string,
- defaultClasses: shape({
- sectionDetails: string,
- title: string,
- description: string,
- ctaButton: string,
- }),
- numColumns: number,
- title: object,
- description: object,
- appearance: object,
- callToAction: object,
- blocks: arrayOf(object),
- isInsideContainer: bool,
- options: propTypeOption,
-};
-
export default SectionColumns;
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionContainer/SectionContainer.js b/src/containers/PageBuilder/SectionBuilder/SectionContainer/SectionContainer.js
index 30f1342e4..deab1eb6a 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionContainer/SectionContainer.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionContainer/SectionContainer.js
@@ -1,13 +1,32 @@
import React from 'react';
-import { func, node, object, shape, string } from 'prop-types';
import classNames from 'classnames';
import Field from '../../Field';
import css from './SectionContainer.module.css';
-// This component can be used to wrap some common styles and features of Section-level components.
-// E.g: const SectionHero = props => (Hello World!
);
+/**
+ * @typedef {Object} FieldComponentConfig
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * This component can be used to wrap some common styles and features of Section-level components.
+ * E.g: const SectionHero = props => (Hello World!
);
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string?} props.id id of the section
+ * @param {string?} props.as tag/element name. Defaults to 'section'.
+ * @param {ReactNode} props.children
+ * @param {Object} props.appearance
+ * @param {Object} props.options extra options for the section component (e.g. custom fieldComponents)
+ * @param {Object?} props.options.fieldComponents custom fields
+ * @returns {JSX.Element} containing wrapper that can be used inside Block components.
+ */
const SectionContainer = props => {
const { className, rootClassName, id, as, children, appearance, options, ...otherProps } = props;
const Tag = as || 'section';
@@ -28,25 +47,4 @@ const SectionContainer = props => {
);
};
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
-});
-
-SectionContainer.defaultProps = {
- rootClassName: null,
- className: null,
- as: 'section',
- children: null,
- appearance: null,
-};
-
-SectionContainer.propTypes = {
- rootClassName: string,
- className: string,
- as: string,
- children: node,
- appearance: object,
- options: propTypeOption,
-};
-
export default SectionContainer;
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionFeatures/SectionFeatures.js b/src/containers/PageBuilder/SectionBuilder/SectionFeatures/SectionFeatures.js
index 4559c8a6d..1eba3441b 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionFeatures/SectionFeatures.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionFeatures/SectionFeatures.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { arrayOf, bool, func, node, object, shape, string } from 'prop-types';
import classNames from 'classnames';
import Field, { hasDataInFields } from '../../Field';
@@ -9,11 +8,47 @@ import SectionContainer from '../SectionContainer';
import css from './SectionFeatures.module.css';
-// Section component that shows features.
-// Block content are shown in a row-like way:
-// [image] text
-// text [image]
-// [image] text
+/**
+ * @typedef {Object} BlockConfig
+ * @property {string} blockId
+ * @property {string} blockName
+ * @property {'defaultBlock' | 'footerBlock' | 'socialMediaLink'} blockType
+ */
+
+/**
+ * @typedef {Object} FieldComponentConfig
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * Section component that shows features.
+ * Block content are shown in a row-like way:
+ * [image] text
+ * text [image]
+ * [image] text
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {Object} props.defaultClasses
+ * @param {string} props.defaultClasses.sectionDetails
+ * @param {string} props.defaultClasses.title
+ * @param {string} props.defaultClasses.description
+ * @param {string} props.defaultClasses.ctaButton
+ * @param {string} props.sectionId id of the section
+ * @param {'features'} props.sectionType
+ * @param {Object?} props.title
+ * @param {Object?} props.description
+ * @param {Object?} props.appearance
+ * @param {Object?} props.callToAction
+ * @param {Array?} props.blocks array of block configs
+ * @param {boolean?} props.isInsideContainer
+ * @param {Object} props.options extra options for the section component (e.g. custom fieldComponents)
+ * @param {Object?} props.options.fieldComponents custom fields
+ * @returns {JSX.Element} Section for article content
+ */
const SectionFeatures = props => {
const {
sectionId,
@@ -24,8 +59,8 @@ const SectionFeatures = props => {
description,
appearance,
callToAction,
- blocks,
- isInsideContainer,
+ blocks = [],
+ isInsideContainer = false,
options,
} = props;
@@ -72,41 +107,4 @@ const SectionFeatures = props => {
);
};
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
-});
-
-SectionFeatures.defaultProps = {
- className: null,
- rootClassName: null,
- defaultClasses: null,
- textClassName: null,
- title: null,
- description: null,
- appearance: null,
- callToAction: null,
- blocks: [],
- isInsideContainer: false,
- options: null,
-};
-
-SectionFeatures.propTypes = {
- sectionId: string.isRequired,
- className: string,
- rootClassName: string,
- defaultClasses: shape({
- sectionDetails: string,
- title: string,
- description: string,
- ctaButton: string,
- }),
- title: object,
- description: object,
- appearance: object,
- callToAction: object,
- blocks: arrayOf(object),
- isInsideContainer: bool,
- options: propTypeOption,
-};
-
export default SectionFeatures;
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionFooter/SectionFooter.js b/src/containers/PageBuilder/SectionBuilder/SectionFooter/SectionFooter.js
index 5a6dc3b08..eaf113bc0 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionFooter/SectionFooter.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionFooter/SectionFooter.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { arrayOf, bool, func, node, number, object, shape, string } from 'prop-types';
import classNames from 'classnames';
import { LinkedLogo } from '../../../../components';
@@ -31,18 +30,56 @@ const getGridCss = numberOfColumns => {
return contentConfig ? contentConfig.gridCss : GRID_CONFIG[0].gridCss;
};
-// Section component that's able to show blocks in multiple different columns (defined by "numberOfColumns" prop)
+/**
+ * @typedef {Object} SocialMediaLinkConfig
+ * @property {'socialMediaLink'} fieldType
+ * @property {string} platform
+ * @property {string} url
+ */
+
+/**
+ * @typedef {Object} BlockConfig
+ * @property {string} blockId
+ * @property {string} blockName
+ * @property {'defaultBlock' | 'footerBlock' | 'socialMediaLink'} blockType
+ */
+
+/**
+ * @typedef {Object} FieldComponentConfig
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * Section component that's able to show blocks in multiple different columns (defined by "numberOfColumns" prop)
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {string} props.sectionId id of the section
+ * @param {'footer'} props.sectionType
+ * @param {number} props.numberOfColumns columns for blocks in footer (1-4)
+ * @param {Array?} props.socialMediaLinks array of social media link configs
+ * @param {Object?} props.slogan
+ * @param {Object?} props.copyright
+ * @param {Object?} props.appearance
+ * @param {Array?} props.blocks array of block configs
+ * @param {Object} props.options extra options for the section component (e.g. custom fieldComponents)
+ * @param {Object?} props.options.fieldComponents custom fields
+ * @returns {JSX.Element} Section for article content
+ */
const SectionFooter = props => {
const {
sectionId,
className,
rootClassName,
- numberOfColumns,
- socialMediaLinks,
+ numberOfColumns = 1,
+ socialMediaLinks = [],
slogan,
appearance,
copyright,
- blocks,
+ blocks = [],
options,
linkLogoToExternalSite,
} = props;
@@ -110,34 +147,4 @@ const SectionFooter = props => {
);
};
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
-});
-
-SectionFooter.defaultProps = {
- className: null,
- rootClassName: null,
- textClassName: null,
- numberOfColumns: 1,
- socialMediaLinks: [],
- slogan: null,
- copyright: null,
- appearance: null,
- blocks: [],
- options: null,
-};
-
-SectionFooter.propTypes = {
- sectionId: string.isRequired,
- className: string,
- rootClassName: string,
- numberOfColumns: number,
- socialMediaLinks: arrayOf(object),
- slogan: object,
- copyright: object,
- appearance: object,
- blocks: arrayOf(object),
- options: propTypeOption,
-};
-
export default SectionFooter;
diff --git a/src/containers/PageBuilder/SectionBuilder/SectionHero/SectionHero.js b/src/containers/PageBuilder/SectionBuilder/SectionHero/SectionHero.js
index 07f86a326..d356d4e71 100644
--- a/src/containers/PageBuilder/SectionBuilder/SectionHero/SectionHero.js
+++ b/src/containers/PageBuilder/SectionBuilder/SectionHero/SectionHero.js
@@ -1,5 +1,4 @@
import React from 'react';
-import { bool, func, node, object, shape, string } from 'prop-types';
import classNames from 'classnames';
import Field, { hasDataInFields } from '../../Field';
@@ -7,8 +6,35 @@ import Field, { hasDataInFields } from '../../Field';
import SectionContainer from '../SectionContainer';
import css from './SectionHero.module.css';
-// Section component for a website's hero section
-// The Section Hero doesn't have any Blocks by default, all the configurations are made in the Section Hero settings
+/**
+ * @typedef {Object} FieldComponentConfig
+ * @property {ReactNode} component
+ * @property {Function} pickValidProps
+ */
+
+/**
+ * Section component for a website's hero section
+ * The Section Hero doesn't have any Blocks by default, all the configurations are made in the Section Hero settings
+ *
+ * @component
+ * @param {Object} props
+ * @param {string?} props.className add more style rules in addition to components own css.root
+ * @param {string?} props.rootClassName overwrite components own css.root
+ * @param {Object} props.defaultClasses
+ * @param {string} props.defaultClasses.sectionDetails
+ * @param {string} props.defaultClasses.title
+ * @param {string} props.defaultClasses.description
+ * @param {string} props.defaultClasses.ctaButton
+ * @param {string} props.sectionId id of the section
+ * @param {'hero'} props.sectionType
+ * @param {Object?} props.title
+ * @param {Object?} props.description
+ * @param {Object?} props.appearance
+ * @param {Object?} props.callToAction
+ * @param {Object} props.options extra options for the section component (e.g. custom fieldComponents)
+ * @param {Object?} props.options.fieldComponents custom fields
+ * @returns {JSX.Element} Section for article content
+ */
const SectionHero = props => {
const {
sectionId,
@@ -48,39 +74,4 @@ const SectionHero = props => {
);
};
-const propTypeOption = shape({
- fieldComponents: shape({ component: node, pickValidProps: func }),
-});
-
-SectionHero.defaultProps = {
- className: null,
- rootClassName: null,
- defaultClasses: null,
- textClassName: null,
- title: null,
- description: null,
- appearance: null,
- callToAction: null,
- isInsideContainer: false,
- options: null,
-};
-
-SectionHero.propTypes = {
- sectionId: string.isRequired,
- className: string,
- rootClassName: string,
- defaultClasses: shape({
- sectionDetails: string,
- title: string,
- description: string,
- ctaButton: string,
- }),
- title: object,
- description: object,
- appearance: object,
- callToAction: object,
- isInsideContainer: bool,
- options: propTypeOption,
-};
-
export default SectionHero;
diff --git a/src/containers/PageBuilder/StaticPage.js b/src/containers/PageBuilder/StaticPage.js
index b7932f122..fd119337c 100644
--- a/src/containers/PageBuilder/StaticPage.js
+++ b/src/containers/PageBuilder/StaticPage.js
@@ -1,23 +1,22 @@
import React from 'react';
-import { node } from 'prop-types';
import { connect } from 'react-redux';
import { isScrollingDisabled } from '../../ducks/ui.duck.js';
import { Page } from '../../components/index.js';
+/**
+ * This component returns a Page component which is connected to Redux store.
+ *
+ * @component
+ * @param {Object} props
+ * @param {JSX.Element} props.children
+ * @returns {JSX.Element} Page component with scrollingDisabled info from Redux state.
+ */
const StaticPageComponent = props => {
const { children, ...pageProps } = props;
return {children};
};
-StaticPageComponent.defaultProps = {
- children: null,
-};
-
-StaticPageComponent.propTypes = {
- children: node,
-};
-
const mapStateToProps = state => {
return {
scrollingDisabled: isScrollingDisabled(state),
diff --git a/src/containers/PasswordChangePage/PasswordChangeForm/PasswordChangeForm.js b/src/containers/PasswordChangePage/PasswordChangeForm/PasswordChangeForm.js
index 8c34e4c5e..e51d9aec2 100644
--- a/src/containers/PasswordChangePage/PasswordChangeForm/PasswordChangeForm.js
+++ b/src/containers/PasswordChangePage/PasswordChangeForm/PasswordChangeForm.js
@@ -1,10 +1,8 @@
import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { compose } from 'redux';
import { Form as FinalForm } from 'react-final-form';
import classNames from 'classnames';
-import { FormattedMessage, injectIntl, intlShape } from '../../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../../util/reactIntl';
import { propTypes } from '../../../util/types';
import * as validators from '../../../util/validators';
import { ensureCurrentUser } from '../../../util/data';
@@ -16,7 +14,24 @@ import css from './PasswordChangeForm.module.css';
const RESET_TIMEOUT = 800;
-class PasswordChangeFormComponent extends Component {
+/**
+ * The change-password form.
+ * TODO: change to functional component
+ *
+ * @component
+ * @param {Object} props
+ * @param {string} [props.formId] - The form ID
+ * @param {string} [props.rootClassName] - Custom class that overrides the default class for the root element
+ * @param {string} [props.className] - Custom class that extends the default class for the root element
+ * @param {function} props.onSubmit - The function to submit the form
+ * @param {boolean} [props.inProgress] - Whether the form is in progress
+ * @param {boolean} [props.resetPasswordInProgress] - Whether the reset password is in progress
+ * @param {boolean} props.ready - Whether the form is ready
+ * @param {propTypes.error} [props.changePasswordError] - The change password error
+ * @param {propTypes.error} [props.resetPasswordError] - The reset password error
+ * @returns {JSX.Element} Change-password form component
+ */
+class PasswordChangeForm extends Component {
constructor(props) {
super(props);
this.state = { showResetPasswordMessage: false };
@@ -47,9 +62,8 @@ class PasswordChangeFormComponent extends Component {
changePasswordError,
currentUser,
handleSubmit,
- inProgress,
- resetPasswordInProgress,
- intl,
+ inProgress = false,
+ resetPasswordInProgress = false,
invalid,
pristine,
ready,
@@ -57,6 +71,7 @@ class PasswordChangeFormComponent extends Component {
values,
} = fieldRenderProps;
+ const intl = useIntl();
const user = ensureCurrentUser(currentUser);
if (!user.id) {
@@ -248,31 +263,4 @@ class PasswordChangeFormComponent extends Component {
}
}
-PasswordChangeFormComponent.defaultProps = {
- rootClassName: null,
- className: null,
- changePasswordError: null,
- inProgress: false,
- formId: null,
- resetPasswordInProgress: false,
- resetPasswordError: null,
-};
-
-const { bool, string } = PropTypes;
-
-PasswordChangeFormComponent.propTypes = {
- rootClassName: string,
- className: string,
- changePasswordError: propTypes.error,
- inProgress: bool,
- intl: intlShape.isRequired,
- ready: bool.isRequired,
- formId: string,
- resetPasswordInProgress: bool,
- resetPasswordError: propTypes.error,
-};
-
-const PasswordChangeForm = compose(injectIntl)(PasswordChangeFormComponent);
-PasswordChangeForm.displayName = 'PasswordChangeForm';
-
export default PasswordChangeForm;
diff --git a/src/containers/PasswordChangePage/PasswordChangePage.js b/src/containers/PasswordChangePage/PasswordChangePage.js
index 6d640229f..a1a4c307d 100644
--- a/src/containers/PasswordChangePage/PasswordChangePage.js
+++ b/src/containers/PasswordChangePage/PasswordChangePage.js
@@ -1,9 +1,8 @@
import React from 'react';
-import PropTypes from 'prop-types';
import { compose } from 'redux';
import { connect } from 'react-redux';
-import { FormattedMessage, injectIntl, intlShape } from '../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../util/reactIntl';
import { propTypes } from '../../util/types';
import { isScrollingDisabled } from '../../ducks/ui.duck';
@@ -17,7 +16,23 @@ import PasswordChangeForm from './PasswordChangeForm/PasswordChangeForm';
import { changePassword, changePasswordClear, resetPassword } from './PasswordChangePage.duck';
import css from './PasswordChangePage.module.css';
+/**
+ * The change-password page.
+ *
+ * @param {Object} props
+ * @param {propTypes.error} props.changePasswordError - The change password error
+ * @param {boolean} props.changePasswordInProgress - Whether the change password is in progress
+ * @param {propTypes.currentUser} props.currentUser - The current user
+ * @param {function} props.onChange - The function to change the password
+ * @param {function} props.onSubmitChangePassword - The function to submit the change password form
+ * @param {boolean} props.passwordChanged - Whether the password has changed
+ * @param {boolean} props.scrollingDisabled - Whether the scrolling is disabled
+ * @param {boolean} props.resetPasswordInProgress - Whether the reset password is in progress
+ * @param {propTypes.error} props.resetPasswordError - The reset password error
+ * @returns {JSX.Element} Password change page component
+ */
export const PasswordChangePageComponent = props => {
+ const intl = useIntl();
const {
changePasswordError,
changePasswordInProgress,
@@ -25,11 +40,10 @@ export const PasswordChangePageComponent = props => {
onChange,
onSubmitChangePassword,
onResetPassword,
- resetPasswordInProgress,
+ resetPasswordInProgress = false,
resetPasswordError,
passwordChanged,
scrollingDisabled,
- intl,
} = props;
const changePasswordForm =
@@ -78,30 +92,6 @@ export const PasswordChangePageComponent = props => {
);
};
-PasswordChangePageComponent.defaultProps = {
- changePasswordError: null,
- currentUser: null,
- resetPasswordInProgress: false,
- resetPasswordError: null,
-};
-
-const { bool, func } = PropTypes;
-
-PasswordChangePageComponent.propTypes = {
- changePasswordError: propTypes.error,
- changePasswordInProgress: bool.isRequired,
- currentUser: propTypes.currentUser,
- onChange: func.isRequired,
- onSubmitChangePassword: func.isRequired,
- passwordChanged: bool.isRequired,
- scrollingDisabled: bool.isRequired,
- resetPasswordInProgress: bool,
- resetPasswordError: propTypes.error,
-
- // from injectIntl
- intl: intlShape.isRequired,
-};
-
const mapStateToProps = state => {
// Topbar needs user info.
const {
@@ -133,8 +123,7 @@ const PasswordChangePage = compose(
connect(
mapStateToProps,
mapDispatchToProps
- ),
- injectIntl
+ )
)(PasswordChangePageComponent);
export default PasswordChangePage;
diff --git a/src/containers/PasswordChangePage/PasswordChangePage.test.js b/src/containers/PasswordChangePage/PasswordChangePage.test.js
index 1eda4b83e..6964550e2 100644
--- a/src/containers/PasswordChangePage/PasswordChangePage.test.js
+++ b/src/containers/PasswordChangePage/PasswordChangePage.test.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { act } from 'react';
import '@testing-library/jest-dom';
import { createCurrentUser, fakeIntl } from '../../util/testData';
@@ -11,7 +11,7 @@ const { screen, userEvent } = testingLibrary;
const noop = () => null;
describe('PasswordChangePageComponent', () => {
- it('Check that newPassword input shows error and submit is enabled if form is filled', () => {
+ it('Check that newPassword input shows error and submit is enabled if form is filled', async () => {
render(
{
);
const newPasswordLabel = 'PasswordChangeForm.newPasswordLabel';
- expect(screen.getByText(newPasswordLabel)).toBeInTheDocument();
+ expect(await screen.findByText(newPasswordLabel)).toBeInTheDocument();
// Save button is disabled
expect(screen.getByRole('button', { name: 'PasswordChangeForm.saveChanges' })).toBeDisabled();
// There's a too short password, there is error text visible
const newPasswordInput = screen.getByLabelText(newPasswordLabel);
- userEvent.type(newPasswordInput, 'short');
- newPasswordInput.blur();
+
+ await act(async () => {
+ userEvent.type(newPasswordInput, 'short');
+ newPasswordInput.blur();
+ });
const passwordTooShort = 'PasswordChangeForm.passwordTooShort';
expect(screen.getByText(passwordTooShort)).toBeInTheDocument();
- // There's a long enough password => there is no error text visible
- userEvent.type(newPasswordInput, 'morethan8characters');
- newPasswordInput.blur();
+ await act(async () => {
+ // There's a long enough password => there is no error text visible
+ userEvent.type(newPasswordInput, 'morethan8characters');
+ newPasswordInput.blur();
+ });
expect(screen.queryByText(passwordTooShort)).not.toBeInTheDocument();
-
const passwordLabel = 'PasswordChangeForm.passwordLabel';
const passwordInput = screen.getByText(passwordLabel);
expect(passwordInput).toBeInTheDocument();
- // Save button is enabled
- userEvent.type(passwordInput, 'somepasswordasoldpassword');
+ await act(async () => {
+ // Save button is enabled
+ userEvent.type(passwordInput, 'somepasswordasoldpassword');
+ });
expect(screen.queryByText(passwordTooShort)).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: 'PasswordChangeForm.saveChanges' })).toBeEnabled();
});
diff --git a/src/containers/PasswordRecoveryPage/DoorIcon.js b/src/containers/PasswordRecoveryPage/DoorIcon.js
index cd0e4483c..8c581f5ba 100644
--- a/src/containers/PasswordRecoveryPage/DoorIcon.js
+++ b/src/containers/PasswordRecoveryPage/DoorIcon.js
@@ -1,7 +1,13 @@
import React from 'react';
-import PropTypes from 'prop-types';
import css from './DoorIcon.module.css';
+/**
+ * The door icon.
+ *
+ * @param {Object} props
+ * @param {string} [props.className] - Custom class that overrides the default class for the root element
+ * @returns {JSX.Element} Door icon component
+ */
const DoorIcon = props => {
const { className } = props;
return (
@@ -43,12 +49,4 @@ const DoorIcon = props => {
);
};
-DoorIcon.defaultProps = { className: null };
-
-const { string } = PropTypes;
-
-DoorIcon.propTypes = {
- className: string,
-};
-
export default DoorIcon;
diff --git a/src/containers/PasswordRecoveryPage/PasswordRecoveryForm/PasswordRecoveryForm.js b/src/containers/PasswordRecoveryPage/PasswordRecoveryForm/PasswordRecoveryForm.js
index 37863b909..52f7a7342 100644
--- a/src/containers/PasswordRecoveryPage/PasswordRecoveryForm/PasswordRecoveryForm.js
+++ b/src/containers/PasswordRecoveryPage/PasswordRecoveryForm/PasswordRecoveryForm.js
@@ -1,11 +1,9 @@
import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { compose } from 'redux';
import { Form as FinalForm } from 'react-final-form';
import isEqual from 'lodash/isEqual';
import classNames from 'classnames';
-import { FormattedMessage, injectIntl, intlShape } from '../../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../../util/reactIntl';
import { propTypes } from '../../../util/types';
import * as validators from '../../../util/validators';
import { isPasswordRecoveryEmailNotFoundError } from '../../../util/errors';
@@ -14,7 +12,19 @@ import { Form, PrimaryButton, FieldTextInput, NamedLink } from '../../../compone
import css from './PasswordRecoveryForm.module.css';
-class PasswordRecoveryFormComponent extends Component {
+/**
+ * The password recovery form.
+ * TODO: change to functional component
+ *
+ * @param {Object} props
+ * @param {string} [props.rootClassName] - Custom class that overrides the default class for the root element
+ * @param {string} [props.className] - Custom class that extends the default class for the root element
+ * @param {string} [props.formId] - The form ID
+ * @param {boolean} [props.inProgress] - Whether the form is in progress
+ * @param {propTypes.error} [props.recoveryError] - The recovery error
+ * @returns {JSX.Element} Password recovery form component
+ */
+class PasswordRecoveryForm extends Component {
constructor(props) {
super(props);
this.submittedValues = {};
@@ -32,12 +42,13 @@ class PasswordRecoveryFormComponent extends Component {
handleSubmit,
pristine,
initialValues,
- intl,
- inProgress,
+ inProgress = false,
recoveryError,
values,
} = fieldRenderProps;
+ const intl = useIntl();
+
// email
const emailLabel = intl.formatMessage({
id: 'PasswordRecoveryForm.emailLabel',
@@ -125,29 +136,4 @@ class PasswordRecoveryFormComponent extends Component {
}
}
-PasswordRecoveryFormComponent.defaultProps = {
- rootClassName: null,
- className: null,
- formId: null,
- inProgress: false,
- recoveryError: null,
-};
-
-const { bool, string } = PropTypes;
-
-PasswordRecoveryFormComponent.propTypes = {
- rootClassName: string,
- className: string,
- formId: string,
-
- inProgress: bool,
- recoveryError: propTypes.error,
-
- // from injectIntl
- intl: intlShape.isRequired,
-};
-
-const PasswordRecoveryForm = compose(injectIntl)(PasswordRecoveryFormComponent);
-PasswordRecoveryForm.displayName = 'PasswordRecoveryForm';
-
export default PasswordRecoveryForm;
diff --git a/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.js b/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.js
index 376ff4fad..b3eb7b086 100644
--- a/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.js
+++ b/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.js
@@ -1,10 +1,9 @@
import React from 'react';
-import PropTypes from 'prop-types';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { useConfiguration } from '../../context/configurationContext';
-import { FormattedMessage, injectIntl, intlShape } from '../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../util/reactIntl';
import { propTypes } from '../../util/types';
import { isPasswordRecoveryEmailNotFoundError } from '../../util/errors';
import { isScrollingDisabled } from '../../ducks/ui.duck';
@@ -123,8 +122,24 @@ const EmailSubmittedContent = props => {
);
};
+/**
+ * The password recovery page.
+ *
+ * @param {Object} props
+ * @param {boolean} props.scrollingDisabled - Whether the scrolling is disabled
+ * @param {string} props.initialEmail - The initial email
+ * @param {string} props.submittedEmail - The submitted email
+ * @param {propTypes.error} props.recoveryError - The recovery error
+ * @param {boolean} props.recoveryInProgress - Whether the recovery is in progress
+ * @param {boolean} props.passwordRequested - Whether the password is requested
+ * @param {function} props.onChange - The function to change the email
+ * @param {function} props.onSubmitEmail - The function to submit the email
+ * @param {function} props.onRetypeEmail - The function to retype the email
+ * @returns {JSX.Element} Password recovery page component
+ */
export const PasswordRecoveryPageComponent = props => {
const config = useConfiguration();
+ const intl = useIntl();
const {
scrollingDisabled,
initialEmail,
@@ -135,7 +150,6 @@ export const PasswordRecoveryPageComponent = props => {
onChange,
onSubmitEmail,
onRetypeEmail,
- intl,
} = props;
const alreadyrequested = submittedEmail || passwordRequested;
const showPasswordRecoveryForm = (
@@ -190,30 +204,6 @@ export const PasswordRecoveryPageComponent = props => {
);
};
-PasswordRecoveryPageComponent.defaultProps = {
- sendVerificationEmailError: null,
- initialEmail: null,
- submittedEmail: null,
- recoveryError: null,
-};
-
-const { bool, func, string } = PropTypes;
-
-PasswordRecoveryPageComponent.propTypes = {
- scrollingDisabled: bool.isRequired,
- initialEmail: string,
- submittedEmail: string,
- recoveryError: propTypes.error,
- recoveryInProgress: bool.isRequired,
- passwordRequested: bool.isRequired,
- onChange: func.isRequired,
- onSubmitEmail: func.isRequired,
- onRetypeEmail: func.isRequired,
-
- // from injectIntl
- intl: intlShape.isRequired,
-};
-
const mapStateToProps = state => {
const {
initialEmail,
@@ -242,8 +232,7 @@ const PasswordRecoveryPage = compose(
connect(
mapStateToProps,
mapDispatchToProps
- ),
- injectIntl
+ )
)(PasswordRecoveryPageComponent);
export default PasswordRecoveryPage;
diff --git a/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.test.js b/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.test.js
index b63f9e5b9..12d1e549c 100644
--- a/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.test.js
+++ b/src/containers/PasswordRecoveryPage/PasswordRecoveryPage.test.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { act } from 'react';
import '@testing-library/jest-dom';
import { fakeIntl } from '../../util/testData';
@@ -6,12 +6,12 @@ import { renderWithProviders as render, testingLibrary } from '../../util/testHe
import { PasswordRecoveryPageComponent } from './PasswordRecoveryPage';
-const { screen, userEvent } = testingLibrary;
+const { screen, userEvent, waitFor } = testingLibrary;
const noop = () => null;
describe('PasswordRecoveryPageComponent', () => {
- it('Check that email input shows error and submit is enabled if form is filled', () => {
+ it('Check that email input shows error and submit is enabled if form is filled', async () => {
render(
{
screen.getByRole('button', { name: 'PasswordRecoveryForm.sendInstructions' })
).toBeDisabled();
- // There's a too short password, there is error text visible
- userEvent.type(emailInput, 'foobar');
- emailInput.blur();
-
+ await act(async () => {
+ // There's a too short password, there is error text visible
+ userEvent.type(emailInput, 'foobar');
+ emailInput.blur();
+ });
const emailInvalid = 'PasswordRecoveryForm.emailInvalid';
expect(screen.getByText(emailInvalid)).toBeInTheDocument();
- // There's a valid email written to input => there is no error text visible
- userEvent.type(emailInput, 'foo@bar.com');
- emailInput.blur();
+ await act(async () => {
+ // There's a valid email written to input => there is no error text visible
+ userEvent.type(emailInput, '@bar.com');
+ emailInput.blur();
+ });
expect(screen.queryByText(emailInvalid)).not.toBeInTheDocument();
// Save button is enabled
diff --git a/src/containers/PasswordResetPage/PasswordResetForm/PasswordResetForm.js b/src/containers/PasswordResetPage/PasswordResetForm/PasswordResetForm.js
index f98d94805..9a510cdd6 100644
--- a/src/containers/PasswordResetPage/PasswordResetForm/PasswordResetForm.js
+++ b/src/containers/PasswordResetPage/PasswordResetForm/PasswordResetForm.js
@@ -1,17 +1,25 @@
import React from 'react';
-import PropTypes from 'prop-types';
-import { compose } from 'redux';
import { Form as FinalForm } from 'react-final-form';
import classNames from 'classnames';
-import { FormattedMessage, injectIntl, intlShape } from '../../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../../util/reactIntl';
import * as validators from '../../../util/validators';
import { Form, PrimaryButton, FieldTextInput } from '../../../components';
import css from './PasswordResetForm.module.css';
-const PasswordResetFormComponent = props => (
+/**
+ * The reset-password form.
+ *
+ * @param {Object} props
+ * @param {string} [props.formId] - The form ID
+ * @param {string} [props.rootClassName] - Custom class that overrides the default class for the root element
+ * @param {string} [props.className] - Custom class that extends the default class for the root element
+ * @param {boolean} [props.inProgress] - Whether the form is in progress
+ * @returns {JSX.Element} Reset-password form component
+ */
+const PasswordResetForm = props => (
{
@@ -20,11 +28,11 @@ const PasswordResetFormComponent = props => (
className,
formId,
handleSubmit,
- inProgress,
- intl,
+ inProgress = false,
invalid,
} = fieldRenderProps;
+ const intl = useIntl();
// password
const passwordLabel = intl.formatMessage({
id: 'PasswordResetForm.passwordLabel',
@@ -91,24 +99,4 @@ const PasswordResetFormComponent = props => (
/>
);
-PasswordResetFormComponent.defaultProps = {
- rootClassName: null,
- className: null,
- inProgress: false,
- formId: null,
-};
-
-const { string, bool } = PropTypes;
-
-PasswordResetFormComponent.propTypes = {
- rootClassName: string,
- className: string,
- inProgress: bool,
- intl: intlShape.isRequired,
- formId: string,
-};
-
-const PasswordResetForm = compose(injectIntl)(PasswordResetFormComponent);
-PasswordResetForm.displayName = 'PasswordResetForm';
-
export default PasswordResetForm;
diff --git a/src/containers/PasswordResetPage/PasswordResetPage.js b/src/containers/PasswordResetPage/PasswordResetPage.js
index 19ddb572e..0f2f6aa27 100644
--- a/src/containers/PasswordResetPage/PasswordResetPage.js
+++ b/src/containers/PasswordResetPage/PasswordResetPage.js
@@ -1,11 +1,10 @@
import React, { useState } from 'react';
-import { bool, func, shape, string } from 'prop-types';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { useConfiguration } from '../../context/configurationContext';
-import { FormattedMessage, injectIntl, intlShape } from '../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../util/reactIntl';
import { propTypes } from '../../util/types';
import { parse } from '../../util/urlHelpers';
import { isScrollingDisabled } from '../../ducks/ui.duck';
@@ -91,11 +90,23 @@ const ResetDoneContent = () => {
);
};
+/**
+ * The reset-password page.
+ *
+ * @param {Object} props
+ * @param {boolean} props.scrollingDisabled - Whether the page is scrolling disabled
+ * @param {boolean} props.resetPasswordInProgress - Whether the reset password is in progress
+ * @param {propTypes.error} props.resetPasswordError - The reset password error
+ * @param {function} props.onSubmitPassword - The function to submit the password
+ * @param {Object} props.location - The location object
+ * @param {string} props.location.search - The search string
+ * @returns {JSX.Element} Password reset page component
+ */
export const PasswordResetPageComponent = props => {
const [state, setState] = useState({ newPasswordSubmitted: false });
const config = useConfiguration();
+ const intl = useIntl();
const {
- intl,
scrollingDisabled,
location,
resetPasswordInProgress,
@@ -153,25 +164,6 @@ export const PasswordResetPageComponent = props => {
);
};
-PasswordResetPageComponent.defaultProps = {
- resetPasswordError: null,
-};
-
-PasswordResetPageComponent.propTypes = {
- scrollingDisabled: bool.isRequired,
- resetPasswordInProgress: bool.isRequired,
- resetPasswordError: propTypes.error,
- onSubmitPassword: func.isRequired,
-
- // from withRouter
- location: shape({
- search: string,
- }).isRequired,
-
- // from injectIntl
- intl: intlShape.isRequired,
-};
-
const mapStateToProps = state => {
const { resetPasswordInProgress, resetPasswordError } = state.PasswordResetPage;
return {
@@ -196,8 +188,7 @@ const PasswordResetPage = compose(
connect(
mapStateToProps,
mapDispatchToProps
- ),
- injectIntl
+ )
)(PasswordResetPageComponent);
export default PasswordResetPage;
diff --git a/src/containers/PaymentMethodsPage/PaymentMethodsForm/PaymentMethodsForm.js b/src/containers/PaymentMethodsPage/PaymentMethodsForm/PaymentMethodsForm.js
index 4a4c5490b..fe1a0166d 100644
--- a/src/containers/PaymentMethodsPage/PaymentMethodsForm/PaymentMethodsForm.js
+++ b/src/containers/PaymentMethodsPage/PaymentMethodsForm/PaymentMethodsForm.js
@@ -4,12 +4,11 @@
* It's also handled separately in handleSubmit function.
*/
import React, { Component } from 'react';
-import { func, object, string } from 'prop-types';
import { Form as FinalForm } from 'react-final-form';
import classNames from 'classnames';
import { useConfiguration } from '../../../context/configurationContext';
-import { FormattedMessage, useIntl, intlShape } from '../../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../../util/reactIntl';
import { Form, PrimaryButton, FieldTextInput, StripePaymentAddress, H4 } from '../../../components';
@@ -294,36 +293,23 @@ class PaymentMethodsForm extends Component {
}
}
-PaymentMethodsForm.defaultProps = {
- className: null,
- rootClassName: null,
- inProgress: false,
- onSubmit: null,
- addPaymentMethodError: null,
- deletePaymentMethodError: null,
- createStripeCustomerError: null,
- handleCardSetupError: null,
- form: null,
-};
-
-PaymentMethodsForm.propTypes = {
- formId: string,
- onSubmit: func,
- addPaymentMethodError: object,
- deletePaymentMethodError: object,
- createStripeCustomerError: object,
- handleCardSetupError: object,
- form: object,
-
- // from useIntl
- intl: intlShape.isRequired,
- // from useConfiguration
- config: object.isRequired,
-};
-
+/**
+ * PaymentMethodsForm
+ *
+ * @component
+ * @param {Object} props
+ * @param {string} [props.formId] - The form ID
+ * @param {Function} props.onSubmit - The function to handle form submission
+ * @param {propTypes.error} props.addPaymentMethodError - The add payment method error
+ * @param {propTypes.error} props.deletePaymentMethodError - The delete payment method error
+ * @param {propTypes.error} props.createStripeCustomerError - The create stripe customer error
+ * @param {propTypes.error} props.handleCardSetupError - The handle card setup error
+ * @returns {JSX.Element} EnhancedPaymentMethodsForm component
+ */
const EnhancedPaymentMethodsForm = props => {
const config = useConfiguration();
const intl = useIntl();
return ;
};
+
export default EnhancedPaymentMethodsForm;
diff --git a/src/containers/PaymentMethodsPage/PaymentMethodsPage.js b/src/containers/PaymentMethodsPage/PaymentMethodsPage.js
index 5bd1b4d6b..ca179b657 100644
--- a/src/containers/PaymentMethodsPage/PaymentMethodsPage.js
+++ b/src/containers/PaymentMethodsPage/PaymentMethodsPage.js
@@ -1,9 +1,8 @@
import React, { useState } from 'react';
-import { bool, func, object } from 'prop-types';
import { compose } from 'redux';
import { connect } from 'react-redux';
-import { FormattedMessage, injectIntl, intlShape } from '../../util/reactIntl';
+import { FormattedMessage, useIntl } from '../../util/reactIntl';
import { ensureCurrentUser, ensureStripeCustomer, ensurePaymentMethodCard } from '../../util/data';
import { propTypes } from '../../util/types';
import { savePaymentMethod, deletePaymentMethod } from '../../ducks/paymentMethods.duck';
@@ -21,9 +20,29 @@ import { createStripeSetupIntent, stripeCustomer } from './PaymentMethodsPage.du
import css from './PaymentMethodsPage.module.css';
+/**
+ * The payment methods page.
+ *
+ * @param {Object} props
+ * @param {propTypes.currentUser} props.currentUser - The current user
+ * @param {boolean} props.scrollingDisabled - Whether the scrolling is disabled
+ * @param {Object} props.addPaymentMethodError - The add payment method error
+ * @param {Object} props.deletePaymentMethodError - The delete payment method error
+ * @param {Object} props.createStripeCustomerError - The create stripe customer error
+ * @param {propTypes.error} props.handleCardSetupError - The handle card setup error
+ * @param {Function} props.onCreateSetupIntent - The function to create a SetupIntent
+ * @param {Function} props.onHandleCardSetup - The function to handle card setup
+ * @param {Function} props.onSavePaymentMethod - The function to save payment method
+ * @param {Function} props.onDeletePaymentMethod - The function to delete payment method
+ * @param {Function} props.fetchStripeCustomer - The function to fetch stripe customer
+ * @param {Function} props.onManageDisableScrolling - The function to manage disable scrolling
+ * @param {boolean} props.stripeCustomerFetched - Whether the stripe customer is fetched
+ * @returns {JSX.Element} Payment methods page component
+ */
const PaymentMethodsPageComponent = props => {
const [isSubmitting, setIsSubmitting] = useState(false);
const [cardState, setCardState] = useState(null);
+ const intl = useIntl();
const {
currentUser,
@@ -39,7 +58,6 @@ const PaymentMethodsPageComponent = props => {
fetchStripeCustomer,
scrollingDisabled,
onManageDisableScrolling,
- intl,
stripeCustomerFetched,
} = props;
@@ -195,31 +213,6 @@ const PaymentMethodsPageComponent = props => {
);
};
-PaymentMethodsPageComponent.defaultProps = {
- currentUser: null,
- addPaymentMethodError: null,
- deletePaymentMethodError: null,
- createStripeCustomerError: null,
- handleCardSetupError: null,
-};
-
-PaymentMethodsPageComponent.propTypes = {
- currentUser: propTypes.currentUser,
- scrollingDisabled: bool.isRequired,
- addPaymentMethodError: object,
- deletePaymentMethodError: object,
- createStripeCustomerError: object,
- handleCardSetupError: object,
- onCreateSetupIntent: func.isRequired,
- onHandleCardSetup: func.isRequired,
- onSavePaymentMethod: func.isRequired,
- onDeletePaymentMethod: func.isRequired,
- fetchStripeCustomer: func.isRequired,
-
- // from injectIntl
- intl: intlShape.isRequired,
-};
-
const mapStateToProps = state => {
const { currentUser } = state.user;
@@ -260,8 +253,7 @@ const PaymentMethodsPage = compose(
connect(
mapStateToProps,
mapDispatchToProps
- ),
- injectIntl
+ )
)(PaymentMethodsPageComponent);
export default PaymentMethodsPage;
diff --git a/src/containers/ProfilePage/ProfilePage.js b/src/containers/ProfilePage/ProfilePage.js
index d4a1a976b..684ba13af 100644
--- a/src/containers/ProfilePage/ProfilePage.js
+++ b/src/containers/ProfilePage/ProfilePage.js
@@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react';
-import { bool, arrayOf, oneOfType } from 'prop-types';
import { compose } from 'redux';
import { connect } from 'react-redux';
import classNames from 'classnames';
@@ -180,13 +179,13 @@ export const CustomUserFields = props => {
<>
{propsForCustomFields.map(customFieldProps => {
- const { schemaType, ...fieldProps } = customFieldProps;
+ const { schemaType, key, ...fieldProps } = customFieldProps;
return schemaType === SCHEMA_TYPE_MULTI_ENUM ? (
-
+
) : schemaType === SCHEMA_TYPE_TEXT ? (
-
+
) : schemaType === SCHEMA_TYPE_YOUTUBE ? (
-
+
) : null;
})}
>
@@ -194,13 +193,18 @@ export const CustomUserFields = props => {
};
export const MainContent = props => {
+ const [mounted, setMounted] = useState(false);
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
const {
userShowError,
bio,
displayName,
listings,
queryListingsError,
- reviews,
+ reviews = [],
queryReviewsError,
publicData,
metadata,
@@ -211,9 +215,10 @@ export const MainContent = props => {
const hasListings = listings.length > 0;
const hasMatchMedia = typeof window !== 'undefined' && window?.matchMedia;
- const isMobileLayout = hasMatchMedia
- ? window.matchMedia(`(max-width: ${MAX_MOBILE_SCREEN_WIDTH}px)`)?.matches
- : true;
+ const isMobileLayout =
+ mounted && hasMatchMedia
+ ? window.matchMedia(`(max-width: ${MAX_MOBILE_SCREEN_WIDTH}px)`)?.matches
+ : true;
const hasBio = !!bio;
const bioWithLinks = richText(bio, {
@@ -272,6 +277,22 @@ export const MainContent = props => {
);
};
+/**
+ * ProfilePageComponent
+ *
+ * @component
+ * @param {Object} props
+ * @param {boolean} props.scrollingDisabled - Whether the scrolling is disabled
+ * @param {propTypes.currentUser} props.currentUser - The current user
+ * @param {boolean} props.useCurrentUser - Whether to use the current user
+ * @param {propTypes.user|propTypes.currentUser} props.user - The user
+ * @param {propTypes.error} props.userShowError - The user show error
+ * @param {propTypes.error} props.queryListingsError - The query listings error
+ * @param {Array} props.listings - The listings
+ * @param {Array} props.reviews - The reviews
+ * @param {propTypes.error} props.queryReviewsError - The query reviews error
+ * @returns {JSX.Element} ProfilePageComponent
+ */
export const ProfilePageComponent = props => {
const config = useConfiguration();
const intl = useIntl();
@@ -405,27 +426,6 @@ export const ProfilePageComponent = props => {
);
};
-ProfilePageComponent.defaultProps = {
- currentUser: null,
- user: null,
- userShowError: null,
- queryListingsError: null,
- reviews: [],
- queryReviewsError: null,
-};
-
-ProfilePageComponent.propTypes = {
- scrollingDisabled: bool.isRequired,
- currentUser: propTypes.currentUser,
- useCurrentUser: bool.isRequired,
- user: oneOfType([propTypes.user, propTypes.currentUser]),
- userShowError: propTypes.error,
- queryListingsError: propTypes.error,
- listings: arrayOf(oneOfType([propTypes.listing, propTypes.ownListing])).isRequired,
- reviews: arrayOf(propTypes.review),
- queryReviewsError: propTypes.error,
-};
-
const mapStateToProps = state => {
const { currentUser } = state.user;
const {
@@ -433,7 +433,7 @@ const mapStateToProps = state => {
userShowError,
queryListingsError,
userListingRefs,
- reviews,
+ reviews = [],
queryReviewsError,
} = state.ProfilePage;
const userMatches = getMarketplaceEntities(state, [{ type: 'user', id: userId }]);
diff --git a/src/containers/ProfilePage/ProfilePage.test.js b/src/containers/ProfilePage/ProfilePage.test.js
index 6c9830538..41e9223da 100644
--- a/src/containers/ProfilePage/ProfilePage.test.js
+++ b/src/containers/ProfilePage/ProfilePage.test.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { act } from 'react';
import '@testing-library/jest-dom';
import { types as sdkTypes } from '../../util/sdkLoader';
@@ -126,21 +126,26 @@ describe('ProfilePage', () => {
params: {},
};
- it('Check that user name and bio is shown correctly', () => {
- render(, {
- initialState: getInitialState(),
- config,
+ it('Check that user name and bio is shown correctly', async () => {
+ await act(async () => {
+ render(, {
+ initialState: getInitialState(),
+ config,
+ });
});
expect(screen.getByText('ProfilePage.desktopHeading')).toBeInTheDocument();
expect(screen.getByText('I am a great cook!')).toBeInTheDocument();
});
- it('Check that custom user information is shown correctly', () => {
- const { getByRole } = render(, {
- initialState: getInitialState(),
- config,
+ it('Check that custom user information is shown correctly', async () => {
+ let rendered = {};
+ await act(async () => {
+ rendered = render(, {
+ initialState: getInitialState(),
+ config,
+ });
});
-
+ const { getByRole } = rendered;
// Show custom fields correctly
expect(getByRole('heading', { name: 'ProfilePage.detailsTitle' })).toBeInTheDocument();
expect(getByRole('heading', { name: 'Dietary preferences' })).toBeInTheDocument();
@@ -158,10 +163,12 @@ describe('ProfilePage', () => {
expect(screen.queryByText('Not shown in profile')).toBeNull();
});
- it('Check that listing information is shown correctly', () => {
- render(, {
- initialState: getInitialState(),
- config,
+ it('Check that listing information is shown correctly', async () => {
+ await act(async () => {
+ render(, {
+ initialState: getInitialState(),
+ config,
+ });
});
expect(screen.getByText('ProfilePage.listingsTitle')).toBeInTheDocument();
@@ -169,11 +176,15 @@ describe('ProfilePage', () => {
expect(screen.getByText('$55.00')).toBeInTheDocument();
});
- it('Check that review information is shown correctly', () => {
- const { getByRole } = render(, {
- initialState: getInitialState(),
- config,
+ it('Check that review information is shown correctly', async () => {
+ let rendered = {};
+ await act(async () => {
+ rendered = render(, {
+ initialState: getInitialState(),
+ config,
+ });
});
+ const { getByRole } = rendered;
expect(
getByRole('heading', { name: 'ProfilePage.reviewsFromMyCustomersTitle' })
diff --git a/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.js b/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.js
index 7fa89f052..e15a22438 100644
--- a/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.js
+++ b/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.js
@@ -1,5 +1,4 @@
import React, { Component } from 'react';
-import { bool, string } from 'prop-types';
import { compose } from 'redux';
import { Field, Form as FinalForm } from 'react-final-form';
import isEqual from 'lodash/isEqual';
@@ -75,6 +74,30 @@ const DisplayNameMaybe = props => {
);
};
+/**
+ * ProfileSettingsForm
+ * TODO: change to functional component
+ *
+ * @component
+ * @param {Object} props
+ * @param {string} [props.rootClassName] - Custom class that overrides the default class for the root element
+ * @param {string} [props.className] - Custom class that extends the default class for the root element
+ * @param {string} [props.formId] - The form id
+ * @param {propTypes.currentUser} props.currentUser - The current user
+ * @param {Object} props.userTypeConfig - The user type config
+ * @param {string} props.userTypeConfig.userType - The user type
+ * @param {Array