@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.
Files changed (87) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +3 -0
  3. package/package.json +101 -0
  4. package/src/client/index.ts +12 -0
  5. package/src/client/sitecore-astro-client.test.ts +271 -0
  6. package/src/client/sitecore-astro-client.ts +137 -0
  7. package/src/components/AstroImage.astro +114 -0
  8. package/src/components/Date.astro +76 -0
  9. package/src/components/DefaultEmptyFieldEditingComponentImage.astro +24 -0
  10. package/src/components/DefaultEmptyFieldEditingComponentText.astro +12 -0
  11. package/src/components/EditingScripts.astro +49 -0
  12. package/src/components/EmptyRendering.astro +3 -0
  13. package/src/components/ErrorBoundary.astro +77 -0
  14. package/src/components/FieldMetadata.astro +30 -0
  15. package/src/components/File.astro +46 -0
  16. package/src/components/HiddenRendering.astro +22 -0
  17. package/src/components/Image.astro +155 -0
  18. package/src/components/Link.astro +105 -0
  19. package/src/components/MissingComponent.astro +39 -0
  20. package/src/components/Placeholder/EmptyPlaceholder.astro +9 -0
  21. package/src/components/Placeholder/Placeholder.astro +100 -0
  22. package/src/components/Placeholder/PlaceholderMetadata.astro +102 -0
  23. package/src/components/Placeholder/PlaceholderUtils.astro +153 -0
  24. package/src/components/Placeholder/index.ts +5 -0
  25. package/src/components/Placeholder/models.ts +82 -0
  26. package/src/components/Placeholder/placeholder-utils.test.ts +162 -0
  27. package/src/components/Placeholder/placeholder-utils.ts +80 -0
  28. package/src/components/RenderWrapper.astro +31 -0
  29. package/src/components/RichText.astro +59 -0
  30. package/src/components/Text.astro +97 -0
  31. package/src/components/sharedTypes/index.ts +1 -0
  32. package/src/components/sharedTypes/props.ts +17 -0
  33. package/src/config/define-config.test.ts +526 -0
  34. package/src/config/define-config.ts +99 -0
  35. package/src/config/index.ts +1 -0
  36. package/src/config-cli/define-cli-config.test.ts +95 -0
  37. package/src/config-cli/define-cli-config.ts +50 -0
  38. package/src/config-cli/index.ts +1 -0
  39. package/src/context.ts +68 -0
  40. package/src/editing/constants.ts +8 -0
  41. package/src/editing/editing-config-middleware.test.ts +166 -0
  42. package/src/editing/editing-config-middleware.ts +111 -0
  43. package/src/editing/editing-render-middleware.test.ts +801 -0
  44. package/src/editing/editing-render-middleware.ts +288 -0
  45. package/src/editing/index.ts +16 -0
  46. package/src/editing/render-middleware.test.ts +57 -0
  47. package/src/editing/render-middleware.ts +51 -0
  48. package/src/editing/utils.test.ts +852 -0
  49. package/src/editing/utils.ts +308 -0
  50. package/src/enhancers/WithEmptyFieldEditingComponent.astro +56 -0
  51. package/src/enhancers/WithFieldMetadata.astro +31 -0
  52. package/src/env.d.ts +12 -0
  53. package/src/index.ts +16 -0
  54. package/src/middleware/index.ts +24 -0
  55. package/src/middleware/middleware.test.ts +507 -0
  56. package/src/middleware/middleware.ts +167 -0
  57. package/src/middleware/multisite-middleware.test.ts +672 -0
  58. package/src/middleware/multisite-middleware.ts +147 -0
  59. package/src/middleware/robots-middleware.test.ts +113 -0
  60. package/src/middleware/robots-middleware.ts +47 -0
  61. package/src/middleware/sitemap-middleware.test.ts +152 -0
  62. package/src/middleware/sitemap-middleware.ts +65 -0
  63. package/src/services/component-props-service.ts +182 -0
  64. package/src/sharedTypes/component-props.ts +17 -0
  65. package/src/site/index.ts +1 -0
  66. package/src/test-data/components/Bar.astro +0 -0
  67. package/src/test-data/components/Baz.astro +0 -0
  68. package/src/test-data/components/Foo.astro +0 -0
  69. package/src/test-data/components/Hero.variant.astro +0 -0
  70. package/src/test-data/components/NotComponent.bsx +0 -0
  71. package/src/test-data/components/Qux.astro +0 -0
  72. package/src/test-data/components/folded/Folded.astro +0 -0
  73. package/src/test-data/components/folded/random-file-2.docx +0 -0
  74. package/src/test-data/components/random-file.txt +0 -0
  75. package/src/test-data/helpers.ts +46 -0
  76. package/src/test-data/personalizeData.ts +63 -0
  77. package/src/tools/generate-map.ts +83 -0
  78. package/src/tools/index.ts +8 -0
  79. package/src/tools/templating/components.test.ts +305 -0
  80. package/src/tools/templating/components.ts +49 -0
  81. package/src/tools/templating/constants.ts +4 -0
  82. package/src/tools/templating/default-component.test.ts +31 -0
  83. package/src/tools/templating/default-component.ts +63 -0
  84. package/src/tools/templating/index.ts +2 -0
  85. package/src/utils/index.ts +1 -0
  86. package/src/utils/utils.test.ts +48 -0
  87. 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,3 @@
1
+ ---
2
+ ---
3
+ <></>
@@ -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>