Skip to content

Commit c947553

Browse files
authored
Merge pull request #1557 from visualize-admin/feat/flags
WIP features behind flags
2 parents fcad0fa + 4fbc715 commit c947553

12 files changed

Lines changed: 130 additions & 74 deletions

File tree

app/browser/dataset-browse.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import Tag from "@/components/tag";
4242
import useDisclosure from "@/components/use-disclosure";
4343
import { SearchCube, Termset } from "@/domain/data";
4444
import { truthy } from "@/domain/types";
45+
import { useFlag } from "@/flags";
4546
import { useFormatDate } from "@/formatters";
4647
import {
4748
DataCubeOrganization,
@@ -870,8 +871,9 @@ export const SearchFilters = ({
870871
/>
871872
) : null;
872873

874+
const termsetFlag = useFlag("search.termsets");
873875
const termsetNav =
874-
termsetCounts.length === 0 ? null : (
876+
termsetCounts.length === 0 || !termsetFlag ? null : (
875877
<TermsetNavSection
876878
termsetCounts={termsetCounts ?? []}
877879
currentFilter={termsetFilter}

app/components/chart-selection-tabs.tsx

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { useSearchDatasetPanelStore } from "@/configurator/components/add-new-da
4747
import { ChartTypeSelector } from "@/configurator/components/chart-type-selector";
4848
import { getIconName } from "@/configurator/components/ui-helpers";
4949
import { useUserConfig } from "@/domain/user-configs";
50+
import { useFlag } from "@/flags";
5051
import { useDataCubesComponentsQuery } from "@/graphql/hooks";
5152
import { Icon, IconName } from "@/icons";
5253
import { useLocale } from "@/src";
@@ -160,6 +161,8 @@ const TabsEditable = (props: TabsEditableProps) => {
160161

161162
const { open: openAddDatasetPanel } = useSearchDatasetPanelStore();
162163

164+
const addNewDatasetFlag = useFlag("configurator.add-dataset.new");
165+
163166
return (
164167
<>
165168
<TabsInner
@@ -213,16 +216,18 @@ const TabsEditable = (props: TabsEditableProps) => {
213216
direction="column"
214217
>
215218
<Stack direction="column" gap="0.5rem" m="1rem">
216-
<Typography
217-
variant="body2"
218-
color="text.secondary"
219-
textAlign="center"
220-
gutterBottom
221-
>
222-
<Trans id="chart-selection-tabs.add-chart-same-dataset.caption">
223-
Add chart based on the same dataset
224-
</Trans>
225-
</Typography>
219+
{addNewDatasetFlag ? (
220+
<Typography
221+
variant="body2"
222+
color="text.secondary"
223+
textAlign="center"
224+
gutterBottom
225+
>
226+
<Trans id="chart-selection-tabs.add-chart-same-dataset.caption">
227+
Add chart based on the same dataset
228+
</Trans>
229+
</Typography>
230+
) : null}
226231
<ChartTypeSelector
227232
state={state}
228233
type="add"
@@ -232,30 +237,32 @@ const TabsEditable = (props: TabsEditableProps) => {
232237
sx={{ width: 320, px: 3, pb: 3 }}
233238
/>
234239
</Stack>
235-
<Stack direction="column" gap="0.5rem" mx="1.5rem" my="1rem">
236-
<Typography
237-
variant="body2"
238-
color="text.secondary"
239-
textAlign="center"
240-
gutterBottom
241-
>
242-
<Trans id="chart-selection-tabs.add-chart-different-dataset.caption">
243-
Add chart based on a different dataset
244-
</Trans>
245-
</Typography>
246-
<Button
247-
fullWidth
248-
sx={{ justifyContent: "center" }}
249-
onClick={() => {
250-
setTabsState({ ...tabsState, popoverOpen: false });
251-
openAddDatasetPanel();
252-
}}
253-
>
254-
<Trans id="chart-selection-tabs.add-chart-different-dataset.button">
255-
Select dataset
256-
</Trans>
257-
</Button>
258-
</Stack>
240+
{addNewDatasetFlag ? (
241+
<Stack direction="column" gap="0.5rem" mx="1.5rem" my="1rem">
242+
<Typography
243+
variant="body2"
244+
color="text.secondary"
245+
textAlign="center"
246+
gutterBottom
247+
>
248+
<Trans id="chart-selection-tabs.add-chart-different-dataset.caption">
249+
Add chart based on a different dataset
250+
</Trans>
251+
</Typography>
252+
<Button
253+
fullWidth
254+
sx={{ justifyContent: "center" }}
255+
onClick={() => {
256+
setTabsState({ ...tabsState, popoverOpen: false });
257+
openAddDatasetPanel();
258+
}}
259+
>
260+
<Trans id="chart-selection-tabs.add-chart-different-dataset.button">
261+
Select dataset
262+
</Trans>
263+
</Button>
264+
</Stack>
265+
) : null}
259266
</Stack>
260267
</Popover>
261268
) : null}

app/configurator/components/chart-options-selector.tsx

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { t, Trans } from "@lingui/macro";
22
import { Box, Stack, Tooltip, Typography } from "@mui/material";
33
import { groups } from "d3-array";
44
import get from "lodash/get";
5-
import React, { useCallback, useEffect, useMemo } from "react";
5+
import { useCallback, useEffect, useMemo } from "react";
66

77
import { DEFAULT_SORTING, getFieldComponentIri } from "@/charts";
88
import {
@@ -93,7 +93,7 @@ import {
9393
useDataCubesComponentsQuery,
9494
useDataCubesObservationsQuery,
9595
} from "@/graphql/hooks";
96-
import SvgIcExclamation from "@/icons/components/IcExclamation";
96+
import SvgIcInfoOutline from "@/icons/components/IcInfoOutline";
9797
import { useLocale } from "@/locales/use-locale";
9898

9999
export const ChartOptionsSelector = ({
@@ -1097,19 +1097,13 @@ const ChartFieldAnimation = ({ field }: { field: AnimationField }) => {
10971097
})}
10981098
>
10991099
<Typography sx={{ color: "primary.main" }}>
1100-
<SvgIcExclamation
1101-
style={{
1102-
transform: "scale(0.8)",
1103-
width: 18,
1104-
height: 18,
1105-
}}
1106-
/>
1100+
<SvgIcInfoOutline width={16} height={16} />
11071101
</Typography>
11081102
</Tooltip>
11091103
</Box>
11101104
}
11111105
/>
1112-
<Flex sx={{ justifyContent: "flex-start" }}>
1106+
<Flex justifyContent="flex-start">
11131107
<ChartOptionRadioField
11141108
label={t({
11151109
id: "controls.section.animation.type.continuous",
@@ -1130,8 +1124,9 @@ const ChartFieldAnimation = ({ field }: { field: AnimationField }) => {
11301124
/>
11311125
</Flex>
11321126
</Box>
1133-
<Box sx={{ display: "flex", alignItems: "center", mt: 5 }}>
1127+
<Box display="flex" alignItems="center" mt={5} gap="0.5rem">
11341128
<ChartOptionSwitchField
1129+
sx={{ mr: 0 }}
11351130
label={t({
11361131
id: "controls.section.animation.dynamic-scaling",
11371132
message: "Dynamic Scaling",
@@ -1147,14 +1142,8 @@ const ChartFieldAnimation = ({ field }: { field: AnimationField }) => {
11471142
"Enable dynamic scaling to adjust the chart's scale based on the data range, ensuring optimal visualization.",
11481143
})}
11491144
>
1150-
<Typography sx={{ ml: "-12px", color: "primary.main" }}>
1151-
<SvgIcExclamation
1152-
style={{
1153-
transform: "scale(0.8)",
1154-
width: 18,
1155-
height: 18,
1156-
}}
1157-
/>
1145+
<Typography color="primary.main">
1146+
<SvgIcInfoOutline width={16} height={16} />
11581147
</Typography>
11591148
</Tooltip>
11601149
</Box>

app/configurator/components/dataset-control-section.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
useConfiguratorState,
2727
} from "@/configurator/configurator-state";
2828
import { DataCubeMetadata } from "@/domain/data";
29+
import useFlag from "@/flags/useFlag";
2930
import { useDataCubesMetadataQuery } from "@/graphql/hooks";
3031
import SvgIcAdd from "@/icons/components/IcAdd";
3132
import SvgIcChecked from "@/icons/components/IcChecked";
@@ -197,6 +198,11 @@ export const DatasetsControlSection = () => {
197198
setActiveSection("general");
198199
};
199200

201+
const addDatasetFlag = useFlag("configurator.add-dataset.shared");
202+
if (!addDatasetFlag) {
203+
return null;
204+
}
205+
200206
return (
201207
<ControlSection collapse defaultExpanded={false}>
202208
<SubsectionTitle titleId="controls-data" gutterBottom={false}>

app/configurator/components/field.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import orderBy from "lodash/orderBy";
1515
import pick from "lodash/pick";
1616
import React, {
1717
ChangeEvent,
18+
ComponentProps,
1819
ReactNode,
1920
useCallback,
2021
useMemo,
@@ -1096,13 +1097,14 @@ export const ChartOptionSwitchField = ({
10961097
path,
10971098
defaultValue = false,
10981099
disabled = false,
1100+
...props
10991101
}: {
11001102
label: React.ComponentProps<typeof FormControlLabel>["label"];
11011103
field: EncodingFieldType | null;
11021104
path: string;
11031105
defaultValue?: boolean;
11041106
disabled?: boolean;
1105-
}) => {
1107+
} & ComponentProps<typeof Switch>) => {
11061108
const fieldProps = useChartOptionBooleanField({
11071109
field,
11081110
path,
@@ -1114,6 +1116,7 @@ export const ChartOptionSwitchField = ({
11141116
disabled={disabled}
11151117
label={label}
11161118
{...fieldProps}
1119+
{...props}
11171120
checked={fieldProps.checked ?? defaultValue}
11181121
/>
11191122
);

app/configurator/components/layout-configurator.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
isJoinByComponent,
4545
isTemporalDimension,
4646
} from "@/domain/data";
47+
import { useFlag } from "@/flags";
4748
import { useTimeFormatLocale, useTimeFormatUnit } from "@/formatters";
4849
import { useDataCubesComponentsQuery } from "@/graphql/hooks";
4950
import { useLocale } from "@/src";
@@ -54,11 +55,12 @@ import {
5455
import { getTimeFilterOptions } from "@/utils/time-filter-options";
5556

5657
export const LayoutConfigurator = () => {
58+
const sharedFiltersFlag = useFlag("layouter.dashboard.shared-filters");
5759
return (
5860
<>
5961
<LayoutAnnotator />
6062
<LayoutLayoutConfigurator />
61-
<LayoutSharedFiltersConfigurator />
63+
{sharedFiltersFlag ? <LayoutSharedFiltersConfigurator /> : null}
6264
</>
6365
);
6466
};
@@ -67,6 +69,8 @@ const LayoutLayoutConfigurator = () => {
6769
const [state] = useConfiguratorState(isLayouting);
6870
const { layout } = state;
6971

72+
const freeCanvasFlag = useFlag("layouter.dashboard.free-canvas");
73+
7074
switch (layout.type) {
7175
case "dashboard":
7276
return (
@@ -92,7 +96,9 @@ const LayoutLayoutConfigurator = () => {
9296
>
9397
<LayoutButton type="tall" layout={layout} />
9498
<LayoutButton type="vertical" layout={layout} />
95-
<LayoutButton type="canvas" layout={layout} />
99+
{freeCanvasFlag ? (
100+
<LayoutButton type="canvas" layout={layout} />
101+
) : null}
96102
</Box>
97103
</ControlSectionContent>
98104
</ControlSection>

app/flags/flag.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import qs from "qs";
22

33
import { isRunningInBrowser } from "@/utils/is-running-in-browser";
44

5-
import FlagStore, { FlagName, FlagValue } from "./store";
5+
import FlagStore from "./store";
6+
import { FlagName, FlagValue } from "./types";
67

78
const FLAG_PREFIX = "flag__";
89

@@ -28,7 +29,7 @@ export const listFlags = () => {
2829

2930
/** Resets all the flags */
3031
export const resetFlags = () => {
31-
listFlags().forEach((name) => store.remove(name));
32+
listFlags().forEach((name) => store.remove(name as FlagName));
3233
};
3334

3435
/**
@@ -63,18 +64,40 @@ const initFromSearchParams = (locationSearch: string) => {
6364
for (const [param, value] of Object.entries(params)) {
6465
if (param.startsWith(FLAG_PREFIX) && typeof value === "string") {
6566
try {
66-
flag(param.substring(FLAG_PREFIX.length), JSON.parse(value));
67+
flag(
68+
param.substring(FLAG_PREFIX.length) as FlagName,
69+
JSON.parse(value)
70+
);
6771
} catch (e) {
6872
console.error(e);
6973
}
7074
}
7175
}
7276
};
7377

78+
const isVercelPreviewHost = (host: string) => {
79+
return !!/visualization\-tool.*ixt1\.vercel\.app/.exec(host);
80+
};
81+
82+
const initFromHost = (host: string) => {
83+
if (
84+
host.includes("localhost") ||
85+
host.includes("test.visualize.admin.ch") ||
86+
isVercelPreviewHost(host)
87+
) {
88+
flag("configurator.add-dataset.new", true);
89+
flag("configurator.add-dataset.shared", true);
90+
flag("layouter.dashboard.free-canvas", true);
91+
flag("layouter.dashboard.shared-filters", true);
92+
flag("search.termsets", true);
93+
}
94+
};
95+
7496
if (isRunningInBrowser()) {
7597
// @ts-ignore
7698
window.flag = flag;
7799
initFromSearchParams(window.location.search);
100+
initFromHost(window.location.host);
78101
}
79102

80103
export { flag };

app/flags/ls-adapter.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { FlagName, FlagValue } from "./store";
1+
import { FlagName, FlagValue } from "./types";
22

33
export const prefix = "flag__";
4-
export const getKey = (name: FlagName) => prefix + name;
4+
5+
type FlagNameOrString = FlagName | (string & {});
6+
7+
export const getKey = (name: FlagNameOrString) => prefix + name;
58

69
const listFlagLocalStorage = () => {
710
return Object.keys(localStorage)
@@ -14,7 +17,7 @@ const listFlagLocalStorage = () => {
1417
*
1518
* @param {String} flag
1619
*/
17-
const getItem = (flag: FlagName) => {
20+
const getItem = (flag: FlagNameOrString) => {
1821
const val = localStorage.getItem(getKey(flag));
1922
const parsed = val ? JSON.parse(val) : val;
2023
return parsed;
@@ -26,7 +29,7 @@ const getItem = (flag: FlagName) => {
2629
* @param {String} flag
2730
* @param {String} value
2831
*/
29-
const setItem = (flag: FlagName, value: FlagValue) => {
32+
const setItem = (flag: FlagNameOrString, value: FlagValue) => {
3033
const str = JSON.stringify(value);
3134
return localStorage.setItem(getKey(flag), str);
3235
};
@@ -36,7 +39,7 @@ const setItem = (flag: FlagName, value: FlagValue) => {
3639
*
3740
* @param {String} flag
3841
*/
39-
const removeItem = (flag: FlagName) => {
42+
const removeItem = (flag: FlagNameOrString) => {
4043
return localStorage.removeItem(getKey(flag));
4144
};
4245

0 commit comments

Comments
 (0)