Course
This lesson is part of a course that teaches you how to build a custom visualization on the New Relic platform.
Use New Relic custom visualizations to display your data, whether it's from New Relic's database or an external source, in unique ways that are distinct from the charts offered by the New Relic platform.
In this lesson, you build a visualization that displays your data in one of two chart types: RadarChart
or Treemap
. You then implement a SegmentedControl
component from the New Relic One SDK, which allows you to alternate between the two chart types. Ultimately, this gives you freedom to view your data in a dynamic way that isn't possible with New Relic's base offerings.
Tip
If you get lost in the code project and would like to see what the files should look like when you're done with each lesson, check out the course project on Github.
Before you begin
Explore our custom visualization guides and build your first visualization. After you're done, you'll have a better foundation for building more complex visualizations, such as the one you'll build in this course.
Finally, if you haven't already:
- Sign up for a New Relic account
- Install Node.js
- Complete the steps in the
nr1
quick start to install and configure the CLI
Create your visualization
Ensure you're working with the latest version of the New Relic One CLI:
$nr1 update
Create a visualization, called radar-or-treemap
, in a Nerdpack, called 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"
Tip
If you receive a RequestError
for a self-signed certificate when you run nr1 create
, you may need to add a certificate to Node's certificate chain. Read more about this and other advanced configurations in Enable advanced configurations for your Nerdpack.
As a result, you have a new visualizations/radar-or-treemap
directory under alternate-viz
:
$cd alternate-viz$ls visualizations/radar-or-treemapindex.js nr1.json styles.scss
Set up your component state
Add component state to the default visualization template that nr1
created for you.
Navigate to alternate-viz/visualizations/radar-or-treemap/index.js. You'll work in here for the rest of this lesson.
Add a constant called CHART_TYPES
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9} from 'recharts';10import {Card, CardBody, HeadingText, NrqlQuery, Spinner, AutoSizer} from 'nr1';11
12const CHART_TYPES = {13 'Radar': 'radar',14 'Treemap': 'treemap'15}16
17export default class RadarOrTreemapVisualization extends React.Component {18 // Custom props you wish to be configurable in the UI must also be defined in19 // the nr1.json file for the visualization. See docs for more details.20 static propTypes = {21 /**22 * A fill color to override the default fill color. This is an example of23 * a custom chart configuration.24 */25 fill: PropTypes.string,26
27 /**28 * A stroke color to override the default stroke color. This is an example of29 * a custom chart configuration.30 */31 stroke: PropTypes.string,32 /**33 * An array of objects consisting of a nrql `query` and `accountId`.34 * This should be a standard prop for any NRQL based visualizations.35 */36 nrqlQueries: PropTypes.arrayOf(37 PropTypes.shape({38 accountId: PropTypes.number,39 query: PropTypes.string,40 })41 ),42 };43
44 /**45 * Restructure the data for a non-time-series, facet-based NRQL query into a46 * form accepted by the Recharts library's RadarChart.47 * (https://recharts.org/api/RadarChart).48 */49 transformData = (rawData) => {50 return rawData.map((entry) => ({51 name: entry.metadata.name,52 // Only grabbing the first data value because this is not time-series data.53 value: entry.data[0].y,54 }));55 };56
57 /**58 * Format the given axis tick's numeric value into a string for display.59 */60 formatTick = (value) => {61 return value.toLocaleString();62 };63
64 render() {65 const {nrqlQueries, stroke, fill} = this.props;66
67 const nrqlQueryPropsAvailable =68 nrqlQueries &&69 nrqlQueries[0] &&70 nrqlQueries[0].accountId &&71 nrqlQueries[0].query;72
73 if (!nrqlQueryPropsAvailable) {74 return <EmptyState />;75 }76
77 return (78 <AutoSizer>79 {({width, height}) => (80 <NrqlQuery81 query={nrqlQueries[0].query}82 accountId={parseInt(nrqlQueries[0].accountId)}83 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}84 >85 {({data, loading, error}) => {86 if (loading) {87 return <Spinner />;88 }89
90 if (error) {91 return <ErrorState />;92 }93
94 const transformedData = this.transformData(data);95
96 return (97 <RadarChart98 width={width}99 height={height}100 data={transformedData}101 >102 <PolarGrid />103 <PolarAngleAxis dataKey="name" />104 <PolarRadiusAxis tickFormatter={this.formatTick} />105 <Radar106 dataKey="value"107 stroke={stroke || '#51C9B7'}108 fill={fill || '#51C9B7'}109 fillOpacity={0.6}110 />111 </RadarChart>112 );113 }}114 </NrqlQuery>115 )}116 </AutoSizer>117 );118 }119}120
121const EmptyState = () => (122 <Card className="EmptyState">123 <CardBody className="EmptyState-cardBody">124 <HeadingText125 spacingType={[HeadingText.SPACING_TYPE.LARGE]}126 type={HeadingText.TYPE.HEADING_3}127 >128 Please provide at least one NRQL query & account ID pair129 </HeadingText>130 <HeadingText131 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}132 type={HeadingText.TYPE.HEADING_4}133 >134 An example NRQL query you can try is:135 </HeadingText>136 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>137 </CardBody>138 </Card>139);140
141const ErrorState = () => (142 <Card className="ErrorState">143 <CardBody className="ErrorState-cardBody">144 <HeadingText145 className="ErrorState-headingText"146 spacingType={[HeadingText.SPACING_TYPE.LARGE]}147 type={HeadingText.TYPE.HEADING_3}148 >149 Oops! Something went wrong.150 </HeadingText>151 </CardBody>152 </Card>153);
CHART_TYPES
enumerates the two chart types you'll alternate between in your visualization.
Initialize selectedChart
in your component's state
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9} from 'recharts';10import {Card, CardBody, HeadingText, NrqlQuery, Spinner, AutoSizer} from 'nr1';11
12const CHART_TYPES = {13 'Radar': 'radar',14 'Treemap': 'treemap'15}16
17export default class RadarOrTreemapVisualization extends React.Component {18 // Custom props you wish to be configurable in the UI must also be defined in19 // the nr1.json file for the visualization. See docs for more details.20 static propTypes = {21 /**22 * A fill color to override the default fill color. This is an example of23 * a custom chart configuration.24 */25 fill: PropTypes.string,26
27 /**28 * A stroke color to override the default stroke color. This is an example of29 * a custom chart configuration.30 */31 stroke: PropTypes.string,32 /**33 * An array of objects consisting of a nrql `query` and `accountId`.34 * This should be a standard prop for any NRQL based visualizations.35 */36 nrqlQueries: PropTypes.arrayOf(37 PropTypes.shape({38 accountId: PropTypes.number,39 query: PropTypes.string,40 })41 ),42 };43
44 state = {45 selectedChart: CHART_TYPES.Radar,46 };47
48 /**49 * Restructure the data for a non-time-series, facet-based NRQL query into a50 * form accepted by the Recharts library's RadarChart.51 * (https://recharts.org/api/RadarChart).52 */53 transformData = (rawData) => {54 return rawData.map((entry) => ({55 name: entry.metadata.name,56 // Only grabbing the first data value because this is not time-series data.57 value: entry.data[0].y,58 }));59 };60
61 /**62 * Format the given axis tick's numeric value into a string for display.63 */64 formatTick = (value) => {65 return value.toLocaleString();66 };67
68 render() {69 const {nrqlQueries, stroke, fill} = this.props;70
71 const nrqlQueryPropsAvailable =72 nrqlQueries &&73 nrqlQueries[0] &&74 nrqlQueries[0].accountId &&75 nrqlQueries[0].query;76
77 if (!nrqlQueryPropsAvailable) {78 return <EmptyState />;79 }80
81 return (82 <AutoSizer>83 {({width, height}) => (84 <NrqlQuery85 query={nrqlQueries[0].query}86 accountId={parseInt(nrqlQueries[0].accountId)}87 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}88 >89 {({data, loading, error}) => {90 if (loading) {91 return <Spinner />;92 }93
94 if (error) {95 return <ErrorState />;96 }97
98 const transformedData = this.transformData(data);99
100 return (101 <RadarChart102 width={width}103 height={height}104 data={transformedData}105 >106 <PolarGrid />107 <PolarAngleAxis dataKey="name" />108 <PolarRadiusAxis tickFormatter={this.formatTick} />109 <Radar110 dataKey="value"111 stroke={stroke || '#51C9B7'}112 fill={fill || '#51C9B7'}113 fillOpacity={0.6}114 />115 </RadarChart>116 );117 }}118 </NrqlQuery>119 )}120 </AutoSizer>121 );122 }123}124
125const EmptyState = () => (126 <Card className="EmptyState">127 <CardBody className="EmptyState-cardBody">128 <HeadingText129 spacingType={[HeadingText.SPACING_TYPE.LARGE]}130 type={HeadingText.TYPE.HEADING_3}131 >132 Please provide at least one NRQL query & account ID pair133 </HeadingText>134 <HeadingText135 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}136 type={HeadingText.TYPE.HEADING_4}137 >138 An example NRQL query you can try is:139 </HeadingText>140 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>141 </CardBody>142 </Card>143);144
145const ErrorState = () => (146 <Card className="ErrorState">147 <CardBody className="ErrorState-cardBody">148 <HeadingText149 className="ErrorState-headingText"150 spacingType={[HeadingText.SPACING_TYPE.LARGE]}151 type={HeadingText.TYPE.HEADING_3}152 >153 Oops! Something went wrong.154 </HeadingText>155 </CardBody>156 </Card>157);
This state
value stores the chart type in which you want to show your data.
Now that you've created an object which enumerates the chart type options for your visualization, and you've initialized state.selectedChart
, you're ready to implement a control UI for switching between the two chart types.
Add SegmentedControl
components
state.selectedChart
isn't useful unless your visualization's users can actually select a chart type. Use SegmentedControl
and SegmentedControlItem
to switch between the two chart types.
Tip
To learn more about the components available in the New Relic One SDK, go to our Intro to New Relic One SDK.
Import SegmentedControl
and SegmentedControlItem
from nr1
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9} from 'recharts';10import {11 AutoSizer,12 Card,13 CardBody,14 HeadingText,15 NrqlQuery,16 SegmentedControl,17 SegmentedControlItem,18 Spinner,19} from 'nr1';20
21const CHART_TYPES = {22 'Radar': 'radar',23 'Treemap': 'treemap'24}25
26export default class RadarOrTreemapVisualization extends React.Component {27 // Custom props you wish to be configurable in the UI must also be defined in28 // the nr1.json file for the visualization. See docs for more details.29 static propTypes = {30 /**31 * A fill color to override the default fill color. This is an example of32 * a custom chart configuration.33 */34 fill: PropTypes.string,35
36 /**37 * A stroke color to override the default stroke color. This is an example of38 * a custom chart configuration.39 */40 stroke: PropTypes.string,41 /**42 * An array of objects consisting of a nrql `query` and `accountId`.43 * This should be a standard prop for any NRQL based visualizations.44 */45 nrqlQueries: PropTypes.arrayOf(46 PropTypes.shape({47 accountId: PropTypes.number,48 query: PropTypes.string,49 })50 ),51 };52
53 state = {54 selectedChart: CHART_TYPES.Radar,55 };56
57 /**58 * Restructure the data for a non-time-series, facet-based NRQL query into a59 * form accepted by the Recharts library's RadarChart.60 * (https://recharts.org/api/RadarChart).61 */62 transformData = (rawData) => {63 return rawData.map((entry) => ({64 name: entry.metadata.name,65 // Only grabbing the first data value because this is not time-series data.66 value: entry.data[0].y,67 }));68 };69
70 /**71 * Format the given axis tick's numeric value into a string for display.72 */73 formatTick = (value) => {74 return value.toLocaleString();75 };76
77 render() {78 const {nrqlQueries, stroke, fill} = this.props;79
80 const nrqlQueryPropsAvailable =81 nrqlQueries &&82 nrqlQueries[0] &&83 nrqlQueries[0].accountId &&84 nrqlQueries[0].query;85
86 if (!nrqlQueryPropsAvailable) {87 return <EmptyState />;88 }89
90 return (91 <AutoSizer>92 {({width, height}) => (93 <NrqlQuery94 query={nrqlQueries[0].query}95 accountId={parseInt(nrqlQueries[0].accountId)}96 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}97 >98 {({data, loading, error}) => {99 if (loading) {100 return <Spinner />;101 }102
103 if (error) {104 return <ErrorState />;105 }106
107 const transformedData = this.transformData(data);108
109 return (110 <RadarChart111 width={width}112 height={height}113 data={transformedData}114 >115 <PolarGrid />116 <PolarAngleAxis dataKey="name" />117 <PolarRadiusAxis tickFormatter={this.formatTick} />118 <Radar119 dataKey="value"120 stroke={stroke || '#51C9B7'}121 fill={fill || '#51C9B7'}122 fillOpacity={0.6}123 />124 </RadarChart>125 );126 }}127 </NrqlQuery>128 )}129 </AutoSizer>130 );131 }132}133
134const EmptyState = () => (135 <Card className="EmptyState">136 <CardBody className="EmptyState-cardBody">137 <HeadingText138 spacingType={[HeadingText.SPACING_TYPE.LARGE]}139 type={HeadingText.TYPE.HEADING_3}140 >141 Please provide at least one NRQL query & account ID pair142 </HeadingText>143 <HeadingText144 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}145 type={HeadingText.TYPE.HEADING_4}146 >147 An example NRQL query you can try is:148 </HeadingText>149 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>150 </CardBody>151 </Card>152);153
154const ErrorState = () => (155 <Card className="ErrorState">156 <CardBody className="ErrorState-cardBody">157 <HeadingText158 className="ErrorState-headingText"159 spacingType={[HeadingText.SPACING_TYPE.LARGE]}160 type={HeadingText.TYPE.HEADING_3}161 >162 Oops! Something went wrong.163 </HeadingText>164 </CardBody>165 </Card>166);
In render()
, wrap RadarChart
in a React.Fragment
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9} from 'recharts';10import {11 AutoSizer,12 Card,13 CardBody,14 HeadingText,15 NrqlQuery,16 SegmentedControl,17 SegmentedControlItem,18 Spinner,19} from 'nr1';20
21const CHART_TYPES = {22 'Radar': 'radar',23 'Treemap': 'treemap'24}25
26export default class RadarOrTreemapVisualization extends React.Component {27 // Custom props you wish to be configurable in the UI must also be defined in28 // the nr1.json file for the visualization. See docs for more details.29 static propTypes = {30 /**31 * A fill color to override the default fill color. This is an example of32 * a custom chart configuration.33 */34 fill: PropTypes.string,35
36 /**37 * A stroke color to override the default stroke color. This is an example of38 * a custom chart configuration.39 */40 stroke: PropTypes.string,41 /**42 * An array of objects consisting of a nrql `query` and `accountId`.43 * This should be a standard prop for any NRQL based visualizations.44 */45 nrqlQueries: PropTypes.arrayOf(46 PropTypes.shape({47 accountId: PropTypes.number,48 query: PropTypes.string,49 })50 ),51 };52
53 state = {54 selectedChart: CHART_TYPES.Radar,55 };56
57 /**58 * Restructure the data for a non-time-series, facet-based NRQL query into a59 * form accepted by the Recharts library's RadarChart.60 * (https://recharts.org/api/RadarChart).61 */62 transformData = (rawData) => {63 return rawData.map((entry) => ({64 name: entry.metadata.name,65 // Only grabbing the first data value because this is not time-series data.66 value: entry.data[0].y,67 }));68 };69
70 /**71 * Format the given axis tick's numeric value into a string for display.72 */73 formatTick = (value) => {74 return value.toLocaleString();75 };76
77 render() {78 const {nrqlQueries, stroke, fill} = this.props;79
80 const nrqlQueryPropsAvailable =81 nrqlQueries &&82 nrqlQueries[0] &&83 nrqlQueries[0].accountId &&84 nrqlQueries[0].query;85
86 if (!nrqlQueryPropsAvailable) {87 return <EmptyState />;88 }89
90 return (91 <AutoSizer>92 {({width, height}) => (93 <NrqlQuery94 query={nrqlQueries[0].query}95 accountId={parseInt(nrqlQueries[0].accountId)}96 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}97 >98 {({data, loading, error}) => {99 if (loading) {100 return <Spinner />;101 }102
103 if (error) {104 return <ErrorState />;105 }106
107 const transformedData = this.transformData(data);108
109 return (110 <React.Fragment>111 <RadarChart112 width={width}113 height={height}114 data={transformedData}115 >116 <PolarGrid />117 <PolarAngleAxis dataKey="name" />118 <PolarRadiusAxis tickFormatter={this.formatTick} />119 <Radar120 dataKey="value"121 stroke={stroke || '#51C9B7'}122 fill={fill || '#51C9B7'}123 fillOpacity={0.6}124 />125 </RadarChart>126 </React.Fragment>127 );128 }}129 </NrqlQuery>130 )}131 </AutoSizer>132 );133 }134}135
136const EmptyState = () => (137 <Card className="EmptyState">138 <CardBody className="EmptyState-cardBody">139 <HeadingText140 spacingType={[HeadingText.SPACING_TYPE.LARGE]}141 type={HeadingText.TYPE.HEADING_3}142 >143 Please provide at least one NRQL query & account ID pair144 </HeadingText>145 <HeadingText146 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}147 type={HeadingText.TYPE.HEADING_4}148 >149 An example NRQL query you can try is:150 </HeadingText>151 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>152 </CardBody>153 </Card>154);155
156const ErrorState = () => (157 <Card className="ErrorState">158 <CardBody className="ErrorState-cardBody">159 <HeadingText160 className="ErrorState-headingText"161 spacingType={[HeadingText.SPACING_TYPE.LARGE]}162 type={HeadingText.TYPE.HEADING_3}163 >164 Oops! Something went wrong.165 </HeadingText>166 </CardBody>167 </Card>168);
This allows you to return multiple components from the same render()
.
Add a SegmentedControl
and two SegmentedControlItem
components, each with a value
and a label
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9} from 'recharts';10import {11 AutoSizer,12 Card,13 CardBody,14 HeadingText,15 NrqlQuery,16 SegmentedControl,17 SegmentedControlItem,18 Spinner,19} from 'nr1';20
21const CHART_TYPES = {22 'Radar': 'radar',23 'Treemap': 'treemap'24}25
26export default class RadarOrTreemapVisualization extends React.Component {27 // Custom props you wish to be configurable in the UI must also be defined in28 // the nr1.json file for the visualization. See docs for more details.29 static propTypes = {30 /**31 * A fill color to override the default fill color. This is an example of32 * a custom chart configuration.33 */34 fill: PropTypes.string,35
36 /**37 * A stroke color to override the default stroke color. This is an example of38 * a custom chart configuration.39 */40 stroke: PropTypes.string,41 /**42 * An array of objects consisting of a nrql `query` and `accountId`.43 * This should be a standard prop for any NRQL based visualizations.44 */45 nrqlQueries: PropTypes.arrayOf(46 PropTypes.shape({47 accountId: PropTypes.number,48 query: PropTypes.string,49 })50 ),51 };52
53 state = {54 selectedChart: CHART_TYPES.Radar,55 };56
57 /**58 * Restructure the data for a non-time-series, facet-based NRQL query into a59 * form accepted by the Recharts library's RadarChart.60 * (https://recharts.org/api/RadarChart).61 */62 transformData = (rawData) => {63 return rawData.map((entry) => ({64 name: entry.metadata.name,65 // Only grabbing the first data value because this is not time-series data.66 value: entry.data[0].y,67 }));68 };69
70 /**71 * Format the given axis tick's numeric value into a string for display.72 */73 formatTick = (value) => {74 return value.toLocaleString();75 };76
77 render() {78 const {nrqlQueries, stroke, fill} = this.props;79
80 const nrqlQueryPropsAvailable =81 nrqlQueries &&82 nrqlQueries[0] &&83 nrqlQueries[0].accountId &&84 nrqlQueries[0].query;85
86 if (!nrqlQueryPropsAvailable) {87 return <EmptyState />;88 }89
90 return (91 <AutoSizer>92 {({width, height}) => (93 <NrqlQuery94 query={nrqlQueries[0].query}95 accountId={parseInt(nrqlQueries[0].accountId)}96 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}97 >98 {({data, loading, error}) => {99 if (loading) {100 return <Spinner />;101 }102
103 if (error) {104 return <ErrorState />;105 }106
107 const transformedData = this.transformData(data);108
109 return (110 <React.Fragment>111 <SegmentedControl112 onChange={(event, value) => console.log(value)}113 >114 <SegmentedControlItem115 value={CHART_TYPES.Radar}116 label="Radar chart"117 />118 <SegmentedControlItem119 value={CHART_TYPES.Treemap}120 label="Treemap chart"121 />122 </SegmentedControl>123 <RadarChart124 width={width}125 height={height}126 data={transformedData}127 >128 <PolarGrid />129 <PolarAngleAxis dataKey="name" />130 <PolarRadiusAxis tickFormatter={this.formatTick} />131 <Radar132 dataKey="value"133 stroke={stroke || '#51C9B7'}134 fill={fill || '#51C9B7'}135 fillOpacity={0.6}136 />137 </RadarChart>138 </React.Fragment>139 );140 }}141 </NrqlQuery>142 )}143 </AutoSizer>144 );145 }146}147
148const EmptyState = () => (149 <Card className="EmptyState">150 <CardBody className="EmptyState-cardBody">151 <HeadingText152 spacingType={[HeadingText.SPACING_TYPE.LARGE]}153 type={HeadingText.TYPE.HEADING_3}154 >155 Please provide at least one NRQL query & account ID pair156 </HeadingText>157 <HeadingText158 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}159 type={HeadingText.TYPE.HEADING_4}160 >161 An example NRQL query you can try is:162 </HeadingText>163 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>164 </CardBody>165 </Card>166);167
168const ErrorState = () => (169 <Card className="ErrorState">170 <CardBody className="ErrorState-cardBody">171 <HeadingText172 className="ErrorState-headingText"173 spacingType={[HeadingText.SPACING_TYPE.LARGE]}174 type={HeadingText.TYPE.HEADING_3}175 >176 Oops! Something went wrong.177 </HeadingText>178 </CardBody>179 </Card>180);
Here, your SegmentedControl
logs the SegmentedControlItem.value
to the console when you change your selection. The values you've defined for your SegmentedControlItem
components correspond to the two CHART_TYPES
you created in a previous step.
Navigate to the root of your Nerdpack at alternate-viz.
$nr1 nerdpack:serve
Open the link to your visualization that's shown in the terminal when the Node server starts:
Visualizations: ⁎ radar-or-treemap https://one.nr/012ab3cd4Ef
Configure your visualization with an account ID and a query:
With some required data for your chart to process, you now see a RadarChart
with the SegmentedControl
at the top of the view.
Look at your browser's console to see your SegmentedControl
logs:
Connect your component's state
to the SegmentedControl
Add a method to update state
and connect that method with the SegmentedControl
you added in the last section.
Add a component method, called updateSelectedChart()
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9} from 'recharts';10import {11 AutoSizer,12 Card,13 CardBody,14 HeadingText,15 NrqlQuery,16 SegmentedControl,17 SegmentedControlItem,18 Spinner,19} from 'nr1';20
21const CHART_TYPES = {22 'Radar': 'radar',23 'Treemap': 'treemap'24}25
26export default class RadarOrTreemapVisualization extends React.Component {27 // Custom props you wish to be configurable in the UI must also be defined in28 // the nr1.json file for the visualization. See docs for more details.29 static propTypes = {30 /**31 * A fill color to override the default fill color. This is an example of32 * a custom chart configuration.33 */34 fill: PropTypes.string,35
36 /**37 * A stroke color to override the default stroke color. This is an example of38 * a custom chart configuration.39 */40 stroke: PropTypes.string,41 /**42 * An array of objects consisting of a nrql `query` and `accountId`.43 * This should be a standard prop for any NRQL based visualizations.44 */45 nrqlQueries: PropTypes.arrayOf(46 PropTypes.shape({47 accountId: PropTypes.number,48 query: PropTypes.string,49 })50 ),51 };52
53 state = {54 selectedChart: CHART_TYPES.Radar,55 };56
57 /**58 * Restructure the data for a non-time-series, facet-based NRQL query into a59 * form accepted by the Recharts library's RadarChart.60 * (https://recharts.org/api/RadarChart).61 */62 transformData = (rawData) => {63 return rawData.map((entry) => ({64 name: entry.metadata.name,65 // Only grabbing the first data value because this is not time-series data.66 value: entry.data[0].y,67 }));68 };69
70 /**71 * Format the given axis tick's numeric value into a string for display.72 */73 formatTick = (value) => {74 return value.toLocaleString();75 };76
77 updateSelectedChart = (evt, value) => {78 this.setState({ selectedChart: value })79 };80
81 render() {82 const {nrqlQueries, stroke, fill} = this.props;83
84 const nrqlQueryPropsAvailable =85 nrqlQueries &&86 nrqlQueries[0] &&87 nrqlQueries[0].accountId &&88 nrqlQueries[0].query;89
90 if (!nrqlQueryPropsAvailable) {91 return <EmptyState />;92 }93
94 return (95 <AutoSizer>96 {({width, height}) => (97 <NrqlQuery98 query={nrqlQueries[0].query}99 accountId={parseInt(nrqlQueries[0].accountId)}100 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}101 >102 {({data, loading, error}) => {103 if (loading) {104 return <Spinner />;105 }106
107 if (error) {108 return <ErrorState />;109 }110
111 const transformedData = this.transformData(data);112
113 return (114 <React.Fragment>115 <SegmentedControl116 onChange={(event, value) => console.log(value)}117 >118 <SegmentedControlItem119 value={CHART_TYPES.Radar}120 label="Radar chart"121 />122 <SegmentedControlItem123 value={CHART_TYPES.Treemap}124 label="Treemap chart"125 />126 </SegmentedControl>127 <RadarChart128 width={width}129 height={height}130 data={transformedData}131 >132 <PolarGrid />133 <PolarAngleAxis dataKey="name" />134 <PolarRadiusAxis tickFormatter={this.formatTick} />135 <Radar136 dataKey="value"137 stroke={stroke || '#51C9B7'}138 fill={fill || '#51C9B7'}139 fillOpacity={0.6}140 />141 </RadarChart>142 </React.Fragment>143 );144 }}145 </NrqlQuery>146 )}147 </AutoSizer>148 );149 }150}151
152const EmptyState = () => (153 <Card className="EmptyState">154 <CardBody className="EmptyState-cardBody">155 <HeadingText156 spacingType={[HeadingText.SPACING_TYPE.LARGE]}157 type={HeadingText.TYPE.HEADING_3}158 >159 Please provide at least one NRQL query & account ID pair160 </HeadingText>161 <HeadingText162 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}163 type={HeadingText.TYPE.HEADING_4}164 >165 An example NRQL query you can try is:166 </HeadingText>167 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>168 </CardBody>169 </Card>170);171
172const ErrorState = () => (173 <Card className="ErrorState">174 <CardBody className="ErrorState-cardBody">175 <HeadingText176 className="ErrorState-headingText"177 spacingType={[HeadingText.SPACING_TYPE.LARGE]}178 type={HeadingText.TYPE.HEADING_3}179 >180 Oops! Something went wrong.181 </HeadingText>182 </CardBody>183 </Card>184);
This new method takes a value
argument and sets state.selectedChart
to that value.
Set SegmentedControl.onChange
to updateSelectedChart()
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9} from 'recharts';10import {11 AutoSizer,12 Card,13 CardBody,14 HeadingText,15 NrqlQuery,16 SegmentedControl,17 SegmentedControlItem,18 Spinner,19} from 'nr1';20
21const CHART_TYPES = {22 'Radar': 'radar',23 'Treemap': 'treemap'24}25
26export default class RadarOrTreemapVisualization extends React.Component {27 // Custom props you wish to be configurable in the UI must also be defined in28 // the nr1.json file for the visualization. See docs for more details.29 static propTypes = {30 /**31 * A fill color to override the default fill color. This is an example of32 * a custom chart configuration.33 */34 fill: PropTypes.string,35
36 /**37 * A stroke color to override the default stroke color. This is an example of38 * a custom chart configuration.39 */40 stroke: PropTypes.string,41 /**42 * An array of objects consisting of a nrql `query` and `accountId`.43 * This should be a standard prop for any NRQL based visualizations.44 */45 nrqlQueries: PropTypes.arrayOf(46 PropTypes.shape({47 accountId: PropTypes.number,48 query: PropTypes.string,49 })50 ),51 };52
53 state = {54 selectedChart: CHART_TYPES.Radar,55 };56
57 /**58 * Restructure the data for a non-time-series, facet-based NRQL query into a59 * form accepted by the Recharts library's RadarChart.60 * (https://recharts.org/api/RadarChart).61 */62 transformData = (rawData) => {63 return rawData.map((entry) => ({64 name: entry.metadata.name,65 // Only grabbing the first data value because this is not time-series data.66 value: entry.data[0].y,67 }));68 };69
70 /**71 * Format the given axis tick's numeric value into a string for display.72 */73 formatTick = (value) => {74 return value.toLocaleString();75 };76
77 updateSelectedChart = (evt, value) => {78 this.setState({ selectedChart: value })79 };80
81 render() {82 const {nrqlQueries, stroke, fill} = this.props;83
84 const nrqlQueryPropsAvailable =85 nrqlQueries &&86 nrqlQueries[0] &&87 nrqlQueries[0].accountId &&88 nrqlQueries[0].query;89
90 if (!nrqlQueryPropsAvailable) {91 return <EmptyState />;92 }93
94 return (95 <AutoSizer>96 {({width, height}) => (97 <NrqlQuery98 query={nrqlQueries[0].query}99 accountId={parseInt(nrqlQueries[0].accountId)}100 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}101 >102 {({data, loading, error}) => {103 if (loading) {104 return <Spinner />;105 }106
107 if (error) {108 return <ErrorState />;109 }110
111 const transformedData = this.transformData(data);112
113 return (114 <React.Fragment>115 <SegmentedControl116 onChange={this.updateSelectedChart}117 >118 <SegmentedControlItem119 value={CHART_TYPES.Radar}120 label="Radar chart"121 />122 <SegmentedControlItem123 value={CHART_TYPES.Treemap}124 label="Treemap chart"125 />126 </SegmentedControl>127 <RadarChart128 width={width}129 height={height}130 data={transformedData}131 >132 <PolarGrid />133 <PolarAngleAxis dataKey="name" />134 <PolarRadiusAxis tickFormatter={this.formatTick} />135 <Radar136 dataKey="value"137 stroke={stroke || '#51C9B7'}138 fill={fill || '#51C9B7'}139 fillOpacity={0.6}140 />141 </RadarChart>142 </React.Fragment>143 );144 }}145 </NrqlQuery>146 )}147 </AutoSizer>148 );149 }150}151
152const EmptyState = () => (153 <Card className="EmptyState">154 <CardBody className="EmptyState-cardBody">155 <HeadingText156 spacingType={[HeadingText.SPACING_TYPE.LARGE]}157 type={HeadingText.TYPE.HEADING_3}158 >159 Please provide at least one NRQL query & account ID pair160 </HeadingText>161 <HeadingText162 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}163 type={HeadingText.TYPE.HEADING_4}164 >165 An example NRQL query you can try is:166 </HeadingText>167 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>168 </CardBody>169 </Card>170);171
172const ErrorState = () => (173 <Card className="ErrorState">174 <CardBody className="ErrorState-cardBody">175 <HeadingText176 className="ErrorState-headingText"177 spacingType={[HeadingText.SPACING_TYPE.LARGE]}178 type={HeadingText.TYPE.HEADING_3}179 >180 Oops! Something went wrong.181 </HeadingText>182 </CardBody>183 </Card>184);
Now, when you change your selection in the SegmentedControl
, your selection will be set in state
.
Implement a Treemap
option
Add a Treemap
to your visualization. This map will be an alternative to the existing RadarChart
.
Technical detail
This guide uses Recharts components for third-party charts, but you can use any other JavaScript charting libraries that are compatible with the current React version when you build New Relic visualizations and apps.
Import Treemap
from recharts
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9 Treemap,10} from 'recharts';11import {12 AutoSizer,13 Card,14 CardBody,15 HeadingText,16 NrqlQuery,17 SegmentedControl,18 SegmentedControlItem,19 Spinner,20} from 'nr1';21
22const CHART_TYPES = {23 'Radar': 'radar',24 'Treemap': 'treemap'25}26
27export default class RadarOrTreemapVisualization extends React.Component {28 // Custom props you wish to be configurable in the UI must also be defined in29 // the nr1.json file for the visualization. See docs for more details.30 static propTypes = {31 /**32 * A fill color to override the default fill color. This is an example of33 * a custom chart configuration.34 */35 fill: PropTypes.string,36
37 /**38 * A stroke color to override the default stroke color. This is an example of39 * a custom chart configuration.40 */41 stroke: PropTypes.string,42 /**43 * An array of objects consisting of a nrql `query` and `accountId`.44 * This should be a standard prop for any NRQL based visualizations.45 */46 nrqlQueries: PropTypes.arrayOf(47 PropTypes.shape({48 accountId: PropTypes.number,49 query: PropTypes.string,50 })51 ),52 };53
54 state = {55 selectedChart: CHART_TYPES.Radar,56 };57
58 /**59 * Restructure the data for a non-time-series, facet-based NRQL query into a60 * form accepted by the Recharts library's RadarChart.61 * (https://recharts.org/api/RadarChart).62 */63 transformData = (rawData) => {64 return rawData.map((entry) => ({65 name: entry.metadata.name,66 // Only grabbing the first data value because this is not time-series data.67 value: entry.data[0].y,68 }));69 };70
71 /**72 * Format the given axis tick's numeric value into a string for display.73 */74 formatTick = (value) => {75 return value.toLocaleString();76 };77
78 updateSelectedChart = (evt, value) => {79 this.setState({ selectedChart: value })80 };81
82 render() {83 const {nrqlQueries, stroke, fill} = this.props;84
85 const nrqlQueryPropsAvailable =86 nrqlQueries &&87 nrqlQueries[0] &&88 nrqlQueries[0].accountId &&89 nrqlQueries[0].query;90
91 if (!nrqlQueryPropsAvailable) {92 return <EmptyState />;93 }94
95 return (96 <AutoSizer>97 {({width, height}) => (98 <NrqlQuery99 query={nrqlQueries[0].query}100 accountId={parseInt(nrqlQueries[0].accountId)}101 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}102 >103 {({data, loading, error}) => {104 if (loading) {105 return <Spinner />;106 }107
108 if (error) {109 return <ErrorState />;110 }111
112 const transformedData = this.transformData(data);113
114 return (115 <React.Fragment>116 <SegmentedControl117 onChange={this.updateSelectedChart}118 >119 <SegmentedControlItem120 value={CHART_TYPES.Radar}121 label="Radar chart"122 />123 <SegmentedControlItem124 value={CHART_TYPES.Treemap}125 label="Treemap chart"126 />127 </SegmentedControl>128 <RadarChart129 width={width}130 height={height}131 data={transformedData}132 >133 <PolarGrid />134 <PolarAngleAxis dataKey="name" />135 <PolarRadiusAxis tickFormatter={this.formatTick} />136 <Radar137 dataKey="value"138 stroke={stroke || '#51C9B7'}139 fill={fill || '#51C9B7'}140 fillOpacity={0.6}141 />142 </RadarChart>143 </React.Fragment>144 );145 }}146 </NrqlQuery>147 )}148 </AutoSizer>149 );150 }151}152
153const EmptyState = () => (154 <Card className="EmptyState">155 <CardBody className="EmptyState-cardBody">156 <HeadingText157 spacingType={[HeadingText.SPACING_TYPE.LARGE]}158 type={HeadingText.TYPE.HEADING_3}159 >160 Please provide at least one NRQL query & account ID pair161 </HeadingText>162 <HeadingText163 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}164 type={HeadingText.TYPE.HEADING_4}165 >166 An example NRQL query you can try is:167 </HeadingText>168 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>169 </CardBody>170 </Card>171);172
173const ErrorState = () => (174 <Card className="ErrorState">175 <CardBody className="ErrorState-cardBody">176 <HeadingText177 className="ErrorState-headingText"178 spacingType={[HeadingText.SPACING_TYPE.LARGE]}179 type={HeadingText.TYPE.HEADING_3}180 >181 Oops! Something went wrong.182 </HeadingText>183 </CardBody>184 </Card>185);
Now, you can use Treemap
in your visualization component.
In render()
, add a Treemap
component:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9 Treemap,10} from 'recharts';11import {12 AutoSizer,13 Card,14 CardBody,15 HeadingText,16 NrqlQuery,17 SegmentedControl,18 SegmentedControlItem,19 Spinner,20} from 'nr1';21
22const CHART_TYPES = {23 'Radar': 'radar',24 'Treemap': 'treemap'25}26
27export default class RadarOrTreemapVisualization extends React.Component {28 // Custom props you wish to be configurable in the UI must also be defined in29 // the nr1.json file for the visualization. See docs for more details.30 static propTypes = {31 /**32 * A fill color to override the default fill color. This is an example of33 * a custom chart configuration.34 */35 fill: PropTypes.string,36
37 /**38 * A stroke color to override the default stroke color. This is an example of39 * a custom chart configuration.40 */41 stroke: PropTypes.string,42 /**43 * An array of objects consisting of a nrql `query` and `accountId`.44 * This should be a standard prop for any NRQL based visualizations.45 */46 nrqlQueries: PropTypes.arrayOf(47 PropTypes.shape({48 accountId: PropTypes.number,49 query: PropTypes.string,50 })51 ),52 };53
54 state = {55 selectedChart: CHART_TYPES.Radar,56 };57
58 /**59 * Restructure the data for a non-time-series, facet-based NRQL query into a60 * form accepted by the Recharts library's RadarChart.61 * (https://recharts.org/api/RadarChart).62 */63 transformData = (rawData) => {64 return rawData.map((entry) => ({65 name: entry.metadata.name,66 // Only grabbing the first data value because this is not time-series data.67 value: entry.data[0].y,68 }));69 };70
71 /**72 * Format the given axis tick's numeric value into a string for display.73 */74 formatTick = (value) => {75 return value.toLocaleString();76 };77
78 updateSelectedChart = (evt, value) => {79 this.setState({ selectedChart: value })80 };81
82 render() {83 const {nrqlQueries, stroke, fill} = this.props;84
85 const nrqlQueryPropsAvailable =86 nrqlQueries &&87 nrqlQueries[0] &&88 nrqlQueries[0].accountId &&89 nrqlQueries[0].query;90
91 if (!nrqlQueryPropsAvailable) {92 return <EmptyState />;93 }94
95 return (96 <AutoSizer>97 {({width, height}) => (98 <NrqlQuery99 query={nrqlQueries[0].query}100 accountId={parseInt(nrqlQueries[0].accountId)}101 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}102 >103 {({data, loading, error}) => {104 if (loading) {105 return <Spinner />;106 }107
108 if (error) {109 return <ErrorState />;110 }111
112 const transformedData = this.transformData(data);113
114 return (115 <React.Fragment>116 <SegmentedControl117 onChange={this.updateSelectedChart}118 >119 <SegmentedControlItem120 value={CHART_TYPES.Radar}121 label="Radar chart"122 />123 <SegmentedControlItem124 value={CHART_TYPES.Treemap}125 label="Treemap chart"126 />127 </SegmentedControl>128 <RadarChart129 width={width}130 height={height}131 data={transformedData}132 >133 <PolarGrid />134 <PolarAngleAxis dataKey="name" />135 <PolarRadiusAxis tickFormatter={this.formatTick} />136 <Radar137 dataKey="value"138 stroke={stroke || '#51C9B7'}139 fill={fill || '#51C9B7'}140 fillOpacity={0.6}141 />142 </RadarChart>143 <Treemap144 width={width}145 height={height}146 data={transformedData}147 dataKey="value"148 ratio={4 / 3}149 stroke={stroke || '#000000'}150 fill={fill || '#51C9B7'}151 />152 </React.Fragment>153 );154 }}155 </NrqlQuery>156 )}157 </AutoSizer>158 );159 }160}161
162const EmptyState = () => (163 <Card className="EmptyState">164 <CardBody className="EmptyState-cardBody">165 <HeadingText166 spacingType={[HeadingText.SPACING_TYPE.LARGE]}167 type={HeadingText.TYPE.HEADING_3}168 >169 Please provide at least one NRQL query & account ID pair170 </HeadingText>171 <HeadingText172 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}173 type={HeadingText.TYPE.HEADING_4}174 >175 An example NRQL query you can try is:176 </HeadingText>177 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>178 </CardBody>179 </Card>180);181
182const ErrorState = () => (183 <Card className="ErrorState">184 <CardBody className="ErrorState-cardBody">185 <HeadingText186 className="ErrorState-headingText"187 spacingType={[HeadingText.SPACING_TYPE.LARGE]}188 type={HeadingText.TYPE.HEADING_3}189 >190 Oops! Something went wrong.191 </HeadingText>192 </CardBody>193 </Card>194);
Here, you've defined a new Treemap
component with some props, including height
, width
, fill
, and stroke
.
With your Nerdpack served locally, view your visualization. The SegmentedControl
and RadarChart
are at the top of the view, but if you scroll down, you'll see your new Treemap
:
Switch between charts with your component's state
Use state.selectedChart
to determine which chart to show: the RadarChart
or the Treemap
.
Destructure this.state
to access selectedChart
as a separate constant. Then, compare selectedChart
to CHART_TYPES.Radar
. If they are the same, render a RadarChart
. Otherwise, render a Treemap
:
1import React from 'react';2import PropTypes from 'prop-types';3import {4 Radar,5 RadarChart,6 PolarGrid,7 PolarAngleAxis,8 PolarRadiusAxis,9 Treemap,10} from 'recharts';11import {12 AutoSizer,13 Card,14 CardBody,15 HeadingText,16 NrqlQuery,17 SegmentedControl,18 SegmentedControlItem,19 Spinner,20} from 'nr1';21
22const CHART_TYPES = {23 'Radar': 'radar',24 'Treemap': 'treemap'25}26
27export default class RadarOrTreemapVisualization extends React.Component {28 // Custom props you wish to be configurable in the UI must also be defined in29 // the nr1.json file for the visualization. See docs for more details.30 static propTypes = {31 /**32 * A fill color to override the default fill color. This is an example of33 * a custom chart configuration.34 */35 fill: PropTypes.string,36
37 /**38 * A stroke color to override the default stroke color. This is an example of39 * a custom chart configuration.40 */41 stroke: PropTypes.string,42 /**43 * An array of objects consisting of a nrql `query` and `accountId`.44 * This should be a standard prop for any NRQL based visualizations.45 */46 nrqlQueries: PropTypes.arrayOf(47 PropTypes.shape({48 accountId: PropTypes.number,49 query: PropTypes.string,50 })51 ),52 };53
54 state = {55 selectedChart: CHART_TYPES.Radar,56 };57
58 /**59 * Restructure the data for a non-time-series, facet-based NRQL query into a60 * form accepted by the Recharts library's RadarChart.61 * (https://recharts.org/api/RadarChart).62 */63 transformData = (rawData) => {64 return rawData.map((entry) => ({65 name: entry.metadata.name,66 // Only grabbing the first data value because this is not time-series data.67 value: entry.data[0].y,68 }));69 };70
71 /**72 * Format the given axis tick's numeric value into a string for display.73 */74 formatTick = (value) => {75 return value.toLocaleString();76 };77
78 updateSelectedChart = (evt, value) => {79 this.setState({ selectedChart: value })80 };81
82 render() {83 const {nrqlQueries, stroke, fill} = this.props;84 const {selectedChart} = this.state;85
86 const nrqlQueryPropsAvailable =87 nrqlQueries &&88 nrqlQueries[0] &&89 nrqlQueries[0].accountId &&90 nrqlQueries[0].query;91
92 if (!nrqlQueryPropsAvailable) {93 return <EmptyState />;94 }95
96 return (97 <AutoSizer>98 {({width, height}) => (99 <NrqlQuery100 query={nrqlQueries[0].query}101 accountId={parseInt(nrqlQueries[0].accountId)}102 pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}103 >104 {({data, loading, error}) => {105 if (loading) {106 return <Spinner />;107 }108
109 if (error) {110 return <ErrorState />;111 }112
113 const transformedData = this.transformData(data);114
115 return (116 <React.Fragment>117 <SegmentedControl118 onChange={this.updateSelectedChart}119 >120 <SegmentedControlItem121 value={CHART_TYPES.Radar}122 label="Radar chart"123 />124 <SegmentedControlItem125 value={CHART_TYPES.Treemap}126 label="Treemap chart"127 />128 </SegmentedControl>129 {selectedChart === CHART_TYPES.Radar ? (130 <RadarChart131 width={width}132 height={height}133 data={transformedData}134 >135 <PolarGrid />136 <PolarAngleAxis dataKey="name" />137 <PolarRadiusAxis tickFormatter={this.formatTick} />138 <Radar139 dataKey="value"140 stroke={stroke || '#51C9B7'}141 fill={fill || '#51C9B7'}142 fillOpacity={0.6}143 />144 </RadarChart>145 ) : (146 <Treemap147 width={width}148 height={height}149 data={transformedData}150 dataKey="value"151 ratio={4 / 3}152 stroke={stroke || '#000000'}153 fill={fill || '#51C9B7'}154 />155 )}156 </React.Fragment>157 );158 }}159 </NrqlQuery>160 )}161 </AutoSizer>162 );163 }164}165
166const EmptyState = () => (167 <Card className="EmptyState">168 <CardBody className="EmptyState-cardBody">169 <HeadingText170 spacingType={[HeadingText.SPACING_TYPE.LARGE]}171 type={HeadingText.TYPE.HEADING_3}172 >173 Please provide at least one NRQL query & account ID pair174 </HeadingText>175 <HeadingText176 spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}177 type={HeadingText.TYPE.HEADING_4}178 >179 An example NRQL query you can try is:180 </HeadingText>181 <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>182 </CardBody>183 </Card>184);185
186const ErrorState = () => (187 <Card className="ErrorState">188 <CardBody className="ErrorState-cardBody">189 <HeadingText190 className="ErrorState-headingText"191 spacingType={[HeadingText.SPACING_TYPE.LARGE]}192 type={HeadingText.TYPE.HEADING_3}193 >194 Oops! Something went wrong.195 </HeadingText>196 </CardBody>197 </Card>198);
Here, you used a ternary expression to render a RadarChart
or a Treemap
. The rendered chart is determined by the value of selectedChart
.
With your Nerdpack served locally, view your visualization.
Select Radar chart from the SegmentedControl
:
Select Treemap chart from the SegmentedControl
:
Summary
Congratulations! In this lesson, you learned how to:
- Customize your visualization using New Relic One SDK components
- Add a new chart type to your visualization
- Create a user interaction in your visualization
Course
This lesson is part of a course that teaches you how to build a custom visualization on the New Relic platform. When you're ready, continue on to the next lesson: Customize visualizations with configuration.