Skip to content

Commit

Permalink
feat: video gallery thumbnail fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
ArturGaspar committed Oct 19, 2023
1 parent cfa4577 commit 1e6fb33
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 60 deletions.
16 changes: 15 additions & 1 deletion src/editors/containers/VideoGallery/index.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useEffect } from 'react';
import { Image } from '@edx/paragon';
import { useSelector } from 'react-redux';
import { selectors } from '../../data/redux';
import hooks from './hooks';
import SelectionModal from '../../sharedComponents/SelectionModal';
import { acceptedImgKeys } from './utils';
import messages from './messages';
import { RequestKeys } from '../../data/constants/requests';
import videoThumbnail from '../../data/images/videoThumbnail.svg';

export const VideoGallery = () => {
const rawVideos = useSelector(selectors.app.videos);
Expand Down Expand Up @@ -45,6 +47,15 @@ export const VideoGallery = () => {
uploadError: messages.uploadVideoError,
};

const thumbnailFallback = (
<Image
thumbnail
className="px-6 py-4.5"
src={videoThumbnail}
style={{ height: '100%' }}
/>
);

return (
<SelectionModal
{...{
Expand All @@ -55,7 +66,10 @@ export const VideoGallery = () => {
galleryError,
inputError,
fileInput,
galleryProps,
galleryProps: {
...galleryProps,
thumbnailFallback,
},
searchSortProps,
selectBtnProps,
acceptedFiles: acceptedImgKeys,
Expand Down
12 changes: 11 additions & 1 deletion src/editors/sharedComponents/SelectionModal/Gallery.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const Gallery = ({
showIdsOnCards,
height,
isLoaded,
thumbnailFallback,
}) => {
const intl = useIntl();
if (!isLoaded) {
Expand Down Expand Up @@ -66,7 +67,14 @@ export const Gallery = ({
type="radio"
value={highlighted}
>
{ displayList.map(asset => <GalleryCard key={asset.id} asset={asset} showId={showIdsOnCards} />) }
{ displayList.map(asset => (
<GalleryCard
key={asset.id}
asset={asset}
showId={showIdsOnCards}
thumbnailFallback={thumbnailFallback}
/>
)) }
</SelectableBox.Set>
</div>
</Scrollable>
Expand All @@ -78,6 +86,7 @@ Gallery.defaultProps = {
showIdsOnCards: false,
height: '375px',
show: true,
thumbnailFallback: undefined,
};
Gallery.propTypes = {
show: PropTypes.bool,
Expand All @@ -90,6 +99,7 @@ Gallery.propTypes = {
emptyGalleryLabel: PropTypes.shape({}).isRequired,
showIdsOnCards: PropTypes.bool,
height: PropTypes.string,
thumbnailFallback: PropTypes.element,
};

export default Gallery;
131 changes: 73 additions & 58 deletions src/editors/sharedComponents/SelectionModal/GalleryCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,67 +14,81 @@ import LanguageNamesWidget from '../../containers/VideoEditor/components/VideoSe

export const GalleryCard = ({
asset,
}) => (
<SelectableBox
className="card bg-white shadow-none border-0 py-0"
key={asset.externalUrl}
type="radio"
value={asset.id}
>
<div className="card-div d-flex flex-row flex-nowrap align-items-center">
<div
className="position-relative"
style={{
width: '200px',
height: '100px',
}}
>
<Image
style={{ border: 'none', width: '200px', height: '100px' }}
src={asset.externalUrl}
/>
{ asset.status && asset.statusBadgeVariant && (
<Badge variant={asset.statusBadgeVariant} style={{ position: 'absolute', left: '6px', top: '6px' }}>
{asset.status}
</Badge>
)}
{ asset.duration >= 0 && (
<Badge
variant="dark"
style={{
position: 'absolute',
right: '6px',
bottom: '6px',
backgroundColor: 'black',
}}
>
{formatDuration(asset.duration)}
</Badge>
)}
</div>
<div className="card-text px-3 py-2" style={{ marginTop: '10px' }}>
<h3 className="text-primary-500">{asset.displayName}</h3>
{ asset.transcripts && (
<div style={{ margin: '0 0 5px 0' }}>
<LanguageNamesWidget
transcripts={asset.transcripts}
thumbnailFallback,
}) => {
const [thumbnailError, setThumbnailError] = React.useState(false);
return (
<SelectableBox
className="card bg-white shadow-none border-0 py-0"
key={asset.externalUrl}
type="radio"
value={asset.id}
>
<div className="card-div d-flex flex-row flex-nowrap align-items-center">
<div
className="position-relative"
style={{
width: '200px',
height: '100px',
}}
>
{(thumbnailError && thumbnailFallback) ? (
<div style={{ width: '200px', height: '100px' }}>
{ thumbnailFallback }
</div>
) : (
<Image
style={{ border: 'none', width: '200px', height: '100px' }}
src={asset.externalUrl}
onError={thumbnailFallback && (() => setThumbnailError(true))}
/>
)}
{ asset.status && asset.statusBadgeVariant && (
<Badge variant={asset.statusBadgeVariant} style={{ position: 'absolute', left: '6px', top: '6px' }}>
{asset.status}
</Badge>
)}
{ asset.duration >= 0 && (
<Badge
variant="dark"
style={{
position: 'absolute',
right: '6px',
bottom: '6px',
backgroundColor: 'black',
}}
>
{formatDuration(asset.duration)}
</Badge>
)}
</div>
<div className="card-text px-3 py-2" style={{ marginTop: '10px' }}>
<h3 className="text-primary-500">{asset.displayName}</h3>
{ asset.transcripts && (
<div style={{ margin: '0 0 5px 0' }}>
<LanguageNamesWidget
transcripts={asset.transcripts}
/>
</div>
)}
<p className="text-gray-500" style={{ fontSize: '11px' }}>
<FormattedMessage
{...messages.addedDate}
values={{
date: <FormattedDate value={asset.dateAdded} />,
time: <FormattedTime value={asset.dateAdded} />,
}}
/>
</div>
)}
<p className="text-gray-500" style={{ fontSize: '11px' }}>
<FormattedMessage
{...messages.addedDate}
values={{
date: <FormattedDate value={asset.dateAdded} />,
time: <FormattedTime value={asset.dateAdded} />,
}}
/>
</p>
</p>
</div>
</div>
</div>
</SelectableBox>
);
</SelectableBox>
);
};

GalleryCard.defaultProps = {
thumbnailFallback: undefined,
};
GalleryCard.propTypes = {
asset: PropTypes.shape({
contentType: PropTypes.string,
Expand All @@ -91,6 +105,7 @@ GalleryCard.propTypes = {
statusBadgeVariant: PropTypes.string,
transcripts: PropTypes.arrayOf(PropTypes.string),
}).isRequired,
thumbnailFallback: PropTypes.element,
};

export default GalleryCard;
10 changes: 10 additions & 0 deletions src/editors/sharedComponents/SelectionModal/GalleryCard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('GalleryCard component', () => {
displayName: 'props.img.displayName',
dateAdded: 12345,
};
const thumbnailFallback = (<span>Image failed to load</span>);
let el;
beforeEach(() => {
el = shallow(<GalleryCard asset={asset} />);
Expand All @@ -20,6 +21,15 @@ describe('GalleryCard component', () => {
it('loads Image with src from image external url', () => {
expect(el.find(Image).props().src).toEqual(asset.externalUrl);
});
it('snapshot with thumbnail fallback and load error', () => {
el = shallow(<GalleryCard asset={asset} thumbnailFallback={thumbnailFallback} />);
el.find(Image).props().onError();
expect(el).toMatchSnapshot();
});
it('snapshot with thumbnail fallback and no error', () => {
el = shallow(<GalleryCard asset={asset} thumbnailFallback={thumbnailFallback} />);
expect(el).toMatchSnapshot();
});
it('snapshot with status badge', () => {
el = shallow(<GalleryCard asset={{ ...asset, status: 'failed', statusBadgeVariant: 'danger' }} />);
expect(el).toMatchSnapshot();
Expand Down
Loading

0 comments on commit 1e6fb33

Please sign in to comment.