fix: UI fixes in legacy library migrator

- Keep state of all migration steps on nevigation
- Reword alert in confirm dialog
- Add scroll to help sidebar in migration
- Keep the same migration filter
This commit is contained in:
Chris Chávez
2025-10-22 16:24:52 -05:00
committed by GitHub
parent 9f1604110b
commit 46fa17ea83
6 changed files with 45 additions and 13 deletions

View File

@@ -162,9 +162,20 @@ describe('<LegacyLibMigrationPage />', () => {
});
it('should back to select legacy libraries', async () => {
const user = userEvent.setup();
renderPage();
expect(await screen.findByText('Migrate Legacy Libraries')).toBeInTheDocument();
// The filter is Unmigrated by default
const filterButton = await screen.findByRole('button', { name: /unmigrated/i });
expect(filterButton).toBeInTheDocument();
// Clear filter to show all
await user.click(filterButton);
const clearButton = await screen.findByRole('button', { name: /clear filter/i });
await user.click(clearButton);
expect(await screen.findByText('MBA')).toBeInTheDocument();
expect(await screen.findByText('Legacy library 1')).toBeInTheDocument();
expect(await screen.findByText('MBA 1')).toBeInTheDocument();
const legacyLibrary = screen.getByRole('checkbox', { name: 'MBA' });
legacyLibrary.click();
@@ -178,7 +189,13 @@ describe('<LegacyLibMigrationPage />', () => {
const backButton = screen.getByRole('button', { name: /back/i });
backButton.click();
// The selected legacy library remains checked
expect(legacyLibrary).toBeChecked();
// The filter remains the same
expect(await screen.findByText('MBA')).toBeInTheDocument();
expect(await screen.findByText('Legacy library 1')).toBeInTheDocument();
expect(await screen.findByText('MBA 1')).toBeInTheDocument();
});
it('should select a library destination', async () => {
@@ -230,6 +247,8 @@ describe('<LegacyLibMigrationPage />', () => {
backButton.click();
expect(await screen.findByText('Test Library 1')).toBeInTheDocument();
// The selected v2 library remains checked
expect(radioButton).toBeChecked();
});
it('should open the create new library modal', async () => {

View File

@@ -80,6 +80,7 @@ export const LegacyLibMigrationPage = () => {
const [currentStep, setCurrentStep] = useState<MigrationStep>('select-libraries');
const [isExitModalOpen, openExitModal, closeExitModal] = useToggle(false);
const [legacyLibraries, setLegacyLibraries] = useState<LibraryV1Data[]>([]);
const [migrationFilter, setMigrationFilter] = useState<Filter[]>([Filter.unmigrated]);
const [destinationLibrary, setDestination] = useState<ContentLibrary>();
const [confirmationButtonState, setConfirmationButtonState] = useState('default');
const migrate = useUpdateContainerCollections();
@@ -127,7 +128,6 @@ export const LegacyLibMigrationPage = () => {
openExitModal();
break;
case 'select-destination':
setDestination(undefined);
setCurrentStep('select-libraries');
break;
case 'confirmation-view':
@@ -193,7 +193,8 @@ export const LegacyLibMigrationPage = () => {
selectedIds={legacyLibrariesIds}
handleCheck={handleUpdateLegacyLibraries}
hideMigationAlert
initialFilter={[Filter.unmigrated]}
migrationFilter={migrationFilter}
setMigrationFilter={setMigrationFilter}
setSelectedLibraries={setLegacyLibraries}
/>
</Stepper.Step>
@@ -221,7 +222,7 @@ export const LegacyLibMigrationPage = () => {
</Stepper>
</div>
</Container>
<div className="content-buttons d-flex justify-content-between pl-6 pr-6 bg-white">
<div className="content-buttons d-flex justify-content-between pl-6 pr-6 bg-white box-shadow-up-1">
<Button className="mt-2 mb-2" variant="outline-primary" onClick={handleBack}>
{currentStep === 'select-libraries'
? intl.formatMessage(messages.cancel)

View File

@@ -49,6 +49,8 @@
position: sticky;
top: 0;
right: 0;
height: 100vh;
overflow-y: auto;
hr {
width: 100%;

View File

@@ -72,8 +72,9 @@ const messages = defineMessages({
confirmationViewAlert: {
id: 'legacy-libraries-migration.select-destination.alert.text',
defaultMessage: 'These {count, plural, one {{count} legacy library} other {{count} legacy libraries}}'
+ ' will be migrated to <b>{libraryName}</b> and organized as collections. Any legacy libraries that are used in'
+ ' problem banks will maintain their link with migrated content the first time they are migrated.',
+ ' will be migrated to <b>{libraryName}</b> and organized as collections. Legacy library content used'
+ ' in courses will continue to work as-is. To receive any future changes to migrated content,'
+ ' you must update these references within your course.',
description: 'Alert text in the confirmation step of the legacy libraries migration page.',
},
previouslyMigratedAlert: {

View File

@@ -14,7 +14,7 @@ import { useNavigate, useLocation } from 'react-router-dom';
import { RequestStatus } from '@src/data/constants';
import { getLoadingStatuses, getStudioHomeData } from '../data/selectors';
import messages from './messages';
import { LibrariesList } from './libraries-tab';
import { BaseFilterState, Filter, LibrariesList } from './libraries-tab';
import LibrariesV2List from './libraries-v2-tab/index';
import CoursesTab from './courses-tab';
import { WelcomeLibrariesV2Alert } from './libraries-v2-tab/WelcomeLibrariesV2Alert';
@@ -29,6 +29,7 @@ const TabsSection = ({
const intl = useIntl();
const navigate = useNavigate();
const { pathname } = useLocation();
const [migrationFilter, setMigrationFilter] = useState<Filter[]>(BaseFilterState);
const TABS_LIST = {
courses: 'courses',
libraries: 'libraries',
@@ -121,7 +122,10 @@ const TabsSection = ({
: messages.librariesTabTitle,
)}
>
<LibrariesList />
<LibrariesList
migrationFilter={migrationFilter}
setMigrationFilter={setMigrationFilter}
/>
</Tab>,
);
}
@@ -137,7 +141,7 @@ const TabsSection = ({
}
return tabs;
}, [showNewCourseContainer, isLoadingCourses]);
}, [showNewCourseContainer, isLoadingCourses, migrationFilter]);
const handleSelectTab = (tab: TabKeyType) => {
if (tab === TABS_LIST.courses) {

View File

@@ -70,7 +70,7 @@ export enum Filter {
unmigrated = 'unmigrated',
}
const BaseFilterState = Object.values(Filter);
export const BaseFilterState = Object.values(Filter);
interface MigrationFilterProps {
filters: Filter[];
@@ -146,7 +146,12 @@ interface LibrariesListProps {
handleCheck?: (library: LibraryV1Data, action: 'add' | 'remove') => void;
setSelectedLibraries?: (libraries: LibraryV1Data[]) => void;
hideMigationAlert?: boolean;
initialFilter?: Filter[];
// We lift `migrationFilter` and `setMigrationFilter` into props
// so that the filter state is maintained consistently across different
// steps of the legacy libraries migration flow, and to allow
// parent components to control and persist the filter context.
migrationFilter: Filter[];
setMigrationFilter: React.Dispatch<React.SetStateAction<Filter[]>>;
}
export const LibrariesList = ({
@@ -154,13 +159,13 @@ export const LibrariesList = ({
handleCheck,
setSelectedLibraries,
hideMigationAlert = false,
initialFilter = BaseFilterState,
migrationFilter,
setMigrationFilter,
}: LibrariesListProps) => {
const intl = useIntl();
const { isPending, data, isError } = useLibrariesV1Data();
const [currentPage, setCurrentPage] = useState<number>(1);
const [search, setSearch] = useState<string>('');
const [migrationFilter, setMigrationFilter] = useState<Filter[]>(initialFilter);
let filteredData = findInValues(data?.libraries, search || '') || [];
if (migrationFilter.length === 1) {