Skip to content

Commit d5d00cd

Browse files
committed
fix: internationalization
1 parent cff5090 commit d5d00cd

9 files changed

Lines changed: 149 additions & 66 deletions

File tree

src/generic/TypeXToConfirmModal.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ interface TypeXToConfirmModalProps {
77
confirmLabel: string;
88
cancelLabel: string;
99
X: string;
10-
context?: any;
10+
// any additional context that the caller wants to pass to the onConfirm callback; not a React context.
11+
context?: Record<string, any> | null;
1112
isOpen: boolean;
12-
onConfirm: (context?: any) => void;
13+
onConfirm: (context?: Record<string, any> | null) => void;
1314
onCancel: () => void;
15+
setContext?: (context: Record<string, any> | null) => void;
1416
}
1517

1618
const TypeXToConfirmModal: React.FC<TypeXToConfirmModalProps> = ({
@@ -23,18 +25,19 @@ const TypeXToConfirmModal: React.FC<TypeXToConfirmModalProps> = ({
2325
context,
2426
onConfirm,
2527
onCancel,
28+
setContext,
2629
}) => {
2730
const [confirmedByTyping, setConfirmedByTyping] = React.useState(false);
2831

2932
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
30-
if (!confirmedByTyping) return;
33+
if (!confirmedByTyping) { return; }
3134
if (e.key === 'Enter') {
3235
onConfirm(context);
3336
}
3437
};
3538

3639
const handleConfirm = () => {
37-
if (!confirmedByTyping) return;
40+
if (!confirmedByTyping) { return; }
3841
setConfirmedByTyping(false);
3942
onConfirm(context);
4043
};
@@ -56,8 +59,12 @@ const TypeXToConfirmModal: React.FC<TypeXToConfirmModalProps> = ({
5659
useEffect(() => {
5760
if (!isOpen) {
5861
setConfirmedByTyping(false);
62+
if (setContext) {
63+
// reset onConfirm callback context when modal is closed
64+
setContext(null);
65+
}
5966
}
60-
}, [X, isOpen]);
67+
}, [X, isOpen, context, setContext]);
6168

6269
return (
6370
<ModalDialog

src/taxonomy/tag-list/Actions.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ interface ActionsMenuProps {
6666
rowData: TagListRowData;
6767
startSubtagDraft: () => void;
6868
disableAddSubtag: boolean;
69-
startEditTag: () => void;
70-
disableEditTag: boolean;
69+
startEditRow: () => void;
70+
disableEditRow: boolean;
7171
reachedMaxDepth: (row: Row<TreeRowData>) => boolean;
72-
startDeleteTag: (row: Row<TreeRowData>) => void;
73-
disableDeleteTag: boolean;
72+
startDeleteRow: (row: Row<TreeRowData>) => void;
73+
disableDeleteRow: boolean;
7474
row: Row<TreeRowData>;
7575
}
7676

@@ -79,27 +79,27 @@ const ActionsMenu = ({
7979
row,
8080
startSubtagDraft,
8181
disableAddSubtag,
82-
startEditTag,
83-
disableEditTag,
82+
startEditRow,
83+
disableEditRow,
8484
reachedMaxDepth,
85-
startDeleteTag,
86-
disableDeleteTag,
85+
startDeleteRow,
86+
disableDeleteRow,
8787
}: ActionsMenuProps) => {
8888
const intl = useIntl();
8989

90-
const deleteTagMenuItem = (
90+
const deleteRowMenuItem = (
9191
<Dropdown.Item
92-
onClick={() => startDeleteTag(row)}
93-
disabled={disableDeleteTag}
92+
onClick={() => startDeleteRow(row)}
93+
disabled={disableDeleteRow}
9494
>
9595
{intl.formatMessage(messages.deleteTag)}
9696
</Dropdown.Item>
9797
);
9898

99-
const editTagMenuItem = (
99+
const editRowMenuItem = (
100100
<Dropdown.Item
101-
onClick={startEditTag}
102-
disabled={disableEditTag}
101+
onClick={startEditRow}
102+
disabled={disableEditRow}
103103
>
104104
{intl.formatMessage(messages.renameTag)}
105105
</Dropdown.Item>
@@ -123,8 +123,8 @@ const ActionsMenu = ({
123123
>
124124
{intl.formatMessage(messages.addSubtag)}
125125
</Dropdown.Item>
126-
{editTagMenuItem}
127-
{deleteTagMenuItem}
126+
{editRowMenuItem}
127+
{deleteRowMenuItem}
128128
</Dropdown.Menu>
129129
</Dropdown>
130130
);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useContext, useMemo } from 'react';
2+
import { Row } from '@tanstack/react-table';
3+
import { useIntl } from '@edx/frontend-platform/i18n';
4+
5+
import TypeXToConfirmModal from '@src/generic/TypeXToConfirmModal';
6+
import { TreeTableContext } from '@src/taxonomy/tree-table';
7+
import { TreeRowData } from '@src/taxonomy/tree-table/types';
8+
import messages from './messages';
9+
import { getTagListRowData, getTagWithDescendantsCount } from './utils';
10+
11+
const DeleteModal = () => {
12+
const {
13+
confirmDeleteDialogOpen,
14+
setConfirmDeleteDialogOpen,
15+
confirmDeleteDialogContext,
16+
setConfirmDeleteDialogContext,
17+
handleDeleteTag,
18+
} = useContext(TreeTableContext);
19+
const intl = useIntl();
20+
21+
const handleConfirm = (row: Row<TreeRowData>) => {
22+
handleDeleteTag(row);
23+
setConfirmDeleteDialogOpen(false);
24+
setConfirmDeleteDialogContext(null);
25+
};
26+
27+
const handleCancel = () => {
28+
setConfirmDeleteDialogOpen(false);
29+
setConfirmDeleteDialogContext(null);
30+
};
31+
32+
const rowData = confirmDeleteDialogContext ? getTagListRowData(confirmDeleteDialogContext) : null;
33+
const count = useMemo(() => rowData ? getTagWithDescendantsCount(rowData) : 0, [confirmDeleteDialogContext]);
34+
35+
const hasSubtags = count > 1;
36+
const bodyText = hasSubtags ? intl.formatMessage(messages.deleteTagWithSubtagsConfirmation, { count }) : intl.formatMessage(messages.deleteTagConfirmation);
37+
const typeToDeleteText = hasSubtags ? intl.formatMessage(messages.typeToConfirmDeleteTagWithSubtags) : intl.formatMessage(messages.typeToConfirmDeleteOneTag);
38+
39+
return (
40+
<TypeXToConfirmModal
41+
label={intl.formatMessage(messages.confirmDeleteTitle, { tagName: rowData?.value })}
42+
X={typeToDeleteText}
43+
bodyText={bodyText}
44+
confirmLabel={intl.formatMessage(messages.deleteLabel)}
45+
cancelLabel={intl.formatMessage(messages.cancelLabel)}
46+
isOpen={confirmDeleteDialogOpen}
47+
context={confirmDeleteDialogContext}
48+
setContext={setConfirmDeleteDialogContext}
49+
onConfirm={handleConfirm}
50+
onCancel={handleCancel}
51+
/>
52+
);
53+
};
54+
55+
export default DeleteModal;

src/taxonomy/tag-list/TagListTable.tsx

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from './constants';
1818
import { useTableModes, useEditActions } from './hooks';
1919
import TypeXToConfirmModal from '@src/generic/TypeXToConfirmModal';
20+
import DeleteModal from './DeleteModal';
2021

2122
interface TagListTableProps {
2223
taxonomyId: number;
@@ -90,7 +91,7 @@ const TagListTable = ({ taxonomyId, maxDepth }: TagListTableProps) => {
9091

9192
// Custom Edit Actions Hook - handles table mode transitions, API calls,
9293
// and updating the table without a full data reload when creating or editing tags.
93-
const { handleCreateTag, handleUpdateTag, validate, startSubtagDraft, startEditTag, startDeleteTag, handleDeleteTag } = useEditActions(
94+
const editActions = useEditActions(
9495
{
9596
enterDraftMode,
9697
enterPreviewMode,
@@ -124,6 +125,7 @@ const TagListTable = ({ taxonomyId, maxDepth }: TagListTableProps) => {
124125

125126
// TreeTable context
126127
const contextValueArgs = {
128+
...editActions,
127129
treeData,
128130
pageCount,
129131
pagination,
@@ -137,22 +139,16 @@ const TagListTable = ({ taxonomyId, maxDepth }: TagListTableProps) => {
137139
setToast,
138140
setIsCreatingTopRow: setIsCreatingTopTag,
139141
exitDraftWithoutSave,
140-
handleCreateRow: handleCreateTag,
141142
creatingParentId,
142143
setCreatingParentId,
143144
setDraftError,
144-
validate,
145-
handleUpdateRow: handleUpdateTag,
146145
editingRowId,
147146
setEditingRowId,
148147
onStartDraft: enterDraftMode,
149148
setActiveActionMenuRowId,
150149
hasOpenDraft,
151150
canAddTag,
152151
maxDepth,
153-
startSubtagDraft,
154-
startEditTag,
155-
startDeleteTag,
156152
confirmDeleteDialogOpen,
157153
setConfirmDeleteDialogOpen,
158154
confirmDeleteDialogContext,
@@ -166,20 +162,7 @@ const TagListTable = ({ taxonomyId, maxDepth }: TagListTableProps) => {
166162
return (
167163
<TreeTableContext.Provider value={contextValue}>
168164
<TableView />
169-
<TypeXToConfirmModal
170-
label="Confirm Delete"
171-
X="DELETE"
172-
bodyText="Are you sure you want to delete this tag?"
173-
confirmLabel="Delete"
174-
cancelLabel="Cancel"
175-
isOpen={confirmDeleteDialogOpen}
176-
context={confirmDeleteDialogContext}
177-
onConfirm={(row) => { handleDeleteTag(row); setConfirmDeleteDialogOpen(false); setConfirmDeleteDialogContext(null); }}
178-
onCancel={() => {
179-
setConfirmDeleteDialogOpen(false);
180-
setConfirmDeleteDialogContext(null);
181-
}}
182-
/>
165+
<DeleteModal />
183166
</TreeTableContext.Provider>
184167
);
185168
};

src/taxonomy/tag-list/hooks.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616

1717
import messages from './messages';
1818
import { start } from '@src/library-authoring/__mocks__/contentLibrariesListV2';
19-
import { getTagListRowData } from './utils';
19+
import { getTagListRowData, getTagWithDescendantsCount } from './utils';
2020
import { Row } from '@tanstack/react-table';
2121

2222
/** Interface for table mode actions for React's `useReducer` hook.
@@ -118,13 +118,6 @@ const useTableModes = (): UseTableModesReturn => {
118118
};
119119
};
120120

121-
const getTagWithDescendantsCount = (rowData: TreeRowData): number => {
122-
if (!rowData.subRows || rowData.subRows.length === 0) {
123-
return 1;
124-
}
125-
return rowData.subRows.reduce((count, subRow) => count + getTagWithDescendantsCount(subRow), 1);
126-
};
127-
128121
const useEditActions = ({
129122
enterDraftMode,
130123
enterPreviewMode,
@@ -318,13 +311,13 @@ const useEditActions = ({
318311

319312
return {
320313
updateTableWithoutDataReload,
321-
handleCreateTag,
322-
handleUpdateTag,
314+
handleCreateRow: handleCreateTag,
315+
handleUpdateRow: handleUpdateTag,
323316
startSubtagDraft,
324-
startEditTag,
325-
startDeleteTag,
317+
startEditRow: startEditTag,
318+
startDeleteRow: startDeleteTag,
326319
validate,
327-
handleDeleteTag,
320+
handleDeleteRow: handleDeleteTag,
328321
};
329322
};
330323

src/taxonomy/tag-list/messages.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,34 @@ const messages = defineMessages({
9393
id: 'course-authoring.tag-list.delete-error',
9494
defaultMessage: 'Error deleting tag: {errorMessage}',
9595
},
96+
confirmDeleteTitle: {
97+
id: 'course-authoring.tag-list.confirm-delete-title',
98+
defaultMessage: 'Delete "{tagName}"',
99+
},
100+
typeToConfirmDeleteOneTag: {
101+
id: 'course-authoring.tag-list.delete-one-tag-type-to-confirm',
102+
defaultMessage: 'DELETE',
103+
},
104+
deleteTagConfirmation: {
105+
id: 'course-authoring.tag-list.delete-tag-confirmation',
106+
defaultMessage: 'Warning! You are about to delete a tag. If the tag is applied to course content it will be removed.',
107+
},
108+
deleteLabel: {
109+
id: 'course-authoring.tag-list.delete-label',
110+
defaultMessage: 'Delete',
111+
},
112+
cancelLabel: {
113+
id: 'course-authoring.tag-list.cancel-label',
114+
defaultMessage: 'Cancel',
115+
},
116+
typeToConfirmDeleteTagWithSubtags: {
117+
id: 'course-authoring.tag-list.delete-tag-with-subtags-type-to-confirm',
118+
defaultMessage: 'DELETE WITH SUB-TAGS',
119+
},
120+
deleteTagWithSubtagsConfirmation: {
121+
id: 'course-authoring.tag-list.delete-tag-with-subtags-confirmation',
122+
defaultMessage: 'Warning! You are about to delete a tag containing sub-tags. If you proceed, {count} sub-tags that you did not directly select will also be deleted. Any tags applied to course content will be removed.',
123+
},
96124
});
97125

98126
export default messages;

src/taxonomy/tag-list/tagColumns.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ interface GetColumnsArgs {
2424
setDraftError: (error: string) => void;
2525
maxDepth: number;
2626
startSubtagDraft: (row: Row<TreeRowData>) => void;
27-
startEditTag: (row: Row<TreeRowData>) => void;
28-
startDeleteTag: (row: Row<TreeRowData>) => void;
27+
startEditRow: (row: Row<TreeRowData>) => void;
28+
startDeleteRow: (row: Row<TreeRowData>) => void;
2929
}
3030

3131
function getColumns({
3232
setIsCreatingTopRow,
3333
setEditingRowId,
3434
onStartDraft,
3535
startSubtagDraft,
36-
startEditTag,
37-
startDeleteTag,
36+
startEditRow,
37+
startDeleteRow,
3838
setActiveActionMenuRowId,
3939
hasOpenDraft,
4040
canAddTag,
@@ -88,8 +88,8 @@ function getColumns({
8888
}
8989

9090
const disableAddSubtag = hasOpenDraft || !canAddTag;
91-
const disableEditTag = hasOpenDraft || rowData.canChangeTag === false;
92-
const disableDeleteTag = hasOpenDraft || rowData.canDeleteTag === false;
91+
const disableEditRow = hasOpenDraft || rowData.canChangeTag === false;
92+
const disableDeleteRow = hasOpenDraft || rowData.canDeleteTag === false;
9393

9494
return (
9595
<div className="d-flex align-items-center justify-content-end gap-2">
@@ -98,11 +98,11 @@ function getColumns({
9898
row={row}
9999
startSubtagDraft={() => startSubtagDraft(row)}
100100
disableAddSubtag={disableAddSubtag}
101-
startEditTag={() => startEditTag(row)}
102-
disableEditTag={disableEditTag}
101+
startEditRow={() => startEditRow(row)}
102+
disableEditRow={disableEditRow}
103103
reachedMaxDepth={reachedMaxDepth}
104-
startDeleteTag={() => startDeleteTag(row)}
105-
disableDeleteTag={disableDeleteTag}
104+
startDeleteRow={() => startDeleteRow(row)}
105+
disableDeleteRow={disableDeleteRow}
106106
/>
107107
</div>
108108
);

src/taxonomy/tag-list/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,10 @@ import { TagListRowData } from './types';
1010
export const getTagListRowData = (row: Row<TreeRowData>): TagListRowData => (
1111
row.original as unknown as TagListRowData
1212
);
13+
14+
export const getTagWithDescendantsCount = (rowData: TreeRowData): number => {
15+
if (!rowData.subRows || rowData.subRows.length === 0) {
16+
return 1;
17+
}
18+
return rowData.subRows.reduce((count, subRow) => count + getTagWithDescendantsCount(subRow), 1);
19+
};

0 commit comments

Comments
 (0)