@exdst-sitecore-content-sdk/astro 0.0.1
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.
- package/LICENSE.txt +202 -0
- package/README.md +3 -0
- package/package.json +101 -0
- package/src/client/index.ts +12 -0
- package/src/client/sitecore-astro-client.test.ts +271 -0
- package/src/client/sitecore-astro-client.ts +137 -0
- package/src/components/AstroImage.astro +114 -0
- package/src/components/Date.astro +76 -0
- package/src/components/DefaultEmptyFieldEditingComponentImage.astro +24 -0
- package/src/components/DefaultEmptyFieldEditingComponentText.astro +12 -0
- package/src/components/EditingScripts.astro +49 -0
- package/src/components/EmptyRendering.astro +3 -0
- package/src/components/ErrorBoundary.astro +77 -0
- package/src/components/FieldMetadata.astro +30 -0
- package/src/components/File.astro +46 -0
- package/src/components/HiddenRendering.astro +22 -0
- package/src/components/Image.astro +155 -0
- package/src/components/Link.astro +105 -0
- package/src/components/MissingComponent.astro +39 -0
- package/src/components/Placeholder/EmptyPlaceholder.astro +9 -0
- package/src/components/Placeholder/Placeholder.astro +100 -0
- package/src/components/Placeholder/PlaceholderMetadata.astro +102 -0
- package/src/components/Placeholder/PlaceholderUtils.astro +153 -0
- package/src/components/Placeholder/index.ts +5 -0
- package/src/components/Placeholder/models.ts +82 -0
- package/src/components/Placeholder/placeholder-utils.test.ts +162 -0
- package/src/components/Placeholder/placeholder-utils.ts +80 -0
- package/src/components/RenderWrapper.astro +31 -0
- package/src/components/RichText.astro +59 -0
- package/src/components/Text.astro +97 -0
- package/src/components/sharedTypes/index.ts +1 -0
- package/src/components/sharedTypes/props.ts +17 -0
- package/src/config/define-config.test.ts +526 -0
- package/src/config/define-config.ts +99 -0
- package/src/config/index.ts +1 -0
- package/src/config-cli/define-cli-config.test.ts +95 -0
- package/src/config-cli/define-cli-config.ts +50 -0
- package/src/config-cli/index.ts +1 -0
- package/src/context.ts +68 -0
- package/src/editing/constants.ts +8 -0
- package/src/editing/editing-config-middleware.test.ts +166 -0
- package/src/editing/editing-config-middleware.ts +111 -0
- package/src/editing/editing-render-middleware.test.ts +801 -0
- package/src/editing/editing-render-middleware.ts +288 -0
- package/src/editing/index.ts +16 -0
- package/src/editing/render-middleware.test.ts +57 -0
- package/src/editing/render-middleware.ts +51 -0
- package/src/editing/utils.test.ts +852 -0
- package/src/editing/utils.ts +308 -0
- package/src/enhancers/WithEmptyFieldEditingComponent.astro +56 -0
- package/src/enhancers/WithFieldMetadata.astro +31 -0
- package/src/env.d.ts +12 -0
- package/src/index.ts +16 -0
- package/src/middleware/index.ts +24 -0
- package/src/middleware/middleware.test.ts +507 -0
- package/src/middleware/middleware.ts +167 -0
- package/src/middleware/multisite-middleware.test.ts +672 -0
- package/src/middleware/multisite-middleware.ts +147 -0
- package/src/middleware/robots-middleware.test.ts +113 -0
- package/src/middleware/robots-middleware.ts +47 -0
- package/src/middleware/sitemap-middleware.test.ts +152 -0
- package/src/middleware/sitemap-middleware.ts +65 -0
- package/src/services/component-props-service.ts +182 -0
- package/src/sharedTypes/component-props.ts +17 -0
- package/src/site/index.ts +1 -0
- package/src/test-data/components/Bar.astro +0 -0
- package/src/test-data/components/Baz.astro +0 -0
- package/src/test-data/components/Foo.astro +0 -0
- package/src/test-data/components/Hero.variant.astro +0 -0
- package/src/test-data/components/NotComponent.bsx +0 -0
- package/src/test-data/components/Qux.astro +0 -0
- package/src/test-data/components/folded/Folded.astro +0 -0
- package/src/test-data/components/folded/random-file-2.docx +0 -0
- package/src/test-data/components/random-file.txt +0 -0
- package/src/test-data/helpers.ts +46 -0
- package/src/test-data/personalizeData.ts +63 -0
- package/src/tools/generate-map.ts +83 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/templating/components.test.ts +305 -0
- package/src/tools/templating/components.ts +49 -0
- package/src/tools/templating/constants.ts +4 -0
- package/src/tools/templating/default-component.test.ts +31 -0
- package/src/tools/templating/default-component.ts +63 -0
- package/src/tools/templating/index.ts +2 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/utils.test.ts +48 -0
- package/src/utils/utils.ts +52 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { mediaApi } from '@sitecore-content-sdk/core/media';
|
|
3
|
+
import { EditableFieldProps } from './sharedTypes';
|
|
4
|
+
import {
|
|
5
|
+
FieldMetadata,
|
|
6
|
+
isFieldValueEmpty,
|
|
7
|
+
} from '@sitecore-content-sdk/core/layout';
|
|
8
|
+
import { addClassName } from '../utils';
|
|
9
|
+
import WithFieldMetadata from '../enhancers/WithFieldMetadata.astro';
|
|
10
|
+
import WithEmptyFieldEditingComponent from '../enhancers/WithEmptyFieldEditingComponent.astro';
|
|
11
|
+
import DefaultEmptyFieldEditingComponentImage from './DefaultEmptyFieldEditingComponentImage.astro';
|
|
12
|
+
|
|
13
|
+
export interface ImageFieldValue {
|
|
14
|
+
[attributeName: string]: unknown;
|
|
15
|
+
src?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ImageField {
|
|
19
|
+
value?: ImageFieldValue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ImageSizeParameters {
|
|
23
|
+
[attr: string]: string | number | undefined;
|
|
24
|
+
/** Fixed width of the image */
|
|
25
|
+
w?: number;
|
|
26
|
+
/** Fixed height of the image */
|
|
27
|
+
h?: number;
|
|
28
|
+
/** Max width of the image */
|
|
29
|
+
mw?: number;
|
|
30
|
+
/** Max height of the image */
|
|
31
|
+
mh?: number;
|
|
32
|
+
/** Ignore aspect ratio */
|
|
33
|
+
iar?: 1 | 0;
|
|
34
|
+
/** Allow stretch */
|
|
35
|
+
as?: 1 | 0;
|
|
36
|
+
/** Image scale. Defaults to 1.0 */
|
|
37
|
+
sc?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface BaseImageProps {
|
|
41
|
+
[attributeName: string]: unknown;
|
|
42
|
+
/** Image field data (consistent with other field types) */
|
|
43
|
+
field?: (ImageField | ImageFieldValue) & FieldMetadata;
|
|
44
|
+
/**
|
|
45
|
+
* Parameters that will be attached to Sitecore media URLs
|
|
46
|
+
*/
|
|
47
|
+
imageParams?: {
|
|
48
|
+
[paramName: string]: string | number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Custom regexp that finds media URL prefix that will be replaced by `/-/jssmedia` or `/~/jssmedia`.
|
|
52
|
+
* @example
|
|
53
|
+
* /\/([-~]{1})assets\//i
|
|
54
|
+
* /-assets/website -> /-/jssmedia/website
|
|
55
|
+
* /~assets/website -> /~/jssmedia/website
|
|
56
|
+
*/
|
|
57
|
+
mediaUrlPrefix?: RegExp;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ImageProps extends BaseImageProps, EditableFieldProps {
|
|
61
|
+
srcSet?: ImageSizeParameters[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const getImageAttrs = (
|
|
65
|
+
{
|
|
66
|
+
src,
|
|
67
|
+
srcSet,
|
|
68
|
+
...otherAttrs
|
|
69
|
+
}: {
|
|
70
|
+
[key: string]: unknown;
|
|
71
|
+
src?: string;
|
|
72
|
+
srcSet?: ImageSizeParameters[];
|
|
73
|
+
},
|
|
74
|
+
imageParams?: { [paramName: string]: string | number },
|
|
75
|
+
mediaUrlPrefix?: RegExp
|
|
76
|
+
) => {
|
|
77
|
+
if (!src) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
addClassName(otherAttrs);
|
|
81
|
+
|
|
82
|
+
const newAttrs: { [attr: string]: unknown } = {
|
|
83
|
+
...otherAttrs,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// update image URL for content sdk handler and image rendering params
|
|
87
|
+
const resolvedSrc = mediaApi.updateImageUrl(src, imageParams, mediaUrlPrefix);
|
|
88
|
+
if (srcSet) {
|
|
89
|
+
// replace with HTML-formatted srcset, including updated image URLs
|
|
90
|
+
newAttrs.srcSet = mediaApi.getSrcSet(
|
|
91
|
+
resolvedSrc,
|
|
92
|
+
srcSet,
|
|
93
|
+
imageParams,
|
|
94
|
+
mediaUrlPrefix
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
// always output original src as fallback for older browsers
|
|
98
|
+
newAttrs.src = resolvedSrc;
|
|
99
|
+
return newAttrs;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const {
|
|
103
|
+
editable = true,
|
|
104
|
+
imageParams,
|
|
105
|
+
field,
|
|
106
|
+
mediaUrlPrefix,
|
|
107
|
+
emptyFieldEditingComponent,
|
|
108
|
+
...otherProps
|
|
109
|
+
} = Astro.props as ImageProps;
|
|
110
|
+
|
|
111
|
+
const dynamicMedia = field as ImageField | ImageFieldValue;
|
|
112
|
+
|
|
113
|
+
const isEmptyField = isFieldValueEmpty(dynamicMedia);
|
|
114
|
+
|
|
115
|
+
let img;
|
|
116
|
+
if (!isEmptyField) {
|
|
117
|
+
// handle image directly on field for forgetful devs
|
|
118
|
+
img = (dynamicMedia as ImageFieldValue).src
|
|
119
|
+
? { ...field }
|
|
120
|
+
: (dynamicMedia.value as ImageFieldValue);
|
|
121
|
+
|
|
122
|
+
if (img) {
|
|
123
|
+
// prevent metadata from being passed to the img tag
|
|
124
|
+
if (img.metadata) {
|
|
125
|
+
delete img.metadata;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const attrs = getImageAttrs(
|
|
131
|
+
{ ...img, ...otherProps },
|
|
132
|
+
imageParams,
|
|
133
|
+
mediaUrlPrefix
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
console.log("Attrs", attrs);
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
{
|
|
140
|
+
(
|
|
141
|
+
<WithFieldMetadata field={field} editable={editable}>
|
|
142
|
+
<WithEmptyFieldEditingComponent
|
|
143
|
+
field={field}
|
|
144
|
+
editable={editable}
|
|
145
|
+
defaultEmptyFieldEditingComponent={
|
|
146
|
+
DefaultEmptyFieldEditingComponentImage
|
|
147
|
+
}
|
|
148
|
+
emptyFieldEditingComponent={emptyFieldEditingComponent}
|
|
149
|
+
{...otherProps}
|
|
150
|
+
>
|
|
151
|
+
{!isEmptyField && img && attrs && <img {...attrs} />}
|
|
152
|
+
</WithEmptyFieldEditingComponent>
|
|
153
|
+
</WithFieldMetadata>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
FieldMetadata,
|
|
4
|
+
isFieldValueEmpty,
|
|
5
|
+
} from '@sitecore-content-sdk/core/layout';
|
|
6
|
+
import { EditableFieldProps } from './sharedTypes';
|
|
7
|
+
import WithFieldMetadata from '../enhancers/WithFieldMetadata.astro';
|
|
8
|
+
import WithEmptyFieldEditingComponent from '../enhancers/WithEmptyFieldEditingComponent.astro';
|
|
9
|
+
import DefaultEmptyFieldEditingComponentText from './DefaultEmptyFieldEditingComponentText.astro';
|
|
10
|
+
|
|
11
|
+
export interface LinkFieldValue {
|
|
12
|
+
[attributeName: string]: unknown;
|
|
13
|
+
href?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
title?: string;
|
|
16
|
+
target?: string;
|
|
17
|
+
text?: string;
|
|
18
|
+
anchor?: string;
|
|
19
|
+
querystring?: string;
|
|
20
|
+
linktype?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface LinkField {
|
|
24
|
+
value: LinkFieldValue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface LinkProps extends EditableFieldProps {
|
|
28
|
+
[htmlAttributes: string]: unknown;
|
|
29
|
+
/** The link field data. */
|
|
30
|
+
field: (LinkField | LinkFieldValue) & FieldMetadata;
|
|
31
|
+
/**
|
|
32
|
+
* Displays a link text ('description' in Sitecore) even when children exist
|
|
33
|
+
*/
|
|
34
|
+
showLinkTextWithChildrenPresent?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
field,
|
|
39
|
+
editable = true,
|
|
40
|
+
showLinkTextWithChildrenPresent,
|
|
41
|
+
emptyFieldEditingComponent,
|
|
42
|
+
...otherProps
|
|
43
|
+
} = Astro.props as LinkProps;
|
|
44
|
+
|
|
45
|
+
const children = await Astro.slots.render('default');
|
|
46
|
+
const dynamicField: LinkField | LinkFieldValue = field;
|
|
47
|
+
|
|
48
|
+
const isEmptyField = isFieldValueEmpty(dynamicField);
|
|
49
|
+
|
|
50
|
+
let link,
|
|
51
|
+
finalChildren,
|
|
52
|
+
anchorAttrs: { [attr: string]: unknown } = {};
|
|
53
|
+
|
|
54
|
+
if (!isEmptyField) {
|
|
55
|
+
// handle link directly on field for forgetful devs
|
|
56
|
+
link = (dynamicField as LinkFieldValue).href
|
|
57
|
+
? (field as LinkFieldValue)
|
|
58
|
+
: (dynamicField as LinkField).value;
|
|
59
|
+
|
|
60
|
+
if (link) {
|
|
61
|
+
const anchor =
|
|
62
|
+
link.linktype !== 'anchor' && link.anchor ? `#${link.anchor}` : '';
|
|
63
|
+
const querystring = link.querystring ? `?${link.querystring}` : '';
|
|
64
|
+
|
|
65
|
+
anchorAttrs = {
|
|
66
|
+
href: `${link.href}${querystring}${anchor}`,
|
|
67
|
+
class: link.class,
|
|
68
|
+
title: link.title,
|
|
69
|
+
target: link.target,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (anchorAttrs.target === '_blank' && !anchorAttrs.rel) {
|
|
73
|
+
// information disclosure attack prevention keeps target blank site from getting ref to window.opener
|
|
74
|
+
anchorAttrs.rel = 'noopener noreferrer';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const linkText =
|
|
78
|
+
showLinkTextWithChildrenPresent || !children
|
|
79
|
+
? link.text || link.href
|
|
80
|
+
: null;
|
|
81
|
+
|
|
82
|
+
finalChildren = children ? [linkText, ...children] : linkText;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const attrs = { ...anchorAttrs, ...otherProps };
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
(
|
|
91
|
+
<WithFieldMetadata field={field} editable={editable}>
|
|
92
|
+
<WithEmptyFieldEditingComponent
|
|
93
|
+
field={field}
|
|
94
|
+
editable={editable}
|
|
95
|
+
defaultEmptyFieldEditingComponent={
|
|
96
|
+
DefaultEmptyFieldEditingComponentText
|
|
97
|
+
}
|
|
98
|
+
emptyFieldEditingComponent={emptyFieldEditingComponent}
|
|
99
|
+
{...otherProps}
|
|
100
|
+
>
|
|
101
|
+
{!isEmptyField && link && <a {...attrs} set:html={finalChildren} />}
|
|
102
|
+
</WithEmptyFieldEditingComponent>
|
|
103
|
+
</WithFieldMetadata>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface MissingComponentProps {
|
|
3
|
+
rendering?: {
|
|
4
|
+
componentName?: string;
|
|
5
|
+
};
|
|
6
|
+
errorOverride?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const props = Astro.props as MissingComponentProps;
|
|
10
|
+
|
|
11
|
+
const componentName =
|
|
12
|
+
props.rendering && props.rendering.componentName
|
|
13
|
+
? props.rendering.componentName
|
|
14
|
+
: 'Unnamed Component';
|
|
15
|
+
|
|
16
|
+
// error override would mean component is not unimplemented
|
|
17
|
+
!props.errorOverride &&
|
|
18
|
+
console.log(
|
|
19
|
+
`Component props for unimplemented '${componentName}' component`,
|
|
20
|
+
props
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const errorMessage =
|
|
24
|
+
props.errorOverride ||
|
|
25
|
+
'Content SDK component is missing Astro implementation. See the developer console for more information.';
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<div
|
|
29
|
+
style={{
|
|
30
|
+
background: 'darkorange',
|
|
31
|
+
outline: '5px solid orange',
|
|
32
|
+
padding: '10px',
|
|
33
|
+
color: 'white',
|
|
34
|
+
maxWidth: '500px',
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<h2>{componentName}</h2>
|
|
38
|
+
<p>{errorMessage}</p>
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ComponentRendering } from '@sitecore-content-sdk/core/layout';
|
|
3
|
+
import ErrorBoundary from '../ErrorBoundary.astro';
|
|
4
|
+
import PlaceholderMetadata from './PlaceholderMetadata.astro';
|
|
5
|
+
import { useSitecore } from '../../context';
|
|
6
|
+
import RenderWrapper from '../RenderWrapper.astro';
|
|
7
|
+
import EmptyPlaceholder from './EmptyPlaceholder.astro';
|
|
8
|
+
import { PlaceholderProps } from './models';
|
|
9
|
+
import { getPlaceholderRenderings } from './placeholder-utils';
|
|
10
|
+
import { getComponentsForRenderingData } from './PlaceholderUtils.astro';
|
|
11
|
+
|
|
12
|
+
const props = Astro.props as PlaceholderProps;
|
|
13
|
+
|
|
14
|
+
const { page } = useSitecore();
|
|
15
|
+
const childProps: PlaceholderProps = { ...props, page };
|
|
16
|
+
|
|
17
|
+
delete childProps.componentMap;
|
|
18
|
+
|
|
19
|
+
const renderingData = childProps.rendering;
|
|
20
|
+
|
|
21
|
+
const placeholderData = getPlaceholderRenderings(
|
|
22
|
+
renderingData,
|
|
23
|
+
childProps.name,
|
|
24
|
+
childProps.page.mode.isEditing
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const isEmpty = !placeholderData.length;
|
|
28
|
+
|
|
29
|
+
const components = getComponentsForRenderingData(placeholderData, childProps);
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
(
|
|
34
|
+
<RenderWrapper
|
|
35
|
+
wrapper={
|
|
36
|
+
isEmpty
|
|
37
|
+
? childProps.page.mode.isEditing && EmptyPlaceholder
|
|
38
|
+
: childProps.render
|
|
39
|
+
}
|
|
40
|
+
>
|
|
41
|
+
<ErrorBoundary>
|
|
42
|
+
<RenderWrapper wrapper={isEmpty && childProps.renderEmpty}>
|
|
43
|
+
{
|
|
44
|
+
// Conditional rendering based on editing mode
|
|
45
|
+
childProps.page.mode.isEditing ? (
|
|
46
|
+
<PlaceholderMetadata
|
|
47
|
+
placeholderName={childProps.name}
|
|
48
|
+
rendering={childProps.rendering as ComponentRendering}
|
|
49
|
+
>
|
|
50
|
+
{components.map((component) => {
|
|
51
|
+
const Component = component.component.component;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<RenderWrapper wrapper={childProps.renderEach}>
|
|
55
|
+
<PlaceholderMetadata
|
|
56
|
+
rendering={component.rendering as ComponentRendering}
|
|
57
|
+
>
|
|
58
|
+
{!component.component.isEmpty && (
|
|
59
|
+
<ErrorBoundary
|
|
60
|
+
errorComponent={childProps.errorComponent}
|
|
61
|
+
{...component.props}
|
|
62
|
+
>
|
|
63
|
+
<Component {...component.props} />
|
|
64
|
+
</ErrorBoundary>
|
|
65
|
+
)}
|
|
66
|
+
{component.component.isEmpty && (
|
|
67
|
+
<Component {...component.props} />
|
|
68
|
+
)}
|
|
69
|
+
</PlaceholderMetadata>
|
|
70
|
+
</RenderWrapper>
|
|
71
|
+
);
|
|
72
|
+
})}
|
|
73
|
+
</PlaceholderMetadata>
|
|
74
|
+
) : (
|
|
75
|
+
<>
|
|
76
|
+
{components.map((component) => {
|
|
77
|
+
const Component = component.component.component;
|
|
78
|
+
return (
|
|
79
|
+
<RenderWrapper
|
|
80
|
+
wrapper={
|
|
81
|
+
isEmpty ? childProps.renderEmpty : childProps.renderEach
|
|
82
|
+
}
|
|
83
|
+
>
|
|
84
|
+
<ErrorBoundary
|
|
85
|
+
errorComponent={childProps.errorComponent}
|
|
86
|
+
{...component.props}
|
|
87
|
+
>
|
|
88
|
+
<Component {...component.props} />
|
|
89
|
+
</ErrorBoundary>
|
|
90
|
+
</RenderWrapper>
|
|
91
|
+
);
|
|
92
|
+
})}
|
|
93
|
+
</>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
</RenderWrapper>
|
|
97
|
+
</ErrorBoundary>
|
|
98
|
+
</RenderWrapper>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* An Astro component to generate metadata blocks for a placeholder or rendering.
|
|
4
|
+
* It utilizes dynamic attributes based on whether the component acts as a placeholder
|
|
5
|
+
* or as a rendering to properly render the surrounding code blocks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
ComponentRendering,
|
|
10
|
+
getDynamicPlaceholderPattern,
|
|
11
|
+
isDynamicPlaceholder,
|
|
12
|
+
} from '@sitecore-content-sdk/core/layout';
|
|
13
|
+
import {
|
|
14
|
+
MetadataKind,
|
|
15
|
+
DEFAULT_PLACEHOLDER_UID,
|
|
16
|
+
} from '@sitecore-content-sdk/core/editing';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Props containing the component data to render.
|
|
20
|
+
*/
|
|
21
|
+
export interface PlaceholderMetadataProps {
|
|
22
|
+
rendering: ComponentRendering;
|
|
23
|
+
placeholderName?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type CodeBlockAttributes = {
|
|
27
|
+
type: string;
|
|
28
|
+
chrometype: string;
|
|
29
|
+
className: string;
|
|
30
|
+
kind: string;
|
|
31
|
+
id?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const props = Astro.props as PlaceholderMetadataProps;
|
|
35
|
+
|
|
36
|
+
const getCodeBlockAttributes = (
|
|
37
|
+
kind: MetadataKind,
|
|
38
|
+
id?: string,
|
|
39
|
+
placeholderName?: string
|
|
40
|
+
): CodeBlockAttributes => {
|
|
41
|
+
const chrometype = placeholderName ? 'placeholder' : 'rendering';
|
|
42
|
+
|
|
43
|
+
const attributes: CodeBlockAttributes = {
|
|
44
|
+
type: 'text/sitecore',
|
|
45
|
+
chrometype: chrometype,
|
|
46
|
+
className: 'scpm',
|
|
47
|
+
kind: kind,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (kind === MetadataKind.Open) {
|
|
51
|
+
if (chrometype === 'placeholder' && placeholderName) {
|
|
52
|
+
let phId = '';
|
|
53
|
+
|
|
54
|
+
for (const placeholder of Object.keys(
|
|
55
|
+
props.rendering.placeholders ?? {}
|
|
56
|
+
)) {
|
|
57
|
+
if (placeholderName === placeholder) {
|
|
58
|
+
phId = id
|
|
59
|
+
? `${placeholderName}_${id}`
|
|
60
|
+
: `${placeholderName}_${DEFAULT_PLACEHOLDER_UID}`;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check if the placeholder is a dynamic placeholder
|
|
65
|
+
if (isDynamicPlaceholder(placeholder)) {
|
|
66
|
+
const pattern = getDynamicPlaceholderPattern(placeholder);
|
|
67
|
+
|
|
68
|
+
// Check if the placeholder matches the dynamic placeholder pattern
|
|
69
|
+
if (pattern.test(placeholderName)) {
|
|
70
|
+
phId = id
|
|
71
|
+
? `${placeholder}_${id}`
|
|
72
|
+
: `${placeholder}_${DEFAULT_PLACEHOLDER_UID}`;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
attributes.id = phId;
|
|
79
|
+
} else {
|
|
80
|
+
attributes.id = id;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return attributes;
|
|
85
|
+
};
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
<>
|
|
89
|
+
<code
|
|
90
|
+
{...getCodeBlockAttributes(
|
|
91
|
+
MetadataKind.Open,
|
|
92
|
+
props.rendering.uid,
|
|
93
|
+
props.placeholderName
|
|
94
|
+
)}></code>
|
|
95
|
+
<slot />
|
|
96
|
+
<code
|
|
97
|
+
{...getCodeBlockAttributes(
|
|
98
|
+
MetadataKind.Close,
|
|
99
|
+
props.rendering.uid,
|
|
100
|
+
props.placeholderName
|
|
101
|
+
)}></code>
|
|
102
|
+
</>
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ComponentRendering } from "@sitecore-content-sdk/core/layout";
|
|
3
|
+
import { constants } from '@sitecore-content-sdk/core';
|
|
4
|
+
import EmptyRendering from "../EmptyRendering.astro";
|
|
5
|
+
import HiddenRendering from "../HiddenRendering.astro";
|
|
6
|
+
import MissingComponent from "../MissingComponent.astro";
|
|
7
|
+
import { getSXAParams } from "./placeholder-utils";
|
|
8
|
+
import { ComponentForRendering, PlaceholderProps } from "./models";
|
|
9
|
+
import { AstroContentSdkComponent, ComponentMap } from "../../sharedTypes/component-props";
|
|
10
|
+
import { useComponentMap } from "../../context";
|
|
11
|
+
|
|
12
|
+
export const getComponentsForRenderingData = (
|
|
13
|
+
placeholderData: ComponentRendering[],
|
|
14
|
+
props: PlaceholderProps
|
|
15
|
+
) => {
|
|
16
|
+
const {
|
|
17
|
+
name,
|
|
18
|
+
fields: placeholderFields,
|
|
19
|
+
params: placeholderParams,
|
|
20
|
+
missingComponentComponent,
|
|
21
|
+
hiddenRenderingComponent,
|
|
22
|
+
...placeholderProps
|
|
23
|
+
} = props;
|
|
24
|
+
|
|
25
|
+
const transformedComponents = placeholderData
|
|
26
|
+
.map((rendering: ComponentRendering, index: number) => {
|
|
27
|
+
const key = (rendering as ComponentRendering).uid
|
|
28
|
+
? (rendering as ComponentRendering).uid
|
|
29
|
+
: `component-${index}`;
|
|
30
|
+
const commonProps = { key };
|
|
31
|
+
|
|
32
|
+
const componentRendering = rendering as ComponentRendering;
|
|
33
|
+
const componentMap = useComponentMap();
|
|
34
|
+
|
|
35
|
+
const component = getComponentForRendering(
|
|
36
|
+
componentRendering,
|
|
37
|
+
name,
|
|
38
|
+
componentMap,
|
|
39
|
+
hiddenRenderingComponent,
|
|
40
|
+
missingComponentComponent
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const finalProps = {
|
|
44
|
+
...commonProps,
|
|
45
|
+
...placeholderProps,
|
|
46
|
+
...((placeholderFields || componentRendering.fields) && {
|
|
47
|
+
fields: { ...placeholderFields, ...componentRendering.fields },
|
|
48
|
+
}),
|
|
49
|
+
...((placeholderParams || componentRendering.params) && {
|
|
50
|
+
params: {
|
|
51
|
+
...placeholderParams,
|
|
52
|
+
...componentRendering.params,
|
|
53
|
+
// Provide SXA styles
|
|
54
|
+
...getSXAParams(componentRendering),
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
rendering: componentRendering,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
let components = {
|
|
61
|
+
component: component,
|
|
62
|
+
props: props.modifyComponentProps ? props.modifyComponentProps(finalProps) : finalProps,
|
|
63
|
+
rendering: rendering,
|
|
64
|
+
placeholderName: name
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return components;
|
|
68
|
+
})
|
|
69
|
+
.filter((element) => element); // remove nulls
|
|
70
|
+
|
|
71
|
+
return transformedComponents;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const getComponentForRendering = (
|
|
75
|
+
renderingDefinition: ComponentRendering,
|
|
76
|
+
placeholderName: string,
|
|
77
|
+
componentMap?: ComponentMap,
|
|
78
|
+
hiddenRenderingComponent?: AstroContentSdkComponent,
|
|
79
|
+
missingComponentComponent?: AstroContentSdkComponent
|
|
80
|
+
): ComponentForRendering => {
|
|
81
|
+
const logUnknownComponentError = (variant?: string) => {
|
|
82
|
+
console.error(
|
|
83
|
+
`Placeholder ${placeholderName} contains unknown component ${
|
|
84
|
+
renderingDefinition.componentName
|
|
85
|
+
}${
|
|
86
|
+
variant ? ` (${variant})` : ''
|
|
87
|
+
}. Ensure that a React component exists for it, and that it is registered in your component-map file.`
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
let component;
|
|
92
|
+
|
|
93
|
+
if (renderingDefinition.componentName === constants.HIDDEN_RENDERING_NAME) {
|
|
94
|
+
return {
|
|
95
|
+
component: hiddenRenderingComponent ?? HiddenRendering,
|
|
96
|
+
isEmpty: true
|
|
97
|
+
};
|
|
98
|
+
} else if (!renderingDefinition.componentName) {
|
|
99
|
+
return {
|
|
100
|
+
component: EmptyRendering,
|
|
101
|
+
isEmpty: true
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!componentMap || componentMap.size === 0) {
|
|
106
|
+
console.warn(
|
|
107
|
+
`No components were available in component map to service request for component ${renderingDefinition}`
|
|
108
|
+
);
|
|
109
|
+
} else {
|
|
110
|
+
component = componentMap.get(renderingDefinition.componentName);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!component) {
|
|
114
|
+
// Fallback/defaults for Sitecore Component renderings (in case not defined in component map)
|
|
115
|
+
// if (renderingDefinition.componentName === FEAAS_COMPONENT_RENDERING_NAME) {
|
|
116
|
+
// return {
|
|
117
|
+
// component: FEaaSComponent,
|
|
118
|
+
// isEmpty: false,
|
|
119
|
+
// };
|
|
120
|
+
// } else if (renderingDefinition.componentName === FEAAS_WRAPPER_RENDERING_NAME) {
|
|
121
|
+
// return {
|
|
122
|
+
// component: FEaaSWrapper,
|
|
123
|
+
// isEmpty: false,
|
|
124
|
+
// };
|
|
125
|
+
// } else if (renderingDefinition.componentName === BYOC_COMPONENT_RENDERING_NAME) {
|
|
126
|
+
// return {
|
|
127
|
+
// component: BYOCComponent,
|
|
128
|
+
// isEmpty: false,
|
|
129
|
+
// };
|
|
130
|
+
// } else if (renderingDefinition.componentName === BYOC_WRAPPER_RENDERING_NAME) {
|
|
131
|
+
// // wrapping with error boundary could cause problems in case where parent component uses withPlaceholder HOC and tries to access its children props
|
|
132
|
+
// // that's why we need to mark BYOC wrapper dynamic
|
|
133
|
+
// return {
|
|
134
|
+
// component: BYOCWrapper,
|
|
135
|
+
// isEmpty: false,
|
|
136
|
+
// };
|
|
137
|
+
// }
|
|
138
|
+
|
|
139
|
+
logUnknownComponentError();
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
component: missingComponentComponent ?? MissingComponent,
|
|
143
|
+
isEmpty: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
component,
|
|
149
|
+
isEmpty: false
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
---
|