diff --git a/src/Card/CardCarousel/tests/__snapshots__/CardCarousel.test.jsx.snap b/src/Card/CardCarousel/tests/__snapshots__/CardCarousel.test.jsx.snap
index 2ad5754ef4..d668a3679d 100644
--- a/src/Card/CardCarousel/tests/__snapshots__/CardCarousel.test.jsx.snap
+++ b/src/Card/CardCarousel/tests/__snapshots__/CardCarousel.test.jsx.snap
@@ -108,12 +108,26 @@ exports[` renders card carousel with custom title and subtitles
{
const { orientation, isLoading } = useContext(CardContext);
- const [showImageCap, setShowImageCap] = useState(false);
- const [showLogoCap, setShowLogoCap] = useState(false);
-
- const wrapperClassName = `pgn__card-wrapper-image-cap ${orientation}`;
-
- if (isLoading) {
- return (
-
-
- {logoSkeleton && (
-
- )}
-
- );
- }
-
- const handleSrcFallback = (event, altSrc, imageKey) => {
- const { currentTarget } = event;
- if (!altSrc || currentTarget.src.endsWith(altSrc)) {
- if (imageKey === 'imageCap') {
- currentTarget.src = cardSrcFallbackImg;
- } else {
- setShowLogoCap(false);
- }
+ const imageSkeletonHeight = useMemo(() => (
+ orientation === 'horizontal' ? '100%' : skeletonHeight
+ ), [orientation, skeletonHeight]);
- return;
- }
-
- currentTarget.src = altSrc;
- };
+ const wrapperClassName = `pgn__card-wrapper-image-cap ${orientation}`;
return (
{!!src && (
-
handleSrcFallback(event, fallbackSrc, 'imageCap')}
- onLoad={() => setShowImageCap(true)}
alt={srcAlt}
- loading={imageLoadingType}
+ fallback={fallbackSrc}
+ className="pgn__card-image-cap"
+ useDefaultSrc
+ withSkeleton
+ skeletonWidth={skeletonWidth}
+ skeletonHeight={imageSkeletonHeight}
+ imageLoadingType={imageLoadingType}
+ skeletonClassName="pgn__card-image-cap-loader"
+ isLoading={isLoading}
/>
)}
- {!!logoSrc && (
-
handleSrcFallback(event, fallbackLogoSrc, 'logoCap')}
- onLoad={() => setShowLogoCap(true)}
alt={logoAlt}
- loading={imageLoadingType}
+ fallback={fallbackLogoSrc}
+ withSkeleton={logoSkeleton}
+ className="pgn__card-logo-cap"
+ skeletonWidth={logoSkeletonWidth}
+ skeletonHeight={logoSkeletonHeight}
+ imageLoadingType={imageLoadingType}
+ skeletonClassName="pgn__card-logo-cap"
/>
)}
@@ -109,9 +79,9 @@ CardImageCap.propTypes = {
/** Specifies logo image alt text. */
logoAlt: PropTypes.string,
/** Specifies height of Image skeleton in loading state. */
- skeletonHeight: PropTypes.number,
+ skeletonHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/** Specifies width of Image skeleton in loading state. */
- skeletonWidth: PropTypes.number,
+ skeletonWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/** Specifies whether the cap should be displayed during loading. */
logoSkeleton: PropTypes.bool,
/** Specifies height of Logo skeleton in loading state. */
diff --git a/src/Card/CardImageWithSkeleton.jsx b/src/Card/CardImageWithSkeleton.jsx
new file mode 100644
index 0000000000..8d326217e7
--- /dev/null
+++ b/src/Card/CardImageWithSkeleton.jsx
@@ -0,0 +1,74 @@
+import React, { useMemo } from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import Skeleton from 'react-loading-skeleton';
+
+import Image from '../Image';
+import useImageLoader from '../hooks/useImageLoader';
+
+function CardImageWithSkeleton({
+ src,
+ alt,
+ fallback,
+ className,
+ withSkeleton,
+ useDefaultSrc,
+ skeletonWidth,
+ skeletonHeight,
+ imageLoadingType,
+ skeletonClassName,
+ isLoading = false,
+}) {
+ const config = useMemo(
+ () => ({
+ mainSrc: src, fallbackSrc: fallback, useDefaultSrc,
+ }),
+ [src, fallback, useDefaultSrc],
+ );
+
+ const { ref, isSrcLoading } = useImageLoader(config);
+
+ return (
+ <>
+
+
+ >
+ );
+}
+
+CardImageWithSkeleton.propTypes = {
+ className: PropTypes.string,
+ src: PropTypes.string.isRequired,
+ fallback: PropTypes.string,
+ useDefaultSrc: PropTypes.bool,
+ alt: PropTypes.string.isRequired,
+ skeletonHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ skeletonWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ skeletonClassName: PropTypes.string,
+ imageLoadingType: PropTypes.oneOf(['eager', 'lazy']),
+ isLoading: PropTypes.bool,
+ withSkeleton: PropTypes.bool,
+};
+
+CardImageWithSkeleton.defaultProps = {
+ className: undefined,
+ skeletonClassName: undefined,
+ fallback: undefined,
+ useDefaultSrc: false,
+ imageLoadingType: 'eager',
+ skeletonHeight: undefined,
+ skeletonWidth: undefined,
+ isLoading: false,
+ withSkeleton: false,
+};
+
+export default CardImageWithSkeleton;
diff --git a/src/Card/_variables.scss b/src/Card/_variables.scss
index 703e99dc26..5e1c581bd8 100644
--- a/src/Card/_variables.scss
+++ b/src/Card/_variables.scss
@@ -48,7 +48,7 @@ $card-footer-text-font-size: $x-small-font-size;
$card-image-horizontal-max-width: 240px !default;
$card-image-horizontal-min-width: $card-image-horizontal-max-width !default;
$card-image-vertical-max-height: 140px !default;
-$loading-skeleton-spacer: .313rem !default;
+$loading-skeleton-spacer: -4px !default;
$card-focus-border-offset: 5px !default;
$card-focus-border-width: 2px !default;
diff --git a/src/Card/constants.js b/src/Card/constants.js
new file mode 100644
index 0000000000..09a1677f28
--- /dev/null
+++ b/src/Card/constants.js
@@ -0,0 +1,2 @@
+export const SKELETON_HEIGHT_VALUE = 140;
+export const LOGO_SKELETON_HEIGHT_VALUE = 41;
diff --git a/src/Card/index.scss b/src/Card/index.scss
index 56d0269aad..33860726ce 100644
--- a/src/Card/index.scss
+++ b/src/Card/index.scss
@@ -337,10 +337,15 @@ a.pgn__card {
}
.pgn__card-image-cap-loader {
+ margin-top: $loading-skeleton-spacer;
+ display: none;
+
+ &.show {
+ display: block;
+ height: 100%;
+ }
+
.react-loading-skeleton {
- margin-bottom: -$loading-skeleton-spacer;
- position: relative;
- top: -$loading-skeleton-spacer;
height: 100%;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
diff --git a/src/Card/tests/CardImageCap.test.jsx b/src/Card/tests/CardImageCap.test.jsx
index e459413d65..b46607d21d 100644
--- a/src/Card/tests/CardImageCap.test.jsx
+++ b/src/Card/tests/CardImageCap.test.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import renderer from 'react-test-renderer';
-import { render, fireEvent, screen } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
import CardImageCap from '../CardImageCap';
import CardContext from '../CardContext';
@@ -61,71 +61,7 @@ describe('
', () => {
/>,
);
- expect(screen.getByAltText('Src alt text').src).toEqual('http://src.image/');
- expect(screen.getByAltText('Logo alt text').src).toEqual('http://logo.image/');
- });
-
- it('render with loading state', () => {
- render(
-
,
- );
-
- expect(screen.queryByAltText('Src alt text')).toBeNull();
- expect(screen.queryByAltText('Logo alt text')).toBeNull();
-
- expect(screen.getByTestId('image-loader-wrapper')).toBeInTheDocument();
- expect(screen.getByTestId('image-loader-wrapper').firstChild).toHaveClass('pgn__card-image-cap-loader');
- expect(screen.getByTestId('image-loader-wrapper').lastChild).toHaveClass('pgn__card-logo-cap');
- });
-
- it('replaces image with fallback one in case of error', () => {
- render(
-
,
- );
- const srcImg = screen.getByAltText('Src alt text');
- expect(srcImg.src).toEqual('http://src.image/');
- fireEvent.load(srcImg);
- fireEvent.error(srcImg);
- expect(srcImg.src).toEqual('http://src.image.fallback/');
-
- const logoImg = screen.getByAltText('Logo alt text');
- expect(logoImg.src).toEqual('http://logo.image/');
- fireEvent.load(srcImg);
- fireEvent.error(logoImg);
- expect(logoImg.src).toEqual('http://logo.image.fallback/');
- });
-
- it('hiding component if it isn`t fallbackLogoSrc and logoSrc don`t work', () => {
- render(
);
-
- const logoImg = screen.getByAltText('Logo alt text');
- fireEvent.load(logoImg);
- expect(logoImg.className).toEqual('pgn__card-logo-cap show');
- fireEvent.error(logoImg);
- expect(logoImg.className).toEqual('pgn__card-logo-cap');
- });
-
- it('hiding component if it isn`t fallbackSrc and src don`t work', () => {
- render(
);
-
- const srcImg = screen.getByAltText('Src alt text');
- fireEvent.load(srcImg);
- fireEvent.error(srcImg);
- // test-file-stub is what our fileMock.js returns for all images
- expect(srcImg.src.endsWith('test-file-stub')).toEqual(true);
+ expect(screen.getByAltText('Src alt text')).toBeInTheDocument();
+ expect(screen.getByAltText('Logo alt text')).toBeInTheDocument();
});
});
diff --git a/src/Card/tests/__snapshots__/CardDeck.test.jsx.snap b/src/Card/tests/__snapshots__/CardDeck.test.jsx.snap
index b5bc5b8e96..58b0c978d5 100644
--- a/src/Card/tests/__snapshots__/CardDeck.test.jsx.snap
+++ b/src/Card/tests/__snapshots__/CardDeck.test.jsx.snap
@@ -18,12 +18,26 @@ exports[`
has tabIndex="-1" when \`hasInteractiveChildren\` is true
+
+
+
+
+
+
has tabIndex="-1" when \`hasInteractiveChildren\` is true
+
+
+
+
+
+
has tabIndex="-1" when \`hasInteractiveChildren\` is true
+
+
+
+
+
+
has tabIndex="-1" when \`hasInteractiveChildren\` is true
+
+
+
+
+
+
has tabIndex="-1" when \`hasInteractiveChildren\` is true
+
+
+
+
+
+
renders default columnSizes 1`] = `
+
+
+
+
+
+
renders default columnSizes 1`] = `
+
+
+
+
+
+
renders default columnSizes 1`] = `
+
+
+
+
+
+
renders default columnSizes 1`] = `
+
+
+
+
+
+
renders default columnSizes 1`] = `
+
+
+
+
+
+
renders with controlled columnSizes 1`] = `
+
+
+
+
+
+
renders with controlled columnSizes 1`] = `
+
+
+
+
+
+
renders with controlled columnSizes 1`] = `
+
+
+
+
+
+
renders with controlled columnSizes 1`] = `
+
+
+
+
+
+
renders with controlled columnSizes 1`] = `
+
+
+
+
+
+
renders with disabled equal height 1`] = `
+
+
+
+
+
+
renders with disabled equal height 1`] = `
+
+
+
+
+
+
renders with disabled equal height 1`] = `
+
+
+
+
+
+
renders with disabled equal height 1`] = `
+
+
+
+
+
+
renders with disabled equal height 1`] = `
+
+
+
+
+
+
renders with disabled equal height 1`] = `
-`;
\ No newline at end of file
+`;
diff --git a/src/Card/tests/__snapshots__/CardGrid.test.jsx.snap b/src/Card/tests/__snapshots__/CardGrid.test.jsx.snap
index 65425dd9d7..0369e7579c 100644
--- a/src/Card/tests/__snapshots__/CardGrid.test.jsx.snap
+++ b/src/Card/tests/__snapshots__/CardGrid.test.jsx.snap
@@ -17,12 +17,26 @@ exports[`Loading...
}
+