Skip to content

Commit 74439e7

Browse files
authored
Merge pull request #1399 from visualize-admin/feat/add-dataset
Support for multi dataset in metadata panel
2 parents 315e52b + d827c71 commit 74439e7

27 files changed

+878
-495
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ You can also check the [release page](https://github.com/visualize-admin/visuali
1111

1212
- Features
1313
- Slightly increased initial map zoom level
14+
- Join a second dataset through temporal dimension (behind `flag__add-dataset` flag)
1415
- Fixes
1516
- SingleURLs layout mode now correctly publishes charts
1617
- Redirecting to latest cube now works correctly for cases of trying to access old cube that didn't look like a versioned cube, but in fact was a versioned cube

app/browser/select-dataset-step.tsx

+33-6
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,20 @@ import {
1717
SearchFilters,
1818
} from "@/browser/dataset-browse";
1919
import { DataSetPreview } from "@/browser/dataset-preview";
20-
import { DataSetMetadata } from "@/components/dataset-metadata";
20+
import { DatasetMetadata } from "@/components/dataset-metadata";
2121
import Flex from "@/components/flex";
2222
import { Footer } from "@/components/footer";
2323
import {
24+
bannerPresenceProps,
2425
BANNER_HEIGHT,
2526
BANNER_MARGIN_TOP,
26-
bannerPresenceProps,
2727
DURATION,
2828
MotionBox,
2929
navPresenceProps,
3030
smoothPresenceProps,
3131
} from "@/components/presence";
3232
import { useRedirectToLatestCube } from "@/components/use-redirect-to-latest-cube";
33+
import { DataSource } from "@/configurator";
3334
import {
3435
PanelBodyWrapper,
3536
PanelLayout,
@@ -39,6 +40,7 @@ import {
3940
DataCubeOrganization,
4041
DataCubeTheme,
4142
SearchCubeFilterType,
43+
useDataCubeMetadataQuery,
4244
useSearchCubesQuery,
4345
} from "@/graphql/query-hooks";
4446
import { Icon } from "@/icons";
@@ -300,8 +302,8 @@ const SelectDatasetStepContent = () => {
300302
</Button>
301303
</NextLink>
302304
<MotionBox sx={{ mt: 6 }} {...smoothPresenceProps}>
303-
<DataSetMetadata
304-
dataSetIri={dataset}
305+
<DatasetMetadataSingleCubeAdapter
306+
datasetIri={dataset}
305307
dataSource={configState.dataSource}
306308
/>
307309
</MotionBox>
@@ -407,6 +409,31 @@ const SelectDatasetStepContent = () => {
407409
);
408410
};
409411

412+
const DatasetMetadataSingleCubeAdapter = ({
413+
dataSource,
414+
datasetIri,
415+
}: {
416+
dataSource: DataSource;
417+
datasetIri: string;
418+
}) => {
419+
const locale = useLocale();
420+
const [data] = useDataCubeMetadataQuery({
421+
variables: {
422+
cubeFilter: { iri: datasetIri },
423+
locale: locale,
424+
sourceType: dataSource.type,
425+
sourceUrl: dataSource.url,
426+
},
427+
});
428+
if (!data.data) {
429+
return null;
430+
}
431+
432+
return (
433+
<DatasetMetadata cube={data.data.dataCubeMetadata} showTitle={false} />
434+
);
435+
};
436+
410437
const PageTitle = () => {
411438
const { search, filters } = useBrowseContext();
412439
return (
@@ -415,8 +442,8 @@ const PageTitle = () => {
415442
{search
416443
? `"${search}"`
417444
: filters?.length > 0 && filters[0].__typename !== "DataCubeAbout"
418-
? filters[0].label
419-
: t({ id: "browse.datasets.all-datasets" })}{" "}
445+
? filters[0].label
446+
: t({ id: "browse.datasets.all-datasets" })}{" "}
420447
- visualize.admin.ch
421448
</title>
422449
</Head>

app/charts/combo/combo-line-column-state.tsx

+21-21
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { InteractionProvider } from "@/charts/shared/use-interaction";
3232
import { Observer } from "@/charts/shared/use-width";
3333
import { ComboLineColumnConfig } from "@/configurator";
3434
import { Observation } from "@/domain/data";
35+
import { truthy } from "@/domain/types";
3536
import { useFormatFullDateAuto } from "@/formatters";
3637
import { TimeUnit } from "@/graphql/resolver-types";
3738
import { getTimeInterval } from "@/intervals";
@@ -155,36 +156,35 @@ const useComboLineColumnState = (
155156
const getAnnotationInfo = (d: Observation): TooltipInfo => {
156157
const x = getX(d);
157158
const xScaled = (xScale(x) as number) + xScale.bandwidth() * 0.5;
158-
159+
const values = [variables.y.left, variables.y.right]
160+
.map(({ orientation, getY, label, chartType }) => {
161+
const y = getY(d);
162+
const yPos = yOrientationScales[orientation](y ?? 0);
163+
if (!Number.isFinite(y) || y === null) {
164+
return null;
165+
}
166+
return {
167+
label,
168+
value: `${y}`,
169+
color: colors(label),
170+
hide: y === null,
171+
yPos,
172+
symbol: chartType === "line" ? "line" : "square",
173+
};
174+
})
175+
.filter(truthy);
176+
const yAnchor = d3.mean(values.map((d) => d.yPos));
159177
return {
160178
datum: { label: "", value: "0", color: d3.schemeCategory10[0] },
161179
xAnchor: xScaled,
162-
yAnchor:
163-
[variables.y.left, variables.y.right]
164-
.map(({ orientation, getY }) =>
165-
yOrientationScales[orientation](getY(d) ?? 0)
166-
)
167-
.reduce((a, b) => a + b, 0) * 0.5,
180+
yAnchor,
168181
xValue: timeFormatUnit(x, variables.xTimeUnit as TimeUnit),
169182
placement: getCenteredTooltipPlacement({
170183
chartWidth,
171184
xAnchor: xScaled,
172185
topAnchor: false,
173186
}),
174-
values: [variables.y.left, variables.y.right].map(
175-
({ orientation, getY, label, chartType }) => {
176-
const y = getY(d);
177-
178-
return {
179-
label,
180-
value: `${y}`,
181-
color: colors(label),
182-
hide: y === null,
183-
yPos: yOrientationScales[orientation](y ?? 0),
184-
symbol: chartType === "line" ? "line" : "square",
185-
};
186-
}
187-
),
187+
values,
188188
} as TooltipInfo;
189189
};
190190

app/charts/combo/combo-line-dual-state.tsx

+23-20
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { InteractionProvider } from "@/charts/shared/use-interaction";
3131
import { Observer } from "@/charts/shared/use-width";
3232
import { ComboLineDualConfig } from "@/configurator";
3333
import { Observation } from "@/domain/data";
34+
import { truthy } from "@/domain/types";
3435
import { getTextWidth } from "@/utils/get-text-width";
3536

3637
import { ChartProps } from "../shared/ChartProps";
@@ -136,35 +137,37 @@ const useComboLineDualState = (
136137
const x = getX(d);
137138
const xScaled = xScale(x);
138139

140+
const values = [variables.y.left, variables.y.right]
141+
.map(({ orientation, getY, label }) => {
142+
const y = getY(d);
143+
const yPos = yOrientationScales[orientation](y ?? 0);
144+
if (!Number.isFinite(y) || y === null) {
145+
return null;
146+
}
147+
148+
return {
149+
label,
150+
value: `${y}`,
151+
color: colors(label),
152+
hide: y === null,
153+
yPos: yPos,
154+
symbol: "line",
155+
};
156+
})
157+
.filter(truthy);
158+
const yAnchor = d3.mean(values.map((d) => d.yPos));
159+
139160
return {
140161
datum: { label: "", value: "0", color: d3.schemeCategory10[0] },
141162
xAnchor: xScaled,
142-
yAnchor:
143-
[variables.y.left, variables.y.right]
144-
.map(({ orientation, getY }) =>
145-
yOrientationScales[orientation](getY(d) ?? 0)
146-
)
147-
.reduce((a, b) => a + b, 0) * 0.5,
163+
yAnchor: yAnchor,
148164
xValue: timeFormatUnit(x, xDimension.timeUnit),
149165
placement: getCenteredTooltipPlacement({
150166
chartWidth,
151167
xAnchor: xScaled,
152168
topAnchor: false,
153169
}),
154-
values: [variables.y.left, variables.y.right].map(
155-
({ orientation, getY, label }) => {
156-
const y = getY(d);
157-
158-
return {
159-
label,
160-
value: `${y}`,
161-
color: colors(label),
162-
hide: y === null,
163-
yPos: yOrientationScales[orientation](y ?? 0),
164-
symbol: "line",
165-
};
166-
}
167-
),
170+
values,
168171
} as TooltipInfo;
169172
};
170173

app/charts/combo/combo-line-single-state.tsx

+20-16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { InteractionProvider } from "@/charts/shared/use-interaction";
2828
import { Observer } from "@/charts/shared/use-width";
2929
import { ComboLineSingleConfig } from "@/configurator";
3030
import { Observation } from "@/domain/data";
31+
import { truthy } from "@/domain/types";
3132

3233
import { ChartProps } from "../shared/ChartProps";
3334

@@ -116,23 +117,12 @@ const useComboLineSingleState = (
116117
const getAnnotationInfo = (d: Observation): TooltipInfo => {
117118
const x = getX(d);
118119
const xScaled = xScale(x);
119-
120-
return {
121-
datum: { label: "", value: "0", color: d3.schemeCategory10[0] },
122-
xAnchor: xScaled,
123-
yAnchor: yScale(
124-
variables.y.lines
125-
.map(({ getY }) => getY(d) ?? 0)
126-
.reduce((a, b) => a + b, 0) / variables.y.lines.length
127-
),
128-
xValue: timeFormatUnit(x, xDimension.timeUnit),
129-
placement: getCenteredTooltipPlacement({
130-
chartWidth,
131-
xAnchor: xScaled,
132-
topAnchor: false,
133-
}),
134-
values: variables.y.lines.map(({ getY, label }) => {
120+
const values = variables.y.lines
121+
.map(({ getY, label }) => {
135122
const y = getY(d);
123+
if (!Number.isFinite(y) || y === null) {
124+
return null;
125+
}
136126

137127
return {
138128
label,
@@ -142,7 +132,21 @@ const useComboLineSingleState = (
142132
yPos: yScale(y ?? 0),
143133
symbol: "line",
144134
};
135+
})
136+
.filter(truthy);
137+
const yAnchor = d3.mean(values.map((d) => d.yPos));
138+
139+
return {
140+
datum: { label: "", value: "0", color: d3.schemeCategory10[0] },
141+
xAnchor: xScaled,
142+
yAnchor: yAnchor,
143+
xValue: timeFormatUnit(x, xDimension.timeUnit),
144+
placement: getCenteredTooltipPlacement({
145+
chartWidth,
146+
xAnchor: xScaled,
147+
topAnchor: false,
145148
}),
149+
values,
146150
} as TooltipInfo;
147151
};
148152

app/components/JoinByChip.tsx

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Chip, ChipProps, chipClasses } from "@mui/material";
2+
3+
import SvgIcMergeddimension from "@/icons/components/IcMergedDimension";
4+
5+
export const JoinByChip = ({ children, ...props }: ChipProps) => {
6+
return (
7+
<Chip
8+
{...props}
9+
label={
10+
<>
11+
<SvgIcMergeddimension />
12+
{props.label}
13+
</>
14+
}
15+
sx={{
16+
background: (theme) => theme.palette.warning.light,
17+
padding: "2px 6px",
18+
minHeight: "auto",
19+
height: 20,
20+
borderRadius: 2,
21+
gap: "4px",
22+
[`& .${chipClasses.label}`]: {
23+
padding: 0,
24+
lineHeight: 1,
25+
display: "flex",
26+
alignItems: "center",
27+
},
28+
}}
29+
/>
30+
);
31+
};

app/components/chart-preview.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,8 @@ export const ChartPreviewInner = (props: ChartPreviewInnerProps) => {
445445
<Flex sx={{ alignItems: "center", gap: 2 }}>
446446
{!disableMetadataPanel && (
447447
<MetadataPanel
448-
// FIXME: adapt to design
449-
datasetIri={chartConfig.cubes[0].iri}
450448
dataSource={dataSource}
449+
chartConfigs={[chartConfig]}
451450
dimensions={allComponents}
452451
top={96}
453452
/>

app/components/chart-published.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ import {
2424
import { ChartWithFilters } from "@/components/chart-with-filters";
2525
import Flex from "@/components/flex";
2626
import { HintBlue, HintRed, HintYellow } from "@/components/hint";
27+
import { MetadataPanel } from "@/components/metadata-panel";
2728
import {
28-
MetadataPanel,
2929
MetadataPanelStoreContext,
3030
createMetadataPanelStore,
31-
} from "@/components/metadata-panel";
31+
} from "@/components/metadata-panel-store";
3232
import {
3333
ChartConfig,
3434
ConfiguratorStatePublished,
@@ -316,9 +316,8 @@ const ChartPublishedInner = (props: ChartPublishInnerProps) => {
316316
>
317317
{meta.title[locale] && <Title text={meta.title[locale]} />}
318318
<MetadataPanel
319-
// FIXME: adapt to design
320-
datasetIri={chartConfig.cubes[0].iri}
321319
dataSource={dataSource}
320+
chartConfigs={[chartConfig]}
322321
dimensions={allComponents}
323322
container={rootRef.current}
324323
/>

0 commit comments

Comments
 (0)