{
const mocks = initializeMocks();
mockShowToast = mocks.mockShowToast;
- mockSearchResult({
- results: [ // @ts-ignore
- {
- hits: [{
- blockType: context,
- displayName: `Test ${context}`,
- }],
- },
- ],
- });
- });
-
- it(`<${context}> is invisible when isOpen is false`, async () => {
- const mockClose = jest.fn();
- const { containerId } = getContainerDetails(context);
- render(, renderArgs);
-
- const modal = screen.queryByRole('dialog', { name: new RegExp(`Delete ${context}`, 'i') });
- expect(modal).not.toBeInTheDocument();
+ mockSearchResult(hydrateSearchResult([{
+ blockType: context,
+ displayName: `Test ${context}`,
+ }]));
});
it(`<${context}> should show a confirmation prompt the card with title and description`, async () => {
@@ -92,7 +73,6 @@ let mockShowToast: { (message: string, action?: ToastActionData | undefined): vo
const { containerId } = getContainerDetails(context);
render(, renderArgs);
@@ -111,7 +91,7 @@ let mockShowToast: { (message: string, action?: ToastActionData | undefined): vo
it(`<${context}> deletes the block when confirmed, shows a toast with undo option and restores block on undo`, async () => {
const mockCancel = jest.fn();
const { containerId } = getContainerDetails(context);
- render(, renderArgs);
+ render(, renderArgs);
const modal = await screen.findByRole('dialog', { name: new RegExp(`Delete ${context}`, 'i') });
expect(modal).toBeVisible();
@@ -138,23 +118,17 @@ let mockShowToast: { (message: string, action?: ToastActionData | undefined): vo
if (!parent) {
return;
}
- mockSearchResult({
- results: [ // @ts-ignore
- {
- hits: [{
- [`${parent}s`]: {
- displayName: [`${parent} 1`],
- key: [`${parent}1`],
- },
- blockType: context,
- }],
- },
- ],
- });
+ mockSearchResult(hydrateSearchResult([{
+ [`${parent}s`]: {
+ displayName: [`${parent} 1`],
+ key: [`${parent}1`],
+ },
+ blockType: context,
+ }]));
+
render(
,
renderArgs,
@@ -173,23 +147,16 @@ let mockShowToast: { (message: string, action?: ToastActionData | undefined): vo
if (!parent) {
return;
}
- mockSearchResult({
- results: [ // @ts-ignore
- {
- hits: [{
- [`${parent}s`]: {
- displayName: [`${parent} 1`, `${parent} 2`],
- key: [`${parent}1`, `${parent}2`],
- },
- blockType: context,
- }],
- },
- ],
- });
+ mockSearchResult(hydrateSearchResult([{
+ [`${parent}s`]: {
+ displayName: [`${parent} 1`, `${parent} 2`],
+ key: [`${parent}1`, `${parent}2`],
+ },
+ blockType: context,
+ }]));
render(
,
renderArgs,
diff --git a/src/library-authoring/containers/ContainerDeleter.tsx b/src/library-authoring/containers/ContainerDeleter.tsx
index c3497d5ce..f878ca25e 100644
--- a/src/library-authoring/containers/ContainerDeleter.tsx
+++ b/src/library-authoring/containers/ContainerDeleter.tsx
@@ -1,27 +1,37 @@
import { useCallback, useContext, useMemo } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Icon } from '@openedx/paragon';
-import { Error, Warning, School } from '@openedx/paragon/icons';
+import { Error, Delete, School } from '@openedx/paragon/icons';
import DeleteModal from '@src/generic/delete-modal/DeleteModal';
import { ToastContext } from '@src/generic/toast-context';
import { ContainerType } from '@src/generic/key-utils';
-import { ContainerHit } from '@src/search-manager';
+import { type ContainerHit } from '@src/search-manager';
import { useEntityLinks } from '@src/course-libraries/data/apiHooks';
-import { LoadingSpinner } from '@src/generic/Loading';
import { useSidebarContext } from '../common/context/SidebarContext';
+import { useLibraryContext } from '../common/context/LibraryContext';
import { useContentFromSearchIndex, useDeleteContainer, useRestoreContainer } from '../data/apiHooks';
import messages from './messages';
type ContainerDeleterProps = {
- isOpen: boolean,
close: () => void,
containerId: string,
};
+type ContainerParents = {
+ displayName?: string[],
+ key?: string[],
+};
+
+const getOtherParentContainers = (containerParents?: ContainerParents, currentParentId?: string) => {
+ const currentParentIndex = containerParents?.key?.findIndex((id) => id === currentParentId);
+ return containerParents?.displayName?.filter(
+ (_, index) => index !== currentParentIndex,
+ );
+};
+
const ContainerDeleter = ({
- isOpen,
close,
containerId,
}: ContainerDeleterProps) => {
@@ -30,20 +40,24 @@ const ContainerDeleter = ({
sidebarItemInfo,
closeLibrarySidebar,
} = useSidebarContext();
+ const {
+ containerId: parentContainerId,
+ } = useLibraryContext();
const deleteContainerMutation = useDeleteContainer(containerId);
const restoreContainerMutation = useRestoreContainer(containerId);
const { showToast } = useContext(ToastContext);
- const { hits, isLoading } = useContentFromSearchIndex([containerId]);
+ const { hits, isPending } = useContentFromSearchIndex([containerId]);
const containerData = (hits as ContainerHit[])?.[0];
const {
data: dataDownstreamLinks,
- isLoading: linksIsLoading,
+ isPending: isPendingLinks,
} = useEntityLinks({ upstreamKey: containerId, contentType: 'containers' });
const downstreamCount = dataDownstreamLinks?.length ?? 0;
const messageMap = useMemo(() => {
const containerType = containerData?.blockType;
- let parentCount = 0;
+ let otherParentContainers: string[] | undefined;
+ let otherParentCount = 0;
let parentMessage: React.ReactNode;
switch (containerType) {
case ContainerType.Section:
@@ -51,42 +65,44 @@ const ContainerDeleter = ({
title: intl.formatMessage(messages.deleteSectionWarningTitle),
parentMessage: '',
courseCount: downstreamCount,
- courseMessage: messages.deleteSectionCourseMessaage,
+ courseMessage: messages.deleteSectionCourseMessage,
deleteSuccess: intl.formatMessage(messages.deleteSectionSuccess),
deleteError: intl.formatMessage(messages.deleteSectionFailed),
undoDeleteError: messages.undoDeleteSectionToastFailed,
};
case ContainerType.Subsection:
- parentCount = containerData?.sections?.displayName?.length || 0;
- if (parentCount === 1) {
+ otherParentContainers = getOtherParentContainers(containerData?.sections, parentContainerId);
+ otherParentCount = otherParentContainers?.length || 0;
+ if (otherParentCount === 1) {
parentMessage = intl.formatMessage(
messages.deleteSubsectionParentMessage,
{ parentName: {containerData?.sections?.displayName?.[0]} },
);
- } else if (parentCount > 1) {
+ } else if (otherParentCount > 1) {
parentMessage = intl.formatMessage(messages.deleteSubsectionMultipleParentMessage, {
- parentCount: {parentCount},
+ parentCount: {otherParentCount},
});
}
return {
title: intl.formatMessage(messages.deleteSubsectionWarningTitle),
parentMessage,
courseCount: downstreamCount,
- courseMessage: messages.deleteSubsectionCourseMessaage,
+ courseMessage: messages.deleteSubsectionCourseMessage,
deleteSuccess: intl.formatMessage(messages.deleteSubsectionSuccess),
deleteError: intl.formatMessage(messages.deleteSubsectionFailed),
undoDeleteError: messages.undoDeleteSubsectionToastFailed,
};
- default:
- parentCount = containerData?.subsections?.displayName?.length || 0;
- if (parentCount === 1) {
+ default: // Unit
+ otherParentContainers = getOtherParentContainers(containerData?.subsections, parentContainerId);
+ otherParentCount = otherParentContainers?.length || 0;
+ if (otherParentCount === 1) {
parentMessage = intl.formatMessage(
messages.deleteUnitParentMessage,
- { parentName: {containerData?.subsections?.displayName?.[0]} },
+ { parentName: {otherParentContainers?.[0]} },
);
- } else if (parentCount > 1) {
+ } else if (otherParentCount > 1) {
parentMessage = intl.formatMessage(messages.deleteUnitMultipleParentMessage, {
- parentCount: {parentCount},
+ parentCount: {otherParentCount},
});
}
return {
@@ -101,8 +117,8 @@ const ContainerDeleter = ({
}
}, [containerData, downstreamCount, messages, intl]);
- const deleteText = intl.formatMessage(messages.deleteUnitConfirm, {
- unitName: {containerData?.displayName},
+ const deleteText = intl.formatMessage(messages.deleteContainerConfirm, {
+ containerName: {containerData?.displayName},
message: (
{messageMap.parentMessage && (
@@ -154,14 +170,20 @@ const ContainerDeleter = ({
});
}, [sidebarItemInfo, showToast, deleteContainerMutation, messageMap]);
+ // istanbul ignore if: loading state
+ if (isPending || isPendingLinks) {
+ // Only render the modal after loading
+ return null;
+ }
+
return (
: deleteText}
+ icon={Delete}
+ description={deleteText}
onDeleteSubmit={onDelete}
/>
);
diff --git a/src/library-authoring/containers/ContainerInfo.test.tsx b/src/library-authoring/containers/ContainerInfo.test.tsx
index db4d9d4c9..b8f378829 100644
--- a/src/library-authoring/containers/ContainerInfo.test.tsx
+++ b/src/library-authoring/containers/ContainerInfo.test.tsx
@@ -8,7 +8,7 @@ import {
} from '@src/testUtils';
import { ContainerType } from '@src/generic/key-utils';
import type { ToastActionData } from '@src/generic/toast-context';
-import { mockContentSearchConfig, mockSearchResult } from '@src/search-manager/data/api.mock';
+import { mockContentSearchConfig, mockSearchResult, hydrateSearchResult } from '@src/search-manager/data/api.mock';
import {
mockContentLibrary,
mockGetContainerChildren,
@@ -162,16 +162,10 @@ let mockShowToast: { (message: string, action?: ToastActionData | undefined): vo
describe(` with containerType: ${containerType}`, () => {
beforeEach(() => {
({ axiosMock, mockShowToast } = initializeMocks());
- mockSearchResult({
- results: [ // @ts-ignore
- {
- hits: [{
- blockType: containerType,
- displayName: `Test ${containerType}`,
- }],
- },
- ],
- });
+ mockSearchResult(hydrateSearchResult([{
+ blockType: containerType,
+ displayName: `Test ${containerType}`,
+ }]));
});
it(`should delete the ${containerType} using the menu`, async () => {
@@ -189,7 +183,8 @@ let mockShowToast: { (message: string, action?: ToastActionData | undefined): vo
fireEvent.click(deleteMenuItem);
// Confirm delete Modal is open
- expect(screen.getByText(`Delete ${containerType[0].toUpperCase()}${containerType.slice(1)}`));
+ const modal = await screen.findByRole('dialog', { name: `Delete ${containerType[0].toUpperCase()}${containerType.slice(1)}` });
+ expect(modal).toBeInTheDocument();
const deleteButton = screen.getByRole('button', { name: /delete/i });
fireEvent.click(deleteButton);
diff --git a/src/library-authoring/containers/ContainerInfo.tsx b/src/library-authoring/containers/ContainerInfo.tsx
index 23c3d063c..2b36a0764 100644
--- a/src/library-authoring/containers/ContainerInfo.tsx
+++ b/src/library-authoring/containers/ContainerInfo.tsx
@@ -60,11 +60,12 @@ const ContainerMenu = ({ containerId }: ContainerPreviewProps) => {
-
+ {isConfirmingDelete && (
+
+ )}
>
);
};
diff --git a/src/library-authoring/containers/ContainerRemover.tsx b/src/library-authoring/containers/ContainerRemover.tsx
index 69196cafb..2261edb59 100644
--- a/src/library-authoring/containers/ContainerRemover.tsx
+++ b/src/library-authoring/containers/ContainerRemover.tsx
@@ -1,24 +1,24 @@
import { useCallback, useContext } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
+import { Warning } from '@openedx/paragon/icons';
import { capitalize } from 'lodash';
import DeleteModal from '@src/generic/delete-modal/DeleteModal';
import { ToastContext } from '@src/generic/toast-context';
import { getBlockType } from '@src/generic/key-utils';
+
import { useSidebarContext } from '../common/context/SidebarContext';
import { useLibraryContext } from '../common/context/LibraryContext';
import { useContainer, useRemoveContainerChildren } from '../data/apiHooks';
import messages from '../components/messages';
type ContainerRemoverProps = {
- isOpen: boolean,
close: () => void,
containerKey: string,
displayName: string,
};
const ContainerRemover = ({
- isOpen,
close,
containerKey,
displayName,
@@ -32,7 +32,7 @@ const ContainerRemover = ({
const { showToast } = useContext(ToastContext);
const removeContainerMutation = useRemoveContainerChildren(containerId);
- const { data: container } = useContainer(containerId);
+ const { data: container, isPending } = useContainer(containerId);
const itemType = getBlockType(containerKey);
const removeWarningTitle = intl.formatMessage(messages.removeContainerWarningTitle, {
@@ -40,10 +40,9 @@ const ContainerRemover = ({
});
const removeText = intl.formatMessage(messages.removeContainerConfirm, {
- containerName: {capitalize(itemType)} {displayName},
+ containerName: {displayName},
containerType: capitalize(itemType),
- parentContainerType: capitalize(container?.containerType),
- parentContainerName: container?.displayName,
+ parentContainerName: {container?.displayName},
});
const removeSuccess = intl.formatMessage(messages.removeComponentFromContainerSuccess);
@@ -72,18 +71,23 @@ const ContainerRemover = ({
close,
]);
+ // istanbul ignore if: loading state
+ if (isPending) {
+ // Only render when data is ready
+ return null;
+ }
+
return (
);
};
diff --git a/src/library-authoring/containers/messages.ts b/src/library-authoring/containers/messages.ts
index 44da1dfbf..0cd961a12 100644
--- a/src/library-authoring/containers/messages.ts
+++ b/src/library-authoring/containers/messages.ts
@@ -101,7 +101,7 @@ const messages = defineMessages({
defaultMessage: 'Delete Section',
description: 'Title text for the warning displayed before deleting a Section',
},
- deleteSectionCourseMessaage: {
+ deleteSectionCourseMessage: {
id: 'course-authoring.library-authoring.section.delete-parent-message',
defaultMessage: 'This section is used {courseCount, plural, one {{courseCountText} time} other {{courseCountText} times}} in courses, and will stop receiving updates there.',
description: 'Course usage details shown before deleting a section',
@@ -121,7 +121,7 @@ const messages = defineMessages({
defaultMessage: 'Delete Subsection',
description: 'Title text for the warning displayed before deleting a Subsection',
},
- deleteSubsectionCourseMessaage: {
+ deleteSubsectionCourseMessage: {
id: 'course-authoring.library-authoring.subsection.delete-course-message',
defaultMessage: 'This subsection is used {courseCount, plural, one {{courseCountText} time} other {{courseCountText} times}} in courses, and will stop receiving updates there.',
description: 'Course usage details shown before deleting a subsection',
@@ -146,10 +146,10 @@ const messages = defineMessages({
defaultMessage: 'This unit is used {courseCount, plural, one {{courseCountText} time} other {{courseCountText} times}} in courses, and will stop receiving updates there.',
description: 'Course usage details shown before deleting a unit',
},
- deleteUnitConfirm: {
- id: 'course-authoring.library-authoring.unit.delete-confirmation-text',
- defaultMessage: 'Delete {unitName}? {message}',
- description: 'Confirmation text to display before deleting a unit',
+ deleteContainerConfirm: {
+ id: 'course-authoring.library-authoring.container.delete-confirmation-text',
+ defaultMessage: 'Are you sure you want to permanently delete {containerName}? This cannot be undone and will remove it from your library. {message}',
+ description: 'Confirmation text to display before deleting a container',
},
deleteUnitSuccess: {
id: 'course-authoring.library-authoring.unit.delete.success',
diff --git a/src/library-authoring/section-subsections/LibrarySubsectionPage.tsx b/src/library-authoring/section-subsections/LibrarySubsectionPage.tsx
index 5839d6575..e99509f01 100644
--- a/src/library-authoring/section-subsections/LibrarySubsectionPage.tsx
+++ b/src/library-authoring/section-subsections/LibrarySubsectionPage.tsx
@@ -25,10 +25,10 @@ export const LibrarySubsectionPage = () => {
const { libraryId, containerId } = useLibraryContext();
const { sidebarItemInfo } = useSidebarContext();
- const { data: libraryData, isLoading: isLibLoading } = useContentLibrary(libraryId);
+ const { data: libraryData, isPending: isLibPending } = useContentLibrary(libraryId);
// fetch subsectionData from index as it includes its parent sections as well.
const {
- hits, isLoading, isError, error,
+ hits, isPending, isError, error,
} = useContentFromSearchIndex(containerId ? [containerId] : []);
const subsectionData = (hits as ContainerHit[])?.[0];
@@ -38,7 +38,7 @@ export const LibrarySubsectionPage = () => {
}
// Only show loading if section or library data is not fetched from index yet
- if (isLibLoading || isLoading) {
+ if (isLibPending || isPending) {
return ;
}
diff --git a/src/library-authoring/units/LibraryUnitBlocks.tsx b/src/library-authoring/units/LibraryUnitBlocks.tsx
index 2381cbc5a..07d40212d 100644
--- a/src/library-authoring/units/LibraryUnitBlocks.tsx
+++ b/src/library-authoring/units/LibraryUnitBlocks.tsx
@@ -290,7 +290,6 @@ export const LibraryUnitBlocks = ({ unitId, readOnly: componentReadOnly }: Libra
>
{orderedBlocks?.map((block, idx) => (
// A container can have multiple instances of the same block
- // eslint-disable-next-line react/no-array-index-key
', () => {
});
it('should remove a component & restore from component card', async () => {
+ const user = userEvent.setup();
const url = getLibraryContainerChildrenApiUrl(mockGetContainerMetadata.unitId);
axiosMock.onDelete(url).reply(200);
renderLibraryUnitPage();
expect(await screen.findByText('text block 0')).toBeInTheDocument();
const menu = screen.getAllByRole('button', { name: /component actions menu/i })[0];
- fireEvent.click(menu);
+ await user.click(menu);
const removeButton = await screen.findByText('Remove from unit');
- fireEvent.click(removeButton);
+ await user.click(removeButton);
+
+ const modal = await screen.findByRole('dialog', { name: 'Remove Component' });
+ expect(modal).toBeVisible();
+
+ const confirmButton = await within(modal).findByRole('button', { name: 'Remove' });
+ await user.click(confirmButton);
await waitFor(() => {
expect(axiosMock.history.delete[0].url).toEqual(url);
@@ -385,16 +392,23 @@ describe('', () => {
});
it('should show error on remove a component', async () => {
+ const user = userEvent.setup();
const url = getLibraryContainerChildrenApiUrl(mockGetContainerMetadata.unitId);
axiosMock.onDelete(url).reply(404);
renderLibraryUnitPage();
expect(await screen.findByText('text block 0')).toBeInTheDocument();
const menu = screen.getAllByRole('button', { name: /component actions menu/i })[0];
- fireEvent.click(menu);
+ await user.click(menu);
const removeButton = await screen.findByText('Remove from unit');
- fireEvent.click(removeButton);
+ await user.click(removeButton);
+
+ const modal = await screen.findByRole('dialog', { name: 'Remove Component' });
+ expect(modal).toBeVisible();
+
+ const confirmButton = await within(modal).findByRole('button', { name: 'Remove' });
+ await user.click(confirmButton);
await waitFor(() => {
expect(axiosMock.history.delete[0].url).toEqual(url);
@@ -403,16 +417,23 @@ describe('', () => {
});
it('should show error on restore removed component', async () => {
+ const user = userEvent.setup();
const url = getLibraryContainerChildrenApiUrl(mockGetContainerMetadata.unitId);
axiosMock.onDelete(url).reply(200);
renderLibraryUnitPage();
expect(await screen.findByText('text block 0')).toBeInTheDocument();
const menu = screen.getAllByRole('button', { name: /component actions menu/i })[0];
- fireEvent.click(menu);
+ await user.click(menu);
const removeButton = await screen.findByText('Remove from unit');
- fireEvent.click(removeButton);
+ await user.click(removeButton);
+
+ const modal = await screen.findByRole('dialog', { name: 'Remove Component' });
+ expect(modal).toBeVisible();
+
+ const confirmButton = await within(modal).findByRole('button', { name: 'Remove' });
+ await user.click(confirmButton);
await waitFor(() => {
expect(axiosMock.history.delete[0].url).toEqual(url);
@@ -446,10 +467,16 @@ describe('', () => {
const { findByRole, findByText } = within(sidebar);
const menu = await findByRole('button', { name: /component actions menu/i });
- fireEvent.click(menu);
+ await user.click(menu);
const removeButton = await findByText('Remove from unit');
- fireEvent.click(removeButton);
+ await user.click(removeButton);
+
+ const modal = await screen.findByRole('dialog', { name: 'Remove Component' });
+ expect(modal).toBeVisible();
+
+ const confirmButton = await within(modal).findByRole('button', { name: 'Remove' });
+ await user.click(confirmButton);
await waitFor(() => {
expect(axiosMock.history.delete[0].url).toEqual(url);
diff --git a/src/library-authoring/units/LibraryUnitPage.tsx b/src/library-authoring/units/LibraryUnitPage.tsx
index 170c6cfc3..f8fd6ea16 100644
--- a/src/library-authoring/units/LibraryUnitPage.tsx
+++ b/src/library-authoring/units/LibraryUnitPage.tsx
@@ -35,10 +35,10 @@ export const LibraryUnitPage = () => {
const { sidebarItemInfo } = useSidebarContext();
- const { data: libraryData, isLoading: isLibLoading } = useContentLibrary(libraryId);
+ const { data: libraryData, isPending: isLibPending } = useContentLibrary(libraryId);
// fetch unitData from index as it includes its parent subsections as well.
const {
- hits, isLoading, isError, error,
+ hits, isPending, isError, error,
} = useContentFromSearchIndex(containerId ? [containerId] : []);
const unitData = (hits as ContainerHit[])?.[0];
@@ -48,7 +48,7 @@ export const LibraryUnitPage = () => {
}
// Only show loading if unit or library data is not fetched from index yet
- if (isLibLoading || isLoading) {
+ if (isLibPending || isPending) {
return ;
}
diff --git a/src/search-manager/SearchManager.ts b/src/search-manager/SearchManager.ts
index d53ceb675..87f70a983 100644
--- a/src/search-manager/SearchManager.ts
+++ b/src/search-manager/SearchManager.ts
@@ -40,7 +40,7 @@ export interface SearchContextData {
defaultSearchSortOrder: SearchSortOption;
hits: HitType[];
totalHits: number;
- isLoading: boolean;
+ isPending: boolean;
hasNextPage: boolean | undefined;
isFetchingNextPage: boolean;
fetchNextPage: () => void;
diff --git a/src/search-manager/data/api.mock.ts b/src/search-manager/data/api.mock.ts
index d11988a5e..59ff21f55 100644
--- a/src/search-manager/data/api.mock.ts
+++ b/src/search-manager/data/api.mock.ts
@@ -23,6 +23,9 @@ mockContentSearchConfig.applyMock = () => (
/**
* Mock all future Meilisearch searches with the given response.
*
+ * If you want to pass only the hits, use `hydrateSearchResult()` to create a full
+ * MultiSearchResponse object.
+ *
* For a given test suite, this mock will stay in effect until you call it with
* a different mock response, or you call `fetchMock.mockReset()`
*/
@@ -45,6 +48,25 @@ export function mockSearchResult(
}, { overwriteRoutes: true });
}
+/** Helper to create a full MultiSearchResponse object from an array of hits.
+ * You can then pass the result to `mockSearchResult()`.
+ */
+export const hydrateSearchResult: (hits: any[]) => MultiSearchResponse = (hits) => ({
+ results: [
+ {
+ hits,
+ offset: 0,
+ limit: 20,
+ nbHits: 0,
+ exhaustiveNbHits: true,
+ processingTimeMs: 1,
+ query: '',
+ params: '',
+ indexUid: 'studio',
+ },
+ ],
+});
+
/**
* Mock the block types returned by the API.
*/
diff --git a/src/search-manager/data/apiHooks.ts b/src/search-manager/data/apiHooks.ts
index 821aece67..51881a1c2 100644
--- a/src/search-manager/data/apiHooks.ts
+++ b/src/search-manager/data/apiHooks.ts
@@ -177,7 +177,7 @@ export const useContentSearchResults = ({
problemTypes: pages?.[0]?.problemTypes ?? {},
publishStatus: pages?.[0]?.publishStatus ?? {},
status: query.status,
- isLoading: query.isPending,
+ isPending: query.isPending,
isError: query.isError,
error: query.error,
isFetchingNextPage: query.isFetchingNextPage,