Skip to content

Commit b933b0c

Browse files
committed
fix: Layout coherence with interactive/non-interactive legend
Use same layout for legend and interactive legend Fix #851
1 parent abd1ff7 commit b933b0c

9 files changed

Lines changed: 252 additions & 204 deletions

File tree

app/charts/area/chart-area.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import { QueryFilters } from "@/charts/shared/chart-helpers";
99
import { ChartContainer, ChartSvg } from "@/charts/shared/containers";
1010
import { Ruler } from "@/charts/shared/interaction/ruler";
1111
import { Tooltip } from "@/charts/shared/interaction/tooltip";
12-
import {
13-
InteractiveLegendColor,
14-
LegendColor,
15-
} from "@/charts/shared/legend-color";
12+
import { LegendColor } from "@/charts/shared/legend-color";
1613
import { InteractionHorizontal } from "@/charts/shared/overlay-horizontal";
1714
import {
1815
AreaConfig,
@@ -95,11 +92,13 @@ export const ChartAreas = memo(
9592
<Tooltip type={fields.segment ? "multiple" : "single"} />
9693
<Ruler />
9794
</ChartContainer>
98-
{fields.segment && interactiveFiltersConfig?.legend.active === true ? (
99-
<InteractiveLegendColor />
100-
) : fields.segment ? (
101-
<LegendColor symbol="square" />
102-
) : null}
95+
96+
<LegendColor
97+
symbol="square"
98+
interactive={
99+
fields.segment && interactiveFiltersConfig?.legend.active === true
100+
}
101+
/>
103102
</AreaChart>
104103
);
105104
}

app/charts/column/chart-column.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ import {
1818
import { BrushTime } from "@/charts/shared/brush";
1919
import { ChartContainer, ChartSvg } from "@/charts/shared/containers";
2020
import { Tooltip } from "@/charts/shared/interaction/tooltip";
21-
import {
22-
InteractiveLegendColor,
23-
LegendColor,
24-
} from "@/charts/shared/legend-color";
21+
import { LegendColor } from "@/charts/shared/legend-color";
2522
import {
2623
ColumnConfig,
2724
ColumnFields,
@@ -107,11 +104,13 @@ export const ChartColumns = memo(
107104
</ChartSvg>
108105
<Tooltip type="multiple" />
109106
</ChartContainer>
110-
{fields.segment && interactiveFiltersConfig?.legend.active ? (
111-
<InteractiveLegendColor />
112-
) : fields.segment ? (
113-
<LegendColor symbol="square" />
114-
) : null}
107+
108+
<LegendColor
109+
symbol="square"
110+
interactive={
111+
fields.segment && interactiveFiltersConfig?.legend.active
112+
}
113+
/>
115114
</StackedColumnsChart>
116115
) : fields.segment?.componentIri &&
117116
fields.segment.type === "grouped" ? (
@@ -136,11 +135,12 @@ export const ChartColumns = memo(
136135
<Tooltip type="multiple" />
137136
</ChartContainer>
138137

139-
{fields.segment && interactiveFiltersConfig?.legend.active ? (
140-
<InteractiveLegendColor />
141-
) : fields.segment ? (
142-
<LegendColor symbol="square" />
143-
) : null}
138+
<LegendColor
139+
symbol="square"
140+
interactive={
141+
fields.segment && interactiveFiltersConfig?.legend.active
142+
}
143+
/>
144144
</GroupedColumnChart>
145145
) : (
146146
<ColumnChart

app/charts/line/chart-lines.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import { ChartContainer, ChartSvg } from "@/charts/shared/containers";
99
import { HoverDotMultiple } from "@/charts/shared/interaction/hover-dots-multiple";
1010
import { Ruler } from "@/charts/shared/interaction/ruler";
1111
import { Tooltip } from "@/charts/shared/interaction/tooltip";
12-
import {
13-
InteractiveLegendColor,
14-
LegendColor,
15-
} from "@/charts/shared/legend-color";
12+
import { LegendColor } from "@/charts/shared/legend-color";
1613
import { InteractionHorizontal } from "@/charts/shared/overlay-horizontal";
1714
import {
1815
DataSource,
@@ -100,11 +97,10 @@ export const ChartLines = memo(function ChartLines({
10097
<Tooltip type={fields.segment ? "multiple" : "single"} />
10198
</ChartContainer>
10299

103-
{fields.segment && interactiveFiltersConfig?.legend.active ? (
104-
<InteractiveLegendColor />
105-
) : fields.segment ? (
106-
<LegendColor symbol="line" />
107-
) : null}
100+
<LegendColor
101+
symbol="line"
102+
interactive={fields.segment && interactiveFiltersConfig?.legend.active}
103+
/>
108104
</LineChart>
109105
);
110106
});

app/charts/pie/chart-pie.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import { PieChart } from "@/charts/pie/pie-state";
66
import { A11yTable } from "@/charts/shared/a11y-table";
77
import { ChartContainer, ChartSvg } from "@/charts/shared/containers";
88
import { Tooltip } from "@/charts/shared/interaction/tooltip";
9-
import {
10-
InteractiveLegendColor,
11-
LegendColor,
12-
} from "@/charts/shared/legend-color";
9+
import { LegendColor } from "@/charts/shared/legend-color";
1310
import {
1411
Loading,
1512
LoadingDataError,
@@ -122,11 +119,12 @@ export const ChartPie = memo(
122119
</ChartSvg>
123120
<Tooltip type="single" />
124121
</ChartContainer>
125-
{fields.segment && interactiveFiltersConfig?.legend.active === true ? (
126-
<InteractiveLegendColor />
127-
) : fields.segment ? (
128-
<LegendColor symbol="square" />
129-
) : null}{" "}
122+
<LegendColor
123+
symbol="square"
124+
interactive={
125+
fields.segment && interactiveFiltersConfig?.legend.active === true
126+
}
127+
/>
130128
</PieChart>
131129
);
132130
}

app/charts/scatterplot/chart-scatterplot.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ import {
1212
} from "@/charts/shared/axis-width-linear";
1313
import { ChartContainer, ChartSvg } from "@/charts/shared/containers";
1414
import { Tooltip } from "@/charts/shared/interaction/tooltip";
15-
import {
16-
InteractiveLegendColor,
17-
LegendColor,
18-
} from "@/charts/shared/legend-color";
15+
import { LegendColor } from "@/charts/shared/legend-color";
1916
import { InteractionVoronoi } from "@/charts/shared/overlay-voronoi";
2017
import {
2118
DataSource,
@@ -100,11 +97,13 @@ export const ChartScatterplot = memo(
10097
</ChartSvg>
10198
<Tooltip type="single" />
10299
</ChartContainer>
103-
{fields.segment && interactiveFiltersConfig?.legend.active === true ? (
104-
<InteractiveLegendColor />
105-
) : fields.segment ? (
106-
<LegendColor symbol="circle" />
107-
) : null}{" "}
100+
101+
<LegendColor
102+
symbol="circle"
103+
interactive={
104+
fields.segment && interactiveFiltersConfig?.legend.active === true
105+
}
106+
/>
108107
</ScatterplotChart>
109108
);
110109
}

app/charts/shared/legend-color.tsx

Lines changed: 72 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from "@/charts/shared/use-chart-state";
1010
import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters";
1111
import Flex from "@/components/flex";
12-
import { Checkbox } from "@/components/form";
12+
import { Checkbox, CheckboxProps } from "@/components/form";
1313
import {
1414
DataSource,
1515
isSegmentInConfig,
@@ -31,7 +31,7 @@ import { getLegendGroups } from "./legend-color-helpers";
3131

3232
type LegendSymbol = "square" | "line" | "circle";
3333

34-
const useStyles = makeStyles<Theme>(() => ({
34+
const useStyles = makeStyles<Theme>((theme) => ({
3535
legendContainer: {
3636
position: "relative",
3737
justifyContent: "flex-start",
@@ -51,6 +51,10 @@ const useStyles = makeStyles<Theme>(() => ({
5151
display: "flex",
5252
flexWrap: "wrap",
5353
columnGap: "1rem",
54+
flexDirection: "column",
55+
},
56+
groupHeader: {
57+
marginBottom: theme.spacing(1),
5458
},
5559
}));
5660

@@ -80,66 +84,11 @@ const useItemStyles = makeStyles<
8084
borderRadius: ({ symbol }) => (symbol === "circle" ? "50%" : 0),
8185
},
8286
},
83-
}));
84-
85-
export const InteractiveLegendColor = () => {
86-
const [state, dispatch] = useInteractiveFilters();
87-
const { categories } = state;
88-
89-
const activeInteractiveFilters = useMemo(() => {
90-
return new Set(Object.keys(categories));
91-
}, [categories]);
92-
93-
const { colors } = useChartState() as ColorsChartState;
94-
95-
const setFilter = useEvent((item: string) => {
96-
if (activeInteractiveFilters.has(item)) {
97-
dispatch({
98-
type: "REMOVE_INTERACTIVE_FILTER",
99-
value: item,
100-
});
101-
} else {
102-
dispatch({
103-
type: "ADD_INTERACTIVE_FILTER",
104-
value: item,
105-
});
106-
}
107-
});
10887

109-
const groups = useMemo(() => {
110-
return [{ label: undefined, value: "", values: colors.domain() }];
111-
}, [colors]);
112-
const classes = useStyles();
113-
return (
114-
<Flex
115-
className={clsx(
116-
classes.legendContainer,
117-
groups.length === 1 ? classes.legendContainerNoGroups : undefined
118-
)}
119-
>
120-
{groups.map((group) => {
121-
return (
122-
<div key={group.value}>
123-
{group.label ? (
124-
<Typography variant="h4">{group.label}</Typography>
125-
) : null}
126-
{colors.domain().map((item, i) => (
127-
<Checkbox
128-
label={item}
129-
name={item}
130-
value={item}
131-
checked={!activeInteractiveFilters.has(item)}
132-
onChange={() => setFilter(item)}
133-
key={i}
134-
color={colors(item)}
135-
/>
136-
))}
137-
</div>
138-
);
139-
})}
140-
</Flex>
141-
);
142-
};
88+
legendCheckbox: {
89+
marginTop: () => "0.25rem",
90+
},
91+
}));
14392

14493
const useDimension = ({
14594
dataset,
@@ -228,8 +177,10 @@ const useLegendGroups = ({
228177

229178
export const LegendColor = memo(function LegendColor({
230179
symbol,
180+
interactive,
231181
}: {
232182
symbol: LegendSymbol;
183+
interactive?: boolean;
233184
}) {
234185
const { colors, getSegmentLabel } = useChartState() as ColorsChartState;
235186
const groups = useLegendGroups({ labels: colors.domain() });
@@ -240,6 +191,7 @@ export const LegendColor = memo(function LegendColor({
240191
getColor={(v) => colors(v)}
241192
getLabel={getSegmentLabel}
242193
symbol={symbol}
194+
interactive={interactive}
243195
/>
244196
);
245197
});
@@ -282,13 +234,37 @@ const LegendColorContent = ({
282234
getColor,
283235
getLabel,
284236
symbol,
237+
interactive,
285238
}: {
286239
groups: ReturnType<typeof useLegendGroups>;
287240
getColor: (d: string) => string;
288241
getLabel: (d: string) => string;
289242
symbol: LegendSymbol;
243+
interactive?: boolean;
290244
}) => {
291245
const classes = useStyles();
246+
const [state, dispatch] = useInteractiveFilters();
247+
const { categories } = state;
248+
249+
const activeInteractiveFilters = useMemo(() => {
250+
return new Set(Object.keys(categories));
251+
}, [categories]);
252+
253+
const handleToggle: CheckboxProps["onChange"] = useEvent((ev) => {
254+
const item = ev.target.value;
255+
console.log({ item });
256+
if (activeInteractiveFilters.has(item)) {
257+
dispatch({
258+
type: "REMOVE_INTERACTIVE_FILTER",
259+
value: item,
260+
});
261+
} else {
262+
dispatch({
263+
type: "ADD_INTERACTIVE_FILTER",
264+
value: item,
265+
});
266+
}
267+
});
292268

293269
return (
294270
<Flex
@@ -300,15 +276,19 @@ const LegendColorContent = ({
300276
{groups
301277
? groups.map(([g, colorValues]) => {
302278
const headerLabelsArray = g.map((n) => n.label);
303-
304279
return (
305280
<div
306281
className={classes.legendGroup}
307282
key={g.map((n) => n.label).join(" > ")}
308283
data-testid="colorLegend"
309284
>
310285
{headerLabelsArray.length > 0 ? (
311-
<Typography variant="h5" display="flex" alignItems="center">
286+
<Typography
287+
variant="h5"
288+
display="flex"
289+
alignItems="center"
290+
className={classes.groupHeader}
291+
>
312292
{interlace(
313293
g.map((n) => n.label),
314294
<SvgIcChevronRight />
@@ -321,6 +301,11 @@ const LegendColorContent = ({
321301
item={getLabel(d)}
322302
color={getColor(d)}
323303
symbol={symbol}
304+
interactive={interactive}
305+
onToggle={handleToggle}
306+
checked={
307+
interactive && !activeInteractiveFilters.has(getLabel(d))
308+
}
324309
/>
325310
))}
326311
</div>
@@ -335,15 +320,36 @@ export const LegendItem = ({
335320
item,
336321
color,
337322
symbol,
323+
interactive,
324+
onToggle,
325+
checked,
338326
}: {
339327
item: string;
340328
color: string;
341329
symbol: LegendSymbol;
330+
interactive?: boolean;
331+
onToggle?: CheckboxProps["onChange"];
332+
checked?: boolean;
342333
}) => {
343334
const classes = useItemStyles({ symbol, color });
344335
return (
345-
<Flex data-testid="legendItem" className={classes.legendItem}>
346-
{item}
347-
</Flex>
336+
<>
337+
{interactive && onToggle ? (
338+
<Checkbox
339+
label={item}
340+
name={item}
341+
value={item}
342+
checked={checked !== undefined ? checked : true}
343+
onChange={onToggle}
344+
key={item}
345+
color={color}
346+
className={classes.legendCheckbox}
347+
/>
348+
) : (
349+
<Flex data-testid="legendItem" className={classes.legendItem}>
350+
{item}
351+
</Flex>
352+
)}
353+
</>
348354
);
349355
};

0 commit comments

Comments
 (0)