Conseil
Cette leçon fait partie d'un cours qui vous apprend à créer une visualisation personnalisée sur la plateforme New Relic.
Utilisez les visualisations personnalisées New Relic pour afficher vos données, qu'elles proviennent de la base de données New Relic ou d'une source externe, de manière unique et distincte des graphiques proposés par la plateforme New Relic.
Dans cette leçon, vous créez une visualisation qui affiche vos données dans l'un des deux types de graphiques : RadarChart
ou Treemap
. Vous implémentez ensuite un composant SegmentedControl
du SDK New Relic One, qui vous permet d'alterner entre les deux types de graphiques. En fin de compte, cela vous donne la liberté de visualiser vos données de manière dynamique, ce qui n'est pas possible avec les offres de base de New Relic.
Conseil
Si vous êtes perdu dans le projet de code et que vous souhaitez voir à quoi devraient ressembler les fichiers lorsque vous avez terminé chaque leçon, consultez le projet de cours sur Github.
Avant de commencer
Enfin, si vous ne l’avez pas déjà fait :
- Inscrivez-vous pour un compte New Relic
- Installer Node.js
- Suivez les étapes du démarrage rapide
nr1
pour installer et configurer la CLI
Créez votre visualisation
Assurez-vous que vous travaillez avec la dernière version de la CLI New Relic :
$nr1 update
Créez une visualisation, appelée radar-or-treemap
, dans un Nerdpack, appelé alternate-viz
:
$nr1 create --type visualization --name radar-or-treemap✔ You’re trying to create a visualization outside of a Nerdpack. We’ll create a Nerdpack for you—what do you want to name it? … alternate-viz✔ nerdpack created successfully! nerdpack alternate-viz is available at "./alternate-viz"✔ visualization created successfully! visualization radar-or-treemap is available at "./alternate-viz/visualizations/radar-or-treemap"
Conseil
Si vous recevez un RequestError
pour un certificat auto-signé lorsque vous exécutez nr1 create
, vous devrez peut-être ajouter un certificat à la chaîne de certificats du nœud.
En conséquence, vous disposez d'un nouveau répertoire visualizations/radar-or-treemap
sous alternate-viz
:
$cd alternate-viz$ls visualizations/radar-or-treemapindex.js nr1.json styles.scss
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} from 'recharts';import {Card, CardBody, HeadingText, NrqlQuery, Spinner, AutoSizer} from 'nr1';
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} = 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 ( <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> ); }} </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>);
Configurer l'état de votre composant
Ajoutez l’état du composant au modèle de visualisation par défaut que nr1
a créé pour vous.
Accédez à alternate-viz/visualizations/radar-or-treemap/index.js
. Vous travaillerez ici pour le reste de cette leçon.
Ajoutez une constante appelée CHART_TYPES
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} from 'recharts';import {Card, CardBody, HeadingText, NrqlQuery, Spinner, AutoSizer} 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} = 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 ( <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> ); }} </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>);
CHART_TYPES
énumère les deux types de graphiques entre lesquels vous alternerez dans votre visualisation.
Initialisez selectedChart
dans state
de votre composant :
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} from 'recharts';import {Card, CardBody, HeadingText, NrqlQuery, Spinner, AutoSizer} 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(); };
render() { const {nrqlQueries, stroke, fill} = 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 ( <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> ); }} </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>);
Cette valeur state
stocke le type de graphique dans lequel vous souhaitez afficher vos données.
Maintenant que vous avez créé un objet qui énumère les options de type de graphique pour votre visualisation et que vous avez initialisé state.selectedChart
, vous êtes prêt à implémenter une interface utilisateur de contrôle pour basculer entre les deux types de graphiques.
Ajouter SegmentedControl
state.selectedChart
n'est pas utile à moins que l'utilisateur de votre visualisation puisse réellement sélectionner un type de graphique. Utilisez SegmentedControl
et SegmentedControlItem
pour basculer entre les deux types de graphiques.
Importer SegmentedControl
et SegmentedControlItem
depuis nr1
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} 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(); };
render() { const {nrqlQueries, stroke, fill} = 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 ( <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> ); }} </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>);
Dans render()
, enveloppez RadarChart
dans un React.Fragment
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} 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(); };
render() { const {nrqlQueries, stroke, fill} = 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> <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> </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>);
Cela vous permet de renvoyer plusieurs composants à partir du même render()
.
Ajoutez un SegmentedControl
et deux composants SegmentedControlItem
, chacun avec un value
et un label
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} 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(); };
render() { const {nrqlQueries, stroke, fill} = 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 onChange={(event, value) => console.log(value)} > <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> <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> </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>);
Ici, votre SegmentedControl
enregistre le SegmentedControlItem.value
dans la console lorsque vous modifiez votre sélection. Les valeurs que vous avez définies pour vos SegmentedControlItem
composants correspondent aux deux CHART_TYPES
que vous avez créés à une étape précédente.
Accédez à la racine de votre Nerdpack à alternate-viz
.
Servez votre Nerdpack localement :
$nr1 nerdpack:serve
Ouvrez le lien vers votre visualisation qui s'affiche dans le terminal au démarrage du serveur Node :
Visualizations: ⁎ radar-or-treemap https://one.nr/012ab3cd4Ef
Configurez votre visualisation avec un identifiant de compte et une requête.
Avec certaines données requises pour le traitement de votre graphique, vous voyez maintenant un RadarChart
avec le SegmentedControl
en haut de la vue.
Regardez la console de votre navigateur pour voir vos logs SegmentedControl
.
Connectez state
de votre composant au SegmentedControl
Ajoutez une méthode pour mettre à jour state
et connectez cette méthode avec le SegmentedControl
que vous avez ajouté dans la dernière section.
Ajoutez une méthode de composant, appelée updateSelectedChart()
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} 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 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={(event, value) => console.log(value)} > <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> <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> </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>);
Cette nouvelle méthode prend un argument value
et définit state.selectedChart
sur cette valeur.
Réglez SegmentedControl.onChange
sur updateSelectedChart()
:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,} 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 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> <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> </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>);
Désormais, lorsque vous modifiez votre sélection dans SegmentedControl
, votre sélection sera définie dans state
.
Mettre en œuvre un Treemap
Ajoutez un Treemap
à votre visualisation. Cette carte sera une alternative à la RadarChart
existante.
Détails techniques
Ce guide utilise des composants Recharts pour les graphiques tiers, mais vous pouvez utiliser n'importe quelle autre bibliothèque de graphiques JavaScript compatible avec la version actuelle de React lorsque vous créez des visualisations et des applications New Relic.
Importer Treemap
depuis recharts
:
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 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> <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> </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>);
Vous pouvez désormais utiliser Treemap
dans votre composant de visualisation.
Dans render()
, ajoutez un composant Treemap
:
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 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> <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>);
Ici, vous avez défini un nouveau composant Treemap
avec quelques accessoires, notamment height
, width
, fill
et stroke
.
Avec votre Nerdpack servi localement, visualisez votre visualisation. Les SegmentedControl
et RadarChart
sont en haut de la vue, mais si vous faites défiler vers le bas, vous verrez votre nouveau Treemap
.
Basculez entre les graphiques avec vos composants state
Utilisez state.selectedChart
pour déterminer quel graphique afficher : le RadarChart
ou le Treemap
.
Déstructurez this.state
pour accéder selectedChart
en tant que constante distincte. Ensuite, comparez selectedChart
à CHART_TYPES.Radar
. S'ils sont identiques, affichez un RadarChart
. Sinon, afficher un Treemap
:
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>);
Ici, vous avez utilisé une expression ternaire pour restituer un RadarChart
ou un Treemap
. Le graphique rendu est déterminé par la valeur de selectedChart
.
Avec votre Nerdpack servi localement, visualisez votre visualisation.
Sélectionnez Radar chart dans le SegmentedControl
.
Sélectionnez Treemap chart dans le SegmentedControl
.
Résumé
Félicitations! Dans cette leçon, vous avez appris à :
- Personnalisez votre visualisation à l'aide des composants du SDK New Relic One
- Ajoutez un nouveau type de graphique à votre visualisation
- Créez une interaction utilisateur dans votre visualisation
Conseil
Cette leçon fait partie d'un cours qui vous apprend à créer une visualisation personnalisée sur la plateforme New Relic. Lorsque vous êtes prêt, passez à la leçon suivante : Personnaliser les visualisations avec la configuration.