Customize your visualization with configuration options

In the previous lesson, you built a custom visualization that shows queried data in one of two chart types:

You used a SegmentedControl to switch between the two chart types in the visualization UI. This implementation takes up space in the visualization, but it offers your users the choice to switch between two chart types even after you've created an instance of your chart. But what if you only need to be able to select an option once, when initializing the visualization?

In this lesson you'll learn how to add a configuration option to your visualization which replaces the SegmentedControl.

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.

Add a new configuration option

Step 1 of 8

In your visualization's nr1.json file, add an enum configuration object for selectedChart:

1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import {
4
Radar,
5
RadarChart,
6
PolarGrid,
7
PolarAngleAxis,
8
PolarRadiusAxis,
9
Treemap,
10
} from 'recharts';
11
import {
12
AutoSizer,
13
Card,
14
CardBody,
15
HeadingText,
16
NrqlQuery,
17
SegmentedControl,
18
SegmentedControlItem,
19
Spinner,
20
} from 'nr1';
21
22
const CHART_TYPES = {
23
Radar: 'radar',
24
Treemap: 'treemap',
25
};
26
27
export default class RadarOrTreemapVisualization extends React.Component {
28
// Custom props you wish to be configurable in the UI must also be defined in
29
// 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 of
33
* 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 of
39
* 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 a
60
* 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
<NrqlQuery
100
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
<SegmentedControl onChange={this.updateSelectedChart}>
118
<SegmentedControlItem
119
value={CHART_TYPES.Radar}
120
label="Radar chart"
121
/>
122
<SegmentedControlItem
123
value={CHART_TYPES.Treemap}
124
label="Treemap chart"
125
/>
126
</SegmentedControl>
127
{selectedChart === CHART_TYPES.Radar ? (
128
<RadarChart
129
width={width}
130
height={height}
131
data={transformedData}
132
>
133
<PolarGrid />
134
<PolarAngleAxis dataKey="name" />
135
<PolarRadiusAxis tickFormatter={this.formatTick} />
136
<Radar
137
dataKey="value"
138
stroke={stroke || '#51C9B7'}
139
fill={fill || '#51C9B7'}
140
fillOpacity={0.6}
141
/>
142
</RadarChart>
143
) : (
144
<Treemap
145
width={width}
146
height={height}
147
data={transformedData}
148
dataKey="value"
149
ratio={4 / 3}
150
stroke={stroke || '#000000'}
151
fill={fill || '#51C9B7'}
152
/>
153
)}
154
</React.Fragment>
155
);
156
}}
157
</NrqlQuery>
158
)}
159
</AutoSizer>
160
);
161
}
162
}
163
164
const EmptyState = () => (
165
<Card className="EmptyState">
166
<CardBody className="EmptyState-cardBody">
167
<HeadingText
168
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
169
type={HeadingText.TYPE.HEADING_3}
170
>
171
Please provide at least one NRQL query & account ID pair
172
</HeadingText>
173
<HeadingText
174
spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}
175
type={HeadingText.TYPE.HEADING_4}
176
>
177
An example NRQL query you can try is:
178
</HeadingText>
179
<code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>
180
</CardBody>
181
</Card>
182
);
183
184
const ErrorState = () => (
185
<Card className="ErrorState">
186
<CardBody className="ErrorState-cardBody">
187
<HeadingText
188
className="ErrorState-headingText"
189
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
190
type={HeadingText.TYPE.HEADING_3}
191
>
192
Oops! Something went wrong.
193
</HeadingText>
194
</CardBody>
195
</Card>
196
);
visualizations/radar-or-treemap/index.js
1
{
2
"schemaType": "VISUALIZATION",
3
"id": "radar-or-treemap",
4
"displayName": "RadarOrTreemap",
5
"description": "",
6
"configuration": [
7
{
8
"name": "selectedChart",
9
"title": "Select chart",
10
"description": "Select which chart to display",
11
"type": "enum",
12
"items": [
13
{
14
"title": "Radar",
15
"value": "radar"
16
},
17
{
18
"title": "Treemap",
19
"value": "treemap"
20
}
21
]
22
},
23
{
24
"name": "nrqlQueries",
25
"title": "NRQL Queries",
26
"type": "collection",
27
"items": [
28
{
29
"name": "accountId",
30
"title": "Account ID",
31
"description": "Account ID to be associated with the query",
32
"type": "account-id"
33
},
34
{
35
"name": "query",
36
"title": "Query",
37
"description": "NRQL query for visualization",
38
"type": "nrql"
39
}
40
]
41
},
42
{
43
"name": "fill",
44
"title": "Fill color",
45
"description": "A fill color to override the default fill color",
46
"type": "string"
47
},
48
{
49
"name": "stroke",
50
"title": "Stroke color",
51
"description": "A stroke color to override the default stroke color",
52
"type": "string"
53
}
54
]
55
}
visualizations/radar-or-treemap/nr1.json
Step 2 of 8

Navigate to the root of your Nerdpack at alternate-viz.

Step 3 of 8
bash
$
nr1 nerdpack:serve

If you're still serving your Nerdpack from the last lesson, you need to stop it with CTRL-X and serve it again to reflect changes to nr1.json.

Step 4 of 8

Go to https://one.newrelic.com/?nerdpacks=local. The nerdpacks=local query string directs the UI to load your visualization from the local server.

Step 5 of 8

Open the Apps page:

Apps navigation is located in the New Relic One top navigation

Step 6 of 8

Go to Custom Visualizations, which is favorited by default:

Navigate to Custom Visualizations

Step 7 of 8

In Custom Visualizations, find and click your visualization:

Navigate to your visualization

Step 8 of 8

Notice the new Select chart configuration option:

Select chart config option

Selecting a chart type doesn't effect your visualization. This is because you first need to introduce the selectedChart property to the visualization component. Then, you use selectedChart to determine the chart type to render.

Replace your SegmentedControl with the configurable property

Step 1 of 5

Open your visualization's index.js file. You'll be working here for the rest of the guide.

Step 2 of 5

In render(), include selectedChart as a constant you get from destructuring props, and remove your component's state:

1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import {
4
Radar,
5
RadarChart,
6
PolarGrid,
7
PolarAngleAxis,
8
PolarRadiusAxis,
9
Treemap,
10
} from 'recharts';
11
import {
12
AutoSizer,
13
Card,
14
CardBody,
15
HeadingText,
16
NrqlQuery,
17
SegmentedControl,
18
SegmentedControlItem,
19
Spinner,
20
} from 'nr1';
21
22
const CHART_TYPES = {
23
Radar: 'radar',
24
Treemap: 'treemap',
25
};
26
27
export default class RadarOrTreemapVisualization extends React.Component {
28
// Custom props you wish to be configurable in the UI must also be defined in
29
// 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 of
33
* 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 of
39
* 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
/**
55
* Restructure the data for a non-time-series, facet-based NRQL query into a
56
* form accepted by the Recharts library's RadarChart.
57
* (https://recharts.org/api/RadarChart).
58
*/
59
transformData = (rawData) => {
60
return rawData.map((entry) => ({
61
name: entry.metadata.name,
62
// Only grabbing the first data value because this is not time-series data.
63
value: entry.data[0].y,
64
}));
65
};
66
67
/**
68
* Format the given axis tick's numeric value into a string for display.
69
*/
70
formatTick = (value) => {
71
return value.toLocaleString();
72
};
73
74
render() {
75
const { nrqlQueries, stroke, fill, selectedChart } = this.props;
76
77
const nrqlQueryPropsAvailable =
78
nrqlQueries &&
79
nrqlQueries[0] &&
80
nrqlQueries[0].accountId &&
81
nrqlQueries[0].query;
82
83
if (!nrqlQueryPropsAvailable) {
84
return <EmptyState />;
85
}
86
87
return (
88
<AutoSizer>
89
{({ width, height }) => (
90
<NrqlQuery
91
query={nrqlQueries[0].query}
92
accountId={parseInt(nrqlQueries[0].accountId)}
93
pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}
94
>
95
{({ data, loading, error }) => {
96
if (loading) {
97
return <Spinner />;
98
}
99
100
if (error) {
101
return <ErrorState />;
102
}
103
104
const transformedData = this.transformData(data);
105
106
return (
107
<React.Fragment>
108
<SegmentedControl>
109
<SegmentedControlItem
110
value={CHART_TYPES.Radar}
111
label="Radar chart"
112
/>
113
<SegmentedControlItem
114
value={CHART_TYPES.Treemap}
115
label="Treemap chart"
116
/>
117
</SegmentedControl>
118
{selectedChart === CHART_TYPES.Radar ? (
119
<RadarChart
120
width={width}
121
height={height}
122
data={transformedData}
123
>
124
<PolarGrid />
125
<PolarAngleAxis dataKey="name" />
126
<PolarRadiusAxis tickFormatter={this.formatTick} />
127
<Radar
128
dataKey="value"
129
stroke={stroke || '#51C9B7'}
130
fill={fill || '#51C9B7'}
131
fillOpacity={0.6}
132
/>
133
</RadarChart>
134
) : (
135
<Treemap
136
width={width}
137
height={height}
138
data={transformedData}
139
dataKey="value"
140
ratio={4 / 3}
141
stroke={stroke || '#000000'}
142
fill={fill || '#51C9B7'}
143
/>
144
)}
145
</React.Fragment>
146
);
147
}}
148
</NrqlQuery>
149
)}
150
</AutoSizer>
151
);
152
}
153
}
154
155
const EmptyState = () => (
156
<Card className="EmptyState">
157
<CardBody className="EmptyState-cardBody">
158
<HeadingText
159
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
160
type={HeadingText.TYPE.HEADING_3}
161
>
162
Please provide at least one NRQL query & account ID pair
163
</HeadingText>
164
<HeadingText
165
spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}
166
type={HeadingText.TYPE.HEADING_4}
167
>
168
An example NRQL query you can try is:
169
</HeadingText>
170
<code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>
171
</CardBody>
172
</Card>
173
);
174
175
const ErrorState = () => (
176
<Card className="ErrorState">
177
<CardBody className="ErrorState-cardBody">
178
<HeadingText
179
className="ErrorState-headingText"
180
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
181
type={HeadingText.TYPE.HEADING_3}
182
>
183
Oops! Something went wrong.
184
</HeadingText>
185
</CardBody>
186
</Card>
187
);
visualizations/radar-or-treemap/index.js
1
{
2
"schemaType": "VISUALIZATION",
3
"id": "radar-or-treemap",
4
"displayName": "RadarOrTreemap",
5
"description": "",
6
"configuration": [
7
{
8
"name": "selectedChart",
9
"title": "Select chart",
10
"description": "Select which chart to display",
11
"type": "enum",
12
"items": [
13
{
14
"title": "Radar",
15
"value": "radar"
16
},
17
{
18
"title": "Treemap",
19
"value": "treemap"
20
}
21
]
22
},
23
{
24
"name": "nrqlQueries",
25
"title": "NRQL Queries",
26
"type": "collection",
27
"items": [
28
{
29
"name": "accountId",
30
"title": "Account ID",
31
"description": "Account ID to be associated with the query",
32
"type": "account-id"
33
},
34
{
35
"name": "query",
36
"title": "Query",
37
"description": "NRQL query for visualization",
38
"type": "nrql"
39
}
40
]
41
},
42
{
43
"name": "fill",
44
"title": "Fill color",
45
"description": "A fill color to override the default fill color",
46
"type": "string"
47
},
48
{
49
"name": "stroke",
50
"title": "Stroke color",
51
"description": "A stroke color to override the default stroke color",
52
"type": "string"
53
}
54
]
55
}
visualizations/radar-or-treemap/nr1.json

Now that you're using selectedChart from the configuration options instead of component state, you can select a chart in the configuration panel and watch the visualization change. Unfortunately, there's a bug. The default chart option is Radar, but the initial render shows a Treemap.

Step 3 of 5

Update your ternary expression to account for the case where there is no selectedChart:

1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import {
4
Radar,
5
RadarChart,
6
PolarGrid,
7
PolarAngleAxis,
8
PolarRadiusAxis,
9
Treemap,
10
} from 'recharts';
11
import {
12
AutoSizer,
13
Card,
14
CardBody,
15
HeadingText,
16
NrqlQuery,
17
SegmentedControl,
18
SegmentedControlItem,
19
Spinner,
20
} from 'nr1';
21
22
const CHART_TYPES = {
23
Radar: 'radar',
24
Treemap: 'treemap',
25
};
26
27
export default class RadarOrTreemapVisualization extends React.Component {
28
// Custom props you wish to be configurable in the UI must also be defined in
29
// 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 of
33
* 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 of
39
* 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
/**
55
* Restructure the data for a non-time-series, facet-based NRQL query into a
56
* form accepted by the Recharts library's RadarChart.
57
* (https://recharts.org/api/RadarChart).
58
*/
59
transformData = (rawData) => {
60
return rawData.map((entry) => ({
61
name: entry.metadata.name,
62
// Only grabbing the first data value because this is not time-series data.
63
value: entry.data[0].y,
64
}));
65
};
66
67
/**
68
* Format the given axis tick's numeric value into a string for display.
69
*/
70
formatTick = (value) => {
71
return value.toLocaleString();
72
};
73
74
render() {
75
const { nrqlQueries, stroke, fill, selectedChart } = this.props;
76
77
const nrqlQueryPropsAvailable =
78
nrqlQueries &&
79
nrqlQueries[0] &&
80
nrqlQueries[0].accountId &&
81
nrqlQueries[0].query;
82
83
if (!nrqlQueryPropsAvailable) {
84
return <EmptyState />;
85
}
86
87
return (
88
<AutoSizer>
89
{({ width, height }) => (
90
<NrqlQuery
91
query={nrqlQueries[0].query}
92
accountId={parseInt(nrqlQueries[0].accountId)}
93
pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}
94
>
95
{({ data, loading, error }) => {
96
if (loading) {
97
return <Spinner />;
98
}
99
100
if (error) {
101
return <ErrorState />;
102
}
103
104
const transformedData = this.transformData(data);
105
106
return (
107
<React.Fragment>
108
<SegmentedControl>
109
<SegmentedControlItem
110
value={CHART_TYPES.Radar}
111
label="Radar chart"
112
/>
113
<SegmentedControlItem
114
value={CHART_TYPES.Treemap}
115
label="Treemap chart"
116
/>
117
</SegmentedControl>
118
{!selectedChart || selectedChart === CHART_TYPES.Radar ? (
119
<RadarChart
120
width={width}
121
height={height}
122
data={transformedData}
123
>
124
<PolarGrid />
125
<PolarAngleAxis dataKey="name" />
126
<PolarRadiusAxis tickFormatter={this.formatTick} />
127
<Radar
128
dataKey="value"
129
stroke={stroke || '#51C9B7'}
130
fill={fill || '#51C9B7'}
131
fillOpacity={0.6}
132
/>
133
</RadarChart>
134
) : (
135
<Treemap
136
width={width}
137
height={height}
138
data={transformedData}
139
dataKey="value"
140
ratio={4 / 3}
141
stroke={stroke || '#000000'}
142
fill={fill || '#51C9B7'}
143
/>
144
)}
145
</React.Fragment>
146
);
147
}}
148
</NrqlQuery>
149
)}
150
</AutoSizer>
151
);
152
}
153
}
154
155
const EmptyState = () => (
156
<Card className="EmptyState">
157
<CardBody className="EmptyState-cardBody">
158
<HeadingText
159
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
160
type={HeadingText.TYPE.HEADING_3}
161
>
162
Please provide at least one NRQL query & account ID pair
163
</HeadingText>
164
<HeadingText
165
spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}
166
type={HeadingText.TYPE.HEADING_4}
167
>
168
An example NRQL query you can try is:
169
</HeadingText>
170
<code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>
171
</CardBody>
172
</Card>
173
);
174
175
const ErrorState = () => (
176
<Card className="ErrorState">
177
<CardBody className="ErrorState-cardBody">
178
<HeadingText
179
className="ErrorState-headingText"
180
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
181
type={HeadingText.TYPE.HEADING_3}
182
>
183
Oops! Something went wrong.
184
</HeadingText>
185
</CardBody>
186
</Card>
187
);
visualizations/radar-or-treemap/index.js
1
{
2
"schemaType": "VISUALIZATION",
3
"id": "radar-or-treemap",
4
"displayName": "RadarOrTreemap",
5
"description": "",
6
"configuration": [
7
{
8
"name": "selectedChart",
9
"title": "Select chart",
10
"description": "Select which chart to display",
11
"type": "enum",
12
"items": [
13
{
14
"title": "Radar",
15
"value": "radar"
16
},
17
{
18
"title": "Treemap",
19
"value": "treemap"
20
}
21
]
22
},
23
{
24
"name": "nrqlQueries",
25
"title": "NRQL Queries",
26
"type": "collection",
27
"items": [
28
{
29
"name": "accountId",
30
"title": "Account ID",
31
"description": "Account ID to be associated with the query",
32
"type": "account-id"
33
},
34
{
35
"name": "query",
36
"title": "Query",
37
"description": "NRQL query for visualization",
38
"type": "nrql"
39
}
40
]
41
},
42
{
43
"name": "fill",
44
"title": "Fill color",
45
"description": "A fill color to override the default fill color",
46
"type": "string"
47
},
48
{
49
"name": "stroke",
50
"title": "Stroke color",
51
"description": "A stroke color to override the default stroke color",
52
"type": "string"
53
}
54
]
55
}
visualizations/radar-or-treemap/nr1.json

Now, your data is rendered in a RadarChart if you haven't yet configured the option.

Step 4 of 5

Remove SegmentedControl from render():

1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import {
4
Radar,
5
RadarChart,
6
PolarGrid,
7
PolarAngleAxis,
8
PolarRadiusAxis,
9
Treemap,
10
} from 'recharts';
11
import {
12
AutoSizer,
13
Card,
14
CardBody,
15
HeadingText,
16
NrqlQuery,
17
Spinner,
18
} from 'nr1';
19
20
const CHART_TYPES = {
21
Radar: 'radar',
22
Treemap: 'treemap',
23
};
24
25
export default class RadarOrTreemapVisualization extends React.Component {
26
// Custom props you wish to be configurable in the UI must also be defined in
27
// the nr1.json file for the visualization. See docs for more details.
28
static propTypes = {
29
/**
30
* A fill color to override the default fill color. This is an example of
31
* a custom chart configuration.
32
*/
33
fill: PropTypes.string,
34
35
/**
36
* A stroke color to override the default stroke color. This is an example of
37
* a custom chart configuration.
38
*/
39
stroke: PropTypes.string,
40
/**
41
* An array of objects consisting of a nrql `query` and `accountId`.
42
* This should be a standard prop for any NRQL based visualizations.
43
*/
44
nrqlQueries: PropTypes.arrayOf(
45
PropTypes.shape({
46
accountId: PropTypes.number,
47
query: PropTypes.string,
48
})
49
),
50
};
51
52
/**
53
* Restructure the data for a non-time-series, facet-based NRQL query into a
54
* form accepted by the Recharts library's RadarChart.
55
* (https://recharts.org/api/RadarChart).
56
*/
57
transformData = (rawData) => {
58
return rawData.map((entry) => ({
59
name: entry.metadata.name,
60
// Only grabbing the first data value because this is not time-series data.
61
value: entry.data[0].y,
62
}));
63
};
64
65
/**
66
* Format the given axis tick's numeric value into a string for display.
67
*/
68
formatTick = (value) => {
69
return value.toLocaleString();
70
};
71
72
render() {
73
const { nrqlQueries, stroke, fill, selectedChart } = this.props;
74
75
const nrqlQueryPropsAvailable =
76
nrqlQueries &&
77
nrqlQueries[0] &&
78
nrqlQueries[0].accountId &&
79
nrqlQueries[0].query;
80
81
if (!nrqlQueryPropsAvailable) {
82
return <EmptyState />;
83
}
84
85
return (
86
<AutoSizer>
87
{({ width, height }) => (
88
<NrqlQuery
89
query={nrqlQueries[0].query}
90
accountId={parseInt(nrqlQueries[0].accountId)}
91
pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}
92
>
93
{({ data, loading, error }) => {
94
if (loading) {
95
return <Spinner />;
96
}
97
98
if (error) {
99
return <ErrorState />;
100
}
101
102
const transformedData = this.transformData(data);
103
104
return (
105
<React.Fragment>
106
{!selectedChart || selectedChart === CHART_TYPES.Radar ? (
107
<RadarChart
108
width={width}
109
height={height}
110
data={transformedData}
111
>
112
<PolarGrid />
113
<PolarAngleAxis dataKey="name" />
114
<PolarRadiusAxis tickFormatter={this.formatTick} />
115
<Radar
116
dataKey="value"
117
stroke={stroke || '#51C9B7'}
118
fill={fill || '#51C9B7'}
119
fillOpacity={0.6}
120
/>
121
</RadarChart>
122
) : (
123
<Treemap
124
width={width}
125
height={height}
126
data={transformedData}
127
dataKey="value"
128
ratio={4 / 3}
129
stroke={stroke || '#000000'}
130
fill={fill || '#51C9B7'}
131
/>
132
)}
133
</React.Fragment>
134
);
135
}}
136
</NrqlQuery>
137
)}
138
</AutoSizer>
139
);
140
}
141
}
142
143
const EmptyState = () => (
144
<Card className="EmptyState">
145
<CardBody className="EmptyState-cardBody">
146
<HeadingText
147
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
148
type={HeadingText.TYPE.HEADING_3}
149
>
150
Please provide at least one NRQL query & account ID pair
151
</HeadingText>
152
<HeadingText
153
spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}
154
type={HeadingText.TYPE.HEADING_4}
155
>
156
An example NRQL query you can try is:
157
</HeadingText>
158
<code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code>
159
</CardBody>
160
</Card>
161
);
162
163
const ErrorState = () => (
164
<Card className="ErrorState">
165
<CardBody className="ErrorState-cardBody">
166
<HeadingText
167
className="ErrorState-headingText"
168
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
169
type={HeadingText.TYPE.HEADING_3}
170
>
171
Oops! Something went wrong.
172
</HeadingText>
173
</CardBody>
174
</Card>
175
);
visualizations/radar-or-treemap/index.js
1
{
2
"schemaType": "VISUALIZATION",
3
"id": "radar-or-treemap",
4
"displayName": "RadarOrTreemap",
5
"description": "",
6
"configuration": [
7
{
8
"name": "selectedChart",
9
"title": "Select chart",
10
"description": "Select which chart to display",
11
"type": "enum",
12
"items": [
13
{
14
"title": "Radar",
15
"value": "radar"
16
},
17
{
18
"title": "Treemap",
19
"value": "treemap"
20
}
21
]
22
},
23
{
24
"name": "nrqlQueries",
25
"title": "NRQL Queries",
26
"type": "collection",
27
"items": [
28
{
29
"name": "accountId",
30
"title": "Account ID",
31
"description": "Account ID to be associated with the query",
32
"type": "account-id"
33
},
34
{
35
"name": "query",
36
"title": "Query",
37
"description": "NRQL query for visualization",
38
"type": "nrql"
39
}
40
]
41
},
42
{
43
"name": "fill",
44
"title": "Fill color",
45
"description": "A fill color to override the default fill color",
46
"type": "string"
47
},
48
{
49
"name": "stroke",
50
"title": "Stroke color",
51
"description": "A stroke color to override the default stroke color",
52
"type": "string"
53
}
54
]
55
}
visualizations/radar-or-treemap/nr1.json
Step 5 of 5

Serve your Nerdpack locally, and view it in the Custom Visualizations app in New Relic. Select a chart type from the dropdown in the configuration sidebar, and see your visualization update to show the matching chart type:

Configured visualization

Summary

Congratulations on completing this lesson! You've learned how to customize your visualization using nr1.json configuration.