Skip to content

Commit 1ab754a

Browse files
committed
refactor: Move out all related to migrate props from CardItem
1 parent 3212a84 commit 1ab754a

7 files changed

Lines changed: 95 additions & 104 deletions

File tree

src/studio-home/card-item/CardItem.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
1111
import messages from '../messages';
1212
import { trimSlashes } from './utils';
13-
import CardItem from '.';
13+
import { CardItem } from '.';
1414

1515
jest.spyOn(reactRedux, 'useSelector').mockImplementation(() => studioHomeMock);
1616

src/studio-home/card-item/index.tsx

Lines changed: 24 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@ import {
1010
IconButton,
1111
Stack,
1212
} from '@openedx/paragon';
13-
import { AccessTime, ArrowForward, MoreHoriz } from '@openedx/paragon/icons';
13+
import { ArrowForward, MoreHoriz } from '@openedx/paragon/icons';
1414
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
1515
import { getConfig } from '@edx/frontend-platform';
1616
import { Link } from 'react-router-dom';
1717

1818
import { useWaffleFlags } from '@src/data/apiHooks';
1919
import { COURSE_CREATOR_STATES } from '@src/constants';
20-
import { parseLibraryKey } from '@src/generic/key-utils';
2120
import classNames from 'classnames';
2221
import { getStudioHomeData } from '../data/selectors';
2322
import messages from '../messages';
2423

25-
const PrevToNextName = ({ from, to }: { from: React.ReactNode, to?: React.ReactNode }) => (
24+
export const PrevToNextName = ({ from, to }: { from: React.ReactNode, to?: React.ReactNode }) => (
2625
<Stack direction="horizontal" gap={2}>
2726
<span>{from}</span>
2827
{to
@@ -35,7 +34,7 @@ const PrevToNextName = ({ from, to }: { from: React.ReactNode, to?: React.ReactN
3534
</Stack>
3635
);
3736

38-
const MakeLinkOrSpan = ({
37+
export const MakeLinkOrSpan = ({
3938
when, to, children, className,
4039
}: {
4140
when: boolean,
@@ -54,21 +53,17 @@ interface CardTitleProps {
5453
selectMode?: 'single' | 'multiple';
5554
destinationUrl: string;
5655
title: string;
56+
secondaryLink?: ReactElement | null;
5757
itemId?: string;
58-
isMigrated?: boolean;
59-
migratedToKey?: string;
60-
migratedToTitle?: string;
6158
}
6259

6360
const CardTitle: React.FC<CardTitleProps> = ({
6461
readOnlyItem,
6562
selectMode,
6663
destinationUrl,
6764
title,
65+
secondaryLink,
6866
itemId,
69-
isMigrated,
70-
migratedToTitle,
71-
migratedToKey,
7267
}) => {
7368
const getTitle = useCallback(() => (
7469
<div style={{ marginTop: selectMode ? '-3px' : '' }}>
@@ -82,24 +77,12 @@ const CardTitle: React.FC<CardTitleProps> = ({
8277
{title}
8378
</MakeLinkOrSpan>
8479
)}
85-
to={
86-
isMigrated && migratedToTitle && (
87-
<MakeLinkOrSpan
88-
when={!readOnlyItem && !selectMode}
89-
to={`/library/${migratedToKey}`}
90-
className="card-item-title"
91-
>
92-
{migratedToTitle}
93-
</MakeLinkOrSpan>
94-
)
95-
}
80+
to={secondaryLink}
9681
/>
9782
</div>
9883
), [
9984
readOnlyItem,
100-
isMigrated,
10185
destinationUrl,
102-
migratedToTitle,
10386
title,
10487
selectMode,
10588
]);
@@ -209,11 +192,10 @@ interface BaseProps {
209192
rerunLink?: string | null;
210193
courseKey?: string;
211194
isLibraries?: boolean;
212-
isMigrated?: boolean;
213-
migratedToKey?: string;
214-
migratedToTitle?: string;
215-
migratedToCollectionKey?: string | null;
216-
subtitleBeforeComponent?: ReactElement | null;
195+
subtitleWrapper?: ((subtitle: JSX.Element) => ReactElement) | null; // Wrapper for the default subtitle element
196+
subtitleBeforeWidget?: ReactElement | null; // Adds a widget before the default subtitle element
197+
cardStatusWidget?: ReactElement | null;
198+
titleSecondaryLink?: ReactElement | null;
217199
selectMode?: 'single' | 'multiple';
218200
selectPosition?: 'card' | 'title';
219201
isSelected?: boolean;
@@ -234,7 +216,7 @@ type Props = BaseProps & (
234216
/**
235217
* A card on the Studio home page that represents a Course or a Library
236218
*/
237-
const CardItem: React.FC<Props> = ({
219+
export const CardItem: React.FC<Props> = ({
238220
displayName,
239221
onClick,
240222
lmsLink = '',
@@ -250,14 +232,12 @@ const CardItem: React.FC<Props> = ({
250232
itemId = '',
251233
path,
252234
url,
253-
isMigrated = false,
254-
migratedToKey,
255-
migratedToTitle,
256-
migratedToCollectionKey,
257-
subtitleBeforeComponent,
235+
subtitleWrapper,
236+
subtitleBeforeWidget,
237+
titleSecondaryLink,
238+
cardStatusWidget,
258239
scrollIntoView = false,
259240
}) => {
260-
const intl = useIntl();
261241
const {
262242
allowCourseReruns,
263243
courseCreatorStatus,
@@ -280,33 +260,19 @@ const CardItem: React.FC<Props> = ({
280260

281261
const getSubtitle = useCallback(() => {
282262
let subtitle = isLibraries ? <>{org} / {number}</> : <>{org} / {number} / {run}</>;
283-
if (isMigrated && migratedToKey) {
284-
const migratedToKeyObj = parseLibraryKey(migratedToKey);
285-
subtitle = (
286-
<PrevToNextName
287-
from={subtitle}
288-
to={<>{migratedToKeyObj.org} / {migratedToKeyObj.lib}</>}
289-
/>
290-
);
263+
if (subtitleWrapper) {
264+
subtitle = subtitleWrapper(subtitle);
291265
}
292-
if (subtitleBeforeComponent) {
266+
if (subtitleBeforeWidget) {
293267
subtitle = (
294268
<Stack direction="horizontal" gap={2}>
295-
{subtitleBeforeComponent}
269+
{subtitleBeforeWidget}
296270
{subtitle}
297271
</Stack>
298272
);
299273
}
300274
return subtitle;
301-
}, [isLibraries, org, number, run, migratedToKey, isMigrated]);
302-
303-
const collectionLink = () => {
304-
let libUrl = `/library/${migratedToKey}`;
305-
if (migratedToCollectionKey) {
306-
libUrl += `/collection/${migratedToCollectionKey}`;
307-
}
308-
return libUrl;
309-
};
275+
}, [isLibraries, org, number, run]);
310276

311277
useEffect(() => {
312278
/* istanbul ignore next */
@@ -332,9 +298,7 @@ const CardItem: React.FC<Props> = ({
332298
destinationUrl={destinationUrl}
333299
title={title}
334300
itemId={itemId}
335-
isMigrated={isMigrated}
336-
migratedToTitle={migratedToTitle}
337-
migratedToKey={migratedToKey}
301+
secondaryLink={titleSecondaryLink}
338302
/>
339303
)}
340304
subtitle={getSubtitle()}
@@ -353,27 +317,12 @@ const CardItem: React.FC<Props> = ({
353317
/>
354318
)}
355319
/>
356-
{isMigrated && migratedToKey
357-
&& (
320+
{cardStatusWidget && (
358321
<Card.Status className="bg-white pt-0 text-gray-500">
359-
<Stack direction="horizontal" gap={2}>
360-
<Icon src={AccessTime} size="sm" className="mb-1" />
361-
{intl.formatMessage(messages.libraryMigrationStatusText)}
362-
<b>
363-
<MakeLinkOrSpan
364-
when={!readOnlyItem}
365-
to={collectionLink()}
366-
className="text-info-500"
367-
>
368-
{migratedToTitle}
369-
</MakeLinkOrSpan>
370-
</b>
371-
</Stack>
322+
{cardStatusWidget}
372323
</Card.Status>
373-
)}
324+
)}
374325
</Card>
375326
</div>
376327
);
377328
};
378-
379-
export default CardItem;

src/studio-home/messages.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,6 @@ const messages = defineMessages({
7373
id: 'course-authoring.studio-home.organization.input.no-options',
7474
defaultMessage: 'No options',
7575
},
76-
libraryMigrationStatusText: {
77-
id: 'course-authoring.studio-home.library-v1.card.status',
78-
description: 'Status text in v1 library card in studio informing user of its migration status',
79-
defaultMessage: 'Previously migrated library. Any problem bank links were already moved to',
80-
},
8176
});
8277

8378
export default messages;

src/studio-home/tabs-section/courses-tab/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { COURSE_CREATOR_STATES } from '@src/constants';
1717
import { getStudioHomeData, getStudioHomeCoursesParams, getLoadingStatuses } from '@src/studio-home/data/selectors';
1818
import { resetStudioHomeCoursesCustomParams, updateStudioHomeCoursesCustomParams } from '@src/studio-home/data/slice';
1919
import { fetchStudioHomeData } from '@src/studio-home/data/thunks';
20-
import CardItem from '@src/studio-home/card-item';
20+
import { CardItem } from '@src/studio-home/card-item';
2121
import CollapsibleStateWithAction from '@src/studio-home/collapsible-state-with-action';
2222
import ProcessingCourses from '@src/studio-home/processing-courses';
2323
import { LoadingSpinner } from '@src/generic/Loading';
@@ -118,7 +118,7 @@ const CardList = ({
118118
selectMode={inSelectMode ? 'single' : undefined}
119119
selectPosition={inSelectMode ? 'card' : undefined}
120120
isSelected={inSelectMode && selectedCourseId === courseKey}
121-
subtitleBeforeComponent={isPreviouslyMigrated(courseKey) && (
121+
subtitleBeforeWidget={isPreviouslyMigrated(courseKey) && (
122122
<div
123123
key={`${courseKey}-${processedMigrationInfo[courseKey].join('-')}`}
124124
className="previously-migrated-chip"

src/studio-home/tabs-section/libraries-tab/index.tsx

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ import { useCallback, useState } from 'react';
22
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
33
import {
44
ActionRow, Form, Icon, Menu, MenuItem, Pagination, Row, SearchField,
5+
Stack,
56
} from '@openedx/paragon';
6-
import { Error, FilterList } from '@openedx/paragon/icons';
7+
import { Error, FilterList, AccessTime } from '@openedx/paragon/icons';
78

89
import { LoadingSpinner } from '@src/generic/Loading';
910
import AlertMessage from '@src/generic/alert-message';
1011
import { useLibrariesV1Data } from '@src/studio-home/data/apiHooks';
11-
import CardItem from '@src/studio-home/card-item';
12+
import { CardItem, MakeLinkOrSpan, PrevToNextName } from '@src/studio-home/card-item';
1213
import SearchFilterWidget from '@src/search-manager/SearchFilterWidget';
1314
import type { LibraryV1Data } from '@src/studio-home/data/api';
15+
import { parseLibraryKey } from '@src/generic/key-utils';
1416

1517
import messages from '../messages';
1618
import { MigrateLegacyLibrariesAlert } from './MigrateLegacyLibrariesAlert';
@@ -37,24 +39,64 @@ const CardList = ({
3739
migratedToTitle,
3840
migratedToCollectionKey,
3941
libraryKey,
40-
}) => (
41-
<CardItem
42-
key={`${org}+${number}`}
43-
isLibraries
44-
displayName={displayName}
45-
org={org}
46-
number={number}
47-
url={url}
48-
itemId={libraryKey}
49-
selectMode={inSelectMode ? 'multiple' : undefined}
50-
selectPosition={inSelectMode ? 'title' : undefined}
51-
isSelected={selectedIds?.includes(libraryKey)}
52-
isMigrated={isMigrated}
53-
migratedToKey={migratedToKey}
54-
migratedToTitle={migratedToTitle}
55-
migratedToCollectionKey={migratedToCollectionKey}
56-
/>
57-
))
42+
}) => {
43+
const collectionLink = () => {
44+
let libUrl = `/library/${migratedToKey}`;
45+
if (migratedToCollectionKey) {
46+
libUrl += `/collection/${migratedToCollectionKey}`;
47+
}
48+
return libUrl;
49+
};
50+
51+
const migratedToKeyObj = migratedToKey ? parseLibraryKey(migratedToKey) : undefined;
52+
53+
const subtitleWrapper = (subtitle) => (
54+
<PrevToNextName
55+
from={subtitle}
56+
to={<>{migratedToKeyObj?.org} / {migratedToKeyObj?.lib}</>}
57+
/>
58+
);
59+
60+
return (
61+
<CardItem
62+
key={`${org}+${number}`}
63+
isLibraries
64+
displayName={displayName}
65+
org={org}
66+
number={number}
67+
url={url}
68+
itemId={libraryKey}
69+
selectMode={inSelectMode ? 'multiple' : undefined}
70+
selectPosition={inSelectMode ? 'title' : undefined}
71+
isSelected={selectedIds?.includes(libraryKey)}
72+
subtitleWrapper={isMigrated ? subtitleWrapper : null}
73+
titleSecondaryLink={(isMigrated && migratedToTitle) ? (
74+
<MakeLinkOrSpan
75+
when={!inSelectMode}
76+
to={`/library/${migratedToKey}`}
77+
className="card-item-title"
78+
>
79+
{migratedToTitle}
80+
</MakeLinkOrSpan>
81+
) : null}
82+
cardStatusWidget={(isMigrated && migratedToKey) ? (
83+
<Stack direction="horizontal" gap={2}>
84+
<Icon src={AccessTime} size="sm" className="mb-1" />
85+
<FormattedMessage {...messages.libraryMigrationStatusText} />
86+
<b>
87+
<MakeLinkOrSpan
88+
when
89+
to={collectionLink()}
90+
className="text-info-500"
91+
>
92+
{migratedToTitle}
93+
</MakeLinkOrSpan>
94+
</b>
95+
</Stack>
96+
) : null}
97+
/>
98+
);
99+
})
58100
}
59101
</>
60102
);

src/studio-home/tabs-section/libraries-v2-tab/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { LoadingSpinner } from '@src/generic/Loading';
1717
import AlertMessage from '@src/generic/alert-message';
1818
import type { ContentLibrary, LibrariesV2Response } from '@src/library-authoring/data/api';
1919

20-
import CardItem from '../../card-item';
20+
import { CardItem } from '../../card-item';
2121
import messages from '../messages';
2222
import LibrariesV2Filters from './libraries-v2-filters';
2323

src/studio-home/tabs-section/messages.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ const messages = defineMessages({
141141
defaultMessage: 'Previously Imported',
142142
description: 'Chip that indicates that the course has been previously imported.',
143143
},
144+
libraryMigrationStatusText: {
145+
id: 'course-authoring.studio-home.library-v1.card.status',
146+
description: 'Status text in v1 library card in studio informing user of its migration status',
147+
defaultMessage: 'Previously migrated library. Any problem bank links were already moved to',
148+
},
144149
});
145150

146151
export default messages;

0 commit comments

Comments
 (0)