Documentation
Feedback
Guides
Learning Center

Learning Center
Learning Center
Componentizando o bloco countdown
View in English
This content was migrated from the VTEX Learning Center and is no longer being actively maintained. For the most accurate and up-to-date information, please check the official documentation.

Introdução

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?

Interface

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:


_10
{
_10
"countdown": {
_10
"component": "Countdown"
_10
}
_10
}

Agora, você irá separar o título do bloco do contador e adicioná-lo à nossa loja embaixo do contador.

Alterando o componente Countdown

  1. Em primeiro lugar, remova os imports, o title da interface e altere a constante do CSS handles:


    _15
    //react/Countdown.tsx
    _15
    import React, { useState } from 'react'
    _15
    import { TimeSplit } from './typings/global'
    _15
    import { tick, getTwoDaysFromNow } from './utils/time'
    _15
    import { useCssHandles } from 'vtex.css-handles'
    _15
    -import { FormattedMessage } from 'react-intl'
    _15
    _15
    interface CountdownProps {
    _15
    targetDate: string,
    _15
    - title: string
    _15
    }
    _15
    _15
    const DEFAULT_TARGET_DATE = getTwoDaysFromNow()
    _15
    -const CSS_HANDLES = ['container', 'countdown', 'title']
    _15
    +const CSS_HANDLES = ['countdown']

  2. Agora, no componente React em si, é preciso retirar o title como prop recebida e a constante do texto do título, além de alterar o que é renderizado:


    _30
    //react/Countdown.tsx
    _30
    const Countdown: StorefrontFunctionComponent<CountdownProps> = ({
    _30
    - title,
    _30
    targetDate = DEFAULT_TARGET_DATE,
    _30
    }) => {
    _30
    const [
    _30
    timeRemaining,
    _30
    setTime
    _30
    ] = useState<TimeSplit>({
    _30
    hours: '00',
    _30
    minutes: '00',
    _30
    seconds: '00'
    _30
    })
    _30
    _30
    - const titleText = title || <FormattedMessage id="countdown.title" />
    _30
    const handles = useCssHandles(CSS_HANDLES)
    _30
    _30
    tick(targetDate, setTime)
    _30
    _30
    return (
    _30
    - <div className={`${handles.container} t-heading-2 fw3 w-100 pt7 pb6 c-muted-1 db tc`}>
    _30
    - <div className={`${handles.title} db tc`}>
    _30
    - { titleText }
    _30
    - </div>
    _30
    <div className={`${handles.countdown} db tc`}>
    _30
    {`${timeRemaining.hours}:${timeRemaining.minutes}:${timeRemaining.seconds}`}
    _30
    </div>
    _30
    - </div>
    _30
    )
    _30
    }

  3. Por fim, retire o título do schema:


    _19
    //react/Countdown.tsx
    _19
    Countdown.schema = {
    _19
    title: 'editor.countdown.title',
    _19
    description: 'editor.countdown.description',
    _19
    type: 'object',
    _19
    properties: {
    _19
    - title: {
    _19
    - title: 'editor.countdown.title.title',
    _19
    - type: 'string',
    _19
    - default: null,
    _19
    - },
    _19
    targetDate: {
    _19
    title: 'editor.countdown.targetDate.title',
    _19
    description: 'editor.countdown.targetDate.description',
    _19
    type: 'string',
    _19
    default: null,
    _19
    },
    _19
    },
    _19
    }

Criando um novo componente

  1. 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. Feito isso, adicione os imports necessários e a constante do CSS handles:


    _10
    //react/Title.tsx
    _10
    import React from 'react'
    _10
    import { FormattedMessage } from 'react-intl'
    _10
    import { useCssHandles } from 'vtex.css-handles'
    _10
    _10
    const CSS_HANDLES = ['title'] as const

  2. Agora, é necessário alterar a função do componente:


    _13
    //react/Title.tsx
    _13
    const Title: StorefrontFunctionComponent<TitleProps> = ({ title }) => {
    _13
    const handles = useCssHandles(CSS_HANDLES)
    _13
    const titleText = title || <FormattedMessage id="countdown.title" />
    _13
    _13
    return (
    _13
    <div
    _13
    className={`${handles.title} t-heading-2 fw3 w-100 c-muted-1 db tc`}
    _13
    >
    _13
    {titleText}
    _13
    </div>
    _13
    )
    _13
    }

  3. Por fim, adicione a interface, o schema e o export:


    _19
    //react/Title.tsx
    _19
    interface TitleProps {
    _19
    title: string
    _19
    }
    _19
    _19
    Title.schema = {
    _19
    title: 'editor.countdown-title.title',
    _19
    description: 'editor.countdown-title.description',
    _19
    type: 'object',
    _19
    properties: {
    _19
    title: {
    _19
    title: 'editor.countdown.title.title',
    _19
    type: 'string',
    _19
    default: null,
    _19
    },
    _19
    },
    _19
    }
    _19
    _19
    export default Title

Alterando o arquivo 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:


_10
{
_10
"countdown": {
_10
"component": "Countdown"
_10
},
_10
+ "countdown.title": {
_10
+ "component": "Title"
_10
+ }
_10
}

Adicionando internacionalização

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:


_10
{
_10
+ "countdown.title": "Countdown",
_10
"editor.countdown.title": "Countdown",
_10
"editor.countdown.description": "Countdown component"
_10
}

Adicionando o novo bloco na home da loja

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.


_11
//home.jsonc
_11
{
_11
"store.home": {
_11
"blocks": [
_11
"countdown",
_11
+ "countdown.title",
_11
...
_11
]
_11
},
_11
...
_11
}

Pronto! Agora vamos ver como deve ser o resultado:
{"base64":"  ","img":{"width":3810,"height":1934,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":1467727,"url":"https://user-images.githubusercontent.com/19495917/75560163-6d294d80-5a23-11ea-859d-35a8239ddfad.png"}}

Answer sheet

See answer sheet for Countdown.tsx
Countdown.tsx

_54
// store-block/react/Countdown.tsx
_54
import React, { useState } from 'react'
_54
import { TimeSplit } from './typings/global'
_54
import { tick, getTwoDaysFromNow } from './utils/time'
_54
import { useCssHandles } from 'vtex.css-handles'
_54
_54
interface CountdownProps {
_54
targetDate: string
_54
}
_54
_54
const DEFAULT_TARGET_DATE = getTwoDaysFromNow()
_54
_54
const CSS_HANDLES = ['countdown']
_54
_54
const Countdown: StorefrontFunctionComponent<CountdownProps> = ({
_54
targetDate = DEFAULT_TARGET_DATE,
_54
}) => {
_54
const [timeRemaining, setTime] = useState<TimeSplit>({
_54
hours: '00',
_54
minutes: '00',
_54
seconds: '00',
_54
})
_54
_54
const handles = useCssHandles(CSS_HANDLES)
_54
_54
tick(targetDate, setTime)
_54
_54
return (
_54
<div className={`${handles.countdown} c-muted-1 db tc`}>
_54
<h1>{`${timeRemaining.hours}:${timeRemaining.minutes}:${timeRemaining.seconds}`}</h1>
_54
</div>
_54
)
_54
}
_54
_54
Countdown.schema = {
_54
title: 'editor.countdown.title',
_54
description: 'editor.countdown.description',
_54
type: 'object',
_54
properties: {
_54
title: {
_54
title: 'I am a title',
_54
type: 'string',
_54
default: null,
_54
},
_54
targetDate: {
_54
title: 'Final date',
_54
description: 'Final date used in the countdown',
_54
type: 'string',
_54
default: null,
_54
},
_54
},
_54
}
_54
_54
export default Countdown

See answer sheet for Title.tsx
Title.tsx

_36
// store-block/react/Title.tsx
_36
import React from 'react'
_36
import { FormattedMessage } from 'react-intl'
_36
import { useCssHandles } from 'vtex.css-handles'
_36
_36
const CSS_HANDLES = ['title']
_36
_36
const Title: StorefrontFunctionComponent<TitleProps> = ({ title }) => {
_36
const handles = useCssHandles(CSS_HANDLES)
_36
const titleText = title || <FormattedMessage id="countdown.title" />
_36
_36
return (
_36
<div className={`${handles.title} t-heading-2 fw3 w-100 c-muted-1 db tc`}>
_36
{titleText}
_36
</div>
_36
)
_36
}
_36
_36
interface TitleProps {
_36
title: string
_36
}
_36
_36
Title.schema = {
_36
title: 'editor.countdown-title.title',
_36
description: 'editor.countdown-title.description',
_36
type: 'object',
_36
properties: {
_36
title: {
_36
title: 'I am a title',
_36
type: 'string',
_36
default: null,
_36
},
_36
},
_36
}
_36
_36
export default Title

See answer sheet for home.jsonc
home.jsonc

_109
// store-theme/store/blocks/home/home.jsonc
_109
{
_109
"store.home": {
_109
"blocks": [
_109
"countdown.title",
_109
"countdown",
_109
"list-context.image-list#demo",
_109
"__fold__.mobile",
_109
"flex-layout.row#deals",
_109
"__fold__.desktop",
_109
"rich-text#shelf-title",
_109
"flex-layout.row#shelf",
_109
"info-card#home",
_109
"rich-text#question",
_109
"rich-text#link",
_109
"newsletter"
_109
]
_109
},
_109
_109
"shelf#home": {
_109
"blocks": ["product-summary.shelf"]
_109
},
_109
_109
"list-context.image-list#demo": {
_109
"children": ["slider-layout#demo-images"],
_109
"props": {
_109
"height": 720,
_109
"images": [
_109
{
_109
"image": "https://storecomponents.vteximg.com.br/arquivos/banner-principal.png",
_109
"mobileImage": "https://storecomponents.vteximg.com.br/arquivos/banner-principal-mobile.jpg"
_109
},
_109
{
_109
"image": "https://storecomponents.vteximg.com.br/arquivos/banner.jpg",
_109
"mobileImage": "https://storecomponents.vteximg.com.br/arquivos/banner-principal-mobile.jpg"
_109
}
_109
]
_109
}
_109
},
_109
"slider-layout#demo-images": {
_109
"props": {
_109
"itemsPerPage": {
_109
"desktop": 1,
_109
"tablet": 1,
_109
"phone": 1
_109
},
_109
"infinite": true,
_109
"showNavigationArrows": "desktopOnly",
_109
"blockClass": "carousel"
_109
}
_109
},
_109
_109
"rich-text#shelf-title": {
_109
"props": {
_109
"text": "## Summer",
_109
"blockClass": "shelfTitle"
_109
}
_109
},
_109
"flex-layout.row#shelf": {
_109
"children": ["list-context.product-list#demo1"]
_109
},
_109
"list-context.product-list#demo1": {
_109
"blocks": ["product-summary.shelf"],
_109
"children": ["slider-layout#demo-products"],
_109
"props": {
_109
"orderBy": "OrderByTopSaleDESC"
_109
}
_109
},
_109
"slider-layout#demo-products": {
_109
"props": {
_109
"itemsPerPage": {
_109
"desktop": 5,
_109
"tablet": 3,
_109
"phone": 1
_109
},
_109
"infinite": true,
_109
"fullWidth": true,
_109
"blockClass": "shelf"
_109
}
_109
},
_109
_109
"info-card#home": {
_109
"props": {
_109
"id": "info-card-home",
_109
"isFullModeStyle": false,
_109
"textPosition": "left",
_109
"imageUrl": "https://storecomponents.vteximg.com.br/arquivos/banner-infocard2.png",
_109
"headline": "Clearance Sale",
_109
"callToActionText": "DISCOVER",
_109
"callToActionUrl": "/sale/d",
_109
"blockClass": "info-card-home",
_109
"textAlignment": "center"
_109
}
_109
},
_109
_109
"rich-text#question": {
_109
"props": {
_109
"text": "**This is an example store built using the VTEX platform.\nWant to know more?**",
_109
"blockClass": "question"
_109
}
_109
},
_109
_109
"rich-text#link": {
_109
"props": {
_109
"text": "\n**Reach us at**\nwww.vtex.com.br",
_109
"blockClass": "link"
_109
}
_109
}
_109
}

See answer sheet for interfaces.json
interfaces.json

_10
// store-block/store/interfaces.json
_10
{
_10
"countdown": {
_10
"component": "Countdown"
_10
},
_10
"countdown.title": {
_10
"component": "Title"
_10
}
_10
}

On this page