@enonic/nextjs-adapter 0.12.0 → 0.13.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.
@@ -14,9 +14,9 @@ export interface ComponentDefinition {
14
14
  processor?: DataProcessor;
15
15
  view?: React.FunctionComponent<any>;
16
16
  }
17
- export type DataProcessor = (data: any, context?: Context) => Promise<Record<string, any>>;
17
+ export type DataProcessor = (data: any, context?: Context, config?: any) => Promise<Record<string, any>>;
18
18
  export type VariablesGetter = (path: string, context?: Context, config?: any) => VariablesGetterResult;
19
- export type QueryGetter = (config?: any) => string;
19
+ export type QueryGetter = (path: string, context?: Context, config?: any) => string;
20
20
  export type VariablesGetterResult = {
21
21
  [variables: string]: any;
22
22
  path: string;
@@ -40,6 +40,7 @@ export interface ServerSideParams extends ParsedUrlQuery {
40
40
  export interface PreviewParams {
41
41
  contentPath: string[];
42
42
  headers: Record<string, string>;
43
+ params: Record<string, string>;
43
44
  }
44
45
  export type Context = GetServerSidePropsContext<ServerSideParams, PreviewParams>;
45
46
  export interface MinimalContext {
@@ -230,27 +230,40 @@ function prefixLayoutPath(contentType, path) {
230
230
  }
231
231
  }
232
232
  function buildPage(contentType, comps = []) {
233
- let page = {
233
+ const page = {
234
234
  type: XP_COMPONENT_TYPE.PAGE,
235
235
  path: '/',
236
236
  };
237
- const tree = {};
238
- if (contentType === FRAGMENT_CONTENTTYPE_NAME) {
239
- page.regions = tree;
237
+ return buildComponentTree(comps, page, contentType);
238
+ }
239
+ function buildComponentTree(comps, rootComp, contentType) {
240
+ const rootTree = {};
241
+ const isFragmentType = contentType === FRAGMENT_CONTENTTYPE_NAME;
242
+ if (isFragmentType) {
243
+ rootComp.regions = rootTree;
240
244
  }
241
245
  comps.forEach(cmp => {
242
246
  var _a;
243
247
  const cmpPath = parseComponentPath(contentType, cmp.path);
244
- let region;
245
- if (cmp.path === '/' && cmp.type === XP_COMPONENT_TYPE.PAGE) {
246
- // add page values to page object
247
- page = Object.assign(page, cmp);
248
- page.page.regions = tree;
249
- return;
248
+ if (cmp.path === '/') {
249
+ if (cmp.type === XP_COMPONENT_TYPE.PAGE && rootComp.type === XP_COMPONENT_TYPE.PAGE) {
250
+ // add page values to page object
251
+ rootComp = Object.assign(rootComp, cmp);
252
+ rootComp.page.regions = rootTree;
253
+ return;
254
+ }
255
+ else if (cmp.type === XP_COMPONENT_TYPE.LAYOUT && rootComp.type === XP_COMPONENT_TYPE.FRAGMENT) {
256
+ // when rendering fragment as part of the page with layout at root path
257
+ // remove other children from list as they will be included in layout
258
+ rootComp.fragment.fragment.components = [cmp];
259
+ cmp.regions = rootTree;
260
+ }
250
261
  }
251
- else {
252
- region = getParentRegion(tree, contentType, cmpPath, comps, true);
262
+ if (cmp.type === XP_COMPONENT_TYPE.FRAGMENT) {
263
+ // build component subtree for fragment
264
+ cmp = buildComponentTree(cmp.fragment.fragment.components, cmp, contentType);
253
265
  }
266
+ const region = getParentRegion(rootTree, contentType, cmpPath, comps, true);
254
267
  if (region) {
255
268
  // getting the index of component from string like '/main/0/left/11'
256
269
  const cmpIndex = (_a = cmpPath[cmpPath.length - 1]) === null || _a === void 0 ? void 0 : _a.index;
@@ -262,7 +275,7 @@ function buildPage(contentType, comps = []) {
262
275
  }
263
276
  }
264
277
  });
265
- return page;
278
+ return rootComp;
266
279
  }
267
280
  function combineMultipleQueries(queriesWithVars) {
268
281
  const queries = [];
@@ -342,11 +355,16 @@ function applyProcessors(componentDescriptors, contentResults, context) {
342
355
  // if there is a query then there must be a result for it
343
356
  data = contentResults.contents[dataCounter++];
344
357
  }
345
- return yield propsProcessor(data, context);
358
+ const config = getComponentConfig(desc.component);
359
+ return yield propsProcessor(data, context, config);
346
360
  }));
347
361
  return Promise.allSettled(processorPromises);
348
362
  });
349
363
  }
364
+ function getComponentConfig(cmp) {
365
+ const cmpData = cmp && cmp[cmp.type];
366
+ return cmpData && 'config' in cmpData ? cmpData.config : undefined;
367
+ }
350
368
  function collectComponentDescriptors(components, componentRegistry, requestedComponentPath, xpContentPath, context) {
351
369
  const descriptors = [];
352
370
  for (const cmp of (components || [])) {
@@ -357,8 +375,7 @@ function collectComponentDescriptors(components, componentRegistry, requestedCom
357
375
  const cmpDef = ComponentRegistry.getByComponent(cmp);
358
376
  if (cmpDef) {
359
377
  // const partPath = `${xpContentPath}/_component${cmp.path}`;
360
- const cmpData = cmp[cmp.type];
361
- const config = cmpData && 'config' in cmpData ? cmpData.config : undefined;
378
+ const config = getComponentConfig(cmp);
362
379
  const queryAndVariables = getQueryAndVariables(cmp.type, xpContentPath, cmpDef.query, context, config);
363
380
  if (queryAndVariables) {
364
381
  descriptors.push({
@@ -427,7 +444,7 @@ function getQueryAndVariables(type, path, selectedQuery, context, config) {
427
444
  throw Error(`Query for content type ${type} should be a string or function, not: ${typeof query}`);
428
445
  }
429
446
  if (typeof query === 'function') {
430
- query = query(config);
447
+ query = query(path, context, config);
431
448
  }
432
449
  if (query) {
433
450
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enonic/nextjs-adapter",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
@@ -13,21 +13,21 @@
13
13
  "lint:fix": "eslint --fix ."
14
14
  },
15
15
  "dependencies": {
16
- "html-react-parser": "^3.0.4",
17
- "next": "^13.0.7",
16
+ "html-react-parser": "^3.0.7",
17
+ "next": "^13.1.2",
18
18
  "react": "^18.2.0",
19
19
  "react-dom": "^18.2.0",
20
20
  "unescape": "^1.0.1"
21
21
  },
22
22
  "devDependencies": {
23
- "@enonic/eslint-config": "^0.1.0",
24
- "@types/node": "^18.11.17",
23
+ "@enonic/eslint-config": "^1.1.0",
24
+ "@types/node": "^18.11.18",
25
25
  "@types/react": "^18.0.26",
26
- "@types/react-dom": "^18.0.9",
27
- "@typescript-eslint/eslint-plugin": "^5.46.1",
28
- "@typescript-eslint/parser": "^5.46.1",
29
- "eslint": "^8.30.0",
30
- "eslint-plugin-react": "^7.31.11",
26
+ "@types/react-dom": "^18.0.10",
27
+ "@typescript-eslint/eslint-plugin": "^5.48.1",
28
+ "@typescript-eslint/parser": "^5.48.1",
29
+ "eslint": "^8.32.0",
30
+ "eslint-plugin-react": "^7.32.0",
31
31
  "typescript": "^4.9.4"
32
32
  }
33
33
  }
package/views/Region.jsx CHANGED
@@ -15,12 +15,7 @@ export const RegionView = (props) => {
15
15
  regionAttrs.id = name + 'Region';
16
16
  regionAttrs[PORTAL_REGION_ATTRIBUTE] = name;
17
17
  }
18
- if (Object.keys(regionAttrs).length) {
19
- return <div {...regionAttrs}>{children}</div>;
20
- }
21
- else {
22
- return <>{children}</>;
23
- }
18
+ return <div {...regionAttrs}>{children}</div>;
24
19
  };
25
20
  /** Multiple regions, or only one if named in props.selected */
26
21
  const RegionsView = (props) => {
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+ export interface StaticContentProps extends Record<string, any> {
3
+ children?: ReactNode | ReactNode[];
4
+ element?: string;
5
+ condition: boolean;
6
+ }
7
+ export default function StaticContent({ children, element, condition, ...props }: StaticContentProps): import("react").DOMElement<{
8
+ [x: string]: any;
9
+ }, Element> | import("react").ReactElement<{
10
+ ref: boolean | import("react").MutableRefObject<any>;
11
+ suppressHydrationWarning: boolean;
12
+ dangerouslySetInnerHTML: {
13
+ __html: string;
14
+ };
15
+ }, string | import("react").JSXElementConstructor<any>>;
@@ -0,0 +1,38 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { createElement, useEffect, useRef, useState } from 'react';
13
+ function useStaticContent() {
14
+ const ref = useRef(null);
15
+ const [render, setRender] = useState(typeof window === 'undefined');
16
+ useEffect(() => {
17
+ var _a;
18
+ // check if the innerHTML is empty as client side navigation
19
+ // need to render the component without server-side backup
20
+ const isEmpty = ((_a = ref === null || ref === void 0 ? void 0 : ref.current) === null || _a === void 0 ? void 0 : _a.innerHTML) === '';
21
+ if (isEmpty) {
22
+ setRender(true);
23
+ }
24
+ }, []);
25
+ return [render, ref];
26
+ }
27
+ export default function StaticContent(_a) {
28
+ var { children, element = 'div', condition = true } = _a, props = __rest(_a, ["children", "element", "condition"]);
29
+ const [render, ref] = useStaticContent();
30
+ // just render if:
31
+ // - condition is false
32
+ // - we're in the server or a spa navigation
33
+ if (render || !condition) {
34
+ return createElement(element, props, ...[].concat(children));
35
+ }
36
+ // avoid re-render on the client
37
+ return createElement(element, Object.assign(Object.assign({}, props), { ref, suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: '' } }));
38
+ }