Pular para o conteúdo principal

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.

info

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:

PropriedadeDescriçãoObrigatórioExemploPadrão
schemaO 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 delesSimVer exemplo abaixo-
defaultContentO conteúdo padrão do componente. Define os valores iniciais para as props do componente.Sim (em 99% dos casos){ text: "Olá Mundo" }-
uiSchemaA UI Schema do componente. São utilitários do CMS que permitem personalizar a interface de edição do componente.NãoVer exemplo abaixo-
pathO 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.
sliceWidgetInformaçõ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ãoVer exemplo abaixo-
idO 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.ts
// ...

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)

Component.schema.ts
// ...
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

Component.schema.ts
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',
},
}
}
},
},
}
info

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

info

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:

Hero/Accordion.schema.ts
// ...
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.

Hero.schema.ts
//...

const path = 'Hero';
Hero/Accordion.schema.ts
//...

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" />',
},
};
  1. Exemplo para um componente de Texto:
const sliceWidget = {
'TextSlice': {
name: 'Texto Slice',
icon: '🍎',
},
};
  1. 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.schema.ts
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

Alert.schema.ts
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
info

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.