@wordpress/server-side-render 6.3.0 → 6.4.1-next.46f643fa0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,53 +1,17 @@
1
- /**
2
- * External dependencies
3
- */
4
- import fastDeepEqual from 'fast-deep-equal/es6';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
9
- import { useDebounce, usePrevious } from '@wordpress/compose';
10
- import { RawHTML, useCallback, useEffect, useLayoutEffect, useRef, useState } from '@wordpress/element';
4
+ import { RawHTML, useEffect, useState, useRef, useMemo } from '@wordpress/element';
11
5
  import { __, sprintf } from '@wordpress/i18n';
12
- import apiFetch from '@wordpress/api-fetch';
13
- import { addQueryArgs } from '@wordpress/url';
14
6
  import { Placeholder, Spinner } from '@wordpress/components';
15
- import { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';
7
+ import { useSelect } from '@wordpress/data';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { useServerSideRender } from './hook';
16
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
14
  const EMPTY_OBJECT = {};
18
- export function rendererPath(block, attributes = null, urlQueryArgs = {}) {
19
- return addQueryArgs(`/wp/v2/block-renderer/${block}`, {
20
- context: 'edit',
21
- ...(null !== attributes ? {
22
- attributes
23
- } : {}),
24
- ...urlQueryArgs
25
- });
26
- }
27
- export function removeBlockSupportAttributes(attributes) {
28
- const {
29
- backgroundColor,
30
- borderColor,
31
- fontFamily,
32
- fontSize,
33
- gradient,
34
- textColor,
35
- className,
36
- ...restAttributes
37
- } = attributes;
38
- const {
39
- border,
40
- color,
41
- elements,
42
- spacing,
43
- typography,
44
- ...restStyles
45
- } = attributes?.style || EMPTY_OBJECT;
46
- return {
47
- ...restAttributes,
48
- style: restStyles
49
- };
50
- }
51
15
  function DefaultEmptyResponsePlaceholder({
52
16
  className
53
17
  }) {
@@ -57,12 +21,12 @@ function DefaultEmptyResponsePlaceholder({
57
21
  });
58
22
  }
59
23
  function DefaultErrorResponsePlaceholder({
60
- response,
24
+ message,
61
25
  className
62
26
  }) {
63
27
  const errorMessage = sprintf(
64
28
  // translators: %s: error message describing the problem
65
- __('Error loading block: %s'), response.errorMsg);
29
+ __('Error loading block: %s'), message);
66
30
  return /*#__PURE__*/_jsx(Placeholder, {
67
31
  className: className,
68
32
  children: errorMessage
@@ -100,118 +64,118 @@ function DefaultLoadingResponsePlaceholder({
100
64
  })]
101
65
  });
102
66
  }
103
- export default function ServerSideRender(props) {
67
+ export function ServerSideRender(props) {
68
+ const prevContentRef = useRef('');
104
69
  const {
105
70
  className,
106
71
  EmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder,
107
72
  ErrorResponsePlaceholder = DefaultErrorResponsePlaceholder,
108
- LoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder
73
+ LoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder,
74
+ ...restProps
109
75
  } = props;
110
- const isMountedRef = useRef(false);
111
- const fetchRequestRef = useRef();
112
- const [response, setResponse] = useState(null);
113
- const prevProps = usePrevious(props);
114
- const [isLoading, setIsLoading] = useState(false);
115
- const latestPropsRef = useRef(props);
116
- useLayoutEffect(() => {
117
- latestPropsRef.current = props;
118
- }, [props]);
119
- const fetchData = useCallback(() => {
120
- var _sanitizedAttributes, _sanitizedAttributes2;
121
- if (!isMountedRef.current) {
122
- return;
123
- }
124
- const {
125
- attributes,
126
- block,
127
- skipBlockSupportAttributes = false,
128
- httpMethod = 'GET',
129
- urlQueryArgs
130
- } = latestPropsRef.current;
131
- setIsLoading(true);
132
- let sanitizedAttributes = attributes && __experimentalSanitizeBlockAttributes(block, attributes);
133
- if (skipBlockSupportAttributes) {
134
- sanitizedAttributes = removeBlockSupportAttributes(sanitizedAttributes);
135
- }
136
-
137
- // If httpMethod is 'POST', send the attributes in the request body instead of the URL.
138
- // This allows sending a larger attributes object than in a GET request, where the attributes are in the URL.
139
- const isPostRequest = 'POST' === httpMethod;
140
- const urlAttributes = isPostRequest ? null : (_sanitizedAttributes = sanitizedAttributes) !== null && _sanitizedAttributes !== void 0 ? _sanitizedAttributes : null;
141
- const path = rendererPath(block, urlAttributes, urlQueryArgs);
142
- const data = isPostRequest ? {
143
- attributes: (_sanitizedAttributes2 = sanitizedAttributes) !== null && _sanitizedAttributes2 !== void 0 ? _sanitizedAttributes2 : null
144
- } : null;
145
-
146
- // Store the latest fetch request so that when we process it, we can
147
- // check if it is the current request, to avoid race conditions on slow networks.
148
- const fetchRequest = fetchRequestRef.current = apiFetch({
149
- path,
150
- data,
151
- method: isPostRequest ? 'POST' : 'GET'
152
- }).then(fetchResponse => {
153
- if (isMountedRef.current && fetchRequest === fetchRequestRef.current && fetchResponse) {
154
- setResponse(fetchResponse.rendered);
155
- }
156
- }).catch(error => {
157
- if (isMountedRef.current && fetchRequest === fetchRequestRef.current) {
158
- setResponse({
159
- error: true,
160
- errorMsg: error.message
161
- });
162
- }
163
- }).finally(() => {
164
- if (isMountedRef.current && fetchRequest === fetchRequestRef.current) {
165
- setIsLoading(false);
166
- }
167
- });
168
- return fetchRequest;
169
- }, []);
170
- const debouncedFetchData = useDebounce(fetchData, 500);
76
+ const {
77
+ content,
78
+ status,
79
+ error
80
+ } = useServerSideRender(restProps);
171
81
 
172
- // When the component unmounts, set isMountedRef to false. This will
173
- // let the async fetch callbacks know when to stop.
174
- useEffect(() => {
175
- isMountedRef.current = true;
176
- return () => {
177
- isMountedRef.current = false;
178
- };
179
- }, []);
82
+ // Store the previous successful HTML response to show while loading.
180
83
  useEffect(() => {
181
- // Don't debounce the first fetch. This ensures that the first render
182
- // shows data as soon as possible.
183
- if (prevProps === undefined) {
184
- fetchData();
185
- } else if (!fastDeepEqual(prevProps, props)) {
186
- debouncedFetchData();
84
+ if (content) {
85
+ prevContentRef.current = content;
187
86
  }
188
- });
189
- const hasResponse = !!response;
190
- const hasEmptyResponse = response === '';
191
- const hasError = !!response?.error;
192
- if (isLoading) {
87
+ }, [content]);
88
+ if (status === 'loading') {
193
89
  return /*#__PURE__*/_jsx(LoadingResponsePlaceholder, {
194
90
  ...props,
195
- children: hasResponse && !hasError && /*#__PURE__*/_jsx(RawHTML, {
91
+ children: !!prevContentRef.current && /*#__PURE__*/_jsx(RawHTML, {
196
92
  className: className,
197
- children: response
93
+ children: prevContentRef.current
198
94
  })
199
95
  });
200
96
  }
201
- if (hasEmptyResponse || !hasResponse) {
97
+ if (status === 'success' && !content) {
202
98
  return /*#__PURE__*/_jsx(EmptyResponsePlaceholder, {
203
99
  ...props
204
100
  });
205
101
  }
206
- if (hasError) {
102
+ if (status === 'error') {
207
103
  return /*#__PURE__*/_jsx(ErrorResponsePlaceholder, {
208
- response: response,
104
+ message: error,
209
105
  ...props
210
106
  });
211
107
  }
212
108
  return /*#__PURE__*/_jsx(RawHTML, {
213
109
  className: className,
214
- children: response
110
+ children: content
111
+ });
112
+ }
113
+
114
+ /**
115
+ * A component that renders server-side content for blocks.
116
+ *
117
+ * Note: URL query will include the current post ID when applicable.
118
+ * This is useful for blocks that depend on the context of the current post for rendering.
119
+ *
120
+ * @example
121
+ * ```jsx
122
+ * import { ServerSideRender } from '@wordpress/server-side-render';
123
+ * // Legacy import for WordPress 6.8 and earlier
124
+ * // import { default as ServerSideRender } from '@wordpress/server-side-render';
125
+ *
126
+ * function Example() {
127
+ * return (
128
+ * <ServerSideRender
129
+ * block="core/archives"
130
+ * attributes={ { showPostCounts: true } }
131
+ * urlQueryArgs={ { customArg: 'value' } }
132
+ * className="custom-class"
133
+ * />
134
+ * );
135
+ * }
136
+ * ```
137
+ *
138
+ * @param {Object} props Component props.
139
+ * @param {string} props.block The identifier of the block to be serverside rendered.
140
+ * @param {Object} props.attributes The block attributes to be sent to the server for rendering.
141
+ * @param {string} [props.className] Additional classes to apply to the wrapper element.
142
+ * @param {string} [props.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'
143
+ * @param {Object} [props.urlQueryArgs] Additional query arguments to append to the request URL.
144
+ * @param {boolean} [props.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.
145
+ * @param {Function} [props.EmptyResponsePlaceholder] Component rendered when the API response is empty.
146
+ * @param {Function} [props.ErrorResponsePlaceholder] Component rendered when the API response is an error.
147
+ * @param {Function} [props.LoadingResponsePlaceholder] Component rendered while the API request is loading.
148
+ *
149
+ * @return {JSX.Element} The rendered server-side content.
150
+ */
151
+ export function ServerSideRenderWithPostId({
152
+ urlQueryArgs = EMPTY_OBJECT,
153
+ ...props
154
+ }) {
155
+ const currentPostId = useSelect(select => {
156
+ // FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.
157
+ // It is used by blocks that can be loaded into a *non-post* block editor.
158
+ // eslint-disable-next-line @wordpress/data-no-store-string-literals
159
+ const postId = select('core/editor')?.getCurrentPostId();
160
+
161
+ // For templates and template parts we use a custom ID format.
162
+ // Since they aren't real posts, we don't want to use their ID
163
+ // for server-side rendering. Since they use a string based ID,
164
+ // we can assume real post IDs are numbers.
165
+ return postId && typeof postId === 'number' ? postId : null;
166
+ }, []);
167
+ const newUrlQueryArgs = useMemo(() => {
168
+ if (!currentPostId) {
169
+ return urlQueryArgs;
170
+ }
171
+ return {
172
+ post_id: currentPostId,
173
+ ...urlQueryArgs
174
+ };
175
+ }, [currentPostId, urlQueryArgs]);
176
+ return /*#__PURE__*/_jsx(ServerSideRender, {
177
+ urlQueryArgs: newUrlQueryArgs,
178
+ ...props
215
179
  });
216
180
  }
217
181
  //# sourceMappingURL=server-side-render.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["fastDeepEqual","useDebounce","usePrevious","RawHTML","useCallback","useEffect","useLayoutEffect","useRef","useState","__","sprintf","apiFetch","addQueryArgs","Placeholder","Spinner","__experimentalSanitizeBlockAttributes","jsx","_jsx","jsxs","_jsxs","EMPTY_OBJECT","rendererPath","block","attributes","urlQueryArgs","context","removeBlockSupportAttributes","backgroundColor","borderColor","fontFamily","fontSize","gradient","textColor","className","restAttributes","border","color","elements","spacing","typography","restStyles","style","DefaultEmptyResponsePlaceholder","children","DefaultErrorResponsePlaceholder","response","errorMessage","errorMsg","DefaultLoadingResponsePlaceholder","showLoader","setShowLoader","timeout","setTimeout","clearTimeout","position","top","left","marginTop","marginLeft","opacity","ServerSideRender","props","EmptyResponsePlaceholder","ErrorResponsePlaceholder","LoadingResponsePlaceholder","isMountedRef","fetchRequestRef","setResponse","prevProps","isLoading","setIsLoading","latestPropsRef","current","fetchData","_sanitizedAttributes","_sanitizedAttributes2","skipBlockSupportAttributes","httpMethod","sanitizedAttributes","isPostRequest","urlAttributes","path","data","fetchRequest","method","then","fetchResponse","rendered","catch","error","message","finally","debouncedFetchData","undefined","hasResponse","hasEmptyResponse","hasError"],"sources":["@wordpress/server-side-render/src/server-side-render.js"],"sourcesContent":["/**\n * External dependencies\n */\nimport fastDeepEqual from 'fast-deep-equal/es6';\n\n/**\n * WordPress dependencies\n */\nimport { useDebounce, usePrevious } from '@wordpress/compose';\nimport {\n\tRawHTML,\n\tuseCallback,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n\tuseState,\n} from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\nimport { Placeholder, Spinner } from '@wordpress/components';\nimport { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';\n\nconst EMPTY_OBJECT = {};\n\nexport function rendererPath( block, attributes = null, urlQueryArgs = {} ) {\n\treturn addQueryArgs( `/wp/v2/block-renderer/${ block }`, {\n\t\tcontext: 'edit',\n\t\t...( null !== attributes ? { attributes } : {} ),\n\t\t...urlQueryArgs,\n\t} );\n}\n\nexport function removeBlockSupportAttributes( attributes ) {\n\tconst {\n\t\tbackgroundColor,\n\t\tborderColor,\n\t\tfontFamily,\n\t\tfontSize,\n\t\tgradient,\n\t\ttextColor,\n\t\tclassName,\n\t\t...restAttributes\n\t} = attributes;\n\n\tconst { border, color, elements, spacing, typography, ...restStyles } =\n\t\tattributes?.style || EMPTY_OBJECT;\n\n\treturn {\n\t\t...restAttributes,\n\t\tstyle: restStyles,\n\t};\n}\n\nfunction DefaultEmptyResponsePlaceholder( { className } ) {\n\treturn (\n\t\t<Placeholder className={ className }>\n\t\t\t{ __( 'Block rendered as empty.' ) }\n\t\t</Placeholder>\n\t);\n}\n\nfunction DefaultErrorResponsePlaceholder( { response, className } ) {\n\tconst errorMessage = sprintf(\n\t\t// translators: %s: error message describing the problem\n\t\t__( 'Error loading block: %s' ),\n\t\tresponse.errorMsg\n\t);\n\treturn <Placeholder className={ className }>{ errorMessage }</Placeholder>;\n}\n\nfunction DefaultLoadingResponsePlaceholder( { children } ) {\n\tconst [ showLoader, setShowLoader ] = useState( false );\n\n\tuseEffect( () => {\n\t\t// Schedule showing the Spinner after 1 second.\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetShowLoader( true );\n\t\t}, 1000 );\n\t\treturn () => clearTimeout( timeout );\n\t}, [] );\n\n\treturn (\n\t\t<div style={ { position: 'relative' } }>\n\t\t\t{ showLoader && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: '50%',\n\t\t\t\t\t\tleft: '50%',\n\t\t\t\t\t\tmarginTop: '-9px',\n\t\t\t\t\t\tmarginLeft: '-9px',\n\t\t\t\t\t} }\n\t\t\t\t>\n\t\t\t\t\t<Spinner />\n\t\t\t\t</div>\n\t\t\t) }\n\t\t\t<div style={ { opacity: showLoader ? '0.3' : 1 } }>\n\t\t\t\t{ children }\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default function ServerSideRender( props ) {\n\tconst {\n\t\tclassName,\n\t\tEmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder,\n\t\tErrorResponsePlaceholder = DefaultErrorResponsePlaceholder,\n\t\tLoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder,\n\t} = props;\n\n\tconst isMountedRef = useRef( false );\n\tconst fetchRequestRef = useRef();\n\tconst [ response, setResponse ] = useState( null );\n\tconst prevProps = usePrevious( props );\n\tconst [ isLoading, setIsLoading ] = useState( false );\n\tconst latestPropsRef = useRef( props );\n\n\tuseLayoutEffect( () => {\n\t\tlatestPropsRef.current = props;\n\t}, [ props ] );\n\n\tconst fetchData = useCallback( () => {\n\t\tif ( ! isMountedRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst {\n\t\t\tattributes,\n\t\t\tblock,\n\t\t\tskipBlockSupportAttributes = false,\n\t\t\thttpMethod = 'GET',\n\t\t\turlQueryArgs,\n\t\t} = latestPropsRef.current;\n\n\t\tsetIsLoading( true );\n\n\t\tlet sanitizedAttributes =\n\t\t\tattributes &&\n\t\t\t__experimentalSanitizeBlockAttributes( block, attributes );\n\n\t\tif ( skipBlockSupportAttributes ) {\n\t\t\tsanitizedAttributes =\n\t\t\t\tremoveBlockSupportAttributes( sanitizedAttributes );\n\t\t}\n\n\t\t// If httpMethod is 'POST', send the attributes in the request body instead of the URL.\n\t\t// This allows sending a larger attributes object than in a GET request, where the attributes are in the URL.\n\t\tconst isPostRequest = 'POST' === httpMethod;\n\t\tconst urlAttributes = isPostRequest\n\t\t\t? null\n\t\t\t: sanitizedAttributes ?? null;\n\t\tconst path = rendererPath( block, urlAttributes, urlQueryArgs );\n\t\tconst data = isPostRequest\n\t\t\t? { attributes: sanitizedAttributes ?? null }\n\t\t\t: null;\n\n\t\t// Store the latest fetch request so that when we process it, we can\n\t\t// check if it is the current request, to avoid race conditions on slow networks.\n\t\tconst fetchRequest = ( fetchRequestRef.current = apiFetch( {\n\t\t\tpath,\n\t\t\tdata,\n\t\t\tmethod: isPostRequest ? 'POST' : 'GET',\n\t\t} )\n\t\t\t.then( ( fetchResponse ) => {\n\t\t\t\tif (\n\t\t\t\t\tisMountedRef.current &&\n\t\t\t\t\tfetchRequest === fetchRequestRef.current &&\n\t\t\t\t\tfetchResponse\n\t\t\t\t) {\n\t\t\t\t\tsetResponse( fetchResponse.rendered );\n\t\t\t\t}\n\t\t\t} )\n\t\t\t.catch( ( error ) => {\n\t\t\t\tif (\n\t\t\t\t\tisMountedRef.current &&\n\t\t\t\t\tfetchRequest === fetchRequestRef.current\n\t\t\t\t) {\n\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\terror: true,\n\t\t\t\t\t\terrorMsg: error.message,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t} )\n\t\t\t.finally( () => {\n\t\t\t\tif (\n\t\t\t\t\tisMountedRef.current &&\n\t\t\t\t\tfetchRequest === fetchRequestRef.current\n\t\t\t\t) {\n\t\t\t\t\tsetIsLoading( false );\n\t\t\t\t}\n\t\t\t} ) );\n\n\t\treturn fetchRequest;\n\t}, [] );\n\n\tconst debouncedFetchData = useDebounce( fetchData, 500 );\n\n\t// When the component unmounts, set isMountedRef to false. This will\n\t// let the async fetch callbacks know when to stop.\n\tuseEffect( () => {\n\t\tisMountedRef.current = true;\n\t\treturn () => {\n\t\t\tisMountedRef.current = false;\n\t\t};\n\t}, [] );\n\n\tuseEffect( () => {\n\t\t// Don't debounce the first fetch. This ensures that the first render\n\t\t// shows data as soon as possible.\n\t\tif ( prevProps === undefined ) {\n\t\t\tfetchData();\n\t\t} else if ( ! fastDeepEqual( prevProps, props ) ) {\n\t\t\tdebouncedFetchData();\n\t\t}\n\t} );\n\n\tconst hasResponse = !! response;\n\tconst hasEmptyResponse = response === '';\n\tconst hasError = !! response?.error;\n\n\tif ( isLoading ) {\n\t\treturn (\n\t\t\t<LoadingResponsePlaceholder { ...props }>\n\t\t\t\t{ hasResponse && ! hasError && (\n\t\t\t\t\t<RawHTML className={ className }>{ response }</RawHTML>\n\t\t\t\t) }\n\t\t\t</LoadingResponsePlaceholder>\n\t\t);\n\t}\n\n\tif ( hasEmptyResponse || ! hasResponse ) {\n\t\treturn <EmptyResponsePlaceholder { ...props } />;\n\t}\n\n\tif ( hasError ) {\n\t\treturn <ErrorResponsePlaceholder response={ response } { ...props } />;\n\t}\n\n\treturn <RawHTML className={ className }>{ response }</RawHTML>;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["RawHTML","useEffect","useState","useRef","useMemo","__","sprintf","Placeholder","Spinner","useSelect","useServerSideRender","jsx","_jsx","jsxs","_jsxs","EMPTY_OBJECT","DefaultEmptyResponsePlaceholder","className","children","DefaultErrorResponsePlaceholder","message","errorMessage","DefaultLoadingResponsePlaceholder","showLoader","setShowLoader","timeout","setTimeout","clearTimeout","style","position","top","left","marginTop","marginLeft","opacity","ServerSideRender","props","prevContentRef","EmptyResponsePlaceholder","ErrorResponsePlaceholder","LoadingResponsePlaceholder","restProps","content","status","error","current","ServerSideRenderWithPostId","urlQueryArgs","currentPostId","select","postId","getCurrentPostId","newUrlQueryArgs","post_id"],"sources":["@wordpress/server-side-render/src/server-side-render.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport {\n\tRawHTML,\n\tuseEffect,\n\tuseState,\n\tuseRef,\n\tuseMemo,\n} from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport { Placeholder, Spinner } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport { useServerSideRender } from './hook';\n\nconst EMPTY_OBJECT = {};\n\nfunction DefaultEmptyResponsePlaceholder( { className } ) {\n\treturn (\n\t\t<Placeholder className={ className }>\n\t\t\t{ __( 'Block rendered as empty.' ) }\n\t\t</Placeholder>\n\t);\n}\n\nfunction DefaultErrorResponsePlaceholder( { message, className } ) {\n\tconst errorMessage = sprintf(\n\t\t// translators: %s: error message describing the problem\n\t\t__( 'Error loading block: %s' ),\n\t\tmessage\n\t);\n\treturn <Placeholder className={ className }>{ errorMessage }</Placeholder>;\n}\n\nfunction DefaultLoadingResponsePlaceholder( { children } ) {\n\tconst [ showLoader, setShowLoader ] = useState( false );\n\n\tuseEffect( () => {\n\t\t// Schedule showing the Spinner after 1 second.\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetShowLoader( true );\n\t\t}, 1000 );\n\t\treturn () => clearTimeout( timeout );\n\t}, [] );\n\n\treturn (\n\t\t<div style={ { position: 'relative' } }>\n\t\t\t{ showLoader && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: '50%',\n\t\t\t\t\t\tleft: '50%',\n\t\t\t\t\t\tmarginTop: '-9px',\n\t\t\t\t\t\tmarginLeft: '-9px',\n\t\t\t\t\t} }\n\t\t\t\t>\n\t\t\t\t\t<Spinner />\n\t\t\t\t</div>\n\t\t\t) }\n\t\t\t<div style={ { opacity: showLoader ? '0.3' : 1 } }>\n\t\t\t\t{ children }\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport function ServerSideRender( props ) {\n\tconst prevContentRef = useRef( '' );\n\tconst {\n\t\tclassName,\n\t\tEmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder,\n\t\tErrorResponsePlaceholder = DefaultErrorResponsePlaceholder,\n\t\tLoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder,\n\t\t...restProps\n\t} = props;\n\n\tconst { content, status, error } = useServerSideRender( restProps );\n\n\t// Store the previous successful HTML response to show while loading.\n\tuseEffect( () => {\n\t\tif ( content ) {\n\t\t\tprevContentRef.current = content;\n\t\t}\n\t}, [ content ] );\n\n\tif ( status === 'loading' ) {\n\t\treturn (\n\t\t\t<LoadingResponsePlaceholder { ...props }>\n\t\t\t\t{ !! prevContentRef.current && (\n\t\t\t\t\t<RawHTML className={ className }>\n\t\t\t\t\t\t{ prevContentRef.current }\n\t\t\t\t\t</RawHTML>\n\t\t\t\t) }\n\t\t\t</LoadingResponsePlaceholder>\n\t\t);\n\t}\n\n\tif ( status === 'success' && ! content ) {\n\t\treturn <EmptyResponsePlaceholder { ...props } />;\n\t}\n\n\tif ( status === 'error' ) {\n\t\treturn <ErrorResponsePlaceholder message={ error } { ...props } />;\n\t}\n\n\treturn <RawHTML className={ className }>{ content }</RawHTML>;\n}\n\n/**\n * A component that renders server-side content for blocks.\n *\n * Note: URL query will include the current post ID when applicable.\n * This is useful for blocks that depend on the context of the current post for rendering.\n *\n * @example\n * ```jsx\n * import { ServerSideRender } from '@wordpress/server-side-render';\n * // Legacy import for WordPress 6.8 and earlier\n * // import { default as ServerSideRender } from '@wordpress/server-side-render';\n *\n * function Example() {\n * return (\n * <ServerSideRender\n * block=\"core/archives\"\n * attributes={ { showPostCounts: true } }\n * urlQueryArgs={ { customArg: 'value' } }\n * className=\"custom-class\"\n * />\n * );\n * }\n * ```\n *\n * @param {Object} props Component props.\n * @param {string} props.block The identifier of the block to be serverside rendered.\n * @param {Object} props.attributes The block attributes to be sent to the server for rendering.\n * @param {string} [props.className] Additional classes to apply to the wrapper element.\n * @param {string} [props.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'\n * @param {Object} [props.urlQueryArgs] Additional query arguments to append to the request URL.\n * @param {boolean} [props.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.\n * @param {Function} [props.EmptyResponsePlaceholder] Component rendered when the API response is empty.\n * @param {Function} [props.ErrorResponsePlaceholder] Component rendered when the API response is an error.\n * @param {Function} [props.LoadingResponsePlaceholder] Component rendered while the API request is loading.\n *\n * @return {JSX.Element} The rendered server-side content.\n */\nexport function ServerSideRenderWithPostId( {\n\turlQueryArgs = EMPTY_OBJECT,\n\t...props\n} ) {\n\tconst currentPostId = useSelect( ( select ) => {\n\t\t// FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.\n\t\t// It is used by blocks that can be loaded into a *non-post* block editor.\n\t\t// eslint-disable-next-line @wordpress/data-no-store-string-literals\n\t\tconst postId = select( 'core/editor' )?.getCurrentPostId();\n\n\t\t// For templates and template parts we use a custom ID format.\n\t\t// Since they aren't real posts, we don't want to use their ID\n\t\t// for server-side rendering. Since they use a string based ID,\n\t\t// we can assume real post IDs are numbers.\n\t\treturn postId && typeof postId === 'number' ? postId : null;\n\t}, [] );\n\n\tconst newUrlQueryArgs = useMemo( () => {\n\t\tif ( ! currentPostId ) {\n\t\t\treturn urlQueryArgs;\n\t\t}\n\t\treturn {\n\t\t\tpost_id: currentPostId,\n\t\t\t...urlQueryArgs,\n\t\t};\n\t}, [ currentPostId, urlQueryArgs ] );\n\n\treturn <ServerSideRender urlQueryArgs={ newUrlQueryArgs } { ...props } />;\n}\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/server-side-render",
3
- "version": "6.3.0",
3
+ "version": "6.4.1-next.46f643fa0.0",
4
4
  "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -29,16 +29,15 @@
29
29
  "wpScript": true,
30
30
  "dependencies": {
31
31
  "@babel/runtime": "7.25.7",
32
- "@wordpress/api-fetch": "^7.27.0",
33
- "@wordpress/blocks": "^15.0.0",
34
- "@wordpress/components": "^30.0.0",
35
- "@wordpress/compose": "^7.27.0",
36
- "@wordpress/data": "^10.27.0",
37
- "@wordpress/deprecated": "^4.27.0",
38
- "@wordpress/element": "^6.27.0",
39
- "@wordpress/i18n": "^6.0.0",
40
- "@wordpress/url": "^4.27.0",
41
- "fast-deep-equal": "^3.1.3"
32
+ "@wordpress/api-fetch": "^7.27.1-next.46f643fa0.0",
33
+ "@wordpress/blocks": "^15.0.1-next.46f643fa0.0",
34
+ "@wordpress/components": "^30.1.1-next.46f643fa0.0",
35
+ "@wordpress/compose": "^7.27.1-next.46f643fa0.0",
36
+ "@wordpress/data": "^10.27.1-next.46f643fa0.0",
37
+ "@wordpress/deprecated": "^4.27.1-next.46f643fa0.0",
38
+ "@wordpress/element": "^6.27.1-next.46f643fa0.0",
39
+ "@wordpress/i18n": "^6.0.1-next.46f643fa0.0",
40
+ "@wordpress/url": "^4.27.1-next.46f643fa0.0"
42
41
  },
43
42
  "peerDependencies": {
44
43
  "react": "^18.0.0",
@@ -47,5 +46,5 @@
47
46
  "publishConfig": {
48
47
  "access": "public"
49
48
  },
50
- "gitHead": "abe06a6f2aef8d03c30ea9d5b3e133f041e523b1"
49
+ "gitHead": "17e600e091675c5e3d809adfea23ac456bbeae19"
51
50
  }
package/src/hook.js ADDED
@@ -0,0 +1,177 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { debounce } from '@wordpress/compose';
5
+ import { useEffect, useState, useRef } from '@wordpress/element';
6
+ import apiFetch from '@wordpress/api-fetch';
7
+ import { addQueryArgs } from '@wordpress/url';
8
+ import { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';
9
+
10
+ export function rendererPath( block, attributes = null, urlQueryArgs = {} ) {
11
+ return addQueryArgs( `/wp/v2/block-renderer/${ block }`, {
12
+ context: 'edit',
13
+ ...( null !== attributes ? { attributes } : {} ),
14
+ ...urlQueryArgs,
15
+ } );
16
+ }
17
+
18
+ export function removeBlockSupportAttributes( attributes ) {
19
+ const {
20
+ backgroundColor,
21
+ borderColor,
22
+ fontFamily,
23
+ fontSize,
24
+ gradient,
25
+ textColor,
26
+ className,
27
+ ...restAttributes
28
+ } = attributes;
29
+
30
+ const {
31
+ border,
32
+ color,
33
+ elements,
34
+ shadow,
35
+ spacing,
36
+ typography,
37
+ ...restStyles
38
+ } = attributes?.style || {};
39
+
40
+ return {
41
+ ...restAttributes,
42
+ style: restStyles,
43
+ };
44
+ }
45
+
46
+ /**
47
+ * @typedef {Object} ServerSideRenderResponse
48
+ * @property {string} status - The current request status: 'idle', 'loading', 'success', or 'error'.
49
+ * @property {string} [content] - The rendered block content (available when status is 'success').
50
+ * @property {string} [error] - The error message (available when status is 'error').
51
+ */
52
+
53
+ /**
54
+ * A hook for server-side rendering a preview of dynamic blocks to display in the editor.
55
+ *
56
+ * Handles fetching server-rendered previews for blocks, managing loading states,
57
+ * and automatically debouncing requests to prevent excessive API calls. It supports both
58
+ * GET and POST requests, with POST requests used for larger attribute payloads.
59
+ *
60
+ * @example
61
+ * Basic usage:
62
+ *
63
+ * ```jsx
64
+ * import { RawHTML } from '@wordpress/element';
65
+ * import { useServerSideRender } from '@wordpress/server-side-render';
66
+ *
67
+ * function MyServerSideRender( { attributes, block } ) {
68
+ * const { content, status, error } = useServerSideRender( {
69
+ * attributes,
70
+ * block,
71
+ * } );
72
+ *
73
+ * if ( status === 'loading' ) {
74
+ * return <div>Loading...</div>;
75
+ * }
76
+ *
77
+ * if ( status === 'error' ) {
78
+ * return <div>Error: { error }</div>;
79
+ * }
80
+ *
81
+ * return <RawHTML>{ content }</RawHTML>;
82
+ * }
83
+ * ```
84
+ *
85
+ * @param {Object} args The hook configuration object.
86
+ * @param {Object} args.attributes The block attributes to be sent to the server for rendering.
87
+ * @param {string} args.block The identifier of the block to be serverside rendered. Example: 'core/archives'.
88
+ * @param {boolean} [args.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.
89
+ * @param {string} [args.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'.
90
+ * @param {Object} [args.urlQueryArgs] Additional query arguments to append to the request URL.
91
+ *
92
+ * @return {ServerSideRenderResponse} The server-side render response object.
93
+ */
94
+ export function useServerSideRender( args ) {
95
+ const [ response, setResponse ] = useState( { status: 'idle' } );
96
+ const shouldDebounceRef = useRef( false );
97
+
98
+ const {
99
+ attributes,
100
+ block,
101
+ skipBlockSupportAttributes = false,
102
+ httpMethod = 'GET',
103
+ urlQueryArgs,
104
+ } = args;
105
+
106
+ let sanitizedAttributes =
107
+ attributes &&
108
+ __experimentalSanitizeBlockAttributes( block, attributes );
109
+
110
+ if ( skipBlockSupportAttributes ) {
111
+ sanitizedAttributes =
112
+ removeBlockSupportAttributes( sanitizedAttributes );
113
+ }
114
+
115
+ // If httpMethod is 'POST', send the attributes in the request body instead of the URL.
116
+ // This allows sending a larger attributes object than in a GET request, where the attributes are in the URL.
117
+ const isPostRequest = 'POST' === httpMethod;
118
+ const urlAttributes = isPostRequest ? null : sanitizedAttributes;
119
+ const path = rendererPath( block, urlAttributes, urlQueryArgs );
120
+ const body = isPostRequest
121
+ ? JSON.stringify( { attributes: sanitizedAttributes ?? null } )
122
+ : undefined;
123
+
124
+ useEffect( () => {
125
+ const controller = new AbortController();
126
+ const debouncedFetch = debounce(
127
+ function () {
128
+ {
129
+ setResponse( { status: 'loading' } );
130
+
131
+ apiFetch( {
132
+ path,
133
+ method: isPostRequest ? 'POST' : 'GET',
134
+ body,
135
+ headers: isPostRequest
136
+ ? {
137
+ 'Content-Type': 'application/json',
138
+ }
139
+ : {},
140
+ signal: controller.signal,
141
+ } )
142
+ .then( ( res ) => {
143
+ setResponse( {
144
+ status: 'success',
145
+ content: res ? res.rendered : '',
146
+ } );
147
+ } )
148
+ .catch( ( error ) => {
149
+ // The request was aborted, do not update the response.
150
+ if ( error.name === 'AbortError' ) {
151
+ return;
152
+ }
153
+
154
+ setResponse( {
155
+ status: 'error',
156
+ error: error.message,
157
+ } );
158
+ } )
159
+ .finally( () => {
160
+ // Debounce requests after first fetch.
161
+ shouldDebounceRef.current = true;
162
+ } );
163
+ }
164
+ },
165
+ shouldDebounceRef.current ? 500 : 0
166
+ );
167
+
168
+ debouncedFetch();
169
+
170
+ return () => {
171
+ controller.abort();
172
+ debouncedFetch.cancel();
173
+ };
174
+ }, [ path, isPostRequest, body ] );
175
+
176
+ return response;
177
+ }
package/src/index.js CHANGED
@@ -1,45 +1,23 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { useMemo } from '@wordpress/element';
5
- import { useSelect } from '@wordpress/data';
6
-
7
1
  /**
8
2
  * Internal dependencies
9
3
  */
10
- import ServerSideRender from './server-side-render';
4
+ import { ServerSideRenderWithPostId } from './server-side-render';
5
+ import { useServerSideRender } from './hook';
11
6
 
12
7
  /**
13
- * Constants
8
+ * A compatibility layer for the `ServerSideRender` component when used with `wp` global namespace.
9
+ *
10
+ * @deprecated Use `ServerSideRender` non-default export instead.
11
+ *
12
+ * @example
13
+ * ```js
14
+ * import ServerSideRender from '@wordpress/server-side-render';
15
+ * ```
14
16
  */
15
- const EMPTY_OBJECT = {};
16
-
17
- export default function ExportedServerSideRender( {
18
- urlQueryArgs = EMPTY_OBJECT,
19
- ...props
20
- } ) {
21
- const currentPostId = useSelect( ( select ) => {
22
- // FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.
23
- // It is used by blocks that can be loaded into a *non-post* block editor.
24
- // eslint-disable-next-line @wordpress/data-no-store-string-literals
25
- const postId = select( 'core/editor' )?.getCurrentPostId();
26
-
27
- // For templates and template parts we use a custom ID format.
28
- // Since they aren't real posts, we don't want to use their ID
29
- // for server-side rendering. Since they use a string based ID,
30
- // we can assume real post IDs are numbers.
31
- return postId && typeof postId === 'number' ? postId : null;
32
- }, [] );
33
-
34
- const newUrlQueryArgs = useMemo( () => {
35
- if ( ! currentPostId ) {
36
- return urlQueryArgs;
37
- }
38
- return {
39
- post_id: currentPostId,
40
- ...urlQueryArgs,
41
- };
42
- }, [ currentPostId, urlQueryArgs ] );
17
+ const ServerSideRenderCompat = ServerSideRenderWithPostId;
18
+ ServerSideRenderCompat.ServerSideRender = ServerSideRenderWithPostId;
19
+ ServerSideRenderCompat.useServerSideRender = useServerSideRender;
43
20
 
44
- return <ServerSideRender urlQueryArgs={ newUrlQueryArgs } { ...props } />;
45
- }
21
+ export { ServerSideRenderWithPostId as ServerSideRender };
22
+ export { useServerSideRender };
23
+ export default ServerSideRenderCompat;