Sugerencia
Esta lección es parte de un curso que le muestra cómo crear una visualización personalizada en la plataforma New Relic.
Cada lección del curso se basa en la anterior, así que cerciorar de completar la última lección, Visualizaciones personalizadas y el SDK de New Relic, antes de comenzar esta.
Comienza aqui
En la lección anterior, creó una visualización personalizada que muestra datos de consulta en uno de dos tipos de gráficos:
Usó un SegmentedControl
para cambiar entre los dos tipos de gráficos en la UI de visualización. Esta implementación ocupa espacio en la visualización, pero ofrece al usuario la opción de cambiar entre dos tipos de gráficos incluso luego de crear una instancia de su gráfico. Pero, ¿qué pasa si solo necesita poder seleccionar una opción una vez, al inicializar la visualización?
En esta lección, aprenderá cómo agregar una opción de configuración a su visualización que reemplaza SegmentedControl
.
Sugerencia
Si se pierde en el proyecto de código y le gustaría ver cómo deberían ver los archivos cuando terminó con cada lección, consulte el proyecto del curso en 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" } ]}
Agregar una nueva opción de configuración
En el archivo nr1.json de su visualización, agregue un objeto de configuración 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 hasta la raíz de su Nerdpack en alternate-viz.
$nr1 nerdpack:serve
Si todavía estás publicando tu Nerdpack de la última lección, debes detenerlo con CTRL-X
y publicarlo nuevamente para reflejar los cambios en nr1.json.
Vaya a https://one.newrelic.com/?nerdpacks=local. La cadena de consulta nerdpacks=local
indica a la UI que cargue su visualización desde el servidor local.
Abra la página de Apps .
Vaya a Custom Visualizations.
En Custom Visualizations, busque y haga clic en su visualización.
Observe la nueva opción de configuración Select chart .
Seleccionar un tipo de gráfico no afecta su visualización. Esto se debe a que primero debe introducir la propiedad selectedChart
en el componente de visualización. Luego, emplea selectedChart
para determinar el tipo de gráfico que se va a representar.
Reemplace su SegmentedControl
con la propiedad configurable
Abra el archivo index.js de su visualización. Trabajarás aquí durante el resto de la guía.
En render()
, incluya selectedChart
como una constante que obtiene al desestructurar props
y elimine el state
de su 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>);
Ahora que está empleando selectedChart
de las opciones de configuración en lugar del componente state
, puede seleccionar un gráfico en el panel de configuración y observar cómo cambia la visualización. Desafortunadamente, hay un error. La opción de gráfico predeterminada es Radar, pero la representación inicial muestra un mapa de árbol.
Actualice su expresión ternaria para tener en cuenta el caso en el que no hay 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>);
Ahora, sus datos se representan en un RadarChart
si aún no configuró la opción.
Eliminar 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>);
Sirve tu Nerdpack localmente y véelo en la aplicación Custom Visualizations en New Relic. Seleccione un tipo de gráfico en el menú desplegable de la barra lateral de configuración y vea la actualización de su visualización para mostrar el tipo de gráfico coincidente.
Resumen
¡Felicitaciones por completar esta lección! Aprendió cómo personalizar su visualización usando la configuración nr1.json .
Curso
Esta lección es parte de un curso que le muestra cómo crear una visualización personalizada en la plataforma New Relic. Cuando esté listo, continúe con la siguiente lección: Agregue visualizaciones personalizadas a sus paneles.