diff --git a/src/course-outline/card-header/CardHeader.jsx b/src/course-outline/card-header/CardHeader.jsx index d4a0af75d..46ac9015d 100644 --- a/src/course-outline/card-header/CardHeader.jsx +++ b/src/course-outline/card-header/CardHeader.jsx @@ -120,7 +120,7 @@ const CardHeader = ({ value={titleValue} name="displayName" onChange={(e) => setTitleValue(e.target.value)} - aria-label="edit field" + aria-label={intl.formatMessage(messages.editFieldAriaLabel)} onBlur={() => onEditSubmit(titleValue)} onKeyDown={(e) => { if (e.key === 'Enter') { diff --git a/src/course-outline/card-header/messages.js b/src/course-outline/card-header/messages.js index 4874525d5..9114b77a8 100644 --- a/src/course-outline/card-header/messages.js +++ b/src/course-outline/card-header/messages.js @@ -1,6 +1,10 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; const messages = defineMessages({ + editFieldAriaLabel: { + id: 'course-authoring.course-outline.card.edit-field.aria-label', + defaultMessage: 'Edit field', + }, expandTooltip: { id: 'course-authoring.course-outline.card.expandTooltip', defaultMessage: 'Collapse/Expand this card', diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx index edfefc8db..60cd97ee6 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx @@ -130,7 +130,7 @@ const AnswerOption = ({
- + ( - level !== LicenseLevel.course && details && license !== 'select' ? ( + level !== LicenseLevel.course && details && license !== 'select' && (
- {license === LicenseTypes.allRightsReserved ? ( + {license === LicenseTypes.allRightsReserved && (
- ) : null} + )} {license === LicenseTypes.creativeCommons ? ( @@ -55,7 +55,6 @@ const LicenseDetails = ({
@@ -80,7 +79,6 @@ const LicenseDetails = ({ noncommercial: e.target.checked, }, })} - aria-label="Checkbox" /> @@ -106,7 +104,6 @@ const LicenseDetails = ({ shareAlike: e.target.checked ? false : details.shareAlike, }, })} - aria-label="Checkbox" /> @@ -132,7 +129,6 @@ const LicenseDetails = ({ noDerivatives: e.target.checked ? false : details.noDerivatives, }, })} - aria-label="Checkbox" /> @@ -144,7 +140,7 @@ const LicenseDetails = ({ ) : null}
- ) : null + ) ); LicenseDetails.propTypes = { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap index 046cc017d..9e608e8c6 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`LicenseDetails snapshots snapshots: renders as expected with default props 1`] = `null`; +exports[`LicenseDetails snapshots snapshots: renders as expected with default props 1`] = `false`; exports[`LicenseDetails snapshots snapshots: renders as expected with level set to block and license set to Creative Commons 1`] = `
@@ -71,7 +70,6 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set @@ -102,7 +100,6 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set @@ -131,7 +128,6 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set @@ -179,7 +175,7 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set
`; -exports[`LicenseDetails snapshots snapshots: renders as expected with level set to block and license set to select 1`] = `null`; +exports[`LicenseDetails snapshots snapshots: renders as expected with level set to block and license set to select 1`] = `false`; exports[`LicenseDetails snapshots snapshots: renders as expected with level set to library 1`] = `
{ await mockStore(RequestStatus.SUCCESSFUL); axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); axiosMock.onPost(getAssetsUrl(courseId)).reply(200, generateNewAssetApiResponse()); - const addFilesButton = screen.getByLabelText('file-input'); + const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFilesButton, file); await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); await waitFor(() => { @@ -221,7 +221,7 @@ describe('FilesAndUploads', () => { axiosMock.onGet( `${getAssetsUrl(courseId)}?display_name=mOckID6&page_size=1`, ).reply(200, { assets: [{ display_name: 'mOckID6' }] }); - const addFilesButton = screen.getByLabelText('file-input'); + const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFilesButton, file); await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); expect(screen.getByText(filesPageMessages.overwriteConfirmMessage.defaultMessage)).toBeVisible(); @@ -242,7 +242,7 @@ describe('FilesAndUploads', () => { }; axiosMock.onPost(getAssetsUrl(courseId)).reply(200, responseData); - const addFilesButton = screen.getByLabelText('file-input'); + const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFilesButton, file); await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); @@ -266,7 +266,7 @@ describe('FilesAndUploads', () => { axiosMock.onGet( `${getAssetsUrl(courseId)}?display_name=mOckID6&page_size=1`, ).reply(200, { assets: [{ display_name: 'mOckID6' }] }); - const addFilesButton = screen.getByLabelText('file-input'); + const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFilesButton, file); await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); @@ -558,7 +558,7 @@ describe('FilesAndUploads', () => { await mockStore(RequestStatus.SUCCESSFUL); axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); axiosMock.onPost(getAssetsUrl(courseId)).reply(413, { error: errorMessage }); - const addFilesButton = screen.getByLabelText('file-input'); + const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFilesButton, file); await waitFor(() => { const addStatus = store.getState().assets.addingStatus; @@ -571,7 +571,7 @@ describe('FilesAndUploads', () => { it('404 validation should show error', async () => { await mockStore(RequestStatus.SUCCESSFUL); axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(404); - const addFilesButton = screen.getByLabelText('file-input'); + const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFilesButton, file); await executeThunk(addAssetFile(courseId, file, 1), store.dispatch); const addStatus = store.getState().assets.addingStatus; @@ -584,7 +584,7 @@ describe('FilesAndUploads', () => { await mockStore(RequestStatus.SUCCESSFUL); axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); axiosMock.onPost(getAssetsUrl(courseId)).reply(404); - const addFilesButton = screen.getByLabelText('file-input'); + const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFilesButton, file); await executeThunk(addAssetFile(courseId, file, 1), store.dispatch); const addStatus = store.getState().assets.addingStatus; diff --git a/src/files-and-videos/generic/FileInput.jsx b/src/files-and-videos/generic/FileInput.jsx index c953994d2..9e7de8a9d 100644 --- a/src/files-and-videos/generic/FileInput.jsx +++ b/src/files-and-videos/generic/FileInput.jsx @@ -1,6 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { useIntl } from '@edx/frontend-platform/i18n'; import { getSupportedFormats } from '../videos-page/data/utils'; +import messages from './messages'; export const useFileInput = ({ onAddFile, @@ -23,17 +25,20 @@ export const useFileInput = ({ }; }; -const FileInput = ({ fileInput: hook, supportedFileFormats, allowMultiple }) => ( - -); +const FileInput = ({ fileInput: hook, supportedFileFormats, allowMultiple }) => { + const intl = useIntl(); + return ( + + ); +}; FileInput.propTypes = { fileInput: PropTypes.shape({ diff --git a/src/files-and-videos/generic/messages.js b/src/files-and-videos/generic/messages.js index 9930ca6b5..bb4badf86 100644 --- a/src/files-and-videos/generic/messages.js +++ b/src/files-and-videos/generic/messages.js @@ -209,6 +209,11 @@ const messages = defineMessages({ defaultMessage: 'Upload error', description: 'Title for upload error alert', }, + fileInputAriaLabel: { + id: 'course-authoring.files-and-uploads.fileInput.ariaLabel', + defaultMessage: 'Upload a file', + description: 'Accessible (screen reader) label for file input', + }, }); export default messages; diff --git a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx index b45c29e80..ac246352e 100644 --- a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx +++ b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx @@ -91,7 +91,7 @@ const SortAndFilterModal = ({ className="text-center" value="displayName,asc" type="radio" - aria-label="name descending radio" + aria-label={intl.formatMessage(messages.sortByNameAscendingAriaLabel)} > @@ -99,7 +99,7 @@ const SortAndFilterModal = ({ className="text-center" value="dateAdded,desc" type="radio" - aria-label="date added descending radio" + aria-label={intl.formatMessage(messages.sortByNewestAriaLabel)} > @@ -107,7 +107,7 @@ const SortAndFilterModal = ({ className="text-center" value="fileSize,desc" type="radio" - aria-label="file size descending radio" + aria-label={intl.formatMessage(messages.sortBySizeDescendingAriaLabel)} > @@ -115,7 +115,7 @@ const SortAndFilterModal = ({ className="text-center" value="displayName,desc" type="radio" - aria-label="name ascending radio" + aria-label={intl.formatMessage(messages.sortByNameDescendingAriaLabel)} > @@ -123,7 +123,7 @@ const SortAndFilterModal = ({ className="text-center" value="dateAdded,asc" type="radio" - aria-label="date added ascending radio" + aria-label={intl.formatMessage(messages.sortByOldestAriaLabel)} > @@ -131,7 +131,7 @@ const SortAndFilterModal = ({ className="text-center" value="fileSize,asc" type="radio" - aria-label="file size ascending radio" + aria-label={intl.formatMessage(messages.sortBySizeAscendingAriaLabel)} > diff --git a/src/files-and-videos/generic/table-components/sort-and-filter-modal/messages.js b/src/files-and-videos/generic/table-components/sort-and-filter-modal/messages.js index 13165bde5..86ec8c1e0 100644 --- a/src/files-and-videos/generic/table-components/sort-and-filter-modal/messages.js +++ b/src/files-and-videos/generic/table-components/sort-and-filter-modal/messages.js @@ -22,31 +22,61 @@ const messages = defineMessages({ defaultMessage: 'Cancel', }, sortByNameAscending: { - id: 'course-authoring..files-and-videos.sort-and-filter.modal.sortByNameAscendingButton.label', + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByNameAscendingButton.label', defaultMessage: 'Name (A-Z)', }, + sortByNameAscendingAriaLabel: { + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByNameAscendingButton.aria-label', + defaultMessage: 'name descending radio', + description: 'Accessible (screen reader) label for "name descending" filter', + }, sortByNewest: { - id: 'course-authoring..files-and-videos.sort-and-filter.modal.sortByNewestButton.label', + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByNewestButton.label', defaultMessage: 'Newest', }, + sortByNewestAriaLabel: { + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByNewestButton.label-label', + defaultMessage: 'date added descending radio', + description: 'Accessible (screen reader) label for "date added descending" filter', + }, sortBySizeDescending: { - id: 'course-authoring..files-and-videos.sort-and-filter.modal.sortBySizeDescendingButton.label', + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortBySizeDescendingButton.label', defaultMessage: 'File size (High to low)', }, + sortBySizeDescendingAriaLabel: { + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortBySizeDescendingButton.aria-label', + defaultMessage: 'file size descending radio', + description: 'Accessible (screen reader) label for "file size descending" filter', + }, sortByNameDescending: { - id: 'course-authoring..files-and-videos.sort-and-filter.modal.sortByNameDescendingButton.label', + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByNameDescendingButton.label', defaultMessage: 'Name (Z-A)', }, + sortByNameDescendingAriaLabel: { + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByNameDescendingButton.aria-label', + defaultMessage: 'name ascending radio', + description: 'Accessible (screen reader) label for "name ascending" filter', + }, sortByOldest: { - id: 'course-authoring..files-and-videos.sort-and-filter.modal.sortByOldestButton.label', + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByOldestButton.label', defaultMessage: 'Oldest', }, + sortByOldestAriaLabel: { + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortByOldestButton.aria-label', + defaultMessage: 'date added ascending radio', + description: 'Accessible (screen reader) label for "date added ascending" filter', + }, sortBySizeAscending: { - id: 'course-authoring..files-and-videos.sort-and-filter.modal.sortBySizeAscendingButton.label', + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortBySizeAscendingButton.label', defaultMessage: 'File size (Low to high)', }, + sortBySizeAscendingAriaLabel: { + id: 'course-authoring.files-and-videos.sort-and-filter.modal.sortBySizeAscendingButton.aria-label', + defaultMessage: 'file size ascending radio', + description: 'Accessible (screen reader) label for "file size ascending" filter', + }, applySortButton: { - id: 'course-authoring..files-and-videos.sort-and-filter.modal.applyySortButton.label', + id: 'course-authoring.files-and-videos.sort-and-filter.modal.applyySortButton.label', defaultMessage: 'Apply', }, }); diff --git a/src/files-and-videos/videos-page/VideosPage.test.jsx b/src/files-and-videos/videos-page/VideosPage.test.jsx index 1a5746839..062ee712f 100644 --- a/src/files-and-videos/videos-page/VideosPage.test.jsx +++ b/src/files-and-videos/videos-page/VideosPage.test.jsx @@ -244,7 +244,7 @@ describe('Videos page', () => { axiosUnauthenticateMock.onPut('http://testing.org').reply(200); axiosMock.onGet(getCourseVideosApiUrl(courseId)).reply(200, generateAddVideoApiResponse()); - const addFilesButton = screen.getAllByLabelText('file-input')[3]; + const addFilesButton = screen.getAllByLabelText(messages.fileInputAriaLabel.defaultMessage)[3]; await act(async () => { userEvent.upload(addFilesButton, file); }); @@ -263,7 +263,7 @@ describe('Videos page', () => { const setFailedSpy = jest.spyOn(api, 'sendVideoUploadStatus').mockImplementation(() => {}); uploadSpy.mockResolvedValue(new Promise(() => {})); - const addFilesButton = screen.getAllByLabelText('file-input')[3]; + const addFilesButton = screen.getAllByLabelText(messages.fileInputAriaLabel.defaultMessage)[3]; await act(async () => { userEvent.upload(addFilesButton, file); }); @@ -294,7 +294,7 @@ describe('Videos page', () => { const setFailedSpy = jest.spyOn(api, 'sendVideoUploadStatus').mockImplementation(() => {}); uploadSpy.mockResolvedValue(new Promise(() => {})); - const addFilesButton = screen.getAllByLabelText('file-input')[3]; + const addFilesButton = screen.getAllByLabelText(messages.fileInputAriaLabel.defaultMessage)[3]; await act(async () => { userEvent.upload(addFilesButton, file); }); @@ -610,7 +610,7 @@ describe('Videos page', () => { axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(413, { error: errorMessage }); axiosMock.onGet(getCourseVideosApiUrl(courseId)).reply(200, generateAddVideoApiResponse()); - const addFilesButton = screen.getAllByLabelText('file-input')[3]; + const addFilesButton = screen.getAllByLabelText(messages.fileInputAriaLabel.defaultMessage)[3]; await act(async () => { userEvent.upload(addFilesButton, file); }); @@ -627,7 +627,7 @@ describe('Videos page', () => { axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(404); axiosMock.onGet(getCourseVideosApiUrl(courseId)).reply(200, generateAddVideoApiResponse()); - const addFilesButton = screen.getAllByLabelText('file-input')[3]; + const addFilesButton = screen.getAllByLabelText(messages.fileInputAriaLabel.defaultMessage)[3]; await act(async () => { userEvent.upload(addFilesButton, file); }); @@ -658,7 +658,7 @@ describe('Videos page', () => { axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(204, generateNewVideoApiResponse()); axiosUnauthenticateMock.onPut('http://testing.org').reply(404); axiosMock.onGet(getCourseVideosApiUrl(courseId)).reply(200, generateAddVideoApiResponse()); - const addFilesButton = screen.getAllByLabelText('file-input')[3]; + const addFilesButton = screen.getAllByLabelText(messages.fileInputAriaLabel.defaultMessage)[3]; await act(async () => { userEvent.upload(addFilesButton, file); }); diff --git a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx index d7a05d9ca..75625eb17 100644 --- a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx +++ b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx @@ -27,6 +27,7 @@ import { import { getApiBaseUrl } from '../data/api'; import messages from './messages'; +import genericMessages from '../../generic/messages'; import transcriptRowMessages from './transcript-item/messages'; import VideosPageProvider from '../VideosPageProvider'; import { deleteVideoTranscript } from '../data/thunks'; @@ -116,7 +117,7 @@ describe('TranscriptTab', () => { it('should upload new transcript', async () => { axiosMock.onPost(`${getApiBaseUrl()}/transcript_upload/`).reply(204); await act(async () => { - const addFileInput = screen.getByLabelText('file-input'); + const addFileInput = screen.getByLabelText(genericMessages.fileInputAriaLabel.defaultMessage); expect(addFileInput).toBeInTheDocument(); userEvent.upload(addFileInput, file); @@ -129,7 +130,7 @@ describe('TranscriptTab', () => { it('should show default error message', async () => { axiosMock.onPost(`${getApiBaseUrl()}/transcript_upload/`).reply(404); await act(async () => { - const addFileInput = screen.getByLabelText('file-input'); + const addFileInput = screen.getByLabelText(genericMessages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFileInput, file); }); const addStatus = store.getState().videos.transcriptStatus; @@ -142,7 +143,7 @@ describe('TranscriptTab', () => { it('should show api provided error message', async () => { axiosMock.onPost(`${getApiBaseUrl()}/transcript_upload/`).reply(404, { error: 'api error' }); await act(async () => { - const addFileInput = screen.getByLabelText('file-input'); + const addFileInput = screen.getByLabelText(genericMessages.fileInputAriaLabel.defaultMessage); userEvent.upload(addFileInput, file); }); const addStatus = store.getState().videos.transcriptStatus; @@ -299,7 +300,7 @@ describe('TranscriptTab', () => { axiosMock.onPost(`${getApiBaseUrl()}/transcript_upload/`).reply(204); await act(async () => { - const addFileInput = screen.getAllByLabelText('file-input')[0]; + const addFileInput = screen.getAllByLabelText(genericMessages.fileInputAriaLabel.defaultMessage)[0]; userEvent.upload(addFileInput, file); }); const addStatus = store.getState().videos.transcriptStatus; @@ -315,7 +316,7 @@ describe('TranscriptTab', () => { axiosMock.onPost(`${getApiBaseUrl()}/transcript_upload/`).reply(404); await act(async () => { - const addFileInput = screen.getAllByLabelText('file-input')[0]; + const addFileInput = screen.getAllByLabelText(genericMessages.fileInputAriaLabel.defaultMessage)[0]; userEvent.upload(addFileInput, file); }); diff --git a/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx b/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx index 28e344e36..1f13871fc 100644 --- a/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx +++ b/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx @@ -119,14 +119,14 @@ const OrderTranscriptForm = ({ > @@ -134,7 +134,7 @@ const OrderTranscriptForm = ({ diff --git a/src/files-and-videos/videos-page/transcript-settings/messages.js b/src/files-and-videos/videos-page/transcript-settings/messages.js index 6585cd8d7..12062056d 100644 --- a/src/files-and-videos/videos-page/transcript-settings/messages.js +++ b/src/files-and-videos/videos-page/transcript-settings/messages.js @@ -28,16 +28,31 @@ const messages = defineMessages({ defaultMessage: 'None', description: 'Label for order transcript None option', }, + noneAriaLabel: { + id: 'course-authoring.video-uploads.transcriptSettings.orderTranscripts.none.aria-label', + defaultMessage: 'none radio', + description: 'Accessible (screen reader) label for order transcript None option', + }, cieloLabel: { id: 'course-authoring.video-uploads.transcriptSettings.orderTranscripts.cielo24.label', defaultMessage: 'Cielo24', description: 'Label for order transcript Cieol24 option', }, + cieloAriaLabel: { + id: 'course-authoring.video-uploads.transcriptSettings.orderTranscripts.cielo24.aria-label', + defaultMessage: 'Cielo24 radio', + description: 'Accessible (screen reader) label for order transcript Cieol24 option', + }, threePlayMediaLabel: { id: 'course-authoring.video-uploads.transcriptSettings.orderTranscripts.3PlayMedia.label', defaultMessage: '3Play Media', description: 'Label for order transcript 3Play Media option', }, + threePlayMediaAriaLabel: { + id: 'course-authoring.video-uploads.transcriptSettings.orderTranscripts.3PlayMedia.aria-label', + defaultMessage: '3PlayMedia radio', + description: 'Accessible (screen reader) label for order transcript 3Play Media option', + }, updateSettingsLabel: { id: 'course-authoring.video-uploads.transcriptSettings.orderTranscripts.updateSettings.label', defaultMessage: 'Update settings',