Skip to content

Commit

Permalink
Merge pull request #281 from adobe/groupData
Browse files Browse the repository at this point in the history
feat: add group data to datum for tooltip when using highlightBy
  • Loading branch information
marshallpete committed May 3, 2024
2 parents b70e41d + 7e9d932 commit efebd55
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 10 deletions.
13 changes: 10 additions & 3 deletions src/RscChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
DEFAULT_COLOR_SCHEME,
DEFAULT_LINE_TYPES,
DEFAULT_LOCALE,
FILTERED_TABLE,
GROUP_DATA,
LEGEND_TOOLTIP_DELAY,
MARK_ID,
SELECTED_ITEM,
Expand Down Expand Up @@ -179,8 +181,8 @@ export const RscChart = forwardRef<ChartHandle, RscChartProps>(
);
}
// get the correct tooltip to render based on the hovered item
const tooltip = tooltips.find((t) => t.name === value.rscComponentName)?.callback;
if (tooltip && !('index' in value)) {
const tooltip = tooltips.find((t) => t.name === value.rscComponentName);
if (tooltip?.callback && !('index' in value)) {
if (controlledHoveredIdSignal) {
chartView.current?.signal(controlledHoveredIdSignal.name, value?.[MARK_ID] ?? null);
}
Expand All @@ -190,9 +192,14 @@ export const RscChart = forwardRef<ChartHandle, RscChartProps>(
chartView.current?.signal(controlledHoveredGroupSignal.name, value[key]);
}
}
if (tooltip.highlightBy && tooltip.highlightBy !== 'item') {
const tableData = chartView.current?.data(FILTERED_TABLE);
const groupId = `${tooltip.name}_groupId`;
value[GROUP_DATA] = tableData?.filter((d) => d[groupId] === value[groupId]);
}
return renderToStaticMarkup(
<div className="rsc-tooltip" data-testid="rsc-tooltip">
{tooltip(value)}
{tooltip.callback(value)}
</div>
);
}
Expand Down
5 changes: 3 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ export const TABLE = 'table';
export const FILTERED_TABLE = 'filteredTable';

// vega data field names
export const SERIES_ID = 'rscSeriesId';
export const GROUP_DATA = 'rscGroupData';
export const MARK_ID = 'rscMarkId';
export const TRENDLINE_VALUE = 'rscTrendlineValue';
export const SERIES_ID = 'rscSeriesId';
export const STACK_ID = 'rscStackId';
export const TRENDLINE_VALUE = 'rscTrendlineValue';

// signal names
export const HIGHLIGHTED_ITEM = 'highlightedItem'; // data point
Expand Down
10 changes: 8 additions & 2 deletions src/hooks/useTooltips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ import { ChartTooltip } from '@components/ChartTooltip';
import { getAllElements } from '@utils';

import { Chart } from '../Chart';
import { ChartChildElement, ChartTooltipElement, TooltipHandler } from '../types';
import { ChartChildElement, ChartTooltipElement, ChartTooltipProps, TooltipHandler } from '../types';

type MappedTooltip = { name: string; element: ChartTooltipElement };

export type TooltipDetail = { name: string; callback: TooltipHandler; width?: number };
export type TooltipDetail = {
name: string;
callback: TooltipHandler;
highlightBy: ChartTooltipProps['highlightBy'];
width?: number;
};

export default function useTooltips(children: ChartChildElement[]): TooltipDetail[] {
const tooltipElements = useMemo(
Expand All @@ -34,6 +39,7 @@ export default function useTooltips(children: ChartChildElement[]): TooltipDetai
.map((tooltip) => ({
name: tooltip.name,
callback: tooltip.element.props.children,
highlightBy: tooltip.element.props.highlightBy,
})) as TooltipDetail[],
[tooltipElements]
);
Expand Down
25 changes: 24 additions & 1 deletion src/stories/components/ChartTooltip/HighlightBy.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { characterData } from '@stories/data/marioKartData';
import { StoryFn } from '@storybook/react';
import { bindWithProps } from '@test-utils';

import { View } from '@adobe/react-spectrum';

export default {
title: 'RSC/ChartTooltip/HighlightBy',
component: ChartTooltip,
Expand Down Expand Up @@ -131,6 +133,27 @@ Keys.args = {
children: dialogCallback,
};

const GroupData = bindWithProps(ScatterStory);
GroupData.args = {
highlightBy: ['weightClass'],
children: (datum: MarioData) => (
<div className="mario-tooltip">
<div>Weight class: {datum.weightClass}</div>
<div>
Characters:
<View marginStart={6}>
{datum.rscGroupData
?.map((d) => d.character)
.flat()
.map((c, i) => (
<div key={i}>{c}</div>
))}
</View>
</div>
</div>
),
};

const AreaChart = bindWithProps(AreaStory);
AreaChart.args = {
highlightBy: 'dimension',
Expand All @@ -149,4 +172,4 @@ ScatterChart.args = {
children: marioDialogCallback,
};

export { Basic, Dimension, Keys, Series, AreaChart, LineChart, ScatterChart };
export { Basic, Dimension, Keys, Series, GroupData, AreaChart, LineChart, ScatterChart };
33 changes: 32 additions & 1 deletion src/stories/components/ChartTooltip/HighlightBy.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
hoverNthElement,
queryAllMarksByGroupName,
render,
screen,
} from '@test-utils';

import { AreaChart, Basic, Dimension, Keys, LineChart, ScatterChart, Series } from './HighlightBy.story';
import { AreaChart, Basic, Dimension, GroupData, Keys, LineChart, ScatterChart, Series } from './HighlightBy.story';

describe('Basic', () => {
test('Only the hovered element should be highlighted', async () => {
Expand Down Expand Up @@ -191,3 +192,33 @@ describe('AreaChart', () => {
).toBe(true);
});
});

describe('GroupData', () => {
test('Should have all group data available to the toolitp', async () => {
render(<GroupData {...GroupData.args} />);

const chart = await findChart();
expect(chart).toBeInTheDocument();

const scatterHoverAreas = await findAllMarksByGroupName(chart, 'scatter0_voronoi');
await hoverNthElement(scatterHoverAreas, 0);

expect(screen.getByText('Baby Peach')).toBeInTheDocument();
expect(screen.getByText('Baby Daisy')).toBeInTheDocument();
expect(screen.getByText('Baby Rosalina')).toBeInTheDocument();
expect(screen.getByText('Lemmy')).toBeInTheDocument();
expect(screen.getByText('Baby Mario')).toBeInTheDocument();
expect(screen.getByText('Baby Luigi')).toBeInTheDocument();
expect(screen.getByText('Dry Bones')).toBeInTheDocument();
expect(screen.getByText('Light Mii')).toBeInTheDocument();
expect(screen.getByText('Koopa Troopa')).toBeInTheDocument();
expect(screen.getByText('Lakitu')).toBeInTheDocument();
expect(screen.getByText('Bowser Jr.')).toBeInTheDocument();
expect(screen.getByText('Toadette')).toBeInTheDocument();
expect(screen.getByText('Wendy')).toBeInTheDocument();
expect(screen.getByText('Isabelle')).toBeInTheDocument();
expect(screen.getByText('Toad')).toBeInTheDocument();
expect(screen.getByText('Shy Guy')).toBeInTheDocument();
expect(screen.getByText('Larry')).toBeInTheDocument();
});
});
4 changes: 3 additions & 1 deletion src/types/Chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/
import { JSXElementConstructor, MutableRefObject, ReactElement, ReactFragment, ReactNode } from 'react';

import { MARK_ID, SERIES_ID, TRENDLINE_VALUE } from '@constants';
import { GROUP_DATA, MARK_ID, SERIES_ID, TRENDLINE_VALUE } from '@constants';
import { Config, Data, FontWeight, Locale, NumberLocale, Padding, Spec, SymbolShape, TimeLocale, View } from 'vega';

import { Theme } from '@react-types/provider';
Expand Down Expand Up @@ -684,12 +684,14 @@ const DatumPredefinedKey = {
markId: MARK_ID,
seriesId: SERIES_ID,
trendlineValue: TRENDLINE_VALUE,
groupData: GROUP_DATA,
} as const;

export interface Datum {
[DatumPredefinedKey.markId]: number;
[DatumPredefinedKey.seriesId]: string;
[DatumPredefinedKey.trendlineValue]?: number;
[DatumPredefinedKey.groupData]?: Datum[];
[key: string]: unknown;
}

Expand Down

0 comments on commit efebd55

Please sign in to comment.