fix: prevent switching preview tabs with unsaved changes (#23115)

* fix: prevent switching preview tabs with unsaved changes

* fix: e2e test
This commit is contained in:
Rémi de Juvigny 2025-03-12 09:45:27 +01:00 committed by GitHub
parent 5017d5e420
commit 70c58458a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 80 additions and 69 deletions

View File

@ -6,6 +6,7 @@ import {
useRBAC,
createContext,
Form as FormContext,
Blocker,
} from '@strapi/admin/strapi-admin';
import { Box, Flex, FocusTrap, IconButton, Portal } from '@strapi/design-system';
import { ArrowLineLeft } from '@strapi/icons';
@ -193,77 +194,80 @@ const PreviewPage = () => {
return yupSchema.validate(values, { abortEarly: false });
}}
>
<Flex direction="column" height="100%" alignItems="stretch">
{window.strapi.future.isEnabled('unstablePreviewSideEditor') ? (
<>
<UnstablePreviewHeader />
<Flex flex={1} overflow="auto" alignItems="stretch">
<Box
overflow="auto"
width={isSideEditorOpen ? '50%' : 0}
borderWidth="0 1px 0 0"
borderColor="neutral150"
paddingTop={6}
paddingBottom={6}
// Remove horizontal padding when the editor is closed or it won't fully disappear
paddingLeft={isSideEditorOpen ? 6 : 0}
paddingRight={isSideEditorOpen ? 6 : 0}
transition="all 0.2s ease-in-out"
>
<FormLayout layout={documentLayoutResponse.edit.layout} hasBackground />
</Box>
<Box position="relative" flex={1} height="100%" overflow="hidden">
{({ resetForm }) => (
<Flex direction="column" height="100%" alignItems="stretch">
{window.strapi.future.isEnabled('unstablePreviewSideEditor') ? (
<>
<Blocker onProceed={resetForm} />
<UnstablePreviewHeader />
<Flex flex={1} overflow="auto" alignItems="stretch">
<Box
data-testid="preview-iframe"
ref={iframeRef}
src={previewUrl}
/**
* For some reason, changing an iframe's src tag causes the browser to add a new item in the
* history stack. This is an issue for us as it means clicking the back button will not let us
* go back to the edit view. To fix it, we need to trick the browser into thinking this is a
* different iframe when the preview URL changes. So we set a key prop to force React
* to mount a different node when the src changes.
*/
key={previewUrl}
title={formatMessage({
id: 'content-manager.preview.panel.title',
defaultMessage: 'Preview',
})}
width="100%"
height="100%"
borderWidth={0}
tag="iframe"
/>
<IconButton
variant="tertiary"
label={formatMessage(
isSideEditorOpen
? {
id: 'content-manager.preview.content.close-editor',
defaultMessage: 'Close editor',
}
: {
id: 'content-manager.preview.content.open-editor',
defaultMessage: 'Open editor',
}
)}
onClick={() => setIsSideEditorOpen((prev) => !prev)}
position="absolute"
top={2}
left={2}
overflow="auto"
width={isSideEditorOpen ? '50%' : 0}
borderWidth="0 1px 0 0"
borderColor="neutral150"
paddingTop={6}
paddingBottom={6}
// Remove horizontal padding when the editor is closed or it won't fully disappear
paddingLeft={isSideEditorOpen ? 6 : 0}
paddingRight={isSideEditorOpen ? 6 : 0}
transition="all 0.2s ease-in-out"
>
<AnimatedArrow isSideEditorOpen={isSideEditorOpen} />
</IconButton>
</Box>
</Flex>
</>
) : (
<>
<PreviewHeader />
<PreviewContent />
</>
)}
</Flex>
<FormLayout layout={documentLayoutResponse.edit.layout} hasBackground />
</Box>
<Box position="relative" flex={1} height="100%" overflow="hidden">
<Box
data-testid="preview-iframe"
ref={iframeRef}
src={previewUrl}
/**
* For some reason, changing an iframe's src tag causes the browser to add a new item in the
* history stack. This is an issue for us as it means clicking the back button will not let us
* go back to the edit view. To fix it, we need to trick the browser into thinking this is a
* different iframe when the preview URL changes. So we set a key prop to force React
* to mount a different node when the src changes.
*/
key={previewUrl}
title={formatMessage({
id: 'content-manager.preview.panel.title',
defaultMessage: 'Preview',
})}
width="100%"
height="100%"
borderWidth={0}
tag="iframe"
/>
<IconButton
variant="tertiary"
label={formatMessage(
isSideEditorOpen
? {
id: 'content-manager.preview.content.close-editor',
defaultMessage: 'Close editor',
}
: {
id: 'content-manager.preview.content.open-editor',
defaultMessage: 'Open editor',
}
)}
onClick={() => setIsSideEditorOpen((prev) => !prev)}
position="absolute"
top={2}
left={2}
>
<AnimatedArrow isSideEditorOpen={isSideEditorOpen} />
</IconButton>
</Box>
</Flex>
</>
) : (
<>
<PreviewHeader />
<PreviewContent />
</>
)}
</Flex>
)}
</FormContext>
</PreviewProvider>
</>

View File

@ -171,6 +171,13 @@ describeOnCondition(process.env.STRAPI_FEATURES_UNSTABLE_PREVIEW_SIDE_EDITOR ===
await clickAndWait(page, saveButton);
await expect(titleBox).toHaveValue(/west ham pre match jokes/i);
await expect(page.getByRole('status', { name: /modified/i })).toBeVisible();
// Edit form again and try switching tab without saving
await titleBox.fill('West Ham pre match jokes and banter');
await clickAndWait(page, publishedTab);
const confirmationDialog = page.getByRole('alertdialog', { name: 'Confirmation' });
await expect(confirmationDialog).toBeVisible();
await confirmationDialog.getByRole('button', { name: /cancel/i }).click();
});
}
);