@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,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FetchOptions,
|
|
3
|
+
Page,
|
|
4
|
+
PageOptions,
|
|
5
|
+
SitecoreClient,
|
|
6
|
+
SitecoreClientInit,
|
|
7
|
+
} from '@sitecore-content-sdk/core/client';
|
|
8
|
+
import {
|
|
9
|
+
AstroContentSdkComponent,
|
|
10
|
+
ComponentMap,
|
|
11
|
+
ComponentPropsCollection,
|
|
12
|
+
ComponentPropsError,
|
|
13
|
+
PreviewData,
|
|
14
|
+
} from '../sharedTypes/component-props';
|
|
15
|
+
import { LayoutServiceData } from '@sitecore-content-sdk/core/layout';
|
|
16
|
+
import { ComponentPropsService } from '../services/component-props-service';
|
|
17
|
+
import { EditingPreviewData } from '@sitecore-content-sdk/core/editing';
|
|
18
|
+
import {
|
|
19
|
+
getSiteRewriteData,
|
|
20
|
+
normalizeSiteRewrite,
|
|
21
|
+
} from '@sitecore-content-sdk/core/site';
|
|
22
|
+
import {
|
|
23
|
+
getPersonalizedRewriteData,
|
|
24
|
+
normalizePersonalizedRewrite,
|
|
25
|
+
} from '@sitecore-content-sdk/core/personalize';
|
|
26
|
+
|
|
27
|
+
export class SitecoreAstroClient extends SitecoreClient {
|
|
28
|
+
protected componentPropsService: ComponentPropsService;
|
|
29
|
+
constructor(protected initOptions: SitecoreClientInit) {
|
|
30
|
+
super(initOptions);
|
|
31
|
+
this.componentPropsService = this.getComponentPropsService();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gets site name based on the provided path
|
|
36
|
+
* @param {string | string[]} path path to get site name from
|
|
37
|
+
* @returns site name, or default site info if not found
|
|
38
|
+
*/
|
|
39
|
+
getSiteNameFromPath(path: string | string[]) {
|
|
40
|
+
const resolvedPath = super.parsePath(path);
|
|
41
|
+
// Get site name (from path rewritten in middleware)
|
|
42
|
+
const siteData = getSiteRewriteData(
|
|
43
|
+
resolvedPath,
|
|
44
|
+
this.initOptions.defaultSite
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return siteData.siteName;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Normalizes a path that could have been rewritten
|
|
52
|
+
* @param {string | string[]} path path
|
|
53
|
+
* @returns normalized path string
|
|
54
|
+
*/
|
|
55
|
+
parsePath(path: string | string[]) {
|
|
56
|
+
const basePath = super.parsePath(path);
|
|
57
|
+
return normalizeSiteRewrite(normalizePersonalizedRewrite(basePath));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getPage(
|
|
61
|
+
path: string | string[],
|
|
62
|
+
pageOptions: PageOptions,
|
|
63
|
+
options?: FetchOptions
|
|
64
|
+
): Promise<Page | null> {
|
|
65
|
+
const resolvedPath = this.parsePath(path);
|
|
66
|
+
// Get variant(s) for personalization (from path), must ensure path is of type string
|
|
67
|
+
const personalizeData =
|
|
68
|
+
pageOptions.personalize ||
|
|
69
|
+
getPersonalizedRewriteData(super.parsePath(path));
|
|
70
|
+
const site = pageOptions.site || this.getSiteNameFromPath(path);
|
|
71
|
+
const page = await super.getPage(
|
|
72
|
+
resolvedPath,
|
|
73
|
+
{
|
|
74
|
+
locale: pageOptions.locale,
|
|
75
|
+
site,
|
|
76
|
+
personalize: personalizeData,
|
|
77
|
+
},
|
|
78
|
+
options
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return page;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Retrieves preview page and layout details
|
|
86
|
+
* @param {PreviewData} previewData - The editing preview data for metadata mode.
|
|
87
|
+
* @param {FetchOptions} [fetchOptions] Additional fetch fetch options to override GraphQL requests (like retries and fetch)
|
|
88
|
+
*/
|
|
89
|
+
async getPreview(
|
|
90
|
+
previewData: PreviewData,
|
|
91
|
+
fetchOptions?: FetchOptions
|
|
92
|
+
): Promise<Page | null> {
|
|
93
|
+
return super.getPreview(previewData as EditingPreviewData, fetchOptions);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Parses components from nextjs component map and layoutData, executes getServerProps/getStaticProps methods
|
|
98
|
+
* and returns resulting props from components
|
|
99
|
+
* @param {LayoutServiceData} layoutData layout data to parse compnents from
|
|
100
|
+
* @param {ComponentMap<AstroContentSdkComponent>} components component map to get props for
|
|
101
|
+
* @returns {ComponentPropsCollection} component props
|
|
102
|
+
*/
|
|
103
|
+
async getComponentData(
|
|
104
|
+
layoutData: LayoutServiceData,
|
|
105
|
+
// context: GetServerSidePropsContext | GetStaticPropsContext,
|
|
106
|
+
components: ComponentMap<AstroContentSdkComponent>
|
|
107
|
+
): Promise<ComponentPropsCollection> {
|
|
108
|
+
let componentProps: ComponentPropsCollection = {};
|
|
109
|
+
if (!layoutData.sitecore.route) return componentProps;
|
|
110
|
+
// Retrieve component props using side-effects defined on components level
|
|
111
|
+
componentProps = await this.componentPropsService.fetchComponentProps({
|
|
112
|
+
layoutData: layoutData,
|
|
113
|
+
// context,
|
|
114
|
+
components,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const errors = Object.keys(componentProps)
|
|
118
|
+
.map((id) => {
|
|
119
|
+
const component = componentProps[id] as ComponentPropsError;
|
|
120
|
+
|
|
121
|
+
return component.error
|
|
122
|
+
? `\nUnable to get component props for ${component.componentName} (${id}): ${component.error}`
|
|
123
|
+
: '';
|
|
124
|
+
})
|
|
125
|
+
.join('');
|
|
126
|
+
|
|
127
|
+
if (errors.length) {
|
|
128
|
+
throw new Error(errors);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return componentProps;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
protected getComponentPropsService(): ComponentPropsService {
|
|
135
|
+
return new ComponentPropsService();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Image as OptimizedImage } from 'astro:assets';
|
|
3
|
+
import { useSitecore } from '../context';
|
|
4
|
+
import type {
|
|
5
|
+
BaseImageProps,
|
|
6
|
+
ImageField,
|
|
7
|
+
ImageFieldValue,
|
|
8
|
+
} from './Image.astro';
|
|
9
|
+
import Image from './Image.astro';
|
|
10
|
+
import { EditableFieldProps } from './sharedTypes';
|
|
11
|
+
import { isFieldValueEmpty } from '@sitecore-content-sdk/core/layout';
|
|
12
|
+
import { addClassName } from '../utils';
|
|
13
|
+
import { mediaApi } from '@sitecore-content-sdk/core/media';
|
|
14
|
+
|
|
15
|
+
const { page } = useSitecore();
|
|
16
|
+
let fallback = false,
|
|
17
|
+
render = true;
|
|
18
|
+
|
|
19
|
+
if (page.mode.isEditing || page.mode.isPreview) {
|
|
20
|
+
fallback = true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AstroImageProps extends BaseImageProps, EditableFieldProps {
|
|
24
|
+
// srcSet attribute for Astro Image
|
|
25
|
+
widths?: number[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
editable = true,
|
|
30
|
+
imageParams,
|
|
31
|
+
field,
|
|
32
|
+
alt,
|
|
33
|
+
mediaUrlPrefix,
|
|
34
|
+
emptyFieldEditingComponent,
|
|
35
|
+
...otherProps
|
|
36
|
+
} = Astro.props as AstroImageProps;
|
|
37
|
+
|
|
38
|
+
const dynamicMedia = field as ImageField | ImageFieldValue;
|
|
39
|
+
|
|
40
|
+
const isEmptyField = isFieldValueEmpty(dynamicMedia);
|
|
41
|
+
|
|
42
|
+
if (fallback || isEmptyField) {
|
|
43
|
+
render = false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let img;
|
|
47
|
+
if (!isEmptyField) {
|
|
48
|
+
// handle image directly on field for forgetful devs
|
|
49
|
+
img = (dynamicMedia as ImageFieldValue).src
|
|
50
|
+
? { ...field }
|
|
51
|
+
: (dynamicMedia.value as ImageFieldValue);
|
|
52
|
+
|
|
53
|
+
if (img) {
|
|
54
|
+
// prevent metadata from being passed to the img tag
|
|
55
|
+
if (img.metadata) {
|
|
56
|
+
delete img.metadata;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const getImageAttrs = (
|
|
62
|
+
{
|
|
63
|
+
src,
|
|
64
|
+
...otherAttrs
|
|
65
|
+
}: {
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
src?: string;
|
|
68
|
+
},
|
|
69
|
+
imageParams?: { [paramName: string]: string | number },
|
|
70
|
+
mediaUrlPrefix?: RegExp
|
|
71
|
+
) => {
|
|
72
|
+
if (!src) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
addClassName(otherAttrs);
|
|
76
|
+
|
|
77
|
+
const newAttrs: { [attr: string]: unknown } = {
|
|
78
|
+
...otherAttrs,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// update image URL for content sdk handler and image rendering params
|
|
82
|
+
const resolvedSrc = mediaApi.updateImageUrl(src, imageParams, mediaUrlPrefix);
|
|
83
|
+
|
|
84
|
+
// always output original src as fallback for older browsers
|
|
85
|
+
newAttrs.src = resolvedSrc;
|
|
86
|
+
return newAttrs;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const attrs = getImageAttrs(
|
|
90
|
+
{ ...img, ...otherProps },
|
|
91
|
+
imageParams,
|
|
92
|
+
mediaUrlPrefix
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const getAttributes = (): any => {
|
|
96
|
+
return {
|
|
97
|
+
...attrs,
|
|
98
|
+
alt: alt ? alt : (attrs?.alt ?? ''),
|
|
99
|
+
src: attrs?.src,
|
|
100
|
+
height: attrs?.height,
|
|
101
|
+
width: attrs?.width,
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (attrs?.width === undefined || attrs?.height === undefined) {
|
|
106
|
+
console.warn('Missing width or height attribute');
|
|
107
|
+
render = false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const attributes = getAttributes();
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
{render && <OptimizedImage {...attributes} />}
|
|
114
|
+
{fallback && <Image {...Astro.props} />}
|
|
@@ -0,0 +1,76 @@
|
|
|
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 DateFieldProps extends EditableFieldProps {
|
|
12
|
+
/** The date field data. */
|
|
13
|
+
[htmlAttributes: string]: unknown;
|
|
14
|
+
field: FieldMetadata & {
|
|
15
|
+
value?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The HTML element that will wrap the contents of the field.
|
|
19
|
+
*/
|
|
20
|
+
tag?: string;
|
|
21
|
+
|
|
22
|
+
render?: (date: Date | null) => string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
field,
|
|
27
|
+
tag,
|
|
28
|
+
editable = true,
|
|
29
|
+
render,
|
|
30
|
+
emptyFieldEditingComponent,
|
|
31
|
+
...otherProps
|
|
32
|
+
} = Astro.props as DateFieldProps;
|
|
33
|
+
|
|
34
|
+
const isEmptyField = isFieldValueEmpty(field);
|
|
35
|
+
|
|
36
|
+
const htmlProps: {
|
|
37
|
+
[htmlAttr: string]: unknown;
|
|
38
|
+
} = {
|
|
39
|
+
...otherProps,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let children;
|
|
43
|
+
if (!isEmptyField) {
|
|
44
|
+
if (render) {
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
children = render(field.value ? new Date(field.value) : null);
|
|
47
|
+
} else {
|
|
48
|
+
children = field.value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const Tag = tag || 'span';
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
{
|
|
56
|
+
(
|
|
57
|
+
<WithFieldMetadata field={field} editable={editable}>
|
|
58
|
+
<WithEmptyFieldEditingComponent
|
|
59
|
+
field={field}
|
|
60
|
+
editable={editable}
|
|
61
|
+
defaultEmptyFieldEditingComponent={
|
|
62
|
+
DefaultEmptyFieldEditingComponentText
|
|
63
|
+
}
|
|
64
|
+
emptyFieldEditingComponent={emptyFieldEditingComponent}
|
|
65
|
+
{...otherProps}
|
|
66
|
+
>
|
|
67
|
+
{!isEmptyField && (
|
|
68
|
+
<>
|
|
69
|
+
{tag && <Tag {...htmlProps} set:html={children} />}
|
|
70
|
+
{!tag && <Fragment {...htmlProps} set:html={children} />}
|
|
71
|
+
</>
|
|
72
|
+
)}
|
|
73
|
+
</WithEmptyFieldEditingComponent>
|
|
74
|
+
</WithFieldMetadata>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
export interface EmptyFieldEditingComponentImageProps {
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { className, ...props} = Astro.props as EmptyFieldEditingComponentImageProps;
|
|
8
|
+
|
|
9
|
+
const inlineStyles = {
|
|
10
|
+
minWidth: '48px',
|
|
11
|
+
minHeight: '48px',
|
|
12
|
+
maxWidth: '400px',
|
|
13
|
+
maxHeight: '400px',
|
|
14
|
+
cursor: 'pointer',
|
|
15
|
+
};
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<img
|
|
19
|
+
{...props}
|
|
20
|
+
alt=""
|
|
21
|
+
src='data:image/svg+xml,%3Csvg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 240 240" style="enable-background:new 0 0 240 240;" xml:space="preserve"%3E%3Cstyle type="text/css"%3E .st0%7Bfill:none;%7D .st1%7Bfill:%23969696;%7D .st2%7Bfill:%23FFFFFF;%7D .st3%7Bfill:%23FFFFFF;stroke:%23FFFFFF;stroke-width:0.75;stroke-miterlimit:10;%7D%0A%3C/style%3E%3Cg%3E%3Crect class="st0" width="240" height="240"/%3E%3Cg%3E%3Cg%3E%3Crect x="20" y="20" class="st1" width="200" height="200"/%3E%3C/g%3E%3Cg%3E%3Ccircle class="st2" cx="174" cy="67" r="14"/%3E%3Cpath class="st2" d="M174,54c7.17,0,13,5.83,13,13s-5.83,13-13,13s-13-5.83-13-13S166.83,54,174,54 M174,52 c-8.28,0-15,6.72-15,15s6.72,15,15,15s15-6.72,15-15S182.28,52,174,52L174,52z"/%3E%3C/g%3E%3Cpolyline class="st3" points="29.5,179.25 81.32,122.25 95.41,137.75 137.23,91.75 209.5,179.75 "/%3E%3C/g%3E%3C/g%3E%3C/svg%3E'
|
|
22
|
+
class={`scEmptyImage ${className || ''}`}
|
|
23
|
+
style={inlineStyles}
|
|
24
|
+
/>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
export interface EmptyFieldEditingComponentTextProps {
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
tag?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const props = Astro.props as EmptyFieldEditingComponentTextProps;
|
|
8
|
+
|
|
9
|
+
const Tag = props.tag || 'span';
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<Tag {...props} set:html="[No text in field]" />
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Renders client scripts and data for editing/preview mode for Pages.
|
|
4
|
+
* Renders script required for the Design Library (when mode.isDesignLibrary is true).
|
|
5
|
+
* @returns An Astro component containing the editing scripts or an empty fragment if not in editing/preview mode.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useSitecore } from '../context';
|
|
9
|
+
import { getContentSdkPagesClientData, getDesignLibraryScriptLink } from '@sitecore-content-sdk/core/editing';
|
|
10
|
+
|
|
11
|
+
const { page: { mode, layout }, api } = useSitecore();
|
|
12
|
+
const { clientData, clientScripts } = layout.sitecore.context;
|
|
13
|
+
|
|
14
|
+
let scriptUrl;
|
|
15
|
+
if (mode.isDesignLibrary) {
|
|
16
|
+
// Add cache buster to the script URL (format hh-dd-mm-yyyy, UTC)
|
|
17
|
+
const now = new Date();
|
|
18
|
+
const hour = String(now.getUTCHours()).padStart(2, '0');
|
|
19
|
+
const day = String(now.getUTCDate()).padStart(2, '0');
|
|
20
|
+
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
21
|
+
const year = String(now.getUTCFullYear());
|
|
22
|
+
const cacheTimestamp = `${hour}-${day}-${month}-${year}`;
|
|
23
|
+
scriptUrl = `${getDesignLibraryScriptLink(api?.edge?.edgeUrl)}?cb=${cacheTimestamp}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const contentSdkClientData = { ...clientData, ...getContentSdkPagesClientData() };
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<>
|
|
30
|
+
<!-- Don't render anything if not in editing mode and rendering type is not component -->
|
|
31
|
+
{ (mode.isNormal || mode.isPreview) && <></> }
|
|
32
|
+
|
|
33
|
+
<!-- In case of Design Library - render only the script for Design Library -->
|
|
34
|
+
{ mode.isDesignLibrary && <script src={scriptUrl}></script> }
|
|
35
|
+
|
|
36
|
+
{ mode.isEditing && <>
|
|
37
|
+
{clientScripts?.map((src) => (
|
|
38
|
+
<script src={src} />
|
|
39
|
+
))}
|
|
40
|
+
{Object.keys(contentSdkClientData).map((id) => (
|
|
41
|
+
<script
|
|
42
|
+
id={id}
|
|
43
|
+
type="application/json"
|
|
44
|
+
set:html={JSON.stringify(contentSdkClientData[id])}
|
|
45
|
+
/>
|
|
46
|
+
))}
|
|
47
|
+
</>
|
|
48
|
+
}
|
|
49
|
+
</>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Page } from '@sitecore-content-sdk/core/client';
|
|
3
|
+
import { ComponentRendering } from '@sitecore-content-sdk/core/layout';
|
|
4
|
+
import { AstroContentSdkComponent } from '../sharedTypes/component-props';
|
|
5
|
+
import { useSitecore } from '../context';
|
|
6
|
+
|
|
7
|
+
export type ErrorBoundaryProps = {
|
|
8
|
+
page: Page;
|
|
9
|
+
isDynamic?: boolean;
|
|
10
|
+
errorComponent?: AstroContentSdkComponent;
|
|
11
|
+
rendering?: ComponentRendering;
|
|
12
|
+
componentLoadingMessage?: string;
|
|
13
|
+
disableSuspense?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let props = Astro.props as ErrorBoundaryProps;
|
|
17
|
+
const { page } = useSitecore();
|
|
18
|
+
props = { ...props, page };
|
|
19
|
+
|
|
20
|
+
const defaultErrorMessage = 'There was a problem loading this section.';
|
|
21
|
+
|
|
22
|
+
const isInDevMode = (): boolean => {
|
|
23
|
+
return process.env.NODE_ENV === 'development';
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const showErrorDetails = (): boolean => {
|
|
27
|
+
return (
|
|
28
|
+
isInDevMode() || props.page.mode.isEditing || props.page.mode.isPreview
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
let state: { error?: Error } = {};
|
|
33
|
+
|
|
34
|
+
let html;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
html = await Astro.slots.render('default');
|
|
38
|
+
} catch (e) {
|
|
39
|
+
state = { error: e };
|
|
40
|
+
if (showErrorDetails()) {
|
|
41
|
+
console.error(
|
|
42
|
+
`An error occurred in component ${props.rendering?.componentName} (${props.rendering?.uid}): `
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
console.error(e);
|
|
46
|
+
}
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
<>
|
|
50
|
+
{!state.error && <Fragment set:html={html} />}
|
|
51
|
+
{
|
|
52
|
+
state.error && props.errorComponent && (
|
|
53
|
+
<props.errorComponent error={state.error} />
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
{
|
|
57
|
+
state.error && !props.errorComponent && showErrorDetails() && (
|
|
58
|
+
<div>
|
|
59
|
+
<div class="sc-content-sdk-placeholder-error">
|
|
60
|
+
A rendering error occurred in component{' '}
|
|
61
|
+
<em>{props.rendering?.componentName}</em>
|
|
62
|
+
<br />
|
|
63
|
+
Error: <em>{state.error.message || JSON.stringify(state.error)}</em>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
{
|
|
69
|
+
state.error && !props.errorComponent && !showErrorDetails() && (
|
|
70
|
+
<div>
|
|
71
|
+
<div class="sc-content-sdk-placeholder-error">
|
|
72
|
+
{defaultErrorMessage}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
</>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* The component which renders field metadata markup
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { MetadataKind } from '@sitecore-content-sdk/core/editing';
|
|
7
|
+
|
|
8
|
+
interface FieldMetadataProps {
|
|
9
|
+
metadata: { [key: string]: unknown };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = Astro.props as FieldMetadataProps;
|
|
13
|
+
|
|
14
|
+
const data = JSON.stringify(props.metadata);
|
|
15
|
+
|
|
16
|
+
const attributes = {
|
|
17
|
+
type: 'text/sitecore',
|
|
18
|
+
chrometype: 'field',
|
|
19
|
+
className: 'scpm',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const codeOpenAttributes = { ...attributes, kind: MetadataKind.Open };
|
|
23
|
+
const codeCloseAttributes = { ...attributes, kind: MetadataKind.Close };
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<>
|
|
27
|
+
<code {...codeOpenAttributes}>{data}</code>
|
|
28
|
+
<slot />
|
|
29
|
+
<code {...codeCloseAttributes}>{}</code>
|
|
30
|
+
</>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { isFieldValueEmpty } from '@sitecore-content-sdk/core/layout';
|
|
3
|
+
|
|
4
|
+
export interface FileFieldValue {
|
|
5
|
+
[propName: string]: unknown;
|
|
6
|
+
src?: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
displayName?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FileField {
|
|
12
|
+
value: FileFieldValue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface FileProps {
|
|
16
|
+
[attributeName: string]: unknown;
|
|
17
|
+
/** The file field data. */
|
|
18
|
+
field: FileFieldValue | FileField;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { field, ...otherProps } = Astro.props as FileProps;
|
|
22
|
+
|
|
23
|
+
const dynamicField: FileField | FileFieldValue = field;
|
|
24
|
+
|
|
25
|
+
const isEmptyField = isFieldValueEmpty(dynamicField);
|
|
26
|
+
|
|
27
|
+
let file, linkText, anchorAttrs, finalChildren;
|
|
28
|
+
|
|
29
|
+
if (!isEmptyField) {
|
|
30
|
+
// handle link directly on field for forgetful devs
|
|
31
|
+
file = (
|
|
32
|
+
(dynamicField as FileFieldValue).src ? field : dynamicField.value
|
|
33
|
+
) as FileFieldValue;
|
|
34
|
+
|
|
35
|
+
if (file) {
|
|
36
|
+
const children = await Astro.slots.render('default');
|
|
37
|
+
linkText = !children ? file.title || file.displayName : null;
|
|
38
|
+
anchorAttrs = { href: file.src };
|
|
39
|
+
finalChildren = children ? children : linkText;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const attrs = { ...anchorAttrs, ...otherProps };
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
{!isEmptyField && file && <a {...attrs} set:html={finalChildren}></a>}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
const baseStyles = {
|
|
3
|
+
backgroundSize: '3px 3px',
|
|
4
|
+
display: 'flex',
|
|
5
|
+
justifyContent: 'center',
|
|
6
|
+
alignItems: 'center',
|
|
7
|
+
padding: '30px',
|
|
8
|
+
color: '#aaa',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const backgroundImageStyle = {
|
|
12
|
+
backgroundImage:
|
|
13
|
+
'linear-gradient(45deg, #ffffff 25%, #dcdcdc 25%, #dcdcdc 50%, #ffffff 50%, #ffffff 75%, #dcdcdc 75%, #dcdcdc 100%)',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const styles =
|
|
17
|
+
process.env.NODE_ENV === 'test'
|
|
18
|
+
? baseStyles
|
|
19
|
+
: { ...baseStyles, ...backgroundImageStyle };
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<div style={styles}>The component is hidden</div>
|