Skip to content

Commit 523dd1f

Browse files
authored
feat: add library v2 alert (#2413)
Adds an Alert to the Legacy Library Page to notify the user of the process of deprecating Legacy Libraries and a Button to open the Migrate Library interface.
1 parent cffc4d7 commit 523dd1f

11 files changed

Lines changed: 139 additions & 59 deletions

File tree

src/generic/alert-error/AlertError.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { render, screen } from '@testing-library/react';
2+
import { render } from '@testing-library/react';
33
import { IntlProvider } from '@edx/frontend-platform/i18n';
44

55
import AlertError from '.';
@@ -33,7 +33,6 @@ describe('<AlertMessage />', () => {
3333
},
3434
};
3535
const { getByText } = render(<RootWrapper error={error} />);
36-
screen.logTestingPlaygroundURL();
3736
expect(getByText(/this is an error message/i)).toBeInTheDocument();
3837
expect(getByText(/\{ "message": "this is a response body" \}/i)).toBeInTheDocument();
3938
});

src/library-authoring/generic/manage-collections/ManageCollections.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ describe('<ManageCollections />', () => {
121121
collections={[]}
122122
useUpdateCollectionsHook={useUpdateComponentCollections}
123123
/>);
124-
screen.logTestingPlaygroundURL();
125124
const manageBtn = await screen.findByRole('button', { name: 'Add to Collection' });
126125
await user.click(manageBtn);
127126
await waitFor(() => { expect(fetchMock).toHaveFetchedTimes(1, searchEndpoint, 'post'); });

src/library-authoring/units/LibraryUnitPage.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ describe('<LibraryUnitPage />', () => {
177177
const textBox = screen.getByRole('textbox', { name: /text input/i });
178178
expect(textBox).toBeInTheDocument();
179179
expect(textBox).toHaveValue('Test Unit');
180-
screen.logTestingPlaygroundURL();
181180
fireEvent.change(textBox, { target: { value: 'New Unit Title' } });
182181
fireEvent.keyDown(textBox, { key: 'Enter', code: 'Enter', charCode: 13 });
183182

src/studio-home/data/apiHooks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const studioHomeQueryKeys = {
1212
export const useLibrariesV1Data = (enabled: boolean = true) => (
1313
useQuery({
1414
queryKey: studioHomeQueryKeys.librariesV1(),
15-
queryFn: () => getStudioHomeLibraries(),
15+
queryFn: getStudioHomeLibraries,
1616
enabled,
1717
})
1818
);

src/studio-home/tabs-section/TabsSection.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,5 +478,42 @@ describe('<TabsSection />', () => {
478478
tabMessages.librariesTabErrorMessage.defaultMessage,
479479
)).toBeVisible();
480480
});
481+
482+
[true, false].forEach((isMigrated) => {
483+
it(`should render v2 libraries migration alert when the libraries have isMigrated=${isMigrated}`, async () => {
484+
setConfig({
485+
...getConfig(),
486+
ENABLE_LEGACY_LIBRARY_MIGRATOR: 'true',
487+
});
488+
const libraries = generateGetStudioHomeLibrariesApiResponse().libraries.map(
489+
library => ({
490+
...library,
491+
isMigrated,
492+
}),
493+
);
494+
const user = userEvent.setup();
495+
await axiosMock.onGet(getStudioHomeApiUrl()).reply(200, generateGetStudioHomeLibrariesApiResponse());
496+
await axiosMock.onGet(libraryApiLink).reply(200, { libraries });
497+
render();
498+
await executeThunk(fetchStudioHomeData(), store.dispatch);
499+
500+
const librariesTab = await screen.findByRole('tab', { name: librariesBetaTabTitle });
501+
await user.click(librariesTab);
502+
503+
expect(librariesTab).toHaveClass('active');
504+
505+
expect(await screen.findByText(/welcome to the new content libraries/i)).toBeVisible();
506+
507+
const migrationPendingText = /legacy libraries can be migrated using the migration tool/i;
508+
509+
if (isMigrated) {
510+
expect(screen.queryByText(migrationPendingText)).not.toBeInTheDocument();
511+
expect(screen.queryByRole('button', { name: 'Review Legacy Libraries' })).not.toBeInTheDocument();
512+
} else {
513+
expect(screen.getByText(migrationPendingText)).toBeVisible();
514+
expect(screen.getByRole('button', { name: 'Review Legacy Libraries' })).toBeVisible();
515+
}
516+
});
517+
});
481518
});
482519
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const MigrateLegacyLibrariesAlert = () => (
1111
</Alert.Heading>
1212
<div className="row">
1313
<div className="col-8">
14-
<FormattedMessage {...messages.alertDescription} />
14+
<FormattedMessage {...messages.alertDescriptionV1} />
1515
</div>
1616
<div className="col-4 d-flex justify-content-center align-items-start">
1717
<Button>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Alert, Button, Hyperlink } from '@openedx/paragon';
2+
import { FormattedMessage } from '@edx/frontend-platform/i18n';
3+
4+
import { useLibrariesV1Data } from '@src/studio-home/data/apiHooks';
5+
6+
import messages from '../messages';
7+
8+
const libraryDocsLink = (
9+
<Hyperlink
10+
target="_blank"
11+
showLaunchIcon={false}
12+
destination="https://docs.openedx.org/en/latest/educators/how-tos/course_development/create_new_library.html"
13+
>
14+
<FormattedMessage {...messages.alertLibrariesDocLinkText} />
15+
</Hyperlink>
16+
);
17+
18+
export const WelcomeLibrariesV2Alert = () => {
19+
const { data, isPending, isError } = useLibrariesV1Data();
20+
21+
// Does not show the alert if we are still loading or if there was an error fetching libraries
22+
if (isPending || isError) {
23+
return null;
24+
}
25+
26+
const hasPendingV1Migrations = data.libraries.some(library => !library.isMigrated);
27+
return (
28+
<Alert variant="info">
29+
{hasPendingV1Migrations ? (
30+
<>
31+
<Alert.Heading>
32+
<FormattedMessage {...messages.alertTitle} />
33+
</Alert.Heading>
34+
<div className="row">
35+
<div className="col-8">
36+
<FormattedMessage {...messages.alertDescriptionV2} values={{ link: libraryDocsLink }} />
37+
<FormattedMessage {...messages.alertDescriptionV2MigrationPending} />
38+
</div>
39+
<div className="col-4 d-flex justify-content-center align-items-start">
40+
<Button>
41+
<FormattedMessage {...messages.alertReviewButton} />
42+
</Button>
43+
</div>
44+
</div>
45+
</>
46+
) : (
47+
<FormattedMessage {...messages.alertDescriptionV2} values={{ link: libraryDocsLink }} />
48+
)}
49+
</Alert>
50+
);
51+
};

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

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ import {
66
Alert,
77
Button,
88
} from '@openedx/paragon';
9+
import { getConfig } from '@edx/frontend-platform';
910
import { useIntl } from '@edx/frontend-platform/i18n';
1011
import { Error } from '@openedx/paragon/icons';
1112

12-
import { useContentLibraryV2List } from '../../../library-authoring';
13-
import { LoadingSpinner } from '../../../generic/Loading';
14-
import AlertMessage from '../../../generic/alert-message';
13+
import { useContentLibraryV2List } from '@src/library-authoring';
14+
import { LoadingSpinner } from '@src/generic/Loading';
15+
import AlertMessage from '@src/generic/alert-message';
1516
import CardItem from '../../card-item';
1617
import messages from '../messages';
1718
import LibrariesV2Filters from './libraries-v2-filters';
19+
import { WelcomeLibrariesV2Alert } from './WelcomeLibrariesV2Alert';
1820

1921
type Props = Record<never, never>;
2022

@@ -37,35 +39,23 @@ const LibrariesV2Tab: React.FC<Props> = () => {
3739

3840
const {
3941
data,
40-
isLoading,
42+
isPending,
4143
isError,
4244
} = useContentLibraryV2List({ page: currentPage, ...filterParams });
4345

44-
if (isLoading && !isFiltered) {
46+
if (isPending && !isFiltered) {
4547
return (
4648
<Row className="m-0 mt-4 justify-content-center">
4749
<LoadingSpinner />
4850
</Row>
4951
);
5052
}
5153

52-
const hasV2Libraries = !isLoading && !isError && ((data!.results.length || 0) > 0);
53-
54-
// TODO: update this link when tutorial is ready.
55-
const librariesTutorialLink = (
56-
<Alert.Link href="https://docs.openedx.org">
57-
{intl.formatMessage(messages.librariesV2TabBetaTutorialLinkText)}
58-
</Alert.Link>
59-
);
54+
const hasV2Libraries = !isPending && !isError && ((data!.results.length || 0) > 0);
6055

6156
return (
6257
<>
63-
<Alert variant="info">
64-
{intl.formatMessage(
65-
messages.librariesV2TabBetaText,
66-
{ link: librariesTutorialLink },
67-
)}
68-
</Alert>
58+
{getConfig().ENABLE_LEGACY_LIBRARY_MIGRATOR === 'true' && (<WelcomeLibrariesV2Alert />)}
6959

7060
{isError ? (
7161
<AlertMessage
@@ -81,24 +71,24 @@ const LibrariesV2Tab: React.FC<Props> = () => {
8171
<div className="courses-tab-container">
8272
<div className="d-flex flex-row justify-content-between my-4">
8373
<LibrariesV2Filters
84-
isLoading={isLoading}
74+
isPending={isPending}
8575
isFiltered={isFiltered}
8676
filterParams={filterParams}
8777
setFilterParams={setFilterParams}
8878
setCurrentPage={setCurrentPage}
8979
/>
90-
{!isLoading && !isError
91-
&& (
92-
<p>
93-
{intl.formatMessage(messages.coursesPaginationInfo, {
94-
length: data!.results.length,
95-
total: data!.count,
96-
})}
97-
</p>
98-
)}
80+
{!isPending && !isError
81+
&& (
82+
<p>
83+
{intl.formatMessage(messages.coursesPaginationInfo, {
84+
length: data.results.length,
85+
total: data.count,
86+
})}
87+
</p>
88+
)}
9989
</div>
10090

101-
{ hasV2Libraries
91+
{hasV2Libraries
10292
? data!.results.map(({
10393
id, org, slug, title,
10494
}) => (
@@ -110,7 +100,7 @@ const LibrariesV2Tab: React.FC<Props> = () => {
110100
number={slug}
111101
path={`/library/${id}`}
112102
/>
113-
)) : isFiltered && !isLoading && (
103+
)) : isFiltered && !isPending && (
114104
<Alert className="mt-4">
115105
<Alert.Heading>
116106
{intl.formatMessage(messages.librariesV2TabLibraryNotFoundAlertTitle)}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,13 @@ describe('LibrariesV2Filters', () => {
115115
});
116116

117117
it('should display the loading spinner when isLoading is true', () => {
118-
renderComponent({ isLoading: true });
118+
renderComponent({ isPending: true });
119119
const spinner = screen.getByText('Loading...');
120120
expect(spinner).toBeInTheDocument();
121121
});
122122

123123
it('should not display the loading spinner when isLoading is false', () => {
124-
renderComponent({ isLoading: false });
124+
renderComponent({ isPending: false });
125125
const spinner = screen.queryByText('Loading...');
126126
expect(spinner).not.toBeInTheDocument();
127127
});

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import LibrariesV2OrderFilterMenu from './libraries-v2-order-filter-menu';
77
import messages from '../../messages';
88

99
export interface LibrariesV2FiltersProps {
10-
isLoading?: boolean;
10+
isPending?: boolean;
1111
isFiltered?: boolean;
1212
filterParams: { search?: string | undefined, order?: string };
1313
setFilterParams: React.Dispatch<React.SetStateAction<{ search: string | undefined, order: string }>>;
1414
setCurrentPage: React.Dispatch<React.SetStateAction<number>>;
1515
}
1616

1717
const LibrariesV2Filters: React.FC<LibrariesV2FiltersProps> = ({
18-
isLoading = false,
18+
isPending = false,
1919
isFiltered = false,
2020
filterParams,
2121
setFilterParams,
@@ -93,7 +93,7 @@ const LibrariesV2Filters: React.FC<LibrariesV2FiltersProps> = ({
9393
className="mr-4"
9494
placeholder={intl.formatMessage(messages.librariesV2TabLibrarySearchPlaceholder)}
9595
/>
96-
{isLoading && (
96+
{isPending && (
9797
<span className="search-field-loading">
9898
<LoadingSpinner size="sm" />
9999
</span>

0 commit comments

Comments
 (0)