feat: Legacy libraries migration help sidebar [FC-0097] (#2503)
- Implements the Legacy Libraries Migration Help Sidebar - Shows the sidebar in the studio home - Shows the sidebar in the Legacy Libraries Migration Page
This commit is contained in:
@@ -53,9 +53,9 @@ describe('<LegacyLibMigrationPage />', () => {
|
||||
// Should render the title
|
||||
expect(await screen.findByText('Migrate Legacy Libraries')).toBeInTheDocument();
|
||||
// Should render the Migration Steps Viewer
|
||||
expect(screen.getByText(/select legacy libraries/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/select destination/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/confirm/i)).toBeInTheDocument();
|
||||
expect(screen.getByText('Select Legacy Libraries')).toBeInTheDocument();
|
||||
expect(screen.getByText('Select Destination')).toBeInTheDocument();
|
||||
expect(screen.getByText('Confirm')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should cancel the migration', async () => {
|
||||
@@ -401,4 +401,12 @@ describe('<LegacyLibMigrationPage />', () => {
|
||||
);
|
||||
expect(mockShowToast).toHaveBeenCalledWith('Legacy libraries migration failed.');
|
||||
});
|
||||
|
||||
it('should show help sidebar', async () => {
|
||||
renderPage();
|
||||
expect(await screen.findByText('Help & Support')).toBeInTheDocument();
|
||||
expect(screen.getByText('What’s different in the new Content Libraries experience?')).toBeInTheDocument();
|
||||
expect(screen.getByText('What happens when I migrate my Legacy Libraries?')).toBeInTheDocument();
|
||||
expect(screen.getByText('How do I migrate my Legacy Libraries?')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ActionRow,
|
||||
Button,
|
||||
Container,
|
||||
Layout,
|
||||
ModalDialog,
|
||||
StatefulButton,
|
||||
Stepper,
|
||||
@@ -27,6 +28,7 @@ import { Filter, LibrariesList } from '@src/studio-home/tabs-section/libraries-t
|
||||
import messages from './messages';
|
||||
import { SelectDestinationView } from './SelectDestinationView';
|
||||
import { ConfirmationView } from './ConfirmationView';
|
||||
import { LegacyMigrationHelpSidebar } from './LegacyMigrationHelpSidebar';
|
||||
import { useUpdateContainerCollections } from './data/apiHooks';
|
||||
|
||||
export type MigrationStep = 'select-libraries' | 'select-destination' | 'confirmation-view';
|
||||
@@ -164,79 +166,90 @@ export const LegacyLibMigrationPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{intl.formatMessage(messages.siteTitle)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<Header isHiddenMainMenu />
|
||||
<div className="legacy-library-migration-page">
|
||||
<Helmet>
|
||||
<title>
|
||||
{intl.formatMessage(messages.siteTitle)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<Header isHiddenMainMenu />
|
||||
<Container className="migration-container d-flex flex-column px-6 mt-5 mb-5">
|
||||
<div className="migration-content">
|
||||
<SubHeader
|
||||
title={intl.formatMessage(messages.siteTitle)}
|
||||
/>
|
||||
<Stepper activeKey={currentStep}>
|
||||
<Stepper.Header />
|
||||
<Stepper.Step
|
||||
eventKey="select-libraries"
|
||||
title={intl.formatMessage(messages.selectLegacyLibrariesStepTitle)}
|
||||
>
|
||||
<LibrariesList
|
||||
selectedIds={legacyLibrariesIds}
|
||||
handleCheck={handleUpdateLegacyLibraries}
|
||||
hideMigationAlert
|
||||
initialFilter={[Filter.unmigrated]}
|
||||
setSelectedLibraries={setLegacyLibraries}
|
||||
/>
|
||||
</Stepper.Step>
|
||||
<Stepper.Step
|
||||
eventKey="select-destination"
|
||||
title={intl.formatMessage(messages.selectDestinationStepTitle)}
|
||||
>
|
||||
<SelectDestinationView
|
||||
destinationId={destinationLibrary?.id}
|
||||
setDestinationId={setDestination}
|
||||
legacyLibCount={legacyLibraries.length}
|
||||
/>
|
||||
</Stepper.Step>
|
||||
<Stepper.Step
|
||||
eventKey="confirmation-view"
|
||||
title={intl.formatMessage(messages.confirmStepTitle)}
|
||||
>
|
||||
{destinationLibrary && (
|
||||
<ConfirmationView
|
||||
destination={destinationLibrary}
|
||||
legacyLibraries={legacyLibraries}
|
||||
<Layout
|
||||
xs={[{ span: 9 }, { span: 3 }]}
|
||||
>
|
||||
<Layout.Element>
|
||||
<div className="flex-fill">
|
||||
<Container className="migration-container d-flex flex-column px-6 mt-5 mb-5">
|
||||
<div className="migration-content">
|
||||
<SubHeader
|
||||
title={intl.formatMessage(messages.siteTitle)}
|
||||
/>
|
||||
<Stepper activeKey={currentStep}>
|
||||
<Stepper.Header />
|
||||
<Stepper.Step
|
||||
eventKey="select-libraries"
|
||||
title={intl.formatMessage(messages.selectLegacyLibrariesStepTitle)}
|
||||
>
|
||||
<LibrariesList
|
||||
selectedIds={legacyLibrariesIds}
|
||||
handleCheck={handleUpdateLegacyLibraries}
|
||||
hideMigationAlert
|
||||
initialFilter={[Filter.unmigrated]}
|
||||
setSelectedLibraries={setLegacyLibraries}
|
||||
/>
|
||||
</Stepper.Step>
|
||||
<Stepper.Step
|
||||
eventKey="select-destination"
|
||||
title={intl.formatMessage(messages.selectDestinationStepTitle)}
|
||||
>
|
||||
<SelectDestinationView
|
||||
destinationId={destinationLibrary?.id}
|
||||
setDestinationId={setDestination}
|
||||
legacyLibCount={legacyLibraries.length}
|
||||
/>
|
||||
</Stepper.Step>
|
||||
<Stepper.Step
|
||||
eventKey="confirmation-view"
|
||||
title={intl.formatMessage(messages.confirmStepTitle)}
|
||||
>
|
||||
{destinationLibrary && (
|
||||
<ConfirmationView
|
||||
destination={destinationLibrary}
|
||||
legacyLibraries={legacyLibraries}
|
||||
/>
|
||||
)}
|
||||
</Stepper.Step>
|
||||
</Stepper>
|
||||
</div>
|
||||
</Container>
|
||||
<div className="content-buttons d-flex justify-content-between pl-6 pr-6 bg-white">
|
||||
<Button className="mt-2 mb-2" variant="outline-primary" onClick={handleBack}>
|
||||
{currentStep === 'select-libraries'
|
||||
? intl.formatMessage(messages.cancel)
|
||||
: intl.formatMessage(messages.back)}
|
||||
</Button>
|
||||
{currentStep !== 'confirmation-view' ? (
|
||||
<Button className="mt-2 mb-2" onClick={handleNext} disabled={isNextDisabled()}>
|
||||
{intl.formatMessage(messages.next)}
|
||||
</Button>
|
||||
) : (
|
||||
<StatefulButton
|
||||
className="mt-2 mb-2"
|
||||
state={confirmationButtonState}
|
||||
disabledStates={['pending']}
|
||||
labels={{
|
||||
default: intl.formatMessage(messages.confirm),
|
||||
pending: intl.formatMessage(messages.confirm),
|
||||
}}
|
||||
onClick={handleNext}
|
||||
/>
|
||||
)}
|
||||
</Stepper.Step>
|
||||
</Stepper>
|
||||
</div>
|
||||
</Container>
|
||||
<div className="content-buttons d-flex justify-content-between pl-6 pr-6 bg-white">
|
||||
<Button className="mt-2 mb-2" variant="outline-primary" onClick={handleBack}>
|
||||
{currentStep === 'select-libraries'
|
||||
? intl.formatMessage(messages.cancel)
|
||||
: intl.formatMessage(messages.back)}
|
||||
</Button>
|
||||
{currentStep !== 'confirmation-view' ? (
|
||||
<Button className="mt-2 mb-2" onClick={handleNext} disabled={isNextDisabled()}>
|
||||
{intl.formatMessage(messages.next)}
|
||||
</Button>
|
||||
) : (
|
||||
<StatefulButton
|
||||
className="mt-2 mb-2"
|
||||
state={confirmationButtonState}
|
||||
disabledStates={['pending']}
|
||||
labels={{
|
||||
default: intl.formatMessage(messages.confirm),
|
||||
pending: intl.formatMessage(messages.confirm),
|
||||
}}
|
||||
onClick={handleNext}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout.Element>
|
||||
<Layout.Element>
|
||||
<LegacyMigrationHelpSidebar />
|
||||
</Layout.Element>
|
||||
</Layout>
|
||||
</div>
|
||||
<ExitModal
|
||||
isExitModalOpen={isExitModalOpen}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { Icon, Stack } from '@openedx/paragon';
|
||||
import { Question } from '@openedx/paragon/icons';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
export const SingleLineBreak = (chunk: string[]) => <div>{chunk}</div>;
|
||||
export const Paragraph = (chunk: string[]) => <p>{chunk}</p>;
|
||||
|
||||
export const LegacyMigrationHelpSidebar = () => (
|
||||
<div className="legacy-libraries-migration-help bg-white pt-3 mt-1">
|
||||
<Stack gap={1} direction="horizontal" className="pl-4 h4 text-primary-700">
|
||||
<Icon src={Question} />
|
||||
<span>
|
||||
<FormattedMessage {...messages.helpAndSupportTitle} />
|
||||
</span>
|
||||
</Stack>
|
||||
<hr />
|
||||
<Stack className="pl-4 pr-4">
|
||||
<Stack>
|
||||
<span className="h5">
|
||||
<FormattedMessage {...messages.helpAndSupportFirstQuestionTitle} />
|
||||
</span>
|
||||
<span className="x-small">
|
||||
<FormattedMessage {...messages.helpAndSupportFirstQuestionBody} />
|
||||
</span>
|
||||
</Stack>
|
||||
<hr />
|
||||
<Stack>
|
||||
<span className="h5">
|
||||
<FormattedMessage {...messages.helpAndSupportSecondQuestionTitle} />
|
||||
</span>
|
||||
<span className="x-small">
|
||||
<FormattedMessage {...messages.helpAndSupportSecondQuestionBody} />
|
||||
</span>
|
||||
</Stack>
|
||||
<hr />
|
||||
<Stack>
|
||||
<span className="h5">
|
||||
<FormattedMessage {...messages.helpAndSupportThirdQuestionTitle} />
|
||||
</span>
|
||||
<span className="x-small">
|
||||
<FormattedMessage
|
||||
{...messages.helpAndSupportThirdQuestionBody}
|
||||
values={{ div: SingleLineBreak, p: Paragraph }}
|
||||
/>
|
||||
</span>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
@@ -1,5 +1,8 @@
|
||||
.legacy-library-migration-page {
|
||||
.migration-container {
|
||||
// Calculate all the screen size subtracting the height of the header and top/bottom margins
|
||||
min-height: calc(calc(100vh - 60px) - calc(var(--pgn-spacing-spacer-base) * 6));
|
||||
|
||||
.courses-tab-container {
|
||||
min-height: auto;
|
||||
}
|
||||
@@ -25,7 +28,29 @@
|
||||
|
||||
.content-buttons {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-right: 0; // To avoid create a horizontal scroll using Layout
|
||||
}
|
||||
|
||||
[class*="col-"] {
|
||||
// To avoid create an empty gray space between the main content and the sidebar
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.legacy-libraries-migration-help {
|
||||
z-index: 1000; // same as header
|
||||
flex: 350px 0 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,58 @@ const messages = defineMessages({
|
||||
+ ' moved will be migrated to <b>{libraryName}</b>',
|
||||
description: 'Alert text when the legacy library is already migrated.',
|
||||
},
|
||||
helpAndSupportTitle: {
|
||||
id: 'legacy-libraries-migration.helpAndSupport.title',
|
||||
defaultMessage: 'Help & Support',
|
||||
description: 'Title of the Help & Support sidebar',
|
||||
},
|
||||
helpAndSupportFirstQuestionTitle: {
|
||||
id: 'legacy-libraries-migration.helpAndSupport.q1.title',
|
||||
defaultMessage: 'What’s different in the new Content Libraries experience?',
|
||||
description: 'Title of the first question in the Help & Support sidebar',
|
||||
},
|
||||
helpAndSupportFirstQuestionBody: {
|
||||
id: 'legacy-libraries-migration.helpAndSupport.q1.body',
|
||||
defaultMessage: 'In the new Content Libraries experience, you can author sections,'
|
||||
+ ' subsections, units, and many types of components. Library content can be reused across many courses,'
|
||||
+ ' and kept up-to-date. Content libraries now support increased collaboration across authoring teams.',
|
||||
description: 'Body of the first question in the Help & Support sidebar',
|
||||
},
|
||||
helpAndSupportSecondQuestionTitle: {
|
||||
id: 'legacy-libraries-migration.helpAndSupport.q2.title',
|
||||
defaultMessage: 'What happens when I migrate my Legacy Libraries?',
|
||||
description: 'Title of the second question in the Help & Support sidebar',
|
||||
},
|
||||
helpAndSupportSecondQuestionBody: {
|
||||
id: 'legacy-libraries-migration.helpAndSupport.q2.body',
|
||||
defaultMessage: 'All legacy library content is supported in the new experience.'
|
||||
+ ' Content from legacy libraries will be migrated to its own collection in the new Content Libraries experience.'
|
||||
+ ' This collection will have the same name as your original library. Courses that use legacy library content will'
|
||||
+ ' continue to function as usual, linked to the migrated version within the new libraries experience.',
|
||||
description: 'Body of the second question in the Help & Support sidebar',
|
||||
},
|
||||
helpAndSupportThirdQuestionTitle: {
|
||||
id: 'legacy-libraries-migration.helpAndSupport.q3.title',
|
||||
defaultMessage: 'How do I migrate my Legacy Libraries?',
|
||||
description: 'Title of the third question in the Help & Support sidebar',
|
||||
},
|
||||
helpAndSupportThirdQuestionBody: {
|
||||
id: 'legacy-libraries-migration.helpAndSupport.q3.body.2',
|
||||
defaultMessage: '<p>There are three steps to migrating legacy libraries:</p>'
|
||||
+ '<p><div>1 - Select Legacy Libraries</div>'
|
||||
+ 'You can select up to 50 legacy libraries for migration in this step. By default, only libraries that have'
|
||||
+ ' not yet been migrated are shown. To see all libraries, remove the filter.'
|
||||
+ ' You can select up to 50 legacy libraries for migration, but only one destination'
|
||||
+ ' v2 Content Library per migration.</p>'
|
||||
+ '<p><div>2 - Select Destination</div>'
|
||||
+ 'You can migrate legacy libraries to an existing Content Library in the new experience,'
|
||||
+ ' or you can create a new destination. You can only select one v2 Content Library per migration.'
|
||||
+ ' All your content will be migrated, and kept organized in collections.</p>'
|
||||
+ '<p><div>3 - Confirm</div>'
|
||||
+ 'In this step, review your migration. Once you confirm, migration will begin.'
|
||||
+ ' It may take some time to complete.</p>',
|
||||
description: 'Part 2 of the Body of the third question in the Help & Support sidebar',
|
||||
},
|
||||
migrationInProgress: {
|
||||
id: 'legacy-libraries-migration.confirmation-step.toast.migration-in-progress',
|
||||
defaultMessage: '{count, plural, one {{count} legacy library is} other {{count} legacy libraries are}} being migrated.',
|
||||
|
||||
Reference in New Issue
Block a user