Skip to content

Commit d717303

Browse files
authored
fix: unit pages ux bugs [FC-0083] (#1884)
This PR fixes some UX bugs related to the unit pages: * Sort for "recently modified" on unit tab does not update after adding new components to units * Change component delete warning message
1 parent 208b0c9 commit d717303

File tree

7 files changed

+61
-47
lines changed

7 files changed

+61
-47
lines changed

src/generic/delete-modal/DeleteModal.jsx renamed to src/generic/delete-modal/DeleteModal.tsx

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import PropTypes from 'prop-types';
21
import {
32
ActionRow,
43
Button,
@@ -9,17 +8,29 @@ import { useIntl } from '@edx/frontend-platform/i18n';
98
import messages from './messages';
109
import LoadingButton from '../loading-button';
1110

11+
interface DeleteModalProps {
12+
isOpen: boolean;
13+
close: () => void;
14+
category?: string;
15+
onDeleteSubmit: () => void | Promise<void>;
16+
title?: string;
17+
description?: React.ReactNode | React.ReactNode[];
18+
variant?: string;
19+
btnLabel?: string;
20+
icon?: React.ElementType;
21+
}
22+
1223
const DeleteModal = ({
13-
category,
24+
category = '',
1425
isOpen,
1526
close,
1627
onDeleteSubmit,
1728
title,
1829
description,
19-
variant,
30+
variant = 'default',
2031
btnLabel,
2132
icon,
22-
}) => {
33+
}: DeleteModalProps) => {
2334
const intl = useIntl();
2435

2536
const modalTitle = title || intl.formatMessage(messages.title, { category });
@@ -62,28 +73,4 @@ const DeleteModal = ({
6273
);
6374
};
6475

65-
DeleteModal.defaultProps = {
66-
category: '',
67-
title: '',
68-
description: '',
69-
variant: 'default',
70-
btnLabel: '',
71-
icon: null,
72-
};
73-
74-
DeleteModal.propTypes = {
75-
isOpen: PropTypes.bool.isRequired,
76-
close: PropTypes.func.isRequired,
77-
category: PropTypes.string,
78-
onDeleteSubmit: PropTypes.func.isRequired,
79-
title: PropTypes.string,
80-
description: PropTypes.oneOfType([
81-
PropTypes.element,
82-
PropTypes.string,
83-
]),
84-
variant: PropTypes.string,
85-
btnLabel: PropTypes.string,
86-
icon: PropTypes.elementType,
87-
};
88-
8976
export default DeleteModal;
File renamed without changes.

src/library-authoring/LibraryAuthoringPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ const LibraryAuthoringPage = ({
256256
[ContentType.units]: intl.formatMessage(messages.unitsTab),
257257
};
258258
const visibleTabsToRender = visibleTabs.map((contentType) => (
259-
<Tab eventKey={contentType} title={tabTitles[contentType]} />
259+
<Tab key={contentType} eventKey={contentType} title={tabTitles[contentType]} />
260260
));
261261

262262
return (

src/library-authoring/components/ComponentDeleter.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useCallback, useContext } from 'react';
22
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
3-
import { Warning } from '@openedx/paragon/icons';
3+
import { Icon } from '@openedx/paragon';
4+
import { CalendarViewDay, School, Warning } from '@openedx/paragon/icons';
45

56
import { useSidebarContext } from '../common/context/SidebarContext';
67
import { useDeleteLibraryBlock, useLibraryBlockMetadata, useRestoreLibraryBlock } from '../data/apiHooks';
@@ -66,23 +67,30 @@ const ComponentDeleter = ({ usageKey, ...props }: Props) => {
6667
return null;
6768
}
6869

70+
const deleteText = intl.formatMessage(messages.deleteComponentConfirm, {
71+
componentName: <b><BlockName usageKey={usageKey} /></b>,
72+
message: (
73+
<>
74+
<div className="d-flex mt-2">
75+
<Icon className="mr-2" src={School} />
76+
{intl.formatMessage(messages.deleteComponentConfirmMsg1)}
77+
</div>
78+
<div className="d-flex mt-2">
79+
<Icon className="mr-2" src={CalendarViewDay} />
80+
{intl.formatMessage(messages.deleteComponentConfirmMsg2)}
81+
</div>
82+
</>
83+
),
84+
});
85+
6986
return (
7087
<DeleteModal
7188
isOpen
7289
close={props.cancelDelete}
7390
variant="warning"
7491
title={intl.formatMessage(messages.deleteComponentWarningTitle)}
7592
icon={Warning}
76-
description={(
77-
<FormattedMessage
78-
{...messages.deleteComponentConfirm}
79-
values={{
80-
componentName: (
81-
<strong><BlockName usageKey={usageKey} /></strong>
82-
),
83-
}}
84-
/>
85-
)}
93+
description={deleteText}
8694
onDeleteSubmit={doDelete}
8795
/>
8896
);

src/library-authoring/components/ContainerDeleter.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactNode, useCallback, useContext } from 'react';
1+
import { useCallback, useContext } from 'react';
22
import { useIntl } from '@edx/frontend-platform/i18n';
33
import { Icon } from '@openedx/paragon';
44
import { Warning, School, Widgets } from '@openedx/paragon/icons';
@@ -47,7 +47,7 @@ const ContainerDeleter = ({
4747
</div>
4848
</>
4949
),
50-
}) as ReactNode as string;
50+
});
5151
const deleteSuccess = intl.formatMessage(messages.deleteUnitSuccess);
5252
const deleteError = intl.formatMessage(messages.deleteUnitFailed);
5353
const undoDeleteError = messages.undoDeleteUnitToastFailed;

src/library-authoring/components/messages.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,19 @@ const messages = defineMessages({
6868
},
6969
deleteComponentConfirm: {
7070
id: 'course-authoring.library-authoring.component.delete-confirmation-text',
71-
defaultMessage: 'Delete {componentName}? If this component has been used in a course, those copies won\'t be deleted, but they will no longer receive updates from the library.',
71+
defaultMessage: 'Delete {componentName}? {message}',
7272
description: 'Confirmation text to display before deleting a component',
7373
},
74+
deleteComponentConfirmMsg1: {
75+
id: 'course-authoring.library-authoring.component.delete-confirmation-msg-1',
76+
defaultMessage: 'If this component has been used in a course, those copies won\'t be deleted, but they will no longer receive updates from the library.',
77+
description: 'First part of confirmation message to display before deleting a component',
78+
},
79+
deleteComponentConfirmMsg2: {
80+
id: 'course-authoring.library-authoring.component.delete-confirmation-msg-2',
81+
defaultMessage: 'If this component has been used in any units, it will also be deleted from those units.',
82+
description: 'Second part of confirmation message to display before deleting a component',
83+
},
7484
deleteComponentCancelButton: {
7585
id: 'course-authoring.library-authoring.component.cancel-delete-button',
7686
defaultMessage: 'Cancel',

src/library-authoring/data/apiHooks.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -642,13 +642,22 @@ export const useAddComponentsToContainer = (containerId?: string) => {
642642
const queryClient = useQueryClient();
643643
return useMutation({
644644
mutationFn: async (componentIds: string[]) => {
645-
if (containerId !== undefined) {
646-
return api.addComponentsToContainer(containerId, componentIds);
645+
// istanbul ignore if: this should never happen
646+
if (!containerId) {
647+
return undefined;
647648
}
648-
return undefined;
649+
return api.addComponentsToContainer(containerId, componentIds);
649650
},
650651
onSettled: () => {
651-
queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.containerChildren(containerId!) });
652+
// istanbul ignore if: this should never happen
653+
if (!containerId) {
654+
return;
655+
}
656+
// NOTE: We invalidate the library query here because we need to update the library's
657+
// container list.
658+
const libraryId = getLibraryId(containerId);
659+
queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.containerChildren(containerId) });
660+
queryClient.invalidateQueries({ predicate: (query) => libraryQueryPredicate(query, libraryId) });
652661
},
653662
});
654663
};

0 commit comments

Comments
 (0)