Skip to content

Commit c7e23a7

Browse files
authored
Merge pull request #658 from adobe/scatterAnnotation
feat: add scatter annotations
2 parents ce44fc9 + 60e3d20 commit c7e23a7

File tree

29 files changed

+490
-10
lines changed

29 files changed

+490
-10
lines changed

packages/docs/docs/api/visualizations/Scatter.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ The `Trendline` component is fully supported by `Scatter`. To plot a trendline,
136136
<tbody>
137137
<tr>
138138
<td>children</td>
139-
<td>ChartTooltip | ChartPopover | Trendline</td>
139+
<td>ChartTooltip | ChartPopover | Trendline | ScatterAnnotation</td>
140140
<td>–</td>
141141
<td>Optional elements that can be rendered within the chart.</td>
142142
</tr>
@@ -198,6 +198,56 @@ The `Trendline` component is fully supported by `Scatter`. To plot a trendline,
198198
</tbody>
199199
</table>
200200

201+
### ScatterAnnotation
202+
203+
The `ScatterAnnotation` component can be passed into `Scatter` as a child. This allows you to add text labels directly on scatter plot points, making it easier to identify specific data points or highlight important information.
204+
205+
The component supports an `anchor` prop to define where annotations should be placed relative to their corresponding points. You can pass a single position or an array of positions. When an array is provided, each position is tried in order until one fits within the chart bounds without overlapping other annotations or points. **If a label cannot be placed without overlapping, it will not be displayed. This means scatter annotations should be considered "nice to have" supplemental information.**
206+
207+
#### Example
208+
209+
```jsx
210+
<Chart data={data}>
211+
<Axis baseline grid position="bottom" ticks title="Speed (normal)" />
212+
<Axis baseline grid position="left" ticks title="Handling (normal)" />
213+
<Scatter dimension="speedNormal" metric="handlingNormal" color="weightClass">
214+
<ScatterAnnotation textKey="character" anchor={['right', 'top', 'bottom', 'left']} />
215+
</Scatter>
216+
<Legend highlight position="right" title="Weight class" />
217+
<Title text="Mario Kart 8 Character Data" />
218+
</Chart>
219+
```
220+
221+
![Scatter plot with text annotations for each point](/img/scatterAnnotation_light.png#gh-light-mode-only)
222+
![Scatter plot with text annotations for each point](/img/scatterAnnotation_dark.png#gh-dark-mode-only)
223+
224+
#### Props
225+
226+
<table>
227+
<thead>
228+
<tr>
229+
<th>name</th>
230+
<th>type</th>
231+
<th>default</th>
232+
<th>description</th>
233+
</tr>
234+
</thead>
235+
<tbody>
236+
<tr>
237+
<td>anchor</td>
238+
<td>LabelAnchor | LabelAnchor[]</td>
239+
<td>['right', 'top', 'bottom', 'left']</td>
240+
<td>Specifies where to position the annotation relative to the data point. Possible values include 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', and 'bottom-right'.<br/>When an array is provided, each position is tried in order until one fits within the chart bounds and doesn't overlap with other annotations or points. If no position fits, the annotation is not displayed. This is true even if only one position is supplied.</td>
241+
</tr>
242+
<tr>
243+
<td>textKey</td>
244+
<td>string</td>
245+
<td>'annotation'</td>
246+
<td>The key in the data that contains the text to display for each annotation.</td>
247+
</tr>
248+
</tbody>
249+
</table>
250+
201251
## Advanced
202252

203253
### ScatterPath
34.6 KB
Loading
35.3 KB
Loading

packages/react-spectrum-charts/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@
6565
"clean": "rm -rf dist",
6666
"g": "plop component",
6767
"generate": "plop component",
68-
"lint": "eslint src --ext .ts,.tsx,.js,.jsx",
69-
"ts": "yarn tsc",
70-
"tsc": "tsc --noEmit --skipLibCheck",
7168
"pack-test": "yarn clean && yarn build && cross-env NODE_ENV=development npm pack",
7269
"pack": "cross-env NODE_ENV=production npm pack",
7370
"sonar": "node ./scripts/runSonarOnBranch.js",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
/* eslint-disable @typescript-eslint/no-unused-vars */
14+
import { FC } from 'react';
15+
16+
import { ScatterAnnotationProps } from '../../types';
17+
18+
// destructure props here and set defaults so that storybook can pick them up
19+
const ScatterAnnotation: FC<ScatterAnnotationProps> = ({
20+
anchor = ['right', 'top', 'bottom', 'left'],
21+
textKey = 'annotation',
22+
}) => {
23+
return null;
24+
};
25+
26+
// displayName is used to validate the component type in the spec builder
27+
ScatterAnnotation.displayName = 'ScatterAnnotation';
28+
export { ScatterAnnotation };
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
export * from './ScatterAnnotation';

packages/react-spectrum-charts/src/components/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export * from './Line';
2424
export * from './ScatterPath';
2525
export * from './MetricRange';
2626
export * from './ReferenceLine';
27+
export * from './Scatter';
28+
export * from './ScatterAnnotation';
2729
export * from './Title';
2830
export * from './Trendline';
2931
export * from './TrendlineAnnotation';
30-
export * from './Scatter';

packages/react-spectrum-charts/src/rscToSbAdapter/chartAdapter.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ describe('rscPropsToSpecBuilderOptions()', () => {
330330
markType: 'scatter',
331331
metric: 'countAvg',
332332
opacity: 'period',
333+
scatterAnnotations: [],
333334
scatterPaths: [{ groupBy: ['event', 'segment'], opacity: 0.2, pathWidth: 'pathWidth' }],
334335
trendlines: [
335336
{

packages/react-spectrum-charts/src/rscToSbAdapter/childrenAdapter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
MarkOptions,
2424
MetricRangeOptions,
2525
ReferenceLineOptions,
26+
ScatterAnnotationOptions,
2627
ScatterPathOptions,
2728
SegmentLabelOptions,
2829
TitleOptions,
@@ -44,6 +45,7 @@ import { Line } from '../components/Line';
4445
import { MetricRange } from '../components/MetricRange';
4546
import { ReferenceLine } from '../components/ReferenceLine';
4647
import { Scatter } from '../components/Scatter';
48+
import { ScatterAnnotation } from '../components/ScatterAnnotation';
4749
import { ScatterPath } from '../components/ScatterPath';
4850
import { Title } from '../components/Title';
4951
import { Trendline } from '../components/Trendline';
@@ -63,6 +65,7 @@ import {
6365
LegendProps,
6466
LineProps,
6567
ReferenceLineProps,
68+
ScatterAnnotationProps,
6669
ScatterPathProps,
6770
ScatterProps,
6871
SegmentLabelProps,
@@ -100,6 +103,7 @@ export const childrenToOptions = (
100103
marks: MarkOptions[];
101104
metricRanges: MetricRangeOptions[];
102105
referenceLines: ReferenceLineOptions[];
106+
scatterAnnotations: ScatterAnnotationOptions[];
103107
scatterPaths: ScatterPathOptions[];
104108
segmentLabels: SegmentLabelOptions[];
105109
titles: TitleOptions[];
@@ -119,6 +123,7 @@ export const childrenToOptions = (
119123
const metricRanges: MetricRangeOptions[] = [];
120124
const referenceLines: ReferenceLineOptions[] = [];
121125
const segmentLabels: SegmentLabelOptions[] = [];
126+
const scatterAnnotations: ScatterAnnotationOptions[] = [];
122127
const scatterPaths: ScatterPathOptions[] = [];
123128
const titles: TitleOptions[] = [];
124129
const trendlineAnnotations: TrendlineAnnotationOptions[] = [];
@@ -199,6 +204,10 @@ export const childrenToOptions = (
199204
marks.push(getScatterOptions(child.props as ScatterProps));
200205
break;
201206

207+
case ScatterAnnotation.displayName:
208+
scatterAnnotations.push(child.props as ScatterAnnotationProps);
209+
break;
210+
202211
case ScatterPath.displayName:
203212
scatterPaths.push(child.props as ScatterPathProps);
204213
break;
@@ -241,6 +250,7 @@ export const childrenToOptions = (
241250
marks,
242251
metricRanges,
243252
referenceLines,
253+
scatterAnnotations,
244254
scatterPaths,
245255
segmentLabels,
246256
titles,

packages/react-spectrum-charts/src/rscToSbAdapter/scatterAdapter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ import { ScatterProps } from '../types';
1515
import { childrenToOptions } from './childrenAdapter';
1616

1717
export const getScatterOptions = ({ children, ...scatterProps }: ScatterProps): ScatterOptions => {
18-
const { chartPopovers, chartTooltips, scatterPaths, trendlines } = childrenToOptions(children);
18+
const { chartPopovers, chartTooltips, scatterAnnotations, scatterPaths, trendlines } = childrenToOptions(children);
1919
return {
2020
...scatterProps,
2121
chartPopovers,
2222
chartTooltips,
2323
markType: 'scatter',
24+
scatterAnnotations,
2425
scatterPaths,
2526
trendlines,
2627
};

0 commit comments

Comments
 (0)