If you have technical questions, please create a support ticket in the DevExpress Support Center.
import React from 'react';
import Chart, {
Series,
Annotation,
Legend,
CommonAnnotationSettings,
} from 'devextreme-react/chart';
import { populationData } from './data.ts';
import AnnotationTemplate from './AnnotationTemplate.tsx';
function App() {
return (
<Chart
id="chart"
dataSource={populationData}
title="Top 5 Most Populated States in US"
>
<Series
type="bar"
argumentField="name"
valueField="population"
name="Population"
>
</Series>
<CommonAnnotationSettings
type="custom"
series="Population"
render={AnnotationTemplate}
allowDragging={true}
>
</CommonAnnotationSettings>
{populationData.map((data) => (
<Annotation
argument={data.name}
key={data.name}
data={data}
>
</Annotation>
))}
<Legend visible={false}></Legend>
</Chart>
);
}
export default App;
xxxxxxxxxx
import React from 'react';
import Chart, {
Series,
Annotation,
Legend,
CommonAnnotationSettings,
} from 'devextreme-react/chart';
import { populationData } from './data.js';
import AnnotationTemplate from './AnnotationTemplate.js';
function App() {
return (
<Chart
id="chart"
dataSource={populationData}
title="Top 5 Most Populated States in US"
>
<Series
type="bar"
argumentField="name"
valueField="population"
name="Population"
></Series>
<CommonAnnotationSettings
type="custom"
series="Population"
render={AnnotationTemplate}
allowDragging={true}
></CommonAnnotationSettings>
{populationData.map((data) => (
<Annotation
argument={data.name}
key={data.name}
data={data}
></Annotation>
))}
<Legend visible={false}></Legend>
</Chart>
);
}
export default App;
xxxxxxxxxx
import React from 'react';
function getImagePath(data: { name: string; }) {
return `images/flags/${data.name.replace(/\s/, '')}.svg`;
}
const formatNumber = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 0,
}).format;
interface AnnotationData {
name: string,
capital: string,
population: number,
area: number,
}
export default function AnnotationTemplate(annotation: { argument?: string; data?: AnnotationData; }) {
const { data } = annotation;
return (
<svg className="annotation">
<image href={getImagePath(data)} width="60" height="40" />
<rect x={0} y={0} className="border"></rect>
<text x="70" y="25" className="state">{annotation.argument}</text>
<text x="0" y="60">
<tspan className="caption">Capital:</tspan>
<tspan className="capital" dx="5">{data.capital}</tspan>
<tspan dy="14" x="0" className="caption">Population:</tspan>
<tspan className="population" dx="5">{formatNumber(data.population)}</tspan>
<tspan dy="14" x="0" className="caption">Area:</tspan>
<tspan className="area" dx="5">{formatNumber(data.area)}</tspan>
<tspan dx="5">km</tspan>
<tspan className="sup" dy="-2">2</tspan>
</text>
</svg>
);
}
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.tsx';
ReactDOM.render(
<App />,
document.getElementById('app'),
);
xxxxxxxxxx
export const populationData = [{
name: 'California',
population: 38802500,
capital: 'Sacramento',
area: 423967,
}, {
name: 'Texas',
population: 26956958,
capital: 'Austin',
area: 695662,
}, {
name: 'Florida',
population: 19893297,
capital: 'Tallahassee',
area: 170312,
}, {
name: 'New York',
population: 19746227,
capital: 'Albany',
area: 141297,
}, {
name: 'Illinois',
population: 12880580,
capital: 'Springfield',
area: 149995,
}];
xxxxxxxxxx
window.exports = window.exports || {};
window.config = {
transpiler: 'ts',
typescriptOptions: {
module: 'system',
emitDecoratorMetadata: true,
experimentalDecorators: true,
jsx: 'react',
},
meta: {
'react': {
'esModule': true,
},
'typescript': {
'exports': 'ts',
},
'devextreme/time_zone_utils.js': {
'esModule': true,
},
'devextreme/localization.js': {
'esModule': true,
},
'devextreme/viz/palette.js': {
'esModule': true,
},
'openai': {
'esModule': true,
},
},
paths: {
'npm:': 'https://unpkg.com/',
'bundles:': 'bundles/',
'externals:': 'bundles/externals/',
},
defaultExtension: 'js',
map: {
'ts': 'npm:plugin-typescript@8.0.0/lib/plugin.js',
'typescript': 'npm:typescript@4.2.4/lib/typescript.js',
'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js',
'react': 'npm:react@17.0.2/umd/react.development.js',
'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js',
'prop-types': 'npm:prop-types/prop-types.js',
'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js',
'luxon': 'npm:luxon@3.4.4/build/global/luxon.min.js',
'es6-object-assign': 'npm:es6-object-assign',
'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs',
'devextreme-react': 'npm:devextreme-react@link:../../packages/devextreme-react/npm/cjs',
'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js',
'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js',
'devexpress-gantt': 'npm:devexpress-gantt@4.1.54/dist/dx-gantt.js',
'@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12',
'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js',
'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js',
'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js',
'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js',
'inferno-hydrate': 'npm:inferno-hydrate/dist/inferno-hydrate.min.js',
'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js',
'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js',
'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js',
'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3',
// SystemJS plugins
'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js',
'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js',
// Prettier
'prettier/standalone': 'npm:prettier@2.8.8/standalone.js',
'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js',
},
packages: {
'devextreme': {
defaultExtension: 'js',
},
'devextreme-react': {
main: 'index.js',
},
'devextreme/events/utils': {
main: 'index',
},
'devextreme/localization/messages': {
format: 'json',
defaultExtension: 'json',
},
'devextreme/events': {
main: 'index',
},
'es6-object-assign': {
main: './index.js',
defaultExtension: 'js',
},
},
packageConfigPaths: [
'npm:@devextreme/*/package.json',
'npm:@devextreme/runtime@3.0.12/inferno/package.json',
],
babelOptions: {
sourceMaps: false,
stage0: true,
react: true,
},
};
System.config(window.config);
xxxxxxxxxx
import React from 'react';
function getImagePath(data) {
return `images/flags/${data.name.replace(/\s/, '')}.svg`;
}
const formatNumber = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 0,
}).format;
export default function AnnotationTemplate(annotation) {
const { data } = annotation;
return (
<svg className="annotation">
<image
href={getImagePath(data)}
width="60"
height="40"
/>
<rect
x={0}
y={0}
className="border"
></rect>
<text
x="70"
y="25"
className="state"
>
{annotation.argument}
</text>
<text
x="0"
y="60"
>
<tspan className="caption">Capital:</tspan>
<tspan
className="capital"
dx="5"
>
{data.capital}
</tspan>
<tspan
dy="14"
x="0"
className="caption"
>
Population:
</tspan>
<tspan
className="population"
dx="5"
>
{formatNumber(data.population)}
</tspan>
<tspan
dy="14"
x="0"
className="caption"
>
Area:
</tspan>
<tspan
className="area"
dx="5"
>
{formatNumber(data.area)}
</tspan>
<tspan dx="5">km</tspan>
<tspan
className="sup"
dy="-2"
>
2
</tspan>
</text>
</svg>
);
}
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(<App />, document.getElementById('app'));
xxxxxxxxxx
export const populationData = [
{
name: 'California',
population: 38802500,
capital: 'Sacramento',
area: 423967,
},
{
name: 'Texas',
population: 26956958,
capital: 'Austin',
area: 695662,
},
{
name: 'Florida',
population: 19893297,
capital: 'Tallahassee',
area: 170312,
},
{
name: 'New York',
population: 19746227,
capital: 'Albany',
area: 141297,
},
{
name: 'Illinois',
population: 12880580,
capital: 'Springfield',
area: 149995,
},
];
xxxxxxxxxx
<html lang="en">
<head></head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"></div>
</div>
</body>
</html>
xxxxxxxxxx
#chart {
height: 440px;
}
.annotation {
font-size: 12px;
}
.border {
width: 60px;
height: 40px;
stroke: rgba(191, 191, 191, 0.25);
stroke-width: 1px;
fill: transparent;
}
.state {
font-weight: 500;
font-size: 14px;
}
.caption {
font-weight: 500;
}
.sup {
font-size: 0.8em;
}
annotations[]
The annotations array specifies a collection of annotations. This demo uses a data source array to create this collection. Each object has the argument property and a data object. The argument property positions an annotation at a specific argument, and the data object stores data from a data source.
You can use the annotations array to configure each annotation individually.
commonAnnotationSettings
Use the commonAnnotationSettings object to specify settings common to all annotations in the chart. In this object, assign custom
to the type property. To anchor annotations to a series point, specify the series property. Finally, declare the SVG markup in the annotation template.
Note that individual annotation settings override the common annotation settings.
customizeAnnotation
You can also implement the customizeAnnotation function to customize an individual chart annotation.