-
Notifications
You must be signed in to change notification settings - Fork 78
Open
Description
I was trying to add a custom component to display a gallery of 6 images fetched from AWS S3 (which contains >= six images at root). For this, I copied the MediaGallerySection and created a file content/pages/backgrounds.md. I've then added mappings for it to src/components/components-registry.tsx, and src/types/generated.ts following the pattern I saw. However, I got an error. In an effort to solve that, I fix a simple mistake, but then got another error.
In the visual editor, it is being rendered as expected. However, I have two issues:-
- When editing another section anywhere through the visual editor, the preview fails to load and I have to restart the editor
- Also, i can't edit this element (section) through the visual editor. I have to go to code every time I want to make changes to it
Here's the error I got in the logs (the first one appeared as soon as I added the new component, and the latter after trying to do some workarounds):-
7:51:05 PM: [error] Error converting value in file 'content/pages/backgrounds.md' at field path 'sections.0' of declared type 'model'. No model found with name: WallpaperGallerySection.
7:51:05 PM: [error] Error converting value in file 'content/pages/backgrounds.md' at field path 'sections' of declared type 'list'. One of the list items could not be converted, ignoring the while list.
7:51:20 PM: [error] AnnotationError: Field path sections.0 not found or does not match content schema fieldPath:'sections.0', value:'{"oid":null,"fp":"sections.0","loc":"*[1]","hasOnlyTextNodes":false}', elementXPath:'/html/body[1]/div[1]/data[1]/data[1]/div[1]/div[2]/main[1]/div[1]/data[1]'
7:51:24 PM: [error] AnnotationError: Field path sections.0 not found or does not match content schema fieldPath:'sections.0', value:'{"oid":null,"fp":"sections.0","loc":"*[1]","hasOnlyTextNodes":false}', elementXPath:'/html/body[1]/div[1]/data[1]/data[1]/div[1]/div[2]/main[1]/div[1]/data[1]'
And here's the code for the component:-
import * as React from 'react';
import classNames from 'classnames';
import { mapStylesToClassNames as mapStyles } from '../../../utils/map-styles-to-class-names';
import Section from '../Section';
import ImageBlock from '../../molecules/ImageBlock';
type MediaGallerySectionProps = {
type: string;
elementId: string;
colors?: 'colors-a' | 'colors-b' | 'colors-c' | 'colors-d' | 'colors-e' | 'colors-f';
title?: string;
subtitle?: string;
spacing?: number;
columns?: number;
aspectRatio?: string;
showCaption: boolean;
enableHover: boolean;
styles?: any;
};
type MediaGalleryItemProps = {
image: Image;
showCaption: boolean;
enableHover: boolean;
aspectRatio: string;
};
type Image = {
url: string;
altText: string;
caption: string;
};
export default function WallpaperGallerySection(props: MediaGallerySectionProps) {
const {
type,
elementId,
colors,
title,
subtitle,
spacing = 16,
columns = 4,
aspectRatio = '1:1',
showCaption,
enableHover,
styles = {}
} = props;
const [images, setImages] = React.useState<Image[]>([]);
const fetchImages = async () => {
const bucketName = '......';
const region = 'ap-south-1';
try {
const response = await fetch(`https://${bucketName}.s3.${region}.amazonaws.com/?list-type=directory`);
const data = await response.text();
const parser = new DOMParser();
const xml = parser.parseFromString(data, 'text/xml');
const contents = xml.getElementsByTagName('Contents');
const imageUrls = Array.from(contents).map(item => {
const imageKey = item.getElementsByTagName('Key')[0].textContent;
return {
url: `https://${bucketName}.s3.${region}.amazonaws.com/${imageKey}`,
altText: `Wallpaper ${imageKey}`, // Customize alt text as needed
caption: imageKey || '' // You can customize captions if needed
};
});
// Randomly select 6 images
const selectedImages = selectRandomImages(imageUrls, 6);
setImages(selectedImages);
} catch (error) {
console.error('Error fetching images:', error);
}
};
// Function to select random images from the array
const selectRandomImages = (imageArray: Image[], count: number): Image[] => {
const shuffled = [...imageArray].sort(() => 0.5 - Math.random());
return shuffled.slice(0, count);
};
React.useEffect(() => {
fetchImages();
}, []);
return (
<Section type={type} elementId={elementId} colors={colors} styles={styles.self}>
{title && <h2 className={classNames(styles.title ? mapStyles(styles.title) : null)}>{title}</h2>}
{subtitle && (
<p
className={classNames('text-lg', 'sm:text-xl', styles.subtitle ? mapStyles(styles.subtitle) : null, {
'mt-6': title
})}
>
{subtitle}
</p>
)}
{images.length > 0 && (
<div
className={classNames('grid', 'place-items-center', mapColStyles(columns), {
'mt-12': title || subtitle
})}
style={{
gap: spacing ? `${spacing}px` : undefined
}}
>
{images.map((image, index) => (
<MediaGalleryImage
key={index}
image={image}
showCaption={showCaption}
enableHover={enableHover}
aspectRatio={aspectRatio}
/>
))}
</div>
)}
</Section>
);
}
function MediaGalleryImage(props: MediaGalleryItemProps) {
const { image, showCaption, enableHover, aspectRatio } = props;
if (!image) {
return null;
}
return (
<figure
className={classNames('overflow-hidden', 'relative', 'w-full', mapAspectRatioStyles(aspectRatio), {
'h-0': aspectRatio !== 'auto'
})}
>
<ImageBlock
url={image.url}
altText={image.altText}
className={classNames('w-full', {
'h-full absolute left-0 top-0 object-cover': aspectRatio !== 'auto',
'transition-transform hover:scale-105': enableHover
})}
/>
{showCaption && image.caption && (
<figcaption className="absolute bg-white/50 text-dark left-0 mx-2 bottom-2 p-1.5 text-xs pointer-events-none">{image.caption}</figcaption>
)}
</figure>
);
}
function mapAspectRatioStyles(aspectRatio: string) {
switch (aspectRatio) {
case '1:1':
return 'pt-1/1';
case '2:3':
return 'pt-3/2';
case '3:2':
return 'pt-2/3';
case '3:4':
return 'pt-4/3';
case '4:3':
return 'pt-3/4';
case '16:9':
return 'pt-9/16';
default:
return null;
}
}
function mapColStyles(columns: number) {
switch (columns) {
case 2:
return 'grid-cols-2';
case 3:
return 'grid-cols-2 sm:grid-cols-3';
case 4:
return 'grid-cols-2 sm:grid-cols-4';
case 5:
return 'grid-cols-2 sm:grid-cols-3 md:grid-cols-5';
case 6:
return 'grid-cols-2 sm:grid-cols-4 md:grid-cols-6';
case 7:
return 'grid-cols-2 sm:grid-cols-4 md:grid-cols-7';
default:
return null;
}
}Metadata
Metadata
Assignees
Labels
No labels