@@ -8,7 +8,15 @@ import {
8
8
Form as FormContext ,
9
9
Blocker ,
10
10
} from '@strapi/admin/strapi-admin' ;
11
- import { Box , Flex , FocusTrap , IconButton , Portal } from '@strapi/design-system' ;
11
+ import {
12
+ Box ,
13
+ Flex ,
14
+ FocusTrap ,
15
+ IconButton ,
16
+ Portal ,
17
+ SingleSelect ,
18
+ SingleSelectOption ,
19
+ } from '@strapi/design-system' ;
12
20
import { ArrowLineLeft } from '@strapi/icons' ;
13
21
import { useIntl } from 'react-intl' ;
14
22
import { useLocation , useParams } from 'react-router-dom' ;
@@ -27,6 +35,31 @@ import { useGetPreviewUrlQuery } from '../services/preview';
27
35
28
36
import type { UID } from '@strapi/types' ;
29
37
38
+ /* -------------------------------------------------------------------------------------------------
39
+ * Constants
40
+ * -----------------------------------------------------------------------------------------------*/
41
+
42
+ const DEVICES = [
43
+ {
44
+ name :'desktop' ,
45
+ label :{
46
+ id :'content-manager.preview.device.desktop' ,
47
+ defaultMessage :'Desktop' ,
48
+ } ,
49
+ width :'100%' ,
50
+ height :'100%' ,
51
+ } ,
52
+ {
53
+ name :'mobile' ,
54
+ label :{
55
+ id :'content-manager.preview.device.mobile' ,
56
+ defaultMessage :'Mobile' ,
57
+ } ,
58
+ width :'375px' ,
59
+ height :'667px' ,
60
+ } ,
61
+ ] ;
62
+
30
63
/* -------------------------------------------------------------------------------------------------
31
64
* PreviewProvider
32
65
* -----------------------------------------------------------------------------------------------*/
@@ -47,9 +80,9 @@ const [PreviewProvider, usePreviewContext] = createContext<PreviewContextValue>(
47
80
* PreviewPage
48
81
* -----------------------------------------------------------------------------------------------*/
49
82
50
- const AnimatedArrow = styled ( ArrowLineLeft ) < { isSideEditorOpen :boolean } > `
83
+ const AnimatedArrow = styled ( ArrowLineLeft ) < { $ isSideEditorOpen :boolean } > `
51
84
will-change: transform;
52
- rotate:${ ( props ) => ( props . isSideEditorOpen ?'0deg' :'180deg' ) } ;
85
+ rotate:${ ( props ) => ( props . $ isSideEditorOpen ?'0deg' :'180deg' ) } ;
53
86
transition: rotate 0.2s ease-in-out;
54
87
` ;
55
88
@@ -77,6 +110,11 @@ const PreviewPage = () => {
77
110
78
111
const params = React . useMemo ( ( ) => buildValidParams ( query ) , [ query ] ) ;
79
112
113
+ const [ deviceName , setDeviceName ] = React . useState < ( typeof DEVICES ) [ number ] [ 'name' ] > (
114
+ DEVICES [ 0 ] . name
115
+ ) ;
116
+ const device = DEVICES . find ( ( d ) => d . name === deviceName ) ?? DEVICES [ 0 ] ;
117
+
80
118
if ( ! collectionType ) {
81
119
throw new Error ( 'Could not find collectionType in url params' ) ;
82
120
}
@@ -110,7 +148,7 @@ const PreviewPage = () => {
110
148
111
149
const isLoading =
112
150
previewUrlResponse . isLoading || documentLayoutResponse . isLoading || documentResponse . isLoading ;
113
- if ( isLoading && ! documentResponse . document ?. documentId ) {
151
+ if ( isLoading ) {
114
152
return < Page . Loading /> ;
115
153
}
116
154
@@ -228,52 +266,80 @@ const PreviewPage = () => {
228
266
/>
229
267
</ Box >
230
268
) }
231
-
232
- < Box position = "relative" flex = { 1 } height = "100%" overflow = "hidden" >
233
- < Box
234
- data-testid = "preview-iframe"
235
- ref = { iframeRef }
236
- src = { previewUrl }
237
- /**
238
- * For some reason, changing an iframe's src tag causes the browser to add a new item in the
239
- * history stack. This is an issue for us as it means clicking the back button will not let us
240
- * go back to the edit view. To fix it, we need to trick the browser into thinking this is a
241
- * different iframe when the preview URL changes. So we set a key prop to force React
242
- * to mount a different node when the src changes.
243
- */
244
- key = { previewUrl }
245
- title = { formatMessage ( {
246
- id :'content-manager.preview.panel.title' ,
247
- defaultMessage :'Preview' ,
248
- } ) }
249
- width = "100%"
250
- height = "100%"
251
- borderWidth = { 0 }
252
- tag = "iframe"
253
- />
254
- { hasAdvancedPreview && (
255
- < IconButton
256
- variant = "tertiary"
257
- label = { formatMessage (
258
- isSideEditorOpen
259
- ?{
260
- id :'content-manager.preview.content.close-editor' ,
261
- defaultMessage :'Close editor' ,
262
- }
263
- :{
264
- id :'content-manager.preview.content.open-editor' ,
265
- defaultMessage :'Open editor' ,
266
- }
267
- ) }
268
- onClick = { ( ) => setIsSideEditorOpen ( ( prev ) => ! prev ) }
269
- position = "absolute"
270
- top = { 2 }
271
- left = { 2 }
272
- >
273
- < AnimatedArrow isSideEditorOpen = { isSideEditorOpen } />
274
- </ IconButton >
275
- ) }
276
- </ Box >
269
+ < Flex
270
+ direction = "column"
271
+ alignItems = "stretch"
272
+ flex = { 1 }
273
+ height = "100%"
274
+ overflow = "hidden"
275
+ >
276
+ < Flex
277
+ direction = "row"
278
+ background = "neutral0"
279
+ padding = { 2 }
280
+ borderWidth = "0 0 1px 0"
281
+ borderColor = "neutral150"
282
+ >
283
+ { hasAdvancedPreview && (
284
+ < IconButton
285
+ variant = "ghost"
286
+ label = { formatMessage (
287
+ isSideEditorOpen
288
+ ?{
289
+ id :'content-manager.preview.content.close-editor' ,
290
+ defaultMessage :'Close editor' ,
291
+ }
292
+ :{
293
+ id :'content-manager.preview.content.open-editor' ,
294
+ defaultMessage :'Open editor' ,
295
+ }
296
+ ) }
297
+ onClick = { ( ) => setIsSideEditorOpen ( ( prev ) => ! prev ) }
298
+ >
299
+ < AnimatedArrow $isSideEditorOpen = { isSideEditorOpen } />
300
+ </ IconButton >
301
+ ) }
302
+ < Flex justifyContent = "center" flex = { 1 } >
303
+ < SingleSelect
304
+ value = { deviceName }
305
+ onChange = { ( name ) => setDeviceName ( name . toString ( ) ) }
306
+ aria-label = { formatMessage ( {
307
+ id :'content-manager.preview.device.select' ,
308
+ defaultMessage :'Select device type' ,
309
+ } ) }
310
+ >
311
+ { DEVICES . map ( ( deviceOption ) => (
312
+ < SingleSelectOption key = { deviceOption . name } value = { deviceOption . name } >
313
+ { formatMessage ( deviceOption . label ) }
314
+ </ SingleSelectOption >
315
+ ) ) }
316
+ </ SingleSelect >
317
+ </ Flex >
318
+ </ Flex >
319
+ < Flex direction = "column" justifyContent = "center" background = "neutral0" flex = { 1 } >
320
+ < Box
321
+ data-testid = "preview-iframe"
322
+ ref = { iframeRef }
323
+ src = { previewUrl }
324
+ /**
325
+ * For some reason, changing an iframe's src tag causes the browser to add a new item in the
326
+ * history stack. This is an issue for us as it means clicking the back button will not let us
327
+ * go back to the edit view. To fix it, we need to trick the browser into thinking this is a
328
+ * different iframe when the preview URL changes. So we set a key prop to force React
329
+ * to mount a different node when the src changes.
330
+ */
331
+ key = { previewUrl }
332
+ title = { formatMessage ( {
333
+ id :'content-manager.preview.panel.title' ,
334
+ defaultMessage :'Preview' ,
335
+ } ) }
336
+ width = { device . width }
337
+ height = { device . height }
338
+ borderWidth = { 0 }
339
+ tag = "iframe"
340
+ />
341
+ </ Flex >
342
+ </ Flex >
277
343
</ Flex >
278
344
</ Flex >
279
345
) }