Como usar componentes para criar uma visualização personalizada

Este tutorial é destinado a desenvolvedores JavaScript experientes. Ele precisa ter alguma familiaridade com técnicas funcionais de programação.

Neste exemplo, vamos começar com um gráfico de barras, uma visualização nativa do Looker que mostra informações hipotéticas de vendas semanais:

Em seguida, usaremos os componentes de visualização para criar uma visualização personalizada que mostre como cada produto da marca está em alta no último trimestre. O resultado é um novo tipo de visualização composto por uma série de minigráficos aninhados em uma tabela, semelhante a este exemplo:

Além de mostrar como criar uma visualização personalizada, este exemplo demonstra algumas práticas recomendadas para trabalhar com a API Looker em um aplicativo React.

Para criar uma visualização personalizada com componentes do Looker, verifique se sua configuração atende aos requisitos e siga estas etapas:

  1. Crie uma consulta em um Explore e copie o valor qid
  2. Transmitir os dados para um componente de visualização personalizado
  3. Criar o componente CustomVis
  4. Transformar os dados normalizados
  5. Inserir os dados transformados em CustomVis
  6. Gerar a visualização personalizada

É adequado usar componentes de visualização para criar uma visualização personalizada quando a visualização for destinada a um aplicativo ou extensão incorporado. Se você quiser disponibilizar a visualização personalizada para usuários do Looker em uma instância do Looker, siga as instruções na página de documentação do visualization. Se você quiser desenvolver uma visualização personalizada e fazer o upload dela para o Looker Marketplace, siga as instruções da página de documentação Como desenvolver uma visualização personalizada para o Looker Marketplace.

Requisitos

Antes de começar, alguns elementos são necessários:

  • Você precisa ter acesso a uma instância do Looker.
  • Não importa se você está criando no framework de extensão ou no seu próprio aplicativo React autônomo, é importante autenticar com a API Looker e ter acesso ao objeto do SDK do Looker. Leia sobre a autenticação da API Looker ou nosso framework de extensão para mais informações.
  • Verifique se você instalou o pacote de NPM e os pacotes do NPM @looker/components-data dos componentes de visualização do Looker. Informações sobre como instalar e usar o pacote de componentes de visualização podem ser encontradas no documento README, disponível no GitHub e no NPM.

Etapa 1: criar uma consulta em um Explorar e copiar o ID da consulta

Neste exemplo, usamos informações de vendas semanais hipotéticas com as marcas que estamos rastreando para um trimestre inteiro.

Em Explorar, podemos executar uma consulta e criar um gráfico dos dados usando um dos tipos de visualização nativa do Looker. O gráfico fornece muitas informações, mas é difícil analisar rapidamente o desempenho dos produtos de cada marca:

Assim como no exemplo de renderização simples, a próxima etapa é copiar o valor de qid da barra de URL da guia "Explorar". Para este exemplo, a qid será 4tQKzCBOwBGNWTskQyEpG8.

Etapa 2: transmitir os dados para um componente de visualização personalizado

Para começar, transmita o valor qid do URL "Explorar" para o componente Query e o objeto do SDK autenticado para DataProvider.

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'></Query>
    </DataProvider>
  )
}

Em vez de renderizar uma visualização nativa do Looker pelo componente Visualization, criaremos nosso próprio componente personalizado chamado CustomVis.

O componente Query pode aceitar qualquer elemento React como filho e vai transmitir os valores config, data, fields e totals como propriedades para renderizar os próprios componentes de visualização. Vamos renderizar o CustomVis como um filho do Query, para que ele possa receber todos os dados relevantes como propriedades.

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'
import { CustomVis } from '../path/to/MyCustomVis'

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'>
        <CustomVis />
      </Query>
    </DataProvider>
  )
}

Etapa 3: criar o componente CustomVis

Agora, vamos criar o componente CustomVis. As propriedades herdadas do componente Query são config, fields, data e totals:

  • config descreve todas as formas de renderização dos dados em um gráfico, como a espessura da linha em um minigráfico ou o tamanho e o formato dos pontos de um gráfico de dispersão.
  • fields armazena metadados adicionais sobre os valores de medida e dimensão retornados da consulta, por exemplo, como os valores precisam ser formatados ou o que rotular cada eixo.
  • data é a resposta de chave-valor retornada da consulta.
  • O totals faz referência aos totais de linha do Looker para uso em visualizações baseadas em tabela.

É possível transmitir essas propriedades não modificadas para uma visualização de tabela inserindo um componente Table.

import React from 'react'
import { Table } from '@looker/visualizations'

export const CustomVis = ({ config, fields, data }) => {
  return <Table config={config} data={data} fields={fields} />
}

Isso nos dá uma ideia dos dados que são retornados diretamente do SDK. Na resposta renderizada, há uma linha para cada marca, toda semana:

Etapa 4: transformar os dados normalizados

Para receber informações de tendência dessas séries temporais, precisamos agrupar as linhas pelo nome da marca. Em vez de ter uma nova linha para cada semana e marca, queremos uma única linha para cada nome de marca. Aninhada nela, haverá uma lista de datas e contagens de pedidos.

Criaremos uma transformação personalizada para agrupar os valores dessa forma. Veja abaixo um exemplo específico desse cenário. Você precisará analisar seus próprios dados adequadamente.

import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  return <Table config={config} data={data} fields={fields} />
}

As etapas a seguir criam essa função:

  1. Primeiro, atribua a referência products.brand_name a uma variável chamada brandKey. É assim que a série é identificada na resposta de dados. É assim que você agrupa as informações.
  2. Em seguida, crie uma lista exclusiva de nomes de marcas mapeando a resposta e retornando apenas os valores atribuídos a products.brand_name. Depois, eles são transmitidos para o construtor de Set. Em JavaScript, um Set é uma lista em que os valores só podem ocorrer uma vez, criando automaticamente uma lista de valores exclusivos.
  3. Em seguida, converta o Set de marcas únicas em uma matriz para usar utilitários de transformação de lista, como map.
  4. Para cada marca, filtre a resposta de dados original apenas para as linhas de séries temporais que são relevantes para o nome da marca.
  5. Por fim, para cada nome de marca, aninhe esses valores na chave do objeto orders.count. Veja novamente um exemplo de como isso seria retornado aqui, em que o nome da marca é definido como o valor da string e orders.count é definido como a lista de todos os valores relevantes para essa marca.

Etapa 5: insira os dados transformados na CustomVis

Agora, transforme os dados usando nossa nova função e atribua a saída a uma nova variável com o nome nestedData.

import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  return <Table config={config} data={data} fields={fields} />
}

Em seguida, faça a repetição da matriz nestedData e transmita os valores aninhados em um gráfico de gráficos padrão, que é fornecido pela biblioteca de componentes de visualização do Looker.

import React from 'react'
import { Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  const nestedSparklines = nestedData.map(d => {
    return {
      ...d,
      'orders.count': () => (
        <Sparkline
          data={d['orders.count']}
          config={config}
          fields={fields}
          height={75}
        />
      ),
    }
  })

  return <Table config={config} data={nestedSparklines} fields={fields} />
}

O seguinte acontece no código acima:

  • Os dados são mapeados mais de uma vez para criar um novo brilho para cada linha.
  • Os valores que foram aninhados na chave orders.count (a série de datas e contagens que pertencem a essa marca) são transmitidos para a propriedade data de Sparkline.
  • Os objetos config e fields, que são necessários para a renderização de todos os nossos gráficos, também são transmitidos. A altura também é definida como 75 pixels para fornecer um layout confortável para cada linha da tabela.
  • Por fim, o objeto de gráfico aninhado é transmitido para o gráfico Table.

Etapa 6: gerar a visualização personalizada

Depois de inserir os dados transformados e configurar o gráfico, a visualização será semelhante a este exemplo de uma tabela com gráficos de gráficos individuais para cada linha:

Veja a seguir todo o código necessário para renderizar a visualização acima:

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query, Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  const nestedSparklines = nestedData.map(d => {
    return {
      ...d,
      'orders.count': () => (
        <Sparkline
          data={d['orders.count']}
          config={config}
          fields={fields}
          height={75}
        />
      ),
    }
  })

  return <Table config={config} data={nestedSparklines} fields={fields} />
}

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'>
        <CustomVis />
      </Query>
    </DataProvider>
  )
}

Próximas etapas