• Log inStart now

Customize open-source Nerdpacks

Most Nerdpacks in the Instant Observability catalog are open-source. This means you can clone or fork their repositories, customize them to suit your specific needs, and re-publish them to use with your account. In this guide, you customize a Nerdpack with visualizations and publish it to your account. However, to customize any Nerdpack, you follow the same steps.

Before you begin

If you haven't already:

View a Nerdpack

Subscribe to the Victory Charts Visualizations Nerdpack and open the Circular progress bar visualization in New Relic.

Step 1 of 6

From your homepage at New Relic, navigate to Instant Observability:

Navigate to Instant Observability

Step 2 of 6

Click the Victory Charts Visualizations Nerdpack in the catalog:

Victory Charts Nerdpack

Step 4 of 6

From Apps, open Custom visualizations:

Navigate to Custom visualizations

From the list of visualizations in Custom visualizations click Circular progress bar, which you installed as part of the Victory Charts Visualizations Nerdpack.

Step 5 of 6

Under Configure visualization properties, select your account and enter a NRQL query:

Configured visualization

Now you see a circular chart that shows a percentage based on your query.

Tip

Read our documentation for instructions on how to configure the progress bar visualization.

Notice a few things about this visualization:

  • You don't control the color of the chart
  • The sections of the chart have rounded edges

For the sake of this tutorial, imagine this chart represents your data exactly how you want it to, except for two things. You'd like to use straight edges and control the chart's colors manually. In the real world you may come across Nerdpacks like this where you like what they offer, but you'd like them better if you could tweak them. Well, you can tweak them, and next, you'll learn how!

Step 6 of 6

Because you're going to use a tweaked version of the Victory Charts Visualizations Nerdpack instead of the one you subscribed to, you can unsubscribe from our version now.

Clone a Nerdpack

Find the source code repository from the Nerdpack's catalog entry and clone it to your local machine.

Step 1 of 5

From your homepage at New Relic, navigate to Instant Observability:

Navigate to Instant Observability

Step 2 of 5

Click the Victory Charts Visualizations Nerdpack in the catalog:

Victory Charts Nerdpack

Step 3 of 5

Go to the Nerdpack's source code repository:

Victory Charts repo

All open-source Nerdpacks in the catalog have links to their source code in their catalog information.

Step 4 of 5

Clone the repository:

bash
$
nr1 nerdpack:clone -r https://github.com/newrelic/nr1-victory-visualizations.git

Now you have a local version of the Victory Charts Visualizations Nerdpack! Notice that you used nr1 nerdpack:clone instead of git clone to copy the repo. nr1 nerdpack:clone offers built-in functionality to help keep your local copy distinct from the original Nerdpack in the Instant Observability public catalog. Specifically, it generates a new Nerdpack UUID so you don't have to do this yourself:

bash
Re-generating UUID...
Committing new UUID...

If you change to the nr1-victory-visualizations directory, and look at the git log, you'll see the new commit:

bash
$
git log -1 -p
commit e356bb5b10c3ecc8f93bae66d5739e1676ee21ef (HEAD -> main)
Author: New Relic CLI <nr1@newrelic.com>
Date: Tue May 25 14:29:37 2021 -0400
"chore: Auto-generated UUID"
diff --git a/nr1.json b/nr1.json
index 054de52..7a107b5 100644
--- a/nr1.json
+++ b/nr1.json
@@ -1,6 +1,6 @@
{
"schemaType": "NERDPACK",
- "id": "cf5f13d9-815f-4907-a21d-83d02fa2a4fb",
+ "id": "ab123c45-678d-9012-efg3-45hi6jkl7890",
"displayName": "Victory charts visualizations",
"description": "Visualizations built on top of Victory charts"
}
Step 5 of 5

In nr1-victory-visualizations/nr1.json, change your Nerdpack's displayName:

{
"schemaType": "NERDPACK",
"id": "269055dd-67e8-4713-8da3-bff01c6a8687",
"displayName": "My custom Victory Charts visualizations",
"description": "Visualizations built on top of Victory charts"
}
nr1-victory-visualizations/nr1.json

Now when you serve or publish your custom Nerdpack, you can easily distinguish it from the original.

Customize a Nerdpack

Tweak the Circular progress bar visualization to use straight edges and customizable colors.

Circular progress bar renders a VictoryPie with some predefined fields. The fields you'll tweak are:

Step 1 of 8

In your local Nerdpack, open nr1-victory-visualizations/visualizations/circular-progress-bar/nr1.json.

nr1.json is the Circular progress bar visualization's metadata file. Use this file to add a configurable colorScale option, which corresponds to the colorScale field on VictoryPie.

Step 2 of 8

Add a collection of string fields for you to customize your chart's colors:

1
{
2
"schemaType": "VISUALIZATION",
3
"id": "circular-progress-bar",
4
"displayName": "Circular progress bar",
5
"description": "",
6
"configuration": [
7
{
8
"name": "nrqlQueries",
9
"title": "NRQL Queries",
10
"type": "collection",
11
"items": [
12
{
13
"name": "accountId",
14
"title": "Account ID",
15
"description": "Account ID to be associated with the query",
16
"type": "account-id"
17
},
18
{
19
"name": "query",
20
"title": "Query",
21
"description": "NRQL query for visualization",
22
"type": "nrql"
23
}
24
]
25
},
26
{
27
"name": "thresholds",
28
"title": "Thresholds",
29
"type": "namespace",
30
"items": [
31
{
32
"name": "criticalThreshold",
33
"title": "Critical threshold",
34
"description": "Value at which progress is displayed as critical",
35
"type": "number"
36
},
37
{
38
"name": "highValuesAreSuccess",
39
"title": "Above threshold is success",
40
"description": "If toggled on, values above the threshold display as successful. Otherwise, values at or above the threshold display as critical.",
41
"type": "boolean"
42
}
43
]
44
},
45
{
46
"name": "colors",
47
"title": "Colors",
48
"type": "collection",
49
"items": [
50
{
51
"name": "segmentColor",
52
"title": "Segment color",
53
"description": "The color of a bar segment.",
54
"type": "string"
55
}
56
]
57
}
58
]
59
}
nr1.json
1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import { VictoryPie, VictoryAnimation, VictoryLabel } from 'victory';
4
import {
5
Card,
6
CardBody,
7
HeadingText,
8
NrqlQuery,
9
Spinner,
10
AutoSizer,
11
} from 'nr1';
12
import NrqlQueryError from '../../src/nrql-query-error';
13
import { baseLabelStyles } from '../../src/theme';
14
import { getUniqueAggregatesAndFacets } from '../../src/utils/nrql-validation-helper';
15
import Colors from '../../src/colors';
16
17
const BOUNDS = {
18
X: 400,
19
Y: 400,
20
};
21
22
const LABEL_SIZE = 24;
23
const LABEL_PADDING = 10;
24
const CHART_WIDTH = BOUNDS.X;
25
const CHART_HEIGHT = BOUNDS.Y - LABEL_SIZE - LABEL_PADDING;
26
27
export default class CircularProgressBar 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
* An array of objects consisting of a nrql `query` and `accountId`.
33
* This should be a standard prop for any NRQL based visualizations.
34
*/
35
nrqlQueries: PropTypes.arrayOf(
36
PropTypes.shape({
37
accountId: PropTypes.number,
38
query: PropTypes.string,
39
})
40
),
41
42
/**
43
* Configuration that determines what values to display as critical or
44
* successful.
45
*/
46
thresholds: PropTypes.shape({
47
criticalThreshold: PropTypes.number,
48
highValuesAreSuccess: PropTypes.bool,
49
}),
50
};
51
52
/**
53
* Restructure the data for a aggregate NRQL query with no TIMESERIES and no
54
* FACET into a for our visualization works well with.
55
*/
56
transformData = (data) => {
57
const {
58
data: [series],
59
metadata: { color: colorFromData, name: label },
60
} = data[0];
61
62
const percent = series.y * 100;
63
const color = this.getColor(percent, colorFromData);
64
65
return {
66
percent,
67
label,
68
series: [
69
{ x: 'progress', y: percent, color },
70
{ x: 'remainder', y: 100 - percent, color: 'transparent' },
71
],
72
};
73
};
74
75
nrqlInputIsValid = (data) => {
76
const { data: seriesEntries } = data[0];
77
const { uniqueAggregates, uniqueFacets } = getUniqueAggregatesAndFacets(
78
data
79
);
80
const isNonTimeseries = seriesEntries.length === 1;
81
82
return (
83
uniqueAggregates.size === 1 && uniqueFacets.size === 0 && isNonTimeseries
84
);
85
};
86
87
getColor = (value, colorFromData) => {
88
const { red6: red, green6: green } = Colors.base;
89
const {
90
thresholds: { criticalThreshold, highValuesAreSuccess },
91
} = this.props;
92
93
const threshold = parseFloat(criticalThreshold);
94
95
if (isNaN(threshold)) {
96
return colorFromData;
97
}
98
99
if (highValuesAreSuccess) {
100
return value > threshold ? green : red;
101
}
102
103
return value < threshold ? green : red;
104
};
105
106
render() {
107
const { nrqlQueries } = this.props;
108
109
const nrqlQueryPropsAvailable =
110
nrqlQueries &&
111
nrqlQueries[0] &&
112
nrqlQueries[0].accountId &&
113
nrqlQueries[0].query;
114
115
if (!nrqlQueryPropsAvailable) {
116
return <EmptyState />;
117
}
118
119
return (
120
<AutoSizer>
121
{({ width, height }) => (
122
<NrqlQuery
123
query={nrqlQueries[0].query}
124
accountId={parseInt(nrqlQueries[0].accountId)}
125
pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}
126
>
127
{({ data, loading, error }) => {
128
if (loading) {
129
return <Spinner />;
130
}
131
132
if (error) {
133
return (
134
<NrqlQueryError
135
title="NRQL Syntax Error"
136
description={error.message}
137
/>
138
);
139
}
140
141
if (!this.nrqlInputIsValid(data)) {
142
return (
143
<NrqlQueryError
144
title="Unsupported NRQL query"
145
description="The provided NRQL query is not supported by this visualization. Please make sure to have exactly 1 aggregate function in the SELECT clause and no FACET or TIMESERIES clauses."
146
/>
147
);
148
}
149
150
const { percent, label, series } = this.transformData(data);
151
152
return (
153
<svg
154
viewBox={`0 0 ${BOUNDS.X} ${BOUNDS.Y}`}
155
width={width}
156
height={height}
157
className="CircularProgressBar"
158
>
159
<VictoryPie
160
standalone={false}
161
animate={{ duration: 1000 }}
162
data={series}
163
width={CHART_WIDTH}
164
height={CHART_HEIGHT}
165
padding={10}
166
innerRadius={135}
167
cornerRadius={25}
168
labels={() => null}
169
style={{ data: { fill: ({ datum }) => datum.color } }}
170
/>
171
<VictoryAnimation duration={1000} data={percent}>
172
{(percent) => (
173
<VictoryLabel
174
textAnchor="middle"
175
verticalAnchor="middle"
176
x={CHART_WIDTH / 2}
177
y={CHART_HEIGHT / 2}
178
text={`${Math.round(percent)}%`}
179
style={{ ...baseLabelStyles, fontSize: 45 }}
180
/>
181
)}
182
</VictoryAnimation>
183
<VictoryLabel
184
text={label}
185
lineHeight={1}
186
x={CHART_WIDTH / 2}
187
y={BOUNDS.Y - LABEL_SIZE}
188
textAnchor="middle"
189
style={{ ...baseLabelStyles, fontSize: LABEL_SIZE }}
190
/>
191
</svg>
192
);
193
}}
194
</NrqlQuery>
195
)}
196
</AutoSizer>
197
);
198
}
199
}
200
201
const EmptyState = () => (
202
<Card className="EmptyState">
203
<CardBody className="EmptyState-cardBody">
204
<HeadingText
205
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
206
type={HeadingText.TYPE.HEADING_3}
207
>
208
Please provide a NRQL query & account ID pair
209
</HeadingText>
210
<HeadingText
211
spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}
212
type={HeadingText.TYPE.HEADING_4}
213
>
214
This Visualization supports NRQL queries with a single SELECT clause
215
returning a percentage value (0 to 100 rather than 0 to 1). For example:
216
</HeadingText>
217
<code>
218
{'FROM Transaction SELECT percentage(count(*), WHERE duration < 0.1)'}
219
</code>
220
</CardBody>
221
</Card>
222
);
index.js

The VictoryPie field that you'll use with this update is called colorScale. It accepts an array of colors and applies each color to a segment of the progress bar. So, in your visualization's configuration options, you've specified a collection of strings that you use to pass colors to your chart.

Step 3 of 8

In the same visualization directory, open index.js.

Step 4 of 8

In render(), set the VictoryPie component's colorScale prop:

1
{
2
"schemaType": "VISUALIZATION",
3
"id": "circular-progress-bar",
4
"displayName": "Circular progress bar",
5
"description": "",
6
"configuration": [
7
{
8
"name": "nrqlQueries",
9
"title": "NRQL Queries",
10
"type": "collection",
11
"items": [
12
{
13
"name": "accountId",
14
"title": "Account ID",
15
"description": "Account ID to be associated with the query",
16
"type": "account-id"
17
},
18
{
19
"name": "query",
20
"title": "Query",
21
"description": "NRQL query for visualization",
22
"type": "nrql"
23
}
24
]
25
},
26
{
27
"name": "thresholds",
28
"title": "Thresholds",
29
"type": "namespace",
30
"items": [
31
{
32
"name": "criticalThreshold",
33
"title": "Critical threshold",
34
"description": "Value at which progress is displayed as critical",
35
"type": "number"
36
},
37
{
38
"name": "highValuesAreSuccess",
39
"title": "Above threshold is success",
40
"description": "If toggled on, values above the threshold display as successful. Otherwise, values at or above the threshold display as critical.",
41
"type": "boolean"
42
}
43
]
44
},
45
{
46
"name": "colors",
47
"title": "Colors",
48
"type": "collection",
49
"items": [
50
{
51
"name": "segmentColor",
52
"title": "Segment color",
53
"description": "The color of a bar segment.",
54
"type": "string"
55
}
56
]
57
}
58
]
59
}
nr1.json
1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import { VictoryPie, VictoryAnimation, VictoryLabel } from 'victory';
4
import {
5
Card,
6
CardBody,
7
HeadingText,
8
NrqlQuery,
9
Spinner,
10
AutoSizer,
11
} from 'nr1';
12
import NrqlQueryError from '../../src/nrql-query-error';
13
import { baseLabelStyles } from '../../src/theme';
14
import { getUniqueAggregatesAndFacets } from '../../src/utils/nrql-validation-helper';
15
import Colors from '../../src/colors';
16
17
const BOUNDS = {
18
X: 400,
19
Y: 400,
20
};
21
22
const LABEL_SIZE = 24;
23
const LABEL_PADDING = 10;
24
const CHART_WIDTH = BOUNDS.X;
25
const CHART_HEIGHT = BOUNDS.Y - LABEL_SIZE - LABEL_PADDING;
26
27
export default class CircularProgressBar 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
* An array of objects consisting of a nrql `query` and `accountId`.
33
* This should be a standard prop for any NRQL based visualizations.
34
*/
35
nrqlQueries: PropTypes.arrayOf(
36
PropTypes.shape({
37
accountId: PropTypes.number,
38
query: PropTypes.string,
39
})
40
),
41
42
/**
43
* Configuration that determines what values to display as critical or
44
* successful.
45
*/
46
thresholds: PropTypes.shape({
47
criticalThreshold: PropTypes.number,
48
highValuesAreSuccess: PropTypes.bool,
49
}),
50
};
51
52
/**
53
* Restructure the data for a aggregate NRQL query with no TIMESERIES and no
54
* FACET into a for our visualization works well with.
55
*/
56
transformData = (data) => {
57
const {
58
data: [series],
59
metadata: { color: colorFromData, name: label },
60
} = data[0];
61
62
const percent = series.y * 100;
63
const color = this.getColor(percent, colorFromData);
64
65
return {
66
percent,
67
label,
68
series: [
69
{ x: 'progress', y: percent, color },
70
{ x: 'remainder', y: 100 - percent, color: 'transparent' },
71
],
72
};
73
};
74
75
nrqlInputIsValid = (data) => {
76
const { data: seriesEntries } = data[0];
77
const { uniqueAggregates, uniqueFacets } = getUniqueAggregatesAndFacets(
78
data
79
);
80
const isNonTimeseries = seriesEntries.length === 1;
81
82
return (
83
uniqueAggregates.size === 1 && uniqueFacets.size === 0 && isNonTimeseries
84
);
85
};
86
87
getColor = (value, colorFromData) => {
88
const { red6: red, green6: green } = Colors.base;
89
const {
90
thresholds: { criticalThreshold, highValuesAreSuccess },
91
} = this.props;
92
93
const threshold = parseFloat(criticalThreshold);
94
95
if (isNaN(threshold)) {
96
return colorFromData;
97
}
98
99
if (highValuesAreSuccess) {
100
return value > threshold ? green : red;
101
}
102
103
return value < threshold ? green : red;
104
};
105
106
render() {
107
const { nrqlQueries, colors } = this.props;
108
const colorScale = Array.from(colors, (x) => x.segmentColor);
109
110
const nrqlQueryPropsAvailable =
111
nrqlQueries &&
112
nrqlQueries[0] &&
113
nrqlQueries[0].accountId &&
114
nrqlQueries[0].query;
115
116
if (!nrqlQueryPropsAvailable) {
117
return <EmptyState />;
118
}
119
120
return (
121
<AutoSizer>
122
{({ width, height }) => (
123
<NrqlQuery
124
query={nrqlQueries[0].query}
125
accountId={parseInt(nrqlQueries[0].accountId)}
126
pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}
127
>
128
{({ data, loading, error }) => {
129
if (loading) {
130
return <Spinner />;
131
}
132
133
if (error) {
134
return (
135
<NrqlQueryError
136
title="NRQL Syntax Error"
137
description={error.message}
138
/>
139
);
140
}
141
142
if (!this.nrqlInputIsValid(data)) {
143
return (
144
<NrqlQueryError
145
title="Unsupported NRQL query"
146
description="The provided NRQL query is not supported by this visualization. Please make sure to have exactly 1 aggregate function in the SELECT clause and no FACET or TIMESERIES clauses."
147
/>
148
);
149
}
150
151
const { percent, label, series } = this.transformData(data);
152
153
return (
154
<svg
155
viewBox={`0 0 ${BOUNDS.X} ${BOUNDS.Y}`}
156
width={width}
157
height={height}
158
className="CircularProgressBar"
159
>
160
<VictoryPie
161
standalone={false}
162
animate={{ duration: 1000 }}
163
data={series}
164
width={CHART_WIDTH}
165
height={CHART_HEIGHT}
166
padding={10}
167
innerRadius={135}
168
cornerRadius={25}
169
labels={() => null}
170
colorScale={colorScale}
171
/>
172
<VictoryAnimation duration={1000} data={percent}>
173
{(percent) => (
174
<VictoryLabel
175
textAnchor="middle"
176
verticalAnchor="middle"
177
x={CHART_WIDTH / 2}
178
y={CHART_HEIGHT / 2}
179
text={`${Math.round(percent)}%`}
180
style={{ ...baseLabelStyles, fontSize: 45 }}
181
/>
182
)}
183
</VictoryAnimation>
184
<VictoryLabel
185
text={label}
186
lineHeight={1}
187
x={CHART_WIDTH / 2}
188
y={BOUNDS.Y - LABEL_SIZE}
189
textAnchor="middle"
190
style={{ ...baseLabelStyles, fontSize: LABEL_SIZE }}
191
/>
192
</svg>
193
);
194
}}
195
</NrqlQuery>
196
)}
197
</AutoSizer>
198
);
199
}
200
}
201
202
const EmptyState = () => (
203
<Card className="EmptyState">
204
<CardBody className="EmptyState-cardBody">
205
<HeadingText
206
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
207
type={HeadingText.TYPE.HEADING_3}
208
>
209
Please provide a NRQL query & account ID pair
210
</HeadingText>
211
<HeadingText
212
spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}
213
type={HeadingText.TYPE.HEADING_4}
214
>
215
This Visualization supports NRQL queries with a single SELECT clause
216
returning a percentage value (0 to 100 rather than 0 to 1). For example:
217
</HeadingText>
218
<code>
219
{'FROM Transaction SELECT percentage(count(*), WHERE duration < 0.1)'}
220
</code>
221
</CardBody>
222
</Card>
223
);
index.js

First, you created a new constant, called colorScale, which is an array of the segmentColor values from this.props.colors. Then, you set the VictoryPie component's colorScale prop. Finally, you removed VictoryPie.style because the colors are now controlled by colorScale.

Step 5 of 8

From your Nerdpack's root directory, run a local server:

bash
$
nr1 nerdpack:serve

Once the server is running, find the url for your local circular-progress-bar:

bash
Visualizations:
circular-progress-bar https://one.nr/04ERPALBYjW
range-chart https://one.nr/0oqQaxezJj1
stacked-bar-chart https://one.nr/0PLRElq3bwa
Step 6 of 8

Open your locally served visualization and configure your chart with your account, data query, and segment colors:

Progress bar with custom colors

Tip

To add a second color, click the + in the top right of the Colors property.

Because there are two segments, you add two colors. The first color is for the progress section. The second color is for the remaining percentage.

Step 7 of 8

In index.js, remove the VictoryPie component's cornerRadius prop:

1
{
2
"schemaType": "VISUALIZATION",
3
"id": "circular-progress-bar",
4
"displayName": "Circular progress bar",
5
"description": "",
6
"configuration": [
7
{
8
"name": "nrqlQueries",
9
"title": "NRQL Queries",
10
"type": "collection",
11
"items": [
12
{
13
"name": "accountId",
14
"title": "Account ID",
15
"description": "Account ID to be associated with the query",
16
"type": "account-id"
17
},
18
{
19
"name": "query",
20
"title": "Query",
21
"description": "NRQL query for visualization",
22
"type": "nrql"
23
}
24
]
25
},
26
{
27
"name": "thresholds",
28
"title": "Thresholds",
29
"type": "namespace",
30
"items": [
31
{
32
"name": "criticalThreshold",
33
"title": "Critical threshold",
34
"description": "Value at which progress is displayed as critical",
35
"type": "number"
36
},
37
{
38
"name": "highValuesAreSuccess",
39
"title": "Above threshold is success",
40
"description": "If toggled on, values above the threshold display as successful. Otherwise, values at or above the threshold display as critical.",
41
"type": "boolean"
42
}
43
]
44
},
45
{
46
"name": "colors",
47
"title": "Colors",
48
"type": "collection",
49
"items": [
50
{
51
"name": "segmentColor",
52
"title": "Segment color",
53
"description": "The color of a bar segment.",
54
"type": "string"
55
}
56
]
57
}
58
]
59
}
nr1.json
1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import { VictoryPie, VictoryAnimation, VictoryLabel } from 'victory';
4
import {
5
Card,
6
CardBody,
7
HeadingText,
8
NrqlQuery,
9
Spinner,
10
AutoSizer,
11
} from 'nr1';
12
import NrqlQueryError from '../../src/nrql-query-error';
13
import { baseLabelStyles } from '../../src/theme';
14
import { getUniqueAggregatesAndFacets } from '../../src/utils/nrql-validation-helper';
15
import Colors from '../../src/colors';
16
17
const BOUNDS = {
18
X: 400,
19
Y: 400,
20
};
21
22
const LABEL_SIZE = 24;
23
const LABEL_PADDING = 10;
24
const CHART_WIDTH = BOUNDS.X;
25
const CHART_HEIGHT = BOUNDS.Y - LABEL_SIZE - LABEL_PADDING;
26
27
export default class CircularProgressBar 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
* An array of objects consisting of a nrql `query` and `accountId`.
33
* This should be a standard prop for any NRQL based visualizations.
34
*/
35
nrqlQueries: PropTypes.arrayOf(
36
PropTypes.shape({
37
accountId: PropTypes.number,
38
query: PropTypes.string,
39
})
40
),
41
42
/**
43
* Configuration that determines what values to display as critical or
44
* successful.
45
*/
46
thresholds: PropTypes.shape({
47
criticalThreshold: PropTypes.number,
48
highValuesAreSuccess: PropTypes.bool,
49
}),
50
};
51
52
/**
53
* Restructure the data for a aggregate NRQL query with no TIMESERIES and no
54
* FACET into a for our visualization works well with.
55
*/
56
transformData = (data) => {
57
const {
58
data: [series],
59
metadata: { color: colorFromData, name: label },
60
} = data[0];
61
62
const percent = series.y * 100;
63
const color = this.getColor(percent, colorFromData);
64
65
return {
66
percent,
67
label,
68
series: [
69
{ x: 'progress', y: percent, color },
70
{ x: 'remainder', y: 100 - percent, color: 'transparent' },
71
],
72
};
73
};
74
75
nrqlInputIsValid = (data) => {
76
const { data: seriesEntries } = data[0];
77
const { uniqueAggregates, uniqueFacets } = getUniqueAggregatesAndFacets(
78
data
79
);
80
const isNonTimeseries = seriesEntries.length === 1;
81
82
return (
83
uniqueAggregates.size === 1 && uniqueFacets.size === 0 && isNonTimeseries
84
);
85
};
86
87
getColor = (value, colorFromData) => {
88
const { red6: red, green6: green } = Colors.base;
89
const {
90
thresholds: { criticalThreshold, highValuesAreSuccess },
91
} = this.props;
92
93
const threshold = parseFloat(criticalThreshold);
94
95
if (isNaN(threshold)) {
96
return colorFromData;
97
}
98
99
if (highValuesAreSuccess) {
100
return value > threshold ? green : red;
101
}
102
103
return value < threshold ? green : red;
104
};
105
106
render() {
107
const { nrqlQueries, colors } = this.props;
108
const colorScale = Array.from(colors, (x) => x.segmentColor);
109
110
const nrqlQueryPropsAvailable =
111
nrqlQueries &&
112
nrqlQueries[0] &&
113
nrqlQueries[0].accountId &&
114
nrqlQueries[0].query;
115
116
if (!nrqlQueryPropsAvailable) {
117
return <EmptyState />;
118
}
119
120
return (
121
<AutoSizer>
122
{({ width, height }) => (
123
<NrqlQuery
124
query={nrqlQueries[0].query}
125
accountId={parseInt(nrqlQueries[0].accountId)}
126
pollInterval={NrqlQuery.AUTO_POLL_INTERVAL}
127
>
128
{({ data, loading, error }) => {
129
if (loading) {
130
return <Spinner />;
131
}
132
133
if (error) {
134
return (
135
<NrqlQueryError
136
title="NRQL Syntax Error"
137
description={error.message}
138
/>
139
);
140
}
141
142
if (!this.nrqlInputIsValid(data)) {
143
return (
144
<NrqlQueryError
145
title="Unsupported NRQL query"
146
description="The provided NRQL query is not supported by this visualization. Please make sure to have exactly 1 aggregate function in the SELECT clause and no FACET or TIMESERIES clauses."
147
/>
148
);
149
}
150
151
const { percent, label, series } = this.transformData(data);
152
153
return (
154
<svg
155
viewBox={`0 0 ${BOUNDS.X} ${BOUNDS.Y}`}
156
width={width}
157
height={height}
158
className="CircularProgressBar"
159
>
160
<VictoryPie
161
standalone={false}
162
animate={{ duration: 1000 }}
163
data={series}
164
width={CHART_WIDTH}
165
height={CHART_HEIGHT}
166
padding={10}
167
innerRadius={135}
168
labels={() => null}
169
colorScale={colorScale}
170
/>
171
<VictoryAnimation duration={1000} data={percent}>
172
{(percent) => (
173
<VictoryLabel
174
textAnchor="middle"
175
verticalAnchor="middle"
176
x={CHART_WIDTH / 2}
177
y={CHART_HEIGHT / 2}
178
text={`${Math.round(percent)}%`}
179
style={{ ...baseLabelStyles, fontSize: 45 }}
180
/>
181
)}
182
</VictoryAnimation>
183
<VictoryLabel
184
text={label}
185
lineHeight={1}
186
x={CHART_WIDTH / 2}
187
y={BOUNDS.Y - LABEL_SIZE}
188
textAnchor="middle"
189
style={{ ...baseLabelStyles, fontSize: LABEL_SIZE }}
190
/>
191
</svg>
192
);
193
}}
194
</NrqlQuery>
195
)}
196
</AutoSizer>
197
);
198
}
199
}
200
201
const EmptyState = () => (
202
<Card className="EmptyState">
203
<CardBody className="EmptyState-cardBody">
204
<HeadingText
205
spacingType={[HeadingText.SPACING_TYPE.LARGE]}
206
type={HeadingText.TYPE.HEADING_3}
207
>
208
Please provide a NRQL query & account ID pair
209
</HeadingText>
210
<HeadingText
211
spacingType={[HeadingText.SPACING_TYPE.MEDIUM]}
212
type={HeadingText.TYPE.HEADING_4}
213
>
214
This Visualization supports NRQL queries with a single SELECT clause
215
returning a percentage value (0 to 100 rather than 0 to 1). For example:
216
</HeadingText>
217
<code>
218
{'FROM Transaction SELECT percentage(count(*), WHERE duration < 0.1)'}
219
</code>
220
</CardBody>
221
</Card>
222
);
index.js

This will revert the bar corners to the default 90-degrees instead of being rounded. While your local server is running, it automatically recognizes changes to index.js. So, view your visualization in your browser to see the update:

Progress bar with straight corners

Perfect! You cloned and updated the open-source Circular progress bar visualization from the Instant Observability catalog. The only thing left to do is publish your version to the catalog so your accounts can subscribe to it.

Step 8 of 8

Now that you're ready to publish your Nerdpack, stop your local server with CTRL+C.

Add a custom visualization to a dashboard

Publish your version of the Victory charts Nerdpack to the catalog. Then subscribe to it and use your visualization in a dashboard.

Tip

Because you used nr1 clone to clone the Nerdpack's repository, your local copy already has its own UUID. This is a prerequisite for publishing your version to the Instant Observability catalog. If you used git clone to copy, you need to update the Nerdpack's UUID manually:

bash
$
nr1 nerdpack:uuid -gf
The new generated id is ab123c45-678d-9012-efg3-45hi6jkl7890
Step 1 of 6

From its root directory, publish your Nerdpack:

bash
$
nr1 nerdpack:publish
Step 2 of 6

Subscribe to your Nerdpack:

bash
$
nr1 nerdpack:subscribe

Here, you subscribed to your Nerdpack with the CLI. This is effectively the same action you performed earlier in this guide within the web UI, but from your terminal.

Step 3 of 6

Go to the Apps view in New Relic:

Navigate to apps

Step 4 of 6

From Apps, open Custom visualizations:

Navigate to Custom visualizations

From here, click the Circular progress bar visualization. Update your visualization's configuration options like you did when you were serving your Nerdpack locally.

Step 5 of 6

Click Add to dashboard:

Add to dashboard

Step 6 of 6

Go to your dashboard and see your new, customized circular progress bar:

Circular progress bar in dashboard

Summary

In this guide, you:

  • Subscribed to a Nerdpack from the Instant Observability catalog
  • Cloned an open-source Nerdpack
  • Edited an existing visualization to meet your needs
  • Published and subscribed to your own custom Nerdpack
  • Added a visualization from your custom Nerdpack to a dashboard

Now you know how to build off the foundation of open-source Nerdpacks, you can use the work of the New Relic developer community to fast-track the creation of apps and visualizations.

Tip

If you want to maintain your own version in a remote repository, consider forking the original repo.

Copyright © 2024 New Relic Inc.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.