Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/course-outline/card-header/CardHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
4 changes: 4 additions & 0 deletions src/course-outline/card-header/messages.js
Original file line number Diff line number Diff line change
@@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ const AnswerOption = ({
</Collapsible.Body>
</div>
<div className="d-flex flex-row flex-nowrap">
<Collapsible.Trigger aria-label="Toggle feedback" className="btn-icon btn-icon-primary btn-icon-md align-items-center">
<Collapsible.Trigger aria-label={intl.formatMessage(messages.feedbackToggleIconAriaLabel)} className="btn-icon btn-icon-primary btn-icon-md align-items-center">
<Icon
src={FeedbackOutline}
alt={intl.formatMessage(messages.feedbackToggleIconAltText)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const messages = defineMessages({
defaultMessage: 'Feedback message',
description: 'Placeholder text for feedback text',
},
feedbackToggleIconAriaLabel: {
id: 'authoring.answerwidget.feedback.icon.aria-label',
defaultMessage: 'Toggle feedback',
description: 'Accessible (screen reader) label for "Toggle Feedback" icon',
},
feedbackToggleIconAltText: {
id: 'authoring.answerwidget.feedback.icon.alt',
defaultMessage: 'Toggle feedback',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ const LicenseDetails = ({
// redux
updateField,
}) => (
level !== LicenseLevel.course && details && license !== 'select' ? (
level !== LicenseLevel.course && details && license !== 'select' && (
<div className="x-small border-primary-100 border-bottom m-0 pr-1">
<Form.Group className="pb-2">
<div className="mb-3">
<FormattedMessage {...messages.detailsSubsectionTitle} />
</div>
{license === LicenseTypes.allRightsReserved ? (
{license === LicenseTypes.allRightsReserved && (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, why are you replacing the ternary operators?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one was a suggestion from a fellow, so the code looks cleaner and more readable.

<div className="mt-2">
<FormattedMessage {...messages.allRightsReservedSectionMessage} />
</div>
) : null}
)}
{license === LicenseTypes.creativeCommons
? (
<Stack gap={4}>
Expand All @@ -55,7 +55,6 @@ const LicenseDetails = ({
<CheckboxControl
disabled
checked
aria-label="Checkbox"
/>
</ActionRow>
</Form.Group>
Expand All @@ -80,7 +79,6 @@ const LicenseDetails = ({
noncommercial: e.target.checked,
},
})}
aria-label="Checkbox"
/>
</ActionRow>
</Form.Group>
Expand All @@ -106,7 +104,6 @@ const LicenseDetails = ({
shareAlike: e.target.checked ? false : details.shareAlike,
},
})}
aria-label="Checkbox"
/>
</ActionRow>
</Form.Group>
Expand All @@ -132,7 +129,6 @@ const LicenseDetails = ({
noDerivatives: e.target.checked ? false : details.noDerivatives,
},
})}
aria-label="Checkbox"
/>
</ActionRow>
</Form.Group>
Expand All @@ -144,7 +140,7 @@ const LicenseDetails = ({
) : null}
</Form.Group>
</div>
) : null
)
);

LicenseDetails.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
@@ -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`] = `
<div
Expand Down Expand Up @@ -40,7 +40,6 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set
</Form.Label>
<ActionRow.Spacer />
<CheckboxControl
aria-label="Checkbox"
checked={true}
disabled={true}
/>
Expand Down Expand Up @@ -71,7 +70,6 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set
</Form.Label>
<ActionRow.Spacer />
<CheckboxControl
aria-label="Checkbox"
disabled={false}
onChange={[Function]}
/>
Expand Down Expand Up @@ -102,7 +100,6 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set
</Form.Label>
<ActionRow.Spacer />
<CheckboxControl
aria-label="Checkbox"
disabled={false}
onChange={[Function]}
/>
Expand Down Expand Up @@ -131,7 +128,6 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set
</Form.Label>
<ActionRow.Spacer />
<CheckboxControl
aria-label="Checkbox"
disabled={false}
onChange={[Function]}
/>
Expand Down Expand Up @@ -179,7 +175,7 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set
</div>
`;

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`] = `
<div
Expand Down
14 changes: 7 additions & 7 deletions src/files-and-videos/files-page/FilesPage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,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(200, generateNewAssetApiResponse());
const addFilesButton = screen.getByLabelText('file-input');
const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage);
Copy link
Copy Markdown
Contributor

@bradenmacdonald bradenmacdonald May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const addFilesButton = screen.getByLabelText(messages.fileInputAriaLabel.defaultMessage);
const addFilesButton = screen.getByLabelText('Upload a file');

Nit: This may seem to go against the usual "DRY" developer maxim, but I generally prefer that strings are hard-coded in test files, because it makes it much more clear when things get changed accidentally. When the test references the code, it's easier to introduce a regression without noticing. In this case it's not a big deal though. Feel free to leave it as is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your point but lets keep it like this this time since it is not a major thing.

userEvent.upload(addFilesButton, file);
await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch);
await waitFor(() => {
Expand All @@ -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();
Expand All @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
27 changes: 16 additions & 11 deletions src/files-and-videos/generic/FileInput.jsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -23,17 +25,20 @@ export const useFileInput = ({
};
};

const FileInput = ({ fileInput: hook, supportedFileFormats, allowMultiple }) => (
<input
accept={getSupportedFormats(supportedFileFormats)}
aria-label="file-input"
className="upload d-none"
onChange={hook.addFile}
ref={hook.ref}
type="file"
multiple={allowMultiple}
/>
);
const FileInput = ({ fileInput: hook, supportedFileFormats, allowMultiple }) => {
const intl = useIntl();
return (
<input
accept={getSupportedFormats(supportedFileFormats)}
aria-label={intl.formatMessage(messages.fileInputAriaLabel)}
className="upload d-none"
onChange={hook.addFile}
ref={hook.ref}
type="file"
multiple={allowMultiple}
/>
);
};

FileInput.propTypes = {
fileInput: PropTypes.shape({
Expand Down
5 changes: 5 additions & 0 deletions src/files-and-videos/generic/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -91,47 +91,47 @@ const SortAndFilterModal = ({
className="text-center"
value="displayName,asc"
type="radio"
aria-label="name descending radio"
aria-label={intl.formatMessage(messages.sortByNameAscendingAriaLabel)}
>
<FormattedMessage {...messages.sortByNameAscending} />
</SelectableBox>
<SelectableBox
className="text-center"
value="dateAdded,desc"
type="radio"
aria-label="date added descending radio"
aria-label={intl.formatMessage(messages.sortByNewestAriaLabel)}
>
<FormattedMessage {...messages.sortByNewest} />
</SelectableBox>
<SelectableBox
className="text-center"
value="fileSize,desc"
type="radio"
aria-label="file size descending radio"
aria-label={intl.formatMessage(messages.sortBySizeDescendingAriaLabel)}
>
<FormattedMessage {...messages.sortBySizeDescending} />
</SelectableBox>
<SelectableBox
className="text-center"
value="displayName,desc"
type="radio"
aria-label="name ascending radio"
aria-label={intl.formatMessage(messages.sortByNameDescendingAriaLabel)}
>
<FormattedMessage {...messages.sortByNameDescending} />
</SelectableBox>
<SelectableBox
className="text-center"
value="dateAdded,asc"
type="radio"
aria-label="date added ascending radio"
aria-label={intl.formatMessage(messages.sortByOldestAriaLabel)}
>
<FormattedMessage {...messages.sortByOldest} />
</SelectableBox>
<SelectableBox
className="text-center"
value="fileSize,asc"
type="radio"
aria-label="file size ascending radio"
aria-label={intl.formatMessage(messages.sortBySizeAscendingAriaLabel)}
>
<FormattedMessage {...messages.sortBySizeAscending} />
</SelectableBox>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
});
Expand Down
Loading