Here you'll learn how to create awesome Store Framework blocks!
goldsteinr / store-block Goto Github PK
View Code? Open in Web Editor NEWHome Page: https://lab.github.com/vtex-trainings/vtex-store-block-course
Home Page: https://lab.github.com/vtex-trainings/vtex-store-block-course
Com o bloco customizado na loja, devemos aprender a internacionalizar o conteúdo apresentado.
É importante lembrar que os blocos devem sempre seguir boas práticas de localização, e não devem mostrar strings hardcoded, mas sim sensíveis a linguagem que a loja opera.
Não se preocupe, você não precisará adicionar traduções de todos os textos para as variadas linguagens nas quais o Store Framework é usado. Portanto, nessa etapa, serão apresentados conceitos acerca da internacionalização de apps e como fazê-la.
O conceito de messages facilita a adição de novos idiomas ao tema. As messages centralizam todos os serviços de tradução na plataforma. Dada um texto a ser traduzido, Messages irá primeiramente checar o contexto definido pelo usuário para, em seguida, checar as traduções das apps e, por fim, passa pelo sistema de tradução automática.
Na estrutura do diretório, é possível observar que há uma pasta chamada messages
, que apresenta três arquivos principais: pt.json
, en.json
e es.json
, cada um responsável pelas traduções: português, inglês e espanhol, respectivamente. Além disso, a fim de fornecer traduções automáticas melhores, é utilizado o arquivo context.json
, responsável por evitar ambiguidades.
Para utilizar tais definições, os arquivos de tradução mencionados anteriormente são JSON, cujas chaves são as mensagens e os valores são as traduções.
O arquivo
context.json
é necessário e precisa conter todas as mensagens, além de oferecer as traduções automáticas em casos excepcionais.
Você já deve ter aprendido a usar o nosso builder messages, e será através dele que serão adicionadas strings internacionalizadas nos componentes.
Para isso, na pasta /messages
, adicione agora uma mensagem de título para o componente:
messages/pt.json
{
...,
+ "countdown.title": "Contagem Regressiva"
}
messages/en.json
{
...,
+ "countdown.title": "Countdown"
}
messages/es.json
{
...,
+ "countdown.title": "Cuenta Regresiva"
}
messages/context.json
{
...,
+ "countdown.title": "Countdown"
}
Feito isso, para renderizar o título deve-se usar o componente FormattedMessage
da biblioteca react-intl.
A biblioteca react-intl dá suporte a várias maneiras de configuração e internacionalização, vale a pena verificá-las.
Adicione a biblioteca usando yarn add react-intl
na pasta react
No código do seu componente Countdown.tsx
importe o FormattedMessage
//react/Countdown.tsx
+ import { FormattedMessage } from 'react-intl'
Adicione uma constante que será o seu título:
//react/Countdown.tsx
const titleText = title || <FormattedMessage id="countdown.title" />
Agora, junte o título e o contador para renderizá-los. Para isso, defina um container por fora. Além disso, o texto do título será passado através da prop title
:
//react/Countdown.tsx
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({
title,
targetDate,
}) => {
const [timeRemaining, setTime] = useState<TimeSplit>({
hours: '00',
minutes: '00',
seconds: '00',
})
const titleText = title || <FormattedMessage id="countdown.title" />
const handles = useCssHandles(CSS_HANDLES)
tick(targetDate, setTime)
return (
<div className={`${handles.container} t-heading-2 fw3 w-100 c-muted-1`}>
<div className={`${handles.title} db tc`}>{titleText}</div>
<div className={`${handles.countdown} db tc`}>
{`${timeRemaining.hours}:${timeRemaining.minutes}:${timeRemaining.seconds}`}
</div>
</div>
)
}
Note que são utilizados três handles novos: container, countdown e title. Dessa forma, lembre-se de declará-los na constante CSS_HANDLES
, vista na etapa anterior:
//react/Countdown.tsx
const CSS_HANDLES = ["container", "countdown", "title"]
Por fim, é preciso adicionar a prop de title
no schema:
//react/Countdown.tsx
Countdown.schema = {
title: 'editor.countdown.title',
description: 'editor.countdown.description',
type: 'object',
properties: {
+ title: {
+ title: 'Sou um título',
+ type: 'string',
+ default: null,
+ },
targetDate: {
title: 'Data final',
description: 'Data final usada no contador',
type: 'string',
default: null,
},
},
}
Pronto! Agora, para testar sua loja em outros idiomas basta adicionar a query string /?cultureInfo=pt-br
ou /?cultureInfo=en-ar
na URL, por exemplo. Ao utilizar tal URL, o resultado esperado é esse aqui:
Agora que já implementamos o countdown
, que tal adicionar um pouco de customização? Nessa etapa, você irá aprender conceitos básicos a respeito de CSS handles e Tachyons para, em seguida, customizar o estilo da sua app.
Os handles de CSS são utilizados para customizar os componentes da sua loja através de classes de CSS no código do tema. Todas essas configurações são definidas através da app vtex.css-handles
, responsável por declarar todos os pontos de customização do seu bloco.
Definindo os nomes dos seus handles e adicionando aos seus respectivos elementos HTML, é possível entregar ao usuário do tema pontos de customização que que permitam criar layouts flexíveis.
O Tachyons é um framework para CSS funcional. Diferentemente de outros frameworks conhecidos, como o Bootstrap, ele não apresenta componentes UI "pré-buildados". Na verdade, seu objetivo é justamente separar as regras de CSS em partes pequenas e reutilizáveis. Esse tipo de estratégia é comumente conhecida como Subatomic Design System e, caso você tenha interesse, pode encontrar uma referência neste link. Essa estratégia torna frameworks como o Tachyons muito flexíveis, escaláveis e rápidos.
Grande parte das definições de Tachyons podem ser alteradas, de forma que sua loja passe a ter um estilo mais customizado. Para isso, basta definir um arquivo JSON na pasta styles/configs
; essas informações podem ser encontradas de forma mais detalhada em: Build a store using VTEX IO - Customizing styles.
Importe o hook useCssHandles
. Para isso, volte ao Countdown.tsx
e faça o import:
// react/Countdown.tsx
import { useCssHandles } from 'vtex.css-handles'
Além disso, defina em um Array todos os handles que serão necessários (neste caso, será utilizado apenas 'countdown'
):
// react/Countdown.tsx
const CSS_HANDLES = ['countdown']
Utilize o useCssHandles
no componente Countdown
para definir o handle do countdown
:
// react/Countdown.tsx
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ targetDate = DEFAULT_TARGET_DATE }) => {
const [timeRemaining, setTime] = useState<TimeSplit>({
hours: '00',
minutes: '00',
seconds: '00'
})
+ const handles = useCssHandles(CSS_HANDLES)
tick(targetDate, setTime)
return (
<div>
<h1>
{ `${timeRemaining.hours}:${timeRemaining.minutes}:${timeRemaining.seconds}` }
</h1>
</div>
)
}
Por fim, é preciso utilizar o handle no componente a fim de ver a customização. Para isso, é necessário utilizar a prop className
com as classes a serem utilizadas e as classes de Tachyons, para os estilos globais.
// react/Countdown.tsx
import React from 'react'
...
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ targetDate = DEFAULT_TARGET_DATE }) => {
const [timeRemaining, setTime] = useState<TimeSplit>({
hours: '00',
minutes: '00',
seconds: '00'
})
const handles = useCssHandles(CSS_HANDLES)
tick(targetDate, setTime)
return (
+ <div className={`${handles.countdown} t-heading-2 fw3 w-100 c-muted-1 db tc`}>
{`${timeRemaining.hours}:${timeRemaining.minutes}:${timeRemaining.seconds}`}
</div>
)
}
Vamos ver o resultado?
Nessa etapa, a app tem dois elementos principais: o título e o contador. Porém, para obter uma maior flexibilidade de posicionamento e customização, é interessante que sejam separadas em dois blocos distintos. Para isso, é preciso apresentar brevemente o conceito de interfaces para, em seguida, ser desenvolvido um novo componente Title
. Um exemplo de customização em termos de posicionamento, que será abordada nessa etapa, é:
E se eu quisesse que o título estivesse embaixo ou ao lado do contador?
Uma interface funciona como um contrato, com restrições bem definidas de como os blocos funcionarão juntos. Define, então, um mapeamento que cria um bloco do Store Framework, a partir de um componente React. É importante destacar que o uso de interfaces, de forma a separar uma app em diversas interfaces torna o poder de customização muito maior.
Ao definir a app na interface, a propriedade component
é responsável por definir o componente React que será usado. É importante ressaltar que o nome do component
tem que ser igual ao nome do arquivo do componente dentro da pasta react/
.
Exemplo de interfaces.json
:
{
"countdown": {
"component": "Countdown"
}
}
Nessa atividade, será separado o título e adicionado à nossa loja embaixo do contador.
Countdown
title
da interface e altere a constante do CSS handles:
//react/Countdown.tsx
import React, { useState } from 'react'
import { TimeSplit } from './typings/global'
import { tick } from './utils/time'
import { useCssHandles } from 'vtex.css-handles'
-import { FormattedMessage } from 'react-intl'
interface CountdownProps {
targetDate: string,
- title: string
}
const DEFAULT_TARGET_DATE = (new Date('2020-03-02')).toISOString()
-const CSS_HANDLES = ['container', 'countdown', 'title']
+const CSS_HANDLES = ['countdown']
title
como prop recebida e a constante do texto do título, além de alterar o que é renderizado:
//react/Countdown.tsx
- const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ title, targetDate = DEFAULT_TARGET_DATE }) => {
+ const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ targetDate = DEFAULT_TARGET_DATE }) => {
const [
timeRemaining,
setTime
] = useState<TimeSplit>({
hours: '00',
minutes: '00',
seconds: '00'
})
- const titleText = title || <FormattedMessage id="countdown.title" />
const handles = useCssHandles(CSS_HANDLES)
tick(targetDate, setTime)
return (
<div className={`${handles.container} t-heading-2 fw3 w-100 pt7 pb6 c-muted-1 db tc`}>
- <div className={`${handles.title} db tc`}>
- { titleText }
- </div>
<div className={`db tc`}>
{`${timeRemaining.hours}:${timeRemaining.minutes}:${timeRemaining.seconds}`}
</div>
</div>
)
}
//react/Countdown.tsx
Countdown.schema = {
title: 'editor.countdown.title',
description: 'editor.countdown.description',
type: 'object',
properties: {
- title: {
- title: 'editor.countdown.title.title',
- type: 'string',
- default: null,
- },
targetDate: {
title: 'editor.countdown.targetDate.title',
description: 'editor.countdown.targetDate.description',
type: 'string',
default: null,
},
},
}
Crie um novo arquivo dentro da pasta /react
, chamado Title.tsx
, ele será o novo componente do título. Nele, alguns imports precisam ser feitos. A estrutura básica do código é muito similar a do componente Countdown
.
Adicione os imports necessários e a constante do CSS handles:
//react/Title.tsx
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'
const CSS_HANDLES = ['title'] as const
Altere a função do componente:
//react/Title.tsx
const Title: StorefrontFunctionComponent<TitleProps> = ({title}) => {
const handles = useCssHandles(CSS_HANDLES)
const titleText = title || <FormattedMessage id="countdown.title" />
return (
<div className={`${handles.title} t-heading-2 fw3 w-100 c-muted-1 db tc`}>
{ titleText }
</div>
)
}
Adicione a interface, o schema e o export:
//react/Title.tsx
interface TitleProps {
title: string
}
Title.schema = {
title: 'editor.countdown-title.title',
description: 'editor.countdown-title.description',
type: 'object',
properties: {
title: {
title: 'editor.countdown.title.title',
type: 'string',
default: null,
}
}
}
export default Title
interfaces.json
Nesta altura, há dois componentes na app: o título e o contador. Porém, é preciso alterar o arquivo interfaces.json
, que se encontra na pasta store
. É preciso declarar os componentes separadamente. No início, nossa interface tinha apenas o Countdown
. É necessário adicionar o outro componente:
{
"countdown": {
"component": "Countdown"
},
+ "countdown.title": {
+ "component": "Title"
+ }
}
Também é preciso adicionar ao Messages as traduções cujas chaves são as strings do schema que incluímos no arquivo Title.tsx
logo acima. Como visto na etapa de Messages, vá à pasta /messages
e adicione em cada um dos arquivos as traduções necessárias (pt.json
, es.json
e en.json
). Abaixo há um exemplo para o caso do arquivo en.json
:
{
+ "countdown.title": "Countdown",
"editor.countdown.title": "Countdown",
"editor.countdown.description": "Countdown component"
}
Por fim, para ver as mudanças, volte ao tema para alterá-lo a fim de incluir o novo bloco. Para isso, basta adicionar à home o título! Assim como feito para o contador, é necessário adicionar o countdown.title
como um bloco no tema da loja, ou seja, no arquivo home.jsonc
do store-theme.
//home.jsonc
{
"store.home": {
"blocks": [
"countdown",
+ "countdown.title",
...
]
},
...
}
Agora aprenderemos como recuperar dados do backend e exibí-los na interface. O VTEX IO utiliza GraphQL como linguagem/tecnologia para transferência de dados, o que torna a programação dos nossos componentes bastante simples. Iremos modificar o nosso componente Countdown para buscar o targetDate do campo releaseDate
de um produto da VTEX. Para realizar queries GraphQL em React, é utilizado o Apollo Client, uma biblioteca de gerenciamento de estado que facilita a integração de uma API GraphQL com a aplicação front-end.
A biblioteca Apollo Client disponibiliza uma integração nativa com React, por meio de hooks. Dessa forma, realizar uma query significa usar um hook que não só realizará as queries e fará o fetch dos dados, mas também proverá cache e atualização do estado da UI. Essa integração, chamada react-apollo
já está declarada no package.json
.
countdown
na página de produto, e também faremos nossos testes nessa página também. Para isso, faça o seguinte:store-theme
) acesse o arquivo store/blocks/product.jsonc
e, no bloco flex-layout.col#right-col
adicione o bloco countdown
, logo antes do buy-button
:
"product-gifts",
+ "countdown",
"flex-layout.row#buy-button",
"availability-subscriber",
vtex link
em seu tema novamente (caso o processo já não esteja sendo executado).Countdown
renderizado.Crie uma pasta react/queries
e nela adicione um arquivo productReleaseDate.graphql
que irá conter a query a ser feita. Em particular, essa query irá receber um termo, que será o slug do produto a ser recuperado a data de lançamento. Ela chamará o resolver product
, já disponível pela app vtex.search-graphql
, e recuperaremos apenas o campo que precisamos.
query productReleaseDate($slug: String){
product(slug: $slug) {
releaseDate
}
}
Perceba que a query precisará do slug do produto que buscamos. Para isso, recuperaremos esta informação do contexto de Produto da VTEX.
Para utilizar essa query, é necessário adicionar a app vtex.search-graphql
como dependência em sua app. Também precisaremos utilizar o hook useProduct
, exportado pela app vtex.product-context
, para recuperar o slug do produto que está carregado na página. Para isso, no manifest.json
de sua app, adicione em dependencies
:
"vtex.search-graphql": "0.x",
"vtex.product-context": "0.x"
Agora, é necessário importar os hook useQuery
, para fazer a query que retornará o dado que descrevemos, e useProduct
, para nos dar a informação sobre o slug do produto atual. Além disso, também é preciso importar a query, definida anteriormente, que se encontra no arquivo productReleaseDate.graphql
. Vale ressaltar também que a prop targetDate
não será mais necessária.
// react/Countdown.tsx
import React from 'react'
+import { useQuery } from 'react-apollo'
+import useProduct from 'vtex.product-context/useProduct'
import { useCssHandles } from 'vtex.css-handles'
+import productReleaseDateQuery from './queries/productReleaseDate.graphql'
É importante notar que há a possibilidade da sua IDE mostrar um erro ao fazer o import do
product-context
.
Defina a query usando o productReleaseDateQuery
importado e o useQuery
, usando os dados do useProduct()
. Como mencionado anteriormente, ambos são hooks, o que significa que devem ser adicionados dentro de um componente funcional React, no caso, o Countdown
.
+ const { product: { linkText } } = useProduct()
+ const { data, loading, error } = useQuery(productReleaseDateQuery, {
+ variables: {
+ slug: linkText
+ },
+ ssr: false
+ })
linkText
será igual a'red-analogic-coffee-and-tea-machine'
, por exemplo, quando o seu componente for renderizado na página deste produto.
Além disso, é preciso tratar os casos de loading e error antes de retornar o componente principal do contador ao utilizar o hook useQuery
. Para isso, é possível retornar um span
em cada um dos casos, como no exemplo abaixo, dentro do componente Countdown
:
if (loading) {
return (
<div>
<span>Loading...</span>
</div>
)
}
if (error) {
return (
<div>
<span>Erro!</span>
</div>
)
}
if (!product) {
return (
<div>
<span>Não há contexto de produto</span>
</div>
)
}
Após enviar as modificações, acesse uma página de produto e verifique se a query está funcionando através de um console.log({data})
após a chamada do useQuery
, que deve mostrar algo como isso:
{
data: {
product: {
releaseDate: '2019-01-01T00:00:00"',
__typename: "Product"
}
}
}
Para fazer com que o Countdown marque as horas para o releaseDate
do produto, mude o parâmetro da função tick
. Você também pode remover as props
recebidas no componente, já que não serão mais usadas.
-tick(targetDate, setTime)
+tick(data?.product?.releaseDate || DEFAULT_TARGET_DATE, setTime)
Resultado no produto Red Front-Loading Washer:
No caso de você se deparar com casos em que o contador está contando para cima ou até mesmo com valores negativos, não se preocupe! Isso está relacionado ao fato de que o
releaseDate
pode estar no passado.
Antes de começar, é necessário relembrar alguns conceitos importantes para uma maior compreensão do fluxo lógico ao desenvolver uma app.
Define o nome da conta VTEX que está desenvolvendo a app. Essa conta é responsável pela manutenção e distribuição da app (pode ser instalada em outras contas ou somente na própria)
O vendor
vtex
é utilizado em casos de apps nativas.
Identifica o nome da aplicação. Não deve ter caracteres especiais - exceto -
- ou caracteres maiúsculos.
Identifica a versão atual da app. Para versionamento, utilizamos a especificação Semantic Versioning 2.0.0. O formato do versionamento é bem definido, com o uso de patches, minors e majors.
Abaixo um resumo da especificação:
Exemplo: Se uma API que está na versão 2.3.2
e uma nova funcionalidade não tiver breaking changes, você pode atualizar a versão para 2.4.0
.
No momento que o deploy é feito, há um worker chamado housekeeper responsável por atualizar a versão automaticamente para todas as contas. No caso de minors e patches, o housekeeper atualiza a app automaticamente em todas as contas, já que as mudanças são retrocompatíveis. Atualizações de majors, no entanto, possuem breaking changes, por isso o housekeeper não atualiza a app em todas as contas; sendo assim, a atualização deve ser feita manualmente.
O desenvolvimento de apps no VTEX IO utiliza o conceito de Code as Configuration (CaC). Este paradigma é abstraído através do campo de builders
que facilita o desenvolvimento, abstraindo a configuração de serviços.
Exemplo: para criar uma extensão no painel administrativo criam-se apps que utilizam o builder de admin
.
Ao linkar a app, portanto, uma pasta de nome correspondente é enviada ao seu builder, que, por sua vez, transforma cada arquivo em configuração para o serviço competente.
Uma app pode depender de outras aplicações. Esse campo lista todas as dependências necessárias para o correto funcionamento da app.
No exemplo da estrutura do manifest.json
abaixo, é possível observar características mencionadas acima. Em particular, a versão é 0.0.1
, onde os números são, respectivamente, major, minor e patch.
{
"vendor": "vtex",
"name": "countdown",
"version": "0.0.1",
"title": "Countdown",
"description": "Countdown component",
"defaultLocale": "pt-BR",
"builders": {
"messages": "1.x",
"store": "0.x",
"react": "3.x"
},
"mustUpdateAt": "2019-04-02",
"scripts": {
"postreleasy": "vtex publish --verbose"
},
"dependencies": {
"vtex.styleguide": "9.x",
"vtex.css-handles": "0.x"
},
"$schema": "https://raw.githubusercontent.com/vtex/node-vtex-a pi/master/gen/manifest.schema"
}
Agora que temos um elemento h1
renderizado, é possível utilizá-lo para mostrar informações que dependam de uma prop do componente. Para isso, alguns conceitos serão apresentados, já que são necessários para desenvolver uma aplicação.
Hook
Hooks são APIs que permitem utilizar funcionalidades do React dentro de componentes funcionais. Sem os hooks, um componente funcional em React só consegue renderizar elementos de UI. Hooks permitem, entre outras coisas, armazenar estado entre diferentes renderizações e executar efeitos colaterais no ciclo de vida de um componente. Obs.: eles não funcionam dentro de classes.
Exemplo:
const [count, setCount] = useState(0)
Interface para props
Define os tipos Typescript das props que o componente poderá receber, permitindo o Intelissense da IDE sobre o componente que você criou.
interface CountdownProps {
exampleProp: string
}
Schema do bloco
No VTEX IO, oferecemos uma ferramenta de gestão de conteúdo da loja chamada Site Editor. Com essa ferramenta, acessada através do Admin VTEX, podemos alterar imagens e texto dos blocos sem precisar modificar o código da loja.
Para que o seu bloco possa aceitar configurações do usuário, é preciso exportar um schema
no componente React responsável por aquele bloco utilizando JSON schema. Isso irá, automaticamente, gerar um formulário para o Site Editor relativo ao bloco que você está desenvolvendo. Abaixo é possível ver um exemplo de schema:
// react/Countdown.tsx
Countdown.schema = {
title: 'editor.countdown.title',
description: 'editor.countdown.description',
type: 'object',
properties: {},
}
O schema também é responsável por definir os textos que serão vistos pelo usuário do admin no formulário.
Na interface definida no Countdown.tsx
, adicione uma prop chamada targetDate
, ela é do tipo string. Com isso, estamos definindo uma prop do componente que será utilizada para inicializar o contador.
A definição da prop em si é feita através da declaração dela na interface CountdownProps
no arquivo Countdown.tsx
, mostrada anteriormente. Assim, adicione uma linha que defina uma prop chamada targetDate
, do tipo string.
// react/Countdown.tsx
interface CountdownProps {
+ targetDate: string
}
Feito isso, é preciso utilizá-la no componente, substituindo o texto de antes, "Teste Countdown" por um outro texto, através do Site Editor.
No futuro, esse targetDate será utilizado para definir a data de término para o contador. Porém, por enquanto, esse campo pode ser genérico.
Primeiramente, é preciso alterar o componente para utilizar a prop targetDate
definida anteriormente. Para isso, é preciso adicionar dentro do componente React a variável a ser utilizada no h1
. Você lembra do bloco de código do componente na etapa anterior? Vamos utilizá-lo novamente para fazer as alterações.
// react/Countdown.tsx
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ targetDate }) => {
return (
<div>
<h1>{ targetDate }</h1>
</div>
)
}
Além disso, para alterar essa propriedade através do Site Editor, é necessário adicionar essa mesma prop ao schema. Isso é feito através da adição de um objeto com chave targetDate
dentro do objeto properties
no schema. Ou seja:
// react/Countdown.tsx
Countdown.schema = {
title: 'editor.countdown.title',
description: 'editor.countdown.description',
type: 'object',
properties: {
+ targetDate: {
+ title: 'Data final',
+ description: 'Data final utilizada no contador',
+ type: 'string',
+ default: null,
+ },
},
}
Pronto! Agora você pode alterar o conteúdo do texto através do Site Editor. Vamos ver como ficou? Vá até o Site Editor e clique em Countdown
no menu lateral, isso abrirá o menu de edição da app, que será como a imagem abaixo.
Agora, no campo abaixo do título, digite uma data no formato AAAA-MM-DD
e veja a alteração, que passará a exibir o texto que você digitou.
Para desenvolver um bloco de frente de loja, similar aos que oferecemos nativamente no Store Framework, utilizamos a biblioteca de desenvolvimento de UIs React.js.
A unidade básica de desenvolvimento em React é o componente, onde pode ser implementada a interface visual do componente, como também sua lógica de estado, ou recuperação de dados. Seguindo as recomendações mais modernas de desenvolvimento, iremos focar no uso da API de Hooks do React, não utilizando de classes para a construção dos componentes.
No VTEX IO, adotamos Typescript como linguagem padrão para programação frontend. Apesar de ser necessário aprender sintaxes novas, o esforço é rapidamente recompensado! Utilizando Typescript, ganha-se alta previsibilidade de bugs, por oferecer tipagem estática. Além disso, com as IDEs certas, é possível aumentar a velocidade de implementação através de um code completion mais esperto, com a tipagem de objetos no código.
Neste curso, será utilizado exclusivamente Typescript. Caso você não tenha familiaridade com a linguagem, será uma excelente oportunidade de experimentá-la!
Como você já tem familiaridade com o Store Framework, já sabe que utilizamos blocos, como shelf
e sku-selector
, para a montagem de uma VTEX IO Store. Nesta etapa você irá criar um bloco que será utilizado no tema da home page de sua loja.
No template clonado localmente, abra o arquivo Countdown.tsx
:
//react/Countdown.tsx
import React from 'react'
interface CountdownProps {}
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({}) => {
return <div></div>
}
Countdown.schema = {
title: 'editor.countdown.title',
description: 'editor.countdown.description',
type: 'object',
properties: {},
}
export default Countdown
Para ver o seu componente na home page, linke o tema em um terminal e a app em outro terminal. Adicione uma tag h1
dentro do nosso componente e declarar o bloco no tema.
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({}) => {
- return <div></div>
+ return (
+ <div>
+ <h1>Teste Countdown</h1>
+ </div>
+ )
}
Para que o componente seja visto funcionando na loja, é preciso declarar o bloco que a app define no tema. Em primeiro lugar, será necessário ter um tema para adicionar a app, para isso, será necessário cloná-lo do Github. Nesse curso, o
store-theme
será utilizado. Para clonar o repositório, basta executar o seguinte comando:
git clone https://github.com/vtex-apps/store-theme.git
Com o repositório clonado, vá ao terminal para deslinkar quaisquer temas ou apps que estejam linkados. Para isso, basta digitar o seguinte comando:
vtex unlink --all
Com o repositório já clonado, vá até a pasta com cd store-theme
; linke o tema no seu workspace.
vtex link
Agora, com o tema linkado, é preciso também linkar a app. Para isso, vá até a pasta da app, store-block
, repositório do Github que foi criado quando você iniciou o curso, e faça o link.
vtex link
Para que a app seja utilizada no tema, é preciso adicioná-la às suas dependências, que como visto anteriormente, ficam no manifest.json
. Dessa forma, adicione ao manifesto do tema, que se encontra na pasta store-theme
, "vtex.countdown" como dependência. A versão dela está definida no manifesto da app (0.0.1
). Feito isso, o JSON terá mais uma linha, como mostrado abaixo:
{
...
"dependencies": {
...
+ "vtex.countdown": "0.x",
...
},
...
}
Por fim, é preciso adicionar o bloco na loja. Dentro do arquivo store-theme/store/blocks/home/home.jsonc
, declare um bloco chamado "countdown"
.
{
"store.home": {
"blocks": [
"countdown",
...
]
...
}
...
}
O resultado esperado é encontrar um header na home da sua loja, como a imagem abaixo:
Agora que o básico do nosso componente está funcional, é hora de implementar efetivamente o contador. Para isso, é preciso utilizar um hook do React, chamado useState
;
useState
É chamado dentro de um componente funcional para atualizar e consumir o state de um componente. O state simboliza o estado atual de um componente.
O
useState
retorna um par: o valor do estado atual e uma função para atualizá-lo.
Voltando ao exemplo apresentado na etapa anterior, podemos mostrar na prática os conceitos abordados anteriormente. Para lembrar do exemplo, veja o código abaixo:
const [count, setCount] = useState(0)
No trecho acima é importante observar três coisas:
count
, é possível consumir o estado atual;setCount
é uma função para atualizá-lo;0
é o valor do estado inicialconst [timeRemaining, setTime] = useState<TimeSplit>({
hours: '00',
minutes: '00',
seconds: '00'
})
Precisamos importar algumas funções e tipos para continuar:
//react/Countdown.tsx
import React, { useState } from 'react'
import { TimeSplit } from './typings/global'
import { tick } from './utils/time'
Adicione o hook de atualização de estado (useState
)
//react/Countdown.tsx
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ targetDate }) => {
+ const [timeRemaining, setTime] = useState<TimeSplit>({
+ hours: '00',
+ minutes: '00',
+ seconds: '00'
+ })
return (
<div>
{ targetDate }
</div>
)
}
Observe os detalhes:
timeRemaining
é o estado atual,setTime
é a função de atualização do estado,TimeSplit
é o tipo e, por fim, o objeto{hours: '00', minutes: '00', seconds: '00'}
é o estado inicial do componente.
Adicione uma targetDate
padrão para o caso de não haver um valor inicial definido. Para isso, declare uma constante que será utilizada como padrão:
//react/Countdown.tsx
const DEFAULT_TARGET_DATE = (new Date('2020-06-25')).toISOString()
Utilize a função tick
e a constante DEFAULT_TARGET_DATE
para fazer o contador:
//react/Countdown.tsx
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ targetDate = DEFAULT_TARGET_DATE }) => {
const [timeRemaining, setTime] = useState<TimeSplit>({
hours: '00',
minutes: '00',
seconds: '00'
})
+ tick(targetDate, setTime)
return (
<div>
{ targetDate }
</div>
)
}
Altere o h1
para que ele exiba o contador que criamos. Para isso, precisamos utilizar o estado atual timeRemaining
:
//react/Countdown.tsx
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({ targetDate = DEFAULT_TARGET_DATE }) => {
const [timeRemaining, setTime] = useState<TimeSplit>({
hours: '00',
minutes: '00',
seconds: '00'
})
tick(targetDate, setTime)
return (
<div>
- <h1>{ targetDate }</h1>
+ <h1>{ `${timeRemaining.hours}:${timeRemaining.minutes}:${timeRemaining.seconds}` }</h1>
</div>
)
}
A formatação da string do contador está no formato
HH:MM:SS
, feita através do split emhours
,minutes
eseconds
.
Assim, com essas alterações, veremos a atualização em tempo real do contador! O resultado na home é esse:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.