Arquivo *.schema
Introdução
Por padrão, sempre que desejamos transformar algum dos nossos componentes em um componente editável pelo CMS, criamos um arquivo chamado nome_componente.schema.ts
. Nesse arquivo, definimos as propriedades para configurar o componente no CMS. Para que um componente seja editável, ele precisa estender a tipagem de CmsFunctionComponent
.
Na prática, caso você não queira seguir essa convenção, dentro do próprio arquivo do componente, você pode definir as propriedades do componente. No entanto, lembre-se de que isso pode dificultar a manutenção do componente. Utilizando os arquivos .schema
, é possível separar as propriedades e a lógica do componente das configurações do CMS.
Estrutura do Arquivo .schema
O arquivo .schema
deve ser escrito em TypeScript e seguir um padrão específico que permitirá a integração adequada do componente com o CMS. Esse padrão exige a implementação de algumas propriedades específicas. A seguir, descrevemos essas propriedades e o que elas representam:
Propriedade | Descrição | Obrigatório | Exemplo | Padrão |
---|---|---|---|---|
schema | O schema do componente. Será usado para gerar o formulário no CMS com as props atuais do componente para edição. Por baixo dos panos e' usado a mesma de estrutura de JSON Content Schemas. Qual quer duvida sobre isso recomendamos que deem uma olhada na documentação oficial deles | Sim | Ver exemplo abaixo | - |
defaultContent | O conteúdo padrão do componente. Define os valores iniciais para as props do componente. | Sim (em 99% dos casos) | { text: "Olá Mundo" } | - |
uiSchema | A UI Schema do componente. São utilitários do CMS que permitem personalizar a interface de edição do componente. | Não | Ver exemplo abaixo | - |
path | O caminho do componente no CMS, usado para gerar a estrutura do menu lateral do CMS. Sempre vai seguir o padrão "{tag}" ou "{tag}/{tag}..." . Só recomendamos usar caso você tenha componentes editáveis que estão dentro de outros componentes editáveis. | Não | "HeroSection" ou "HeroSection/Card" | Se não for definido, o padrão será o id do componente. |
sliceWidget | Informações para transformar o componente disponível na área editável. É usado para criar componentes adicionáveis, removíveis e manipulados pelo usuário. | Não | Ver exemplo abaixo | - |
id | O ID único do componente. É usado para identificar o componente no CMS e fazer o link entre o sliceWidget e o componente React. Esse ID não pode se repetir entre os componentes React. | Sim | "nome_componente" | - |
Exemplos de propriedades
schema
Exemplo para um componente de botão simples:
const schema = {
type: 'object',
title: 'Button',
properties: {
text: {
type: 'string',
title: 'Text',
default: 'Click me',
},
color: {
type: 'string',
title: 'Color',
enum: ['primary', 'secondary'],
default: 'primary',
},
},
};
Exemplo para um componente de imagem:
const schema = {
type: 'object',
title: 'Image',
properties: {
src: {
type: 'string',
title: 'Source',
default: 'https://example.com/image.jpg',
},
alt: {
type: 'string',
title: 'Alternative Text',
default: 'A beautiful image',
},
width: {
type: 'number',
title: 'Width',
default: 400,
},
height: {
type: 'number',
title: 'Height',
default: 300,
},
},
};
Exemplo para um componente de formulário:
const schema = {
type: 'object',
title: 'Form',
properties: {
username: {
type: 'string',
title: 'Username',
default: '',
},
password: {
type: 'string',
title: 'Password',
default: '',
},
rememberMe: {
type: 'boolean',
title: 'Remember Me',
default: true,
},
},
};
Usando $ref para reutilizar schemas:
// ...
component.schema = {
type: 'object',
title: "Mapa",
properties: {
primaryAddress: {
$ref: '#/definitions/address',
},
othersAddress: {
type: 'array',
items: {
$ref: '#/definitions/address',
},
}
},
definitions: {
address: {
title: "Endereço",
type: "object",
properties: {
streetAddress: {
title: "Rua",
type: "string",
description: "O endereço da rua. Por exemplo, 1600 Amphitheatre Pkwy.",
},
addressLocality: {
title: "Localidade",
type: "string",
description: "A localidade. Por exemplo, Mountain View.",
},
addressRegion: {
title: "Região",
type: "string",
description: "A região. Por exemplo, CA.",
},
postalCode: {
title: "Código postal",
type: "string",
description: "O código postal. Por exemplo, 94043.",
},
addressCountry: {
title: "País",
type: "string",
description: "O país. Por exemplo, USA. Você pode usar o código de país de 2 letras em ISO 3166-1 alpha-2.",
},
},
required: ["streetAddress", "addressLocality", "postalCode", "addressCountry"],
},
}
defaultContent
Componente de Card:
const defaultContent = {
title: 'Card Title',
content: 'This is the default content for the card.',
imageSrc: 'https://example.com/card-image.jpg',
};
Componente de Slider:
const defaultContent = {
slides: [
{ image: 'https://example.com/slide1.jpg', caption: 'Slide 1' },
{ image: 'https://example.com/slide2.jpg', caption: 'Slide 2' },
{ image: 'https://example.com/slide3.jpg', caption: 'Slide 3' },
],
autoPlay: true,
interval: 5000,
};
Componente de Tabela:
const defaultContent = {
headers: ['Name', 'Age', 'Email'],
rows: [
['John Doe', 30, '[email protected]'],
['Jane Smith', 25, '[email protected]'],
],
sortable: true,
};
uiSchema
Exemplo para um componente de DatePicker:
const uiSchema = {
date: {
'ui:widget': 'DatePickerWidget',
},
};
Upload de imagem :
const uiSchema: CmsFunctionalComponent<HeroProps>['uiSchema'] = {
imageSrc: {
'ui:widget': 'UploadImageWidget',
},
}
Widget dentro de um array de primitivos (string
, number
, boolean
)
// ...
const schema: CmsFunctionalComponent<HeroProps>['schema'] = {
title: id,
type: 'object',
properties: {
images: {
title: 'Images',
type: 'array',
items: {
type: 'string',
}
},
},
}
const uiSchema: CmsFunctionalComponent<ComponentProps>['uiSchema'] = {
images: {
items: {
'ui:widget': 'UploadImageWidget',
}
}
}
// ...
Widgeet dentro de um array de objetos
const uiSchema: CmsFunctionalComponent<ComponentProps>['uiSchema'] = {
images: {
items: {
imageSrc: {
'ui:widget': 'UploadImageWidget',
}
}
}
}
Nesse caso temos que seguir a mesma estrutura do schema
e adicionar o uiSchema
dentro do items
do array.
Olha como estava o schema
:
const schema: CmsFunctionalComponent<HeroProps>['schema'] = {
title: id,
type: 'object',
properties: {
images: {
title: 'Images',
type: 'array',
items: {
type: 'object',
properties: {
imageSrc: {
title: 'Image source',
type: 'string',
description: 'The source of the image',
// Se passar uma url de imagem que nao exista como padrão, ela pode nao aparecer no CMS
},
imageAlt: {
title: 'Image alt',
type: 'string',
description: 'The alt of the image',
},
}
}
},
},
}
Se voce estiver na versão 1.1.2 do @m3cms/react tem um bug de tipagem, então precisamos usar o @ts-ignore
path
Se por algum motivo o voce adicionar um path de 2 ou mais níveis e a sua nao existem um componente pai, o seu componente nao vai aparecer no CMS.
Exemplo:
// ...
const path = 'Hero/Accordion';
mas o componente Hero
nao existe, entao o componente Accordion
nao vai aparecer no CMS.
para isso funcionar precisamos criar o componente Hero
e adicionar o componente Accordion
como um filho dele.
//...
const path = 'Hero';
//...
const path = 'Hero/Accordion';
Exemplo para um componente:
const path = 'ComponentePai/ComponenteFilho';
sliceWidget
de Banner:
const sliceWidget = {
'BannerSlice': {
name: 'Banner Slice',
icon: '<img src="https://m3cms.vercel.app/img/logo.svg" />',
},
};
- Exemplo para um componente de Texto:
const sliceWidget = {
'TextSlice': {
name: 'Texto Slice',
icon: '🍎',
},
};
- Usando um SVG como ícone:
const sliceWidget = {
'GallerySlice': {
name: 'Galeria Slice',
icon: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M0 0h24v24H0z' fill='none'/><path d='M19 13H5v-2h14v2z'/></svg>",
},
};
id
Exemplo para um componente de Banner:
const id = 'Banner';
Exemplos arquivos .schema completos
Banner
import type { CmsFunctionalComponent } from '@m3cms/react'
import type { HeroProps } from './Hero'
import { default as component } from './Hero'
const id = 'FsHero' as const
const defaultContent: CmsFunctionalComponent<HeroProps>['defaultContent'] = {
title: '20% off on first purchase',
subtitle:
'At FastStore you can shop the best tech of 2023. Enjoy and get 10% off on your first purchase!',
linkText: 'See',
imageAlt: 'office',
imageSrc:
'https://storeframework.vtexassets.com/assets/vtex.file-manager-graphql/images/17555ae3-3405-4be6-a19a-e76997a333ea___1fa52ed2b11886ffe215bab9588c345d.jpg',
colorVariant: 'main',
variant: 'primary',
link: '',
}
const schema: CmsFunctionalComponent<HeroProps>['schema'] = {
title: id,
type: 'object',
properties: {
title: {
title: 'Title',
type: 'string',
default: defaultContent.title,
description: 'The title of the hero',
},
subtitle: {
title: 'Subtitle',
type: 'string',
default: defaultContent.subtitle,
description: 'The subtitle of the hero',
},
linkText: {
title: 'Link text',
type: 'string',
default: defaultContent.linkText,
description: 'The text of the link',
},
link: {
title: 'Link',
type: 'string',
default: defaultContent.link,
},
imageSrc: {
title: 'Image source',
type: 'string',
default: defaultContent.imageSrc,
description: 'The source of the image',
},
imageAlt: {
title: 'Image alt',
type: 'string',
default: defaultContent.imageAlt,
description: 'The alt of the image',
},
colorVariant: {
title: 'Color variant',
type: 'string',
enum: ['main', 'light', 'accent'],
default: defaultContent.colorVariant,
},
variant: {
title: 'Variant',
type: 'string',
enum: ['primary', 'secondary'],
default: defaultContent.variant,
},
},
}
const uiSchema: CmsFunctionalComponent<HeroProps>['uiSchema'] = {
imageSrc: {
'ui:widget': 'UploadImageWidget',
},
}
const sliceWidget: CmsFunctionalComponent<HeroProps>['sliceWidget'] = {
[id]: {
name: 'Hero',
icon: '📝',
},
}
component.id = id
component.schema = schema
component.defaultContent = defaultContent
component.uiSchema = uiSchema
component.sliceWidget = sliceWidget
export default component
Alert
import type { CmsFunctionalComponent } from '@m3cms/react'
import type { AlertProps } from './Alert'
import { default as component } from './Alert'
const id = 'Alert' as const
const defaultContent: Required<
CmsFunctionalComponent<AlertProps>['defaultContent']
> = {
icon: 'Bell',
content: 'Get 10% off today: NEW10',
link: {
to: '/office',
text: 'Buy now',
},
dismissible: true,
}
const schema: CmsFunctionalComponent<AlertProps>['schema'] = {
type: 'object',
title: id,
properties: {
icon: {
type: 'string',
title: 'Icon',
default: defaultContent.icon,
},
content: {
type: 'string',
title: 'Content',
default: defaultContent.content as string,
},
link: {
type: 'object',
title: 'Link',
properties: {
to: {
type: 'string',
title: 'To',
default: defaultContent.link.to,
},
text: {
type: 'string',
title: 'Text',
default: defaultContent.link.text,
},
},
},
dismissible: {
type: 'boolean',
title: 'Dismissible',
default: defaultContent.dismissible,
},
},
}
const uiSchema: CmsFunctionalComponent<AlertProps>['uiSchema'] = {
dismissible: {
'ui:widget': 'SwitchWidget',
},
}
component.id = id
component.schema = schema
component.defaultContent = defaultContent
component.uiSchema = uiSchema
export default component
Lembre-se que o arquivo .schema
é um arquivo que vai refletir as propriedades do componente react. Então se voce alterar alguma propriedade do componente, voce precisa alterar o arquivo .schema
também.
Ou se voce alterar o arquivo .schema
voce precisa alterar o componente. E' uma via de mão dupla.