Skip to content

Commit ca3f054

Browse files
authored
fix(next): force inactive live preview after passing conditions (#14048)
Follow-up to #14012. Once live preview conditions have passed, it is jarring for the live preview window to suddenly appear. It should be that, despite preferences, if the live preview window did not _load_ active, then it should not become active until the user explicitly toggles it on. This is especially poor UX while creating a new doc. If the conditional URL is based on a field that has't been filled yet, upon filling that field (with autosave), live preview suddenly appears and the entire page shifts mid-edit. Before: https://github.com/user-attachments/assets/0da75306-eed3-4a77-bc58-d8a8dd0254bf After: https://github.com/user-attachments/assets/c7918601-959d-4ac5-b168-066afc3d879d --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211534009142634
1 parent cb7a24a commit ca3f054

File tree

6 files changed

+43
-12
lines changed

6 files changed

+43
-12
lines changed

packages/next/src/views/Document/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,9 @@ export const renderDocument = async ({
392392
<LivePreviewProvider
393393
breakpoints={livePreviewConfig?.breakpoints}
394394
isLivePreviewEnabled={isLivePreviewEnabled && operation !== 'create'}
395-
isLivePreviewing={entityPreferences?.value?.editViewType === 'live-preview'}
395+
isLivePreviewing={Boolean(
396+
entityPreferences?.value?.editViewType === 'live-preview' && livePreviewURL,
397+
)}
396398
typeofLivePreviewURL={typeof livePreviewConfig?.url as 'function' | 'string' | undefined}
397399
url={livePreviewURL}
398400
>

packages/ui/src/providers/LivePreview/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = ({
9898
incomingURL = formatAbsoluteURL(_incomingURL)
9999
}
100100

101+
if (!incomingURL) {
102+
setIsLivePreviewing(false)
103+
}
104+
101105
if (incomingURL !== url) {
102106
setAppIsReady(false)
103107
setURL(incomingURL)

test/live-preview/collections/NoURL.ts renamed to test/live-preview/collections/ConditionalURL.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CollectionConfig } from 'payload'
22

3-
export const NoURLCollection: CollectionConfig = {
4-
slug: 'no-url',
3+
export const ConditionalURL: CollectionConfig = {
4+
slug: 'conditional-url',
55
admin: {
66
livePreview: {
77
url: ({ data }) => (data?.enabled ? '/live-preview/static' : null),

test/live-preview/collections/StaticURL.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export const StaticURLCollection: CollectionConfig = {
44
slug: 'static-url',
55
admin: {
66
livePreview: {
7-
url: '/live-preview/hello-world',
7+
url: '/live-preview/static',
88
},
99
},
1010
fields: [

test/live-preview/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
66
import { MediaBlock } from './blocks/MediaBlock/index.js'
77
import { Categories } from './collections/Categories.js'
88
import { CollectionLevelConfig } from './collections/CollectionLevelConfig.js'
9+
import { ConditionalURL } from './collections/ConditionalURL.js'
910
import { CustomLivePreview } from './collections/CustomLivePreview.js'
1011
import { Media } from './collections/Media.js'
11-
import { NoURLCollection } from './collections/NoURL.js'
1212
import { Pages } from './collections/Pages.js'
1313
import { Posts } from './collections/Posts.js'
1414
import { SSR } from './collections/SSR.js'
@@ -68,7 +68,7 @@ export default buildConfigWithDefaults({
6868
CollectionLevelConfig,
6969
StaticURLCollection,
7070
CustomLivePreview,
71-
NoURLCollection,
71+
ConditionalURL,
7272
],
7373
globals: [Header, Footer],
7474
onInit: seed,

test/live-preview/e2e.spec.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,22 +179,47 @@ describe('Live Preview', () => {
179179
await expect.poll(async () => iframe.getAttribute('src')).toMatch(/\/live-preview/)
180180
})
181181

182-
test('collection — does not render iframe when live preview url is falsy', async () => {
183-
const noURL = new AdminUrlUtil(serverURL, 'no-url')
182+
test('collection — does not render live preview when url is null', async () => {
183+
const noURL = new AdminUrlUtil(serverURL, 'conditional-url')
184184
await page.goto(noURL.create)
185185
await page.locator('#field-title').fill('No URL')
186186
await saveDocAndAssert(page)
187+
188+
// No toggler should render
187189
const toggler = page.locator('button#live-preview-toggler')
188190
await expect(toggler).toBeHidden()
189191
await expect(page.locator('iframe.live-preview-iframe')).toBeHidden()
190192

193+
// Check the `enabled` field
191194
const enabledCheckbox = page.locator('#field-enabled')
192195
await enabledCheckbox.check()
193196
await saveDocAndAssert(page)
194197

198+
// Toggler is present but not iframe
195199
await expect(toggler).toBeVisible()
196-
await toggleLivePreview(page)
197-
await expect(page.locator('iframe.live-preview-iframe')).toBeVisible()
200+
await expect(page.locator('iframe.live-preview-iframe')).toBeHidden()
201+
202+
// Toggle the iframe back on, which will save to prefs
203+
// We need to explicitly test for this, as we don't want live preview to suddenly appear
204+
await toggleLivePreview(page, {
205+
targetState: 'on',
206+
})
207+
208+
// Uncheck the `enabled` field
209+
await enabledCheckbox.uncheck()
210+
await saveDocAndAssert(page)
211+
212+
// Toggler and iframe are gone
213+
await expect(toggler).toBeHidden()
214+
await expect(page.locator('iframe.live-preview-iframe')).toBeHidden()
215+
216+
// Check the `enabled` field
217+
await enabledCheckbox.check()
218+
await saveDocAndAssert(page)
219+
220+
// Toggler is present but still not iframe
221+
await expect(toggler).toBeVisible()
222+
await expect(page.locator('iframe.live-preview-iframe')).toBeHidden()
198223
})
199224

200225
test('collection — retains static URL across edits', async () => {
@@ -204,12 +229,12 @@ describe('Live Preview', () => {
204229
await toggleLivePreview(page, { targetState: 'on' })
205230

206231
const iframe = page.locator('iframe.live-preview-iframe')
207-
await expect.poll(async () => iframe.getAttribute('src')).toMatch(/\/live-preview\/hello/)
232+
await expect.poll(async () => iframe.getAttribute('src')).toMatch(/\/live-preview\/static/)
208233

209234
const titleField = page.locator('#field-title')
210235
await titleField.fill('New Title')
211236
await saveDocAndAssert(page)
212-
await expect.poll(async () => iframe.getAttribute('src')).toMatch(/\/live-preview\/hello/)
237+
await expect.poll(async () => iframe.getAttribute('src')).toMatch(/\/live-preview\/static/)
213238
})
214239

215240
test('collection csr — iframe reflects form state on change', async () => {

0 commit comments

Comments
 (0)