Skip to content

Commit c795006

Browse files
Move WordPress & Gutenberg PR Preview to Playground website (#1938)
## Motivation for the change, related issues #1655 (comment) I moved wordpress.html and gutenberg.html to Playground itself. By the way, I also made a mini appearance refactor for the modal, based on Figma. And I used components from WordPress packages. ## Implementation details - [x] Add modals with input to fill PR number - [x] Move & unify logic to fetch PR by number - [x] Error handling - [x] Appearance refactor for Modals - [x] Implement new Modal to other components - [x] Fetch PR number directly from query - [x] New Dropdown Menu in main sidebar (thanks to this, we can import playground when we don't have any active one) ## Testing Instructions (or ideally a Blueprint) - Any blueprint - Go to Playground settings - Click three dots next to ~~Homepage button~~ future Logo place - Select Preview a WordPress PR or Preview a Gutenberg PR It is also possible to open modal via URL params: /?modal=preview-pr-wordpress or /?modal=preview-pr-gutenberg ![image](https://github.com/user-attachments/assets/56bf1e59-2186-47b0-8991-b55f32237289) ![image](https://github.com/user-attachments/assets/821703ed-0fe4-4a7d-a266-2981a312370c) ![image](https://github.com/user-attachments/assets/765f3b91-c42a-4635-8e6b-00d4a631291b) --------- Co-authored-by: Brandon Payton <[email protected]>
1 parent 07200bf commit c795006

File tree

30 files changed

+575
-280
lines changed

30 files changed

+575
-280
lines changed

packages/playground/website/src/components/browser-chrome/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
useAppDispatch,
1111
} from '../../lib/state/redux/store';
1212
import { SyncLocalFilesButton } from '../sync-local-files-button';
13-
import { Dropdown, Icon, Modal } from '@wordpress/components';
13+
import { Dropdown, Icon } from '@wordpress/components';
14+
import { Modal } from '../../components/modal';
1415
import { cog } from '@wordpress/icons';
1516
import Button from '../button';
1617
import { ActiveSiteSettingsForm } from '../site-manager/site-settings-form';

packages/playground/website/src/components/error-report-modal/index.tsx

Lines changed: 28 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
import { useEffect, useState } from 'react';
2-
import Modal from '../modal';
1+
import React, { useEffect, useState } from 'react';
32
import { logger } from '@php-wasm/logger';
4-
import { Button, TextareaControl, TextControl } from '@wordpress/components';
5-
6-
import css from './style.module.css';
7-
3+
import { TextareaControl, TextControl } from '@wordpress/components';
84
import { Blueprint } from '@wp-playground/blueprints';
95
import { useDispatch } from 'react-redux';
106
import {
@@ -13,6 +9,8 @@ import {
139
useAppSelector,
1410
} from '../../lib/state/redux/store';
1511
import { setActiveModal } from '../../lib/state/redux/slice-ui';
12+
import { Modal } from '../../components/modal';
13+
import ModalButtons from '../modal/modal-buttons';
1614

1715
export function ErrorReportModal(props: { blueprint: Blueprint }) {
1816
const activeModal = useAppSelector(
@@ -157,49 +155,32 @@ export function ErrorReportModal(props: { blueprint: Blueprint }) {
157155
}
158156

159157
return (
160-
<Modal isOpen={true} onRequestClose={onClose}>
161-
<header
162-
className={css.errorReportModalHeader}
163-
aria-label="Error reporting form header"
164-
>
165-
<h2>{getTitle()}</h2>
166-
<p>{getContent()}</p>
167-
</header>
158+
<Modal title={getTitle()} onRequestClose={onClose} small>
159+
<p>{getContent()}</p>
168160
{showForm() && (
169161
<>
170-
<main>
171-
<TextareaControl
172-
label="How can we recreate this error?"
173-
help="Describe what caused the error and how can we recreate it."
174-
value={text}
175-
onChange={setText}
176-
className={css.errorReportModalTextarea}
177-
required={true}
178-
/>
179-
<TextareaControl
180-
label="Logs"
181-
value={logs}
182-
onChange={setLogs}
183-
className={css.errorReportModalTextarea}
184-
/>
185-
186-
<TextControl
187-
label="Url"
188-
value={url}
189-
onChange={setUrl}
190-
/>
191-
</main>
192-
<footer className={css.errorReportModalFooter}>
193-
<Button
194-
variant="primary"
195-
onClick={onSubmit}
196-
isBusy={loading}
197-
disabled={loading || !text}
198-
>
199-
Report error
200-
</Button>
201-
<Button onClick={onClose}>Cancel</Button>
202-
</footer>
162+
<TextareaControl
163+
label="How can we recreate this error?"
164+
help="Describe what caused the error and how can we recreate it."
165+
value={text}
166+
onChange={setText}
167+
required={true}
168+
/>
169+
<TextareaControl
170+
label="Logs"
171+
value={logs}
172+
onChange={setLogs}
173+
/>
174+
175+
<TextControl label="Url" value={url} onChange={setUrl} />
176+
177+
<ModalButtons
178+
areBusy={loading}
179+
areDisabled={loading || !text}
180+
onCancel={onClose}
181+
onSubmit={onSubmit}
182+
submitText="Report error"
183+
/>
203184
</>
204185
)}
205186
</Modal>

packages/playground/website/src/components/error-report-modal/style.module.css

Lines changed: 0 additions & 19 deletions
This file was deleted.

packages/playground/website/src/components/import-form/modal.tsx renamed to packages/playground/website/src/components/import-form-modal/index.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import React from 'react';
2+
import { useDispatch } from 'react-redux';
13
import { usePlaygroundClient } from '../../lib/use-playground-client';
2-
import ImportForm from './index';
3-
import Modal from '../modal';
4+
import ImportForm from '../import-form/index';
5+
import { Modal } from '../../components/modal';
46
import { setActiveModal } from '../../lib/state/redux/slice-ui';
57
import { PlaygroundDispatch } from '../../lib/state/redux/store';
6-
import { useDispatch } from 'react-redux';
78

89
export const ImportFormModal = () => {
910
const playground = usePlaygroundClient();
@@ -23,7 +24,7 @@ export const ImportFormModal = () => {
2324

2425
return (
2526
<Modal
26-
isOpen={!!playground}
27+
title="Import Playground"
2728
contentLabel='This is a dialog window which overlays the main content of the
2829
page. The modal begins with a heading 2 called "Import
2930
Playground". Pressing the Close Import Window will close

packages/playground/website/src/components/import-form/index.tsx

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import React from 'react';
22
import { useRef, useState } from 'react';
33
import { PlaygroundClient, importWordPressFiles } from '@wp-playground/client';
4-
5-
import css from './style.module.css';
64
import forms from '../../forms.module.css';
7-
import Button from '../button';
85
import { logger } from '@php-wasm/logger';
6+
import ModalButtons from '../modal/modal-buttons';
97

108
interface ImportFormProps {
119
playground: PlaygroundClient;
@@ -18,7 +16,6 @@ export default function ImportForm({
1816
onImported,
1917
onClose,
2018
}: ImportFormProps) {
21-
const form = useRef<any>();
2219
const fileInputRef = useRef<any>();
2320
const [file, setFile] = useState<File | null>(null);
2421
const [error, setError] = useState<string>('');
@@ -46,11 +43,8 @@ export default function ImportForm({
4643
}
4744

4845
return (
49-
<form id="import-playground-form" ref={form} onSubmit={handleSubmit}>
50-
<h2 tabIndex={0} style={{ marginTop: 0, textAlign: 'center' }}>
51-
Import Playground
52-
</h2>
53-
<p className={css.modalText}>
46+
<>
47+
<p>
5448
You may replace the current WordPress Playground site with a
5549
previously exported one.
5650
</p>
@@ -64,17 +58,13 @@ export default function ImportForm({
6458
accept="application/zip"
6559
/>
6660
</div>
67-
<div className={forms.submitRow}>
68-
<Button
69-
id="import-submit--btn"
70-
className={forms.btn}
71-
disabled={!file}
72-
variant="primary"
73-
size="large"
74-
>
75-
Import
76-
</Button>
77-
</div>
78-
</form>
61+
62+
<ModalButtons
63+
areDisabled={!file}
64+
onCancel={onClose}
65+
onSubmit={handleSubmit}
66+
submitText="Import"
67+
/>
68+
</>
7969
);
8070
}

packages/playground/website/src/components/layout/index.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ import {
3232
setActiveModal,
3333
setSiteManagerOpen,
3434
} from '../../lib/state/redux/slice-ui';
35-
import { ImportFormModal } from '../import-form/modal';
35+
import { ImportFormModal } from '../import-form-modal';
36+
import { PreviewPRModal } from '../../github/preview-pr';
3637

3738
acquireOAuthTokenIfNeeded();
3839

@@ -42,7 +43,9 @@ export const modalSlugs = {
4243
START_ERROR: 'start-error',
4344
IMPORT_FORM: 'import-form',
4445
GITHUB_IMPORT: 'github-import',
45-
GITHUB_EXPORT: 'github-export'
46+
GITHUB_EXPORT: 'github-export',
47+
PREVIEW_PR_WP: 'preview-pr-wordpress',
48+
PREVIEW_PR_GUTENBERG: 'preview-pr-gutenberg',
4649
}
4750

4851
const displayMode = getDisplayModeFromQuery();
@@ -178,6 +181,10 @@ function Modals(blueprint: Blueprint) {
178181
return <StartErrorModal />;
179182
} else if (currentModal === modalSlugs.IMPORT_FORM) {
180183
return <ImportFormModal />;
184+
} else if (currentModal === modalSlugs.PREVIEW_PR_WP) {
185+
return <PreviewPRModal target="wordpress" />;
186+
} else if (currentModal === modalSlugs.PREVIEW_PR_GUTENBERG) {
187+
return <PreviewPRModal target="gutenberg" />;
181188
} else if (currentModal === modalSlugs.GITHUB_IMPORT) {
182189
return <GithubImportModal
183190
onImported={({

packages/playground/website/src/components/log-modal/index.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { useEffect, useState } from 'react';
2-
import Modal from '../modal';
32
import { logEventType, logger } from '@php-wasm/logger';
43

54
import classNames from 'classnames';
65
import css from './style.module.css';
7-
6+
import { Modal } from '../modal';
87
import { TextControl } from '@wordpress/components';
98
import {
109
PlaygroundDispatch,
@@ -23,16 +22,9 @@ export function LogModal(props: { description?: JSX.Element; title?: string }) {
2322
dispatch(setActiveModal(null));
2423
}
2524

26-
const styles = {
27-
content: { width: 800 },
28-
};
29-
3025
return (
31-
<Modal isOpen={true} onRequestClose={onClose} styles={styles}>
32-
<header aria-label="Error logs list header">
33-
<h2>{props.title || 'Error Logs'}</h2>
34-
{props.description}
35-
</header>
26+
<Modal title={props.title || 'Error Logs'} onRequestClose={onClose}>
27+
<div>{props.description}</div>
3628
<SiteLogs key={activeModal} className={css.logsInsideModal} />
3729
</Modal>
3830
);
@@ -91,7 +83,7 @@ export function SiteLogs({ className }: { className?: string }) {
9183
No matching logs found.
9284
</div>
9385
) : (
94-
<div className={css.logEmptyPlaceholder}>
86+
<div>
9587
Error logs for Playground, WordPress, and PHP will show
9688
up here when something goes wrong.
9789
<br />

packages/playground/website/src/components/log-modal/style.module.css

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,6 @@
2525
flex-grow: 1;
2626
}
2727

28-
.log__list {
29-
padding-bottom: 20px;
30-
flex-grow: 1;
31-
max-height: 100%;
32-
}
33-
34-
.log__empty_placeholder {
35-
padding-bottom: 20px;
36-
}
37-
3828
.log__entry {
3929
font-family: monospace;
4030
word-wrap: break-word;
Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,24 @@
1-
import ReactModal from 'react-modal';
1+
import React from 'react';
2+
import { Modal as WordPressModal } from '@wordpress/components';
3+
import type { ModalProps as WordPressModalProps } from '@wordpress/components/build-types/modal/types';
4+
import classNames from 'classnames';
25
import css from './style.module.css';
36

4-
ReactModal.setAppElement('#root');
5-
6-
interface ModalProps extends ReactModal.Props {
7-
styles?: ReactModal.Styles;
7+
interface ModalProps extends WordPressModalProps {
8+
small?: boolean;
89
}
9-
export const defaultStyles: ReactModal.Styles = {
10-
content: {
11-
top: '50%',
12-
left: '50%',
13-
right: 'auto',
14-
bottom: 'auto',
15-
marginRight: '-50%',
16-
transform: 'translate(-50%, -50%)',
17-
width: 400,
18-
maxWidth: '100vw',
19-
zIndex: 200,
20-
textAlign: 'center',
21-
color: '#000',
22-
border: '#000 1px solid',
23-
borderRadius: '6px',
24-
background: '#fff',
25-
},
26-
overlay: {
27-
background: '#1e2327d0',
28-
zIndex: 10,
29-
},
30-
};
31-
export default function Modal(props: ModalProps) {
32-
const styles = {
33-
overlay: { ...defaultStyles.overlay, ...props.styles?.overlay },
34-
content: { ...defaultStyles.content, ...props.styles?.content },
35-
};
10+
export function Modal({ small, className, children, ...rest }: ModalProps) {
11+
const modalClass = classNames(
12+
css.modal,
13+
{
14+
[css.modalSmall]: small,
15+
},
16+
className
17+
);
18+
3619
return (
37-
<ReactModal style={styles} {...props}>
38-
<div className={css.modalInner} id="modal-content">
39-
<button
40-
id="import-close-modal--btn"
41-
onClick={props.onRequestClose}
42-
className={`${css.btn} ${css.btnClose}`}
43-
aria-label="Close"
44-
title="Close"
45-
>
46-
<svg
47-
xmlns="http://www.w3.org/2000/svg"
48-
viewBox="0 0 24 24"
49-
width="32"
50-
height="32"
51-
aria-hidden="true"
52-
focusable="false"
53-
>
54-
<path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path>
55-
</svg>
56-
</button>
57-
{props.children}
58-
</div>
59-
</ReactModal>
20+
<WordPressModal className={modalClass} {...rest}>
21+
{children}
22+
</WordPressModal>
6023
);
6124
}

0 commit comments

Comments
 (0)