diff --git a/src/editors/containers/VideoGallery/index.test.jsx b/src/editors/containers/VideoGallery/index.test.jsx index 1ac21014d..293d8583d 100644 --- a/src/editors/containers/VideoGallery/index.test.jsx +++ b/src/editors/containers/VideoGallery/index.test.jsx @@ -124,17 +124,17 @@ describe('VideoGallery', () => { expect(window.location.assign).toHaveBeenCalled(); }); it.each([ - [/by date added \(newest\)/i, [2, 1, 3]], - [/by date added \(oldest\)/i, [3, 1, 2]], - [/by name \(ascending\)/i, [1, 2, 3]], - [/by name \(descending\)/i, [3, 2, 1]], - [/by duration \(longest\)/i, [3, 1, 2]], - [/by duration \(shortest\)/i, [2, 1, 3]], + [/newest/i, [2, 1, 3]], + [/oldest/i, [3, 1, 2]], + [/name A-Z/i, [1, 2, 3]], + [/name Z-A/i, [3, 2, 1]], + [/longest/i, [3, 1, 2]], + [/shortest/i, [2, 1, 3]], ])('videos can be sorted %s', async (sortBy, order) => { await renderComponent(); fireEvent.click(screen.getByRole('button', { - name: /by date added \(newest\)/i, + name: /By newest/i, })); fireEvent.click(screen.getByRole('link', { name: sortBy, @@ -165,7 +165,7 @@ describe('VideoGallery', () => { await act(async () => { fireEvent.click(screen.getByRole('button', { - name: 'Video status', + name: 'Filter by video status', })); }); await act(async () => { diff --git a/src/editors/containers/VideoGallery/messages.js b/src/editors/containers/VideoGallery/messages.js index a846b3956..c05b7d798 100644 --- a/src/editors/containers/VideoGallery/messages.js +++ b/src/editors/containers/VideoGallery/messages.js @@ -25,40 +25,40 @@ export const messages = { // Sort Dropdown sortByDateNewest: { id: 'authoring.selectvideomodal.sort.datenewest.label', - defaultMessage: 'By date added (newest)', + defaultMessage: 'newest', description: 'Dropdown label for sorting by date (newest)', }, sortByDateOldest: { id: 'authoring.selectvideomodal.sort.dateoldest.label', - defaultMessage: 'By date added (oldest)', + defaultMessage: 'oldest', description: 'Dropdown label for sorting by date (oldest)', }, sortByNameAscending: { id: 'authoring.selectvideomodal.sort.nameascending.label', - defaultMessage: 'By name (ascending)', + defaultMessage: 'name A-Z', description: 'Dropdown label for sorting by name (ascending)', }, sortByNameDescending: { id: 'authoring.selectvideomodal.sort.namedescending.label', - defaultMessage: 'By name (descending)', + defaultMessage: 'name Z-A', description: 'Dropdown label for sorting by name (descending)', }, sortByDurationShortest: { id: 'authoring.selectvideomodal.sort.durationshortest.label', - defaultMessage: 'By duration (shortest)', + defaultMessage: 'shortest', description: 'Dropdown label for sorting by duration (shortest)', }, sortByDurationLongest: { id: 'authoring.selectvideomodal.sort.durationlongest.label', - defaultMessage: 'By duration (longest)', + defaultMessage: 'longest', description: 'Dropdown label for sorting by duration (longest)', }, // Filter Dropdown - filterByVideoStatusNone: { - id: 'authoring.selectvideomodal.filter.videostatusnone.label', - defaultMessage: 'Video status', - description: 'Dropdown label for filter by video status (none)', + filterByVideoStatusTitle: { + id: 'authoring.selectvideomodal.filter.title.label', + defaultMessage: 'Filter by video status', + description: 'Title for video status filter dropdown', }, filterByVideoStatusUploading: { id: 'authoring.selectvideomodal.filter.videostatusuploading.label', diff --git a/src/editors/containers/VideoGallery/utils.js b/src/editors/containers/VideoGallery/utils.js index 8a1373948..ff2aec31b 100644 --- a/src/editors/containers/VideoGallery/utils.js +++ b/src/editors/containers/VideoGallery/utils.js @@ -29,7 +29,7 @@ export const filterKeys = StrictDict({ }); export const filterMessages = StrictDict({ - title: messages[messageKeys.filterByVideoStatusNone], + title: messages[messageKeys.filterByVideoStatusTitle], uploading: messages[messageKeys.filterByVideoStatusUploading], processing: messages[messageKeys.filterByVideoStatusProcessing], ready: messages[messageKeys.filterByVideoStatusReady], diff --git a/src/editors/sharedComponents/BaseModal/index.jsx b/src/editors/sharedComponents/BaseModal/index.jsx index 388652b6d..85869da13 100644 --- a/src/editors/sharedComponents/BaseModal/index.jsx +++ b/src/editors/sharedComponents/BaseModal/index.jsx @@ -20,6 +20,7 @@ export const BaseModal = ({ size, isFullscreenScroll, bodyStyle, + className, }) => ( @@ -59,6 +61,7 @@ BaseModal.defaultProps = { size: 'lg', isFullscreenScroll: true, bodyStyle: null, + className: undefined, }; BaseModal.propTypes = { @@ -72,6 +75,7 @@ BaseModal.propTypes = { size: PropTypes.string, isFullscreenScroll: PropTypes.bool, bodyStyle: PropTypes.shape({}), + className: PropTypes.string, }; export default BaseModal; diff --git a/src/editors/sharedComponents/SelectionModal/SearchSort.jsx b/src/editors/sharedComponents/SelectionModal/SearchSort.jsx index d9b00e554..612bc913f 100644 --- a/src/editors/sharedComponents/SelectionModal/SearchSort.jsx +++ b/src/editors/sharedComponents/SelectionModal/SearchSort.jsx @@ -4,13 +4,14 @@ import PropTypes from 'prop-types'; import { ActionRow, Form, Icon, IconButton, SelectMenu, MenuItem, } from '@edx/paragon'; -import { Close, Search } from '@edx/paragon/icons'; +import { Check, Close, Search } from '@edx/paragon/icons'; import { FormattedMessage, useIntl, } from '@edx/frontend-platform/i18n'; import messages from './messages'; +import './index.scss'; import MultiSelectFilterDropdown from './MultiSelectFilterDropdown'; import { sortKeys, sortMessages } from '../../containers/VideoGallery/utils'; @@ -54,9 +55,18 @@ export const SearchSort = ({ { !showSwitch && } - + {Object.keys(sortKeys).map(key => ( - + + + + + ))} diff --git a/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx b/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx index 74899d3a2..a036bcd6f 100644 --- a/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx +++ b/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx @@ -6,8 +6,9 @@ import { } from '@testing-library/react'; import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { sortKeys, sortMessages } from '../ImageUploadModal/SelectImageModal/utils'; -import { filterMessages } from '../../containers/VideoGallery/utils'; +import { + filterMessages, sortKeys, sortMessages, +} from '../../containers/VideoGallery/utils'; import { SearchSort } from './SearchSort'; import messages from './messages'; @@ -48,30 +49,30 @@ describe('SearchSort component', () => { const { getByRole } = getComponent(); await act(() => { fireEvent.click(screen.getByRole('button', { - name: /by date added \(oldest\)/i, + name: /By oldest/i, })); }); Object.values(sortMessages) .forEach(({ defaultMessage }) => { - expect(getByRole('link', { name: defaultMessage })) + expect(getByRole('link', { name: `By ${defaultMessage}` })) .toBeInTheDocument(); }); }); test('adds a sort option for each sortKey', async () => { const { getByRole } = getComponent(); await act(() => { - fireEvent.click(screen.getByRole('button', { name: /by date added \(oldest\)/i })); + fireEvent.click(screen.getByRole('button', { name: /oldest/i })); }); Object.values(sortMessages) .forEach(({ defaultMessage }) => { - expect(getByRole('link', { name: defaultMessage })) + expect(getByRole('link', { name: `By ${defaultMessage}` })) .toBeInTheDocument(); }); }); test('adds a filter option for each filterKet', async () => { const { getByRole } = getComponent(); await act(() => { - fireEvent.click(screen.getByRole('button', { name: /video status/i })); + fireEvent.click(screen.getByRole('button', { name: /Filter by video status/i })); }); Object.keys(filterMessages) .forEach((key) => { diff --git a/src/editors/sharedComponents/SelectionModal/index.jsx b/src/editors/sharedComponents/SelectionModal/index.jsx index d57c717ed..d0c06a025 100644 --- a/src/editors/sharedComponents/SelectionModal/index.jsx +++ b/src/editors/sharedComponents/SelectionModal/index.jsx @@ -16,6 +16,8 @@ import ErrorAlert from '../ErrorAlerts/ErrorAlert'; import FetchErrorAlert from '../ErrorAlerts/FetchErrorAlert'; import UploadErrorAlert from '../ErrorAlerts/UploadErrorAlert'; +import './index.scss'; + export const SelectionModal = ({ isOpen, close, @@ -85,27 +87,32 @@ export const SelectionModal = ({ )} + className="selection-modal" > - {/* Error Alerts */} - - - - - + {/* + If the modal dialog content is zero height, it shows a bottom shadow + as if there was content to scroll to, so make the min-height 1px. + */} + + {/* Error Alerts */} + + + + + - {/* User Feedback Alerts */} - - - - + {/* User Feedback Alerts */} + + + {showGallery && } diff --git a/src/editors/sharedComponents/SelectionModal/index.scss b/src/editors/sharedComponents/SelectionModal/index.scss new file mode 100644 index 000000000..8e3225945 --- /dev/null +++ b/src/editors/sharedComponents/SelectionModal/index.scss @@ -0,0 +1,32 @@ +.search-sort-menu .pgn__menu-item-text { + text-transform: capitalize; +} + +.search-sort-menu .pgn__menu .search-sort-menu-by { + display: none; +} + +/* Sort options come in pairs of ascending and descending. */ +.search-sort-menu .pgn__menu > div:nth-child(even) { + border-bottom: 1px solid #f4f3f6; +} + +.search-sort-menu .pgn__menu > div:last-child { + border-bottom: none; +} + +.selection-modal .pgn__vstack > .alert { + margin-bottom: 0; + margin-top: 1.5rem; +} + +/* Set top padding to 0 when there is an alert above. */ +.selection-modal .pgn__vstack > .alert + .gallery > .pgn__scrollable-body-content > .p-4 { + padding-top: 0 !important; +} + +.selection-modal .pgn__vstack > .alert > .alert-icon { + /* Vertical margin equal to the vertical padding of the "Dismiss" button. */ + margin-bottom: 0.4375rem; + margin-top: 0.4375rem; +} diff --git a/src/editors/sharedComponents/SelectionModal/messages.js b/src/editors/sharedComponents/SelectionModal/messages.js index 2aa74f9e2..9e865930b 100644 --- a/src/editors/sharedComponents/SelectionModal/messages.js +++ b/src/editors/sharedComponents/SelectionModal/messages.js @@ -24,6 +24,11 @@ export const messages = { defaultMessage: 'Added {date} at {time}', description: 'File date-added string', }, + sortBy: { + id: 'authoring.selectionmodal.sortBy', + defaultMessage: 'By', + description: '"By" before sort option', + }, }; export default messages;