Dica
Esta lição faz parte de um curso que ensina como construir uma visualização personalizada na plataforma New Relic.
Cada lição do curso se baseia na anterior, portanto, certifique-se de ter concluído a última lição, Visualizações personalizadas e o SDK da New Relic, antes de iniciar esta.
Comece aqui
Na lição anterior, você criou uma visualização personalizada que mostra os dados da consulta em um dos dois tipos de gráfico:
Você usou um SegmentedControl
para alternar entre os dois tipos de gráfico na interface de visualização. Essa implementação ocupa espaço na visualização, mas oferece ao usuário a opção de alternar entre dois tipos de gráfico mesmo depois de criar uma instância do seu gráfico. Mas e se você só precisar selecionar uma opção uma vez, ao inicializar a visualização?
Nesta lição você aprenderá como adicionar uma opção de configuração à sua visualização que substitui o SegmentedControl
.
Dica
Se você se perder no projeto de código e quiser ver como os arquivos devem ficar quando terminar cada lição, verifique o projeto do curso no Github.
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, SegmentedControl, SegmentedControlItem, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
state = { selectedChart: CHART_TYPES.Radar, };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
updateSelectedChart = (evt, value) => { this.setState({ selectedChart: value }); };
render() { const { nrqlQueries, stroke, fill } = this.props; const { selectedChart } = this.state;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> <SegmentedControl onChange={this.updateSelectedChart}> <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> {selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
{ "schemaType": "VISUALIZATION", "id": "radar-or-treemap", "displayName": "RadarOrTreemap", "description": "", "configuration": [ { "name": "nrqlQueries", "title": "NRQL Queries", "type": "collection", "items": [ { "name": "accountId", "title": "Account ID", "description": "Account ID to be associated with the query", "type": "account-id" }, { "name": "query", "title": "Query", "description": "NRQL query for visualization", "type": "nrql" } ] }, { "name": "fill", "title": "Fill color", "description": "A fill color to override the default fill color", "type": "string" }, { "name": "stroke", "title": "Stroke color", "description": "A stroke color to override the default stroke color", "type": "string" } ]}
Adicione uma nova opção de configuração
No arquivo nr1.json da sua visualização, adicione um objeto de configuração enum
para selectedChart
:
{ "schemaType": "VISUALIZATION", "id": "radar-or-treemap", "displayName": "RadarOrTreemap", "description": "", "configuration": [ { "name": "selectedChart", "title": "Select chart", "description": "Select which chart to display", "type": "enum", "items": [ { "title": "Radar", "value": "radar" }, { "title": "Treemap", "value": "treemap" } ] }, { "name": "nrqlQueries", "title": "NRQL Queries", "type": "collection", "items": [ { "name": "accountId", "title": "Account ID", "description": "Account ID to be associated with the query", "type": "account-id" }, { "name": "query", "title": "Query", "description": "NRQL query for visualization", "type": "nrql" } ] }, { "name": "fill", "title": "Fill color", "description": "A fill color to override the default fill color", "type": "string" }, { "name": "stroke", "title": "Stroke color", "description": "A stroke color to override the default stroke color", "type": "string" } ]}
Navegue até a raiz do seu Nerdpack em alternate-viz.
Sirva seu Nerdpack localmente:
$nr1 nerdpack:serve
Se você ainda estiver veiculando seu Nerdpack da última lição, será necessário interrompê-lo com CTRL-X
e exibi-lo novamente para refletir as alterações em nr1.json.
Vá para https://one.newrelic.com/?nerdpacks=local. A string de consulta nerdpacks=local
direciona a interface para carregar sua visualização do servidor local.
Abra a página Apps .
Vá para Custom Visualizations.
Em Custom Visualizations, encontre e clique na sua visualização.
Observe a nova opção do configuração Select chart.
A seleção de um tipo de gráfico não afeta sua visualização. Isso ocorre porque primeiro você precisa introduzir a propriedade selectedChart
no componente de visualização. Em seguida, você usa selectedChart
para determinar o tipo de gráfico a ser renderizado.
Substitua seu SegmentedControl
pela propriedade configurável
Abra o arquivo index.js da sua visualização. Você trabalhará aqui durante o restante do guia.
Em render()
, inclua selectedChart
como uma constante obtida da desestruturação props
e remova state
do seu componente:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, SegmentedControl, SegmentedControlItem, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
render() { const { nrqlQueries, stroke, fill, selectedChart } = this.props;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> <SegmentedControl> <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> {selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
Agora que você está usando selectedChart
nas opções de configuração em vez do componente state
, você pode selecionar um gráfico no painel de configuração e observar a mudança na visualização. Infelizmente, há um bug. A opção de gráfico padrão é Radar, mas a renderização inicial mostra um Treemap.
Atualize sua expressão ternária para levar em conta o caso em que não há selectedChart
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, SegmentedControl, SegmentedControlItem, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
render() { const { nrqlQueries, stroke, fill, selectedChart } = this.props;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> <SegmentedControl> <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> {!selectedChart || selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
Agora, seus dados serão renderizados em RadarChart
caso você ainda não tenha configurado a opção.
Remover SegmentedControl
de render()
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
render() { const { nrqlQueries, stroke, fill, selectedChart } = this.props;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> {!selectedChart || selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
Sirva seu Nerdpack localmente e visualize-o no aplicativo Custom Visualizations no New Relic. Selecione um tipo de gráfico no dropdown na barra lateral de configuração e veja sua atualização de visualização para mostrar o tipo de gráfico correspondente.
Resumo
Parabéns por concluir esta lição! Você aprendeu como personalizar sua visualização usando a configuração nr1.json .
Curso
Esta lição faz parte de um curso que ensina como construir uma visualização personalizada na plataforma New Relic. Quando estiver pronto, passe para a próxima lição: Adicione visualizações personalizadas aos seus painéis.