feat: New screens for loading, no videos and error states. Also tests added
This commit is contained in:
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { Outline } from '@edx/paragon/icons';
|
||||
import { Error } from '@edx/paragon/icons';
|
||||
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import messages from './messages';
|
||||
@@ -42,7 +42,7 @@ export const ErrorAlert = ({
|
||||
return (
|
||||
<Alert
|
||||
variant="danger"
|
||||
icon={Outline}
|
||||
icon={Error}
|
||||
dismissible
|
||||
onClose={dismissAlert}
|
||||
>
|
||||
|
||||
@@ -15,6 +15,7 @@ import messages from './messages';
|
||||
import GalleryCard from './GalleryCard';
|
||||
|
||||
export const Gallery = ({
|
||||
show,
|
||||
galleryIsEmpty,
|
||||
searchIsEmpty,
|
||||
displayList,
|
||||
@@ -27,13 +28,24 @@ export const Gallery = ({
|
||||
// injected
|
||||
intl,
|
||||
}) => {
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
if (!isLoaded) {
|
||||
return (
|
||||
<Spinner
|
||||
animation="border"
|
||||
className="mie-3"
|
||||
screenReaderText={intl.formatMessage(messages.loading)}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
}}
|
||||
>
|
||||
<Spinner
|
||||
animation="border"
|
||||
className="mie-3"
|
||||
screenReaderText={intl.formatMessage(messages.loading)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (galleryIsEmpty) {
|
||||
@@ -71,8 +83,10 @@ Gallery.defaultProps = {
|
||||
highlighted: '',
|
||||
showIdsOnCards: false,
|
||||
height: '375px',
|
||||
show: true,
|
||||
};
|
||||
Gallery.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
isLoaded: PropTypes.bool.isRequired,
|
||||
galleryIsEmpty: PropTypes.bool.isRequired,
|
||||
searchIsEmpty: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -37,5 +37,9 @@ describe('TextEditor Image Gallery component', () => {
|
||||
test('snapshot: loaded, show gallery', () => {
|
||||
expect(shallow(<Gallery {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: not shot gallery', () => {
|
||||
const wrapper = shallow(<Gallery {...props} show={false} />);
|
||||
expect(wrapper.type()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,20 +6,21 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { formatMessage } from '../../../testUtils';
|
||||
|
||||
import { sortKeys, sortMessages } from '../ImageUploadModal/SelectImageModal/utils';
|
||||
import { filterKeys, filterMessages } from '../../containers/VideoGallery/utils';
|
||||
import { SearchSort } from './SearchSort';
|
||||
|
||||
describe('SearchSort component', () => {
|
||||
const props = {
|
||||
searchString: 'props.searchString',
|
||||
onSearchChange: jest.fn().mockName('props.onSearchChange'),
|
||||
clearSearchString: jest.fn().mockName('props.clearSearchString'),
|
||||
sortBy: sortKeys.dateOldest,
|
||||
sortKeys,
|
||||
sortMessages,
|
||||
onSortClick: jest.fn().mockName('props.onSortClick'),
|
||||
intl: { formatMessage },
|
||||
};
|
||||
describe('snapshots', () => {
|
||||
describe('snapshots without filterKeys', () => {
|
||||
const props = {
|
||||
searchString: 'props.searchString',
|
||||
onSearchChange: jest.fn().mockName('props.onSearchChange'),
|
||||
clearSearchString: jest.fn().mockName('props.clearSearchString'),
|
||||
sortBy: sortKeys.dateOldest,
|
||||
sortKeys,
|
||||
sortMessages,
|
||||
onSortClick: jest.fn().mockName('props.onSortClick'),
|
||||
intl: { formatMessage },
|
||||
};
|
||||
test('with search string (close button)', () => {
|
||||
expect(shallow(<SearchSort {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
@@ -42,4 +43,59 @@ describe('SearchSort component', () => {
|
||||
)).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('snapshots with filterKeys', () => {
|
||||
const props = {
|
||||
searchString: 'props.searchString',
|
||||
onSearchChange: jest.fn().mockName('props.onSearchChange'),
|
||||
clearSearchString: jest.fn().mockName('props.clearSearchString'),
|
||||
sortBy: sortKeys.dateOldest,
|
||||
sortKeys,
|
||||
sortMessages,
|
||||
filterKeys,
|
||||
filterMessages,
|
||||
showSwitch: true,
|
||||
onSortClick: jest.fn().mockName('props.onSortClick'),
|
||||
onFilterClick: jest.fn().mockName('props.onFilterClick'),
|
||||
intl: { formatMessage },
|
||||
};
|
||||
test('with search string (close button)', () => {
|
||||
expect(shallow(<SearchSort {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('without search string (search icon)', () => {
|
||||
expect(shallow(<SearchSort {...props} searchString="" />)).toMatchSnapshot();
|
||||
});
|
||||
test('adds a sort option for each sortKey', () => {
|
||||
const el = shallow(<SearchSort {...props} />);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...sortMessages.dateNewest} />,
|
||||
)).toEqual(true);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...sortMessages.dateOldest} />,
|
||||
)).toEqual(true);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...sortMessages.nameAscending} />,
|
||||
)).toEqual(true);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...sortMessages.nameDescending} />,
|
||||
)).toEqual(true);
|
||||
});
|
||||
test('adds a filter option for each filterKet', () => {
|
||||
const el = shallow(<SearchSort {...props} />);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...filterMessages.videoStatus} />,
|
||||
)).toEqual(true);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...filterMessages.uploading} />,
|
||||
)).toEqual(true);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...filterMessages.processing} />,
|
||||
)).toEqual(true);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...filterMessages.ready} />,
|
||||
)).toEqual(true);
|
||||
expect(el.find(Dropdown).containsMatchingElement(
|
||||
<FormattedMessage {...filterMessages.failed} />,
|
||||
)).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,9 +85,20 @@ exports[`TextEditor Image Gallery component component snapshot: loaded, show gal
|
||||
`;
|
||||
|
||||
exports[`TextEditor Image Gallery component component snapshot: not loaded, show spinner 1`] = `
|
||||
<Spinner
|
||||
animation="border"
|
||||
className="mie-3"
|
||||
screenReaderText="loading..."
|
||||
/>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"left": "50%",
|
||||
"position": "absolute",
|
||||
"top": "50%",
|
||||
"transform": "translate(-50%, -50%)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Spinner
|
||||
animation="border"
|
||||
className="mie-3"
|
||||
screenReaderText="loading..."
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,291 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SearchSort component snapshots with search string (close button) 1`] = `
|
||||
exports[`SearchSort component snapshots with filterKeys with search string (close button) 1`] = `
|
||||
<ActionRow>
|
||||
<Form.Group
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Form.Control
|
||||
autoFocus={true}
|
||||
onChange={[MockFunction props.onSearchChange]}
|
||||
placeholder="Search"
|
||||
trailingElement={
|
||||
<IconButton
|
||||
iconAs="Icon"
|
||||
invertColors={true}
|
||||
isActive={true}
|
||||
onClick={[MockFunction props.clearSearchString]}
|
||||
size="sm"
|
||||
src={[MockFunction icons.Close]}
|
||||
/>
|
||||
}
|
||||
value="props.searchString"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
id="gallery-sort-button"
|
||||
variant="tertiary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By date added (oldest)"
|
||||
description="Dropdown label for sorting by date (oldest)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.dateoldest.label"
|
||||
/>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
key="dateNewest"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By date added (newest)"
|
||||
description="Dropdown label for sorting by date (newest)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.datenewest.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="dateOldest"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By date added (oldest)"
|
||||
description="Dropdown label for sorting by date (oldest)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.dateoldest.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="nameAscending"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By name (ascending)"
|
||||
description="Dropdown label for sorting by name (ascending)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.nameascending.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="nameDescending"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By name (descending)"
|
||||
description="Dropdown label for sorting by name (descending)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.namedescending.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
id="gallery-filter-button"
|
||||
variant="tertiary"
|
||||
>
|
||||
<FormattedMessage />
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
key="videoStatus"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Video status"
|
||||
description="Dropdown label for filter by video status (none)"
|
||||
id="authoring.selectvideomodal.filter.videostatusnone.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="uploading"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Uploading"
|
||||
description="Dropdown label for filter by video status (uploading)"
|
||||
id="authoring.selectvideomodal.filter.videostatusuploading.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="processing"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Processing"
|
||||
description="Dropdown label for filter by video status (processing)"
|
||||
id="authoring.selectvideomodal.filter.videostatusprocessing.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="ready"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Ready"
|
||||
description="Dropdown label for filter by video status (ready)"
|
||||
id="authoring.selectvideomodal.filter.videostatusready.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="failed"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed"
|
||||
description="Dropdown label for filter by video status (failed)"
|
||||
id="authoring.selectvideomodal.filter.videostatusfailed.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
<ActionRow.Spacer />
|
||||
<Component
|
||||
isInline={true}
|
||||
name="switch"
|
||||
onChange={null}
|
||||
>
|
||||
<Component
|
||||
floatLabelLeft={true}
|
||||
value="switch-value"
|
||||
>
|
||||
<FormattedMessage />
|
||||
</Component>
|
||||
</Component>
|
||||
</ActionRow>
|
||||
`;
|
||||
|
||||
exports[`SearchSort component snapshots with filterKeys without search string (search icon) 1`] = `
|
||||
<ActionRow>
|
||||
<Form.Group
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Form.Control
|
||||
autoFocus={true}
|
||||
onChange={[MockFunction props.onSearchChange]}
|
||||
placeholder="Search"
|
||||
trailingElement={<Icon />}
|
||||
value=""
|
||||
/>
|
||||
</Form.Group>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
id="gallery-sort-button"
|
||||
variant="tertiary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By date added (oldest)"
|
||||
description="Dropdown label for sorting by date (oldest)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.dateoldest.label"
|
||||
/>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
key="dateNewest"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By date added (newest)"
|
||||
description="Dropdown label for sorting by date (newest)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.datenewest.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="dateOldest"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By date added (oldest)"
|
||||
description="Dropdown label for sorting by date (oldest)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.dateoldest.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="nameAscending"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By name (ascending)"
|
||||
description="Dropdown label for sorting by name (ascending)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.nameascending.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="nameDescending"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="By name (descending)"
|
||||
description="Dropdown label for sorting by name (descending)"
|
||||
id="authoring.texteditor.selectimagemodal.sort.namedescending.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
id="gallery-filter-button"
|
||||
variant="tertiary"
|
||||
>
|
||||
<FormattedMessage />
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
key="videoStatus"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Video status"
|
||||
description="Dropdown label for filter by video status (none)"
|
||||
id="authoring.selectvideomodal.filter.videostatusnone.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="uploading"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Uploading"
|
||||
description="Dropdown label for filter by video status (uploading)"
|
||||
id="authoring.selectvideomodal.filter.videostatusuploading.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="processing"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Processing"
|
||||
description="Dropdown label for filter by video status (processing)"
|
||||
id="authoring.selectvideomodal.filter.videostatusprocessing.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="ready"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Ready"
|
||||
description="Dropdown label for filter by video status (ready)"
|
||||
id="authoring.selectvideomodal.filter.videostatusready.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="failed"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed"
|
||||
description="Dropdown label for filter by video status (failed)"
|
||||
id="authoring.selectvideomodal.filter.videostatusfailed.label"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
<ActionRow.Spacer />
|
||||
<Component
|
||||
isInline={true}
|
||||
name="switch"
|
||||
onChange={null}
|
||||
>
|
||||
<Component
|
||||
floatLabelLeft={true}
|
||||
value="switch-value"
|
||||
>
|
||||
<FormattedMessage />
|
||||
</Component>
|
||||
</Component>
|
||||
</ActionRow>
|
||||
`;
|
||||
|
||||
exports[`SearchSort component snapshots without filterKeys with search string (close button) 1`] = `
|
||||
<ActionRow>
|
||||
<Form.Group
|
||||
style={
|
||||
@@ -80,7 +365,7 @@ exports[`SearchSort component snapshots with search string (close button) 1`] =
|
||||
</ActionRow>
|
||||
`;
|
||||
|
||||
exports[`SearchSort component snapshots without search string (search icon) 1`] = `
|
||||
exports[`SearchSort component snapshots without filterKeys without search string (search icon) 1`] = `
|
||||
<ActionRow>
|
||||
<Form.Group
|
||||
style={
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Selection Modal snapshots rendering correctly with expected Input 1`] = `
|
||||
<ContextConsumer>
|
||||
<Component />
|
||||
</ContextConsumer>
|
||||
`;
|
||||
|
||||
exports[`Selection Modal snapshots rendering with props to null 1`] = `
|
||||
<ContextConsumer>
|
||||
<Component />
|
||||
</ContextConsumer>
|
||||
`;
|
||||
@@ -43,8 +43,18 @@ export const SelectionModal = ({
|
||||
fetchError,
|
||||
uploadError,
|
||||
} = modalMessages;
|
||||
|
||||
let background = '#FFFFFF';
|
||||
let showGallery = true;
|
||||
if (isLoaded && !isFetchError && !isUploadError && !inputError.show) {
|
||||
background = '#EBEBEB';
|
||||
} else if (isLoaded) {
|
||||
showGallery = false;
|
||||
}
|
||||
|
||||
const galleryPropsValues = {
|
||||
isLoaded,
|
||||
show: showGallery,
|
||||
...galleryProps,
|
||||
};
|
||||
return (
|
||||
@@ -64,7 +74,7 @@ export const SelectionModal = ({
|
||||
</Button>
|
||||
)}
|
||||
title={intl.formatMessage(titleMsg)}
|
||||
bodyStyle={{ background: '#EBEBEB' }}
|
||||
bodyStyle={{ background, padding: '24px' }}
|
||||
headerComponent={(
|
||||
<div style={{ zIndex: 10000, margin: '18px 0' }}>
|
||||
<SearchSort {...searchSortProps} />
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { formatMessage } from '../../../testUtils';
|
||||
import SelectionModal from '.';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
const props = {
|
||||
isOpen: jest.fn(),
|
||||
@@ -69,7 +71,7 @@ const props = {
|
||||
|
||||
jest.mock('../BaseModal', () => 'BaseModal');
|
||||
jest.mock('./SearchSort', () => 'SearchSort');
|
||||
jest.mock('./Gallery', () => 'Gallery');
|
||||
jest.mock('./Gallery', () => () => 'Gallery');
|
||||
jest.mock('../FileInput', () => 'FileInput');
|
||||
jest.mock('../ErrorAlerts/ErrorAlert', () => 'ErrorAlert');
|
||||
jest.mock('../ErrorAlerts/FetchErrorAlert', () => 'FetchErrorAlert');
|
||||
@@ -77,11 +79,13 @@ jest.mock('../ErrorAlerts/UploadErrorAlert', () => 'UploadErrorAlert');
|
||||
|
||||
describe('Selection Modal', () => {
|
||||
describe('snapshots', () => {
|
||||
test('rendering correctly with expected Input', () => {
|
||||
expect(shallow(<SelectionModal {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('rendering with props to null', () => {
|
||||
expect(shallow(<SelectionModal />)).toMatchSnapshot();
|
||||
test('rendering correctly with expected Input', async () => {
|
||||
render(
|
||||
<IntlProvider>
|
||||
<SelectionModal {...props} />
|
||||
</IntlProvider>,
|
||||
);
|
||||
expect(screen.getByText('Gallery')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user