Skip to content
Snippets Groups Projects
Unverified Commit 84c1b89e authored by github-automation-metabase's avatar github-automation-metabase Committed by GitHub
Browse files

:robot: backported "Move ChartSettings to Dashboard and Question variants" (#49338)


Co-authored-by: default avatarOisin Coveney <oisin@metabase.com>
parent 14c754bb
No related branches found
No related tags found
No related merge requests found
Showing
with 352 additions and 329 deletions
......@@ -20,7 +20,7 @@ export const createMockQueryBuilderUIControlsState = (
isShowingQuestionInfoSidebar: false,
isShowingSnippetSidebar: false,
isShowingTimelineSidebar: false,
initialChartSetting: null,
initialChartSetting: {},
isShowingRawTable: false,
isNativeEditorOpen: false,
queryBuilderMode: "view",
......
import type { QueryModalType } from "metabase/query_builder/constants";
import type { Widget } from "metabase/visualizations/components/ChartSettings/types";
import type {
Card,
DashboardId,
......@@ -11,6 +12,10 @@ import type {
export type QueryBuilderMode = "view" | "notebook" | "dataset";
export type DatasetEditorTab = "query" | "metadata";
export type QueryBuilderQueryStatus = "idle" | "running" | "complete";
export type InitialChartSettingState = {
section?: string | null;
widget?: Widget | null;
};
export type ForeignKeyReference = {
status: number;
......@@ -32,7 +37,7 @@ export interface QueryBuilderUIControls {
isShowingSnippetSidebar: boolean;
isShowingTimelineSidebar: boolean;
isNativeEditorOpen: boolean;
initialChartSetting: null;
initialChartSetting: InitialChartSettingState;
isShowingRawTable: boolean;
queryBuilderMode: QueryBuilderMode | false;
previousQueryBuilderMode: boolean;
......
import { useDisclosure } from "@mantine/hooks";
import { t } from "ttag";
import ModalWithTrigger from "metabase/components/ModalWithTrigger";
import CS from "metabase/css/core/index.css";
import { ChartSettingsWithState } from "metabase/visualizations/components/ChartSettings";
import { DEFAULT_Z_INDEX } from "metabase/components/Popover/constants";
import { Modal } from "metabase/ui";
import { DashboardChartSettings } from "metabase/visualizations/components/ChartSettings";
import type {
Dashboard,
DashboardCard,
......@@ -10,7 +11,7 @@ import type {
VisualizationSettings,
} from "metabase-types/api";
import { DashCardActionButton } from "../DashCardActionButton/DashCardActionButton";
import { DashCardActionButton } from "../DashCardActionButton";
interface Props {
series: Series;
......@@ -25,29 +26,39 @@ export function ChartSettingsButton({
dashcard,
onReplaceAllVisualizationSettings,
}: Props) {
const [isOpened, { open, close }] = useDisclosure(false);
return (
<ModalWithTrigger
wide
tall
triggerElement={
<DashCardActionButton
as="div"
tooltip={t`Visualization options`}
aria-label={t`Show visualization options`}
>
<DashCardActionButton.Icon name="palette" />
</DashCardActionButton>
}
enableMouseEvents
>
<ChartSettingsWithState
className={CS.spread}
series={series}
onChange={onReplaceAllVisualizationSettings}
isDashboard
dashboard={dashboard}
dashcard={dashcard}
/>
</ModalWithTrigger>
<>
<DashCardActionButton
as="div"
tooltip={t`Visualization options`}
aria-label={t`Show visualization options`}
onClick={open}
>
<DashCardActionButton.Icon name="palette" />
</DashCardActionButton>
{/* zIndex is a hack for now until the inner popovers are converted to mantine */}
<Modal.Root
opened={isOpened}
onClose={close}
size="85%"
zIndex={DEFAULT_Z_INDEX - 1}
>
<Modal.Overlay />
<Modal.Content mih="85%">
<Modal.Body>
<DashboardChartSettings
series={series}
onChange={onReplaceAllVisualizationSettings}
dashboard={dashboard}
dashcard={dashcard}
onClose={close}
/>
</Modal.Body>
</Modal.Content>
</Modal.Root>
</>
);
}
export * from "./DashCardActionButton";
......@@ -15,9 +15,9 @@ import {
getVisualizationSettings,
} from "metabase/query_builder/selectors";
import visualizations from "metabase/visualizations";
import { ChartSettings } from "metabase/visualizations/components/ChartSettings";
import { QuestionChartSettings } from "metabase/visualizations/components/ChartSettings";
import type Question from "metabase-lib/v1/Question";
import type { Dataset } from "metabase-types/api";
import type { Dataset, VisualizationSettings } from "metabase-types/api";
interface ChartSettingsSidebarProps {
question: Question;
......@@ -55,6 +55,9 @@ function ChartSettingsSidebarInner({
];
}, [card, result]);
const onChange = (settings: VisualizationSettings, question?: Question) =>
dispatch(onReplaceAllVisualizationSettings(settings, question));
return (
result && (
<SidebarContent
......@@ -63,14 +66,10 @@ function ChartSettingsSidebarInner({
{...sidebarContentProps}
>
<ErrorBoundary>
<ChartSettings
<QuestionChartSettings
question={question}
series={series}
onChange={(settings, question) =>
dispatch(onReplaceAllVisualizationSettings(settings, question))
}
onClose={handleClose}
noPreview
onChange={onChange}
initial={initialChartSetting}
computedSettings={visualizationSettings}
/>
......
......@@ -18,7 +18,7 @@ export const DEFAULT_UI_CONTROLS: QueryBuilderUIControls = {
isShowingQuestionInfoSidebar: false,
isShowingTimelineSidebar: false,
isNativeEditorOpen: false,
initialChartSetting: null,
initialChartSetting: {},
isShowingRawTable: false, // table/viz toggle
queryBuilderMode: false, // "view" | "notebook" | "dataset"
previousQueryBuilderMode: false,
......
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import Radio from "metabase/core/components/Radio";
interface SectionContainerProps {
isDashboard: boolean;
}
export const SectionContainer = styled.div<SectionContainerProps>`
${({ isDashboard }) =>
isDashboard &&
css`
margin-top: 1rem;
`}
width: 100%;
${Radio.RadioGroupVariants.join(", ")} {
border-bottom: 1px solid var(--mb-color-border);
}
${Radio.RadioContainerVariants.join(", ")} {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
${Radio.RadioLabelVariants.join(", ")} {
flex-grow: 1;
margin-right: 0;
display: flex;
justify-content: center;
&:not(:last-child) {
margin-right: 0;
}
}
`;
export const ChartSettingsMenu = styled.div`
flex: 1 0 0;
display: flex;
flex-direction: column;
overflow-y: auto;
`;
export const ChartSettingsListContainer = styled.div`
position: relative;
padding: 1.5rem 0;
`;
import { assocIn } from "icepick";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { t } from "ttag";
import _ from "underscore";
import Radio from "metabase/core/components/Radio";
import CS from "metabase/css/core/index.css";
import {
extractRemappings,
getVisualizationTransformed,
} from "metabase/visualizations";
import { ChartSettingsFooter } from "metabase/visualizations/components/ChartSettings/ChartSettingsFooter";
import Visualization from "metabase/visualizations/components/Visualization";
import { updateSeriesColor } from "metabase/visualizations/lib/series";
import {
getClickBehaviorSettings,
getComputedSettings,
getSettingsWidgets,
updateSettings,
} from "metabase/visualizations/lib/settings";
import { getSettingDefinitionsForColumn } from "metabase/visualizations/lib/settings/column";
import { keyForSingleSeries } from "metabase/visualizations/lib/settings/series";
import { getSettingsWidgetsForSeries } from "metabase/visualizations/lib/settings/visualization";
import type Question from "metabase-lib/v1/Question";
import { getColumnKey } from "metabase-lib/v1/queries/utils/column-key";
import type { DatasetColumn, VisualizationSettings } from "metabase-types/api";
import type { DatasetColumn } from "metabase-types/api";
import ChartSettingsWidgetList from "../ChartSettingsWidgetList";
import ChartSettingsWidgetPopover from "../ChartSettingsWidgetPopover";
import ChartSettingsWidgetList from "../../ChartSettingsWidgetList";
import ChartSettingsWidgetPopover from "../../ChartSettingsWidgetPopover";
import type { Widget } from "../types";
import {
ChartSettingsListContainer,
ChartSettingsMenu,
ChartSettingsPreview,
ChartSettingsRoot,
ChartSettingsVisualizationContainer,
SectionContainer,
SectionWarnings,
} from "./ChartSettings.styled";
import type {
ChartSettingsProps,
ChartSettingsWithStateProps,
Widget,
} from "./types";
} from "./BaseChartSettings.styled";
import type { BaseChartSettingsProps } from "./types";
// section names are localized
const DEFAULT_TAB_PRIORITY = [t`Data`];
export const ChartSettings = ({
export const BaseChartSettings = ({
initial,
settings: propSettings,
series,
computedSettings: propComputedSettings,
computedSettings = {},
onChange,
isDashboard = false,
noPreview = false,
dashboard,
dashcard,
onDone,
onClose,
question,
className,
widgets: propWidgets,
}: ChartSettingsProps) => {
widgets,
chartSettings,
transformedSeries,
}: BaseChartSettingsProps) => {
const [currentSection, setCurrentSection] = useState<string | null>(
initial?.section ?? null,
);
......@@ -69,54 +45,6 @@ export const ChartSettings = ({
initial?.widget ?? null,
);
const [popoverRef, setPopoverRef] = useState<HTMLElement | null>();
const [warnings, setWarnings] = useState();
const chartSettings = useMemo(
() => propSettings || series[0].card.visualization_settings,
[series, propSettings],
);
const computedSettings = useMemo(
() => propComputedSettings || {},
[propComputedSettings],
);
const handleChangeSettings = useCallback(
(changedSettings: VisualizationSettings, question: Question) => {
onChange?.(updateSettings(chartSettings, changedSettings), question);
},
[chartSettings, onChange],
);
const chartSettingsRawSeries = useMemo(
() => assocIn(series, [0, "card", "visualization_settings"], chartSettings),
[chartSettings, series],
);
const transformedSeries = useMemo(() => {
const { series: transformedSeries } = getVisualizationTransformed(
extractRemappings(chartSettingsRawSeries),
);
return transformedSeries;
}, [chartSettingsRawSeries]);
const widgets = useMemo(
() =>
propWidgets ||
getSettingsWidgetsForSeries(
transformedSeries,
handleChangeSettings,
isDashboard,
{ dashboardId: dashboard?.id },
),
[
propWidgets,
transformedSeries,
handleChangeSettings,
isDashboard,
dashboard?.id,
],
);
const columnHasSettings = useCallback(
(col: DatasetColumn) => {
......@@ -176,7 +104,7 @@ export const ChartSettings = ({
return null;
}
const singleSeriesForColumn = transformedSeries.find(single => {
const singleSeriesForColumn = transformedSeries?.find(single => {
const metricColumn = single.data.cols[1];
if (metricColumn) {
return (
......@@ -230,32 +158,15 @@ export const ChartSettings = ({
setCurrentWidget(null);
}, []);
const handleResetSettings = useCallback(() => {
const originalCardSettings = dashcard?.card.visualization_settings;
const clickBehaviorSettings = getClickBehaviorSettings(chartSettings);
onChange?.({
...originalCardSettings,
...clickBehaviorSettings,
});
}, [chartSettings, dashcard?.card.visualization_settings, onChange]);
const handleChangeSeriesColor = useCallback(
(seriesKey: string, color: string) => {
onChange?.(updateSeriesColor(chartSettings, seriesKey, color));
if (chartSettings) {
onChange?.(updateSeriesColor(chartSettings, seriesKey, color));
}
},
[chartSettings, onChange],
);
const handleDone = useCallback(() => {
onDone?.(chartSettings);
onClose?.();
}, [chartSettings, onClose, onDone]);
const handleCancel = useCallback(() => {
onClose?.();
}, [onClose]);
const sections: Record<string, Widget[]> = useMemo(() => {
const sectionObj: Record<string, Widget[]> = {};
for (const widget of widgets) {
......@@ -316,12 +227,6 @@ export const ChartSettings = ({
onChangeSeriesColor: handleChangeSeriesColor,
};
const onResetToDefault =
// resetting virtual cards wipes the text and broke the UI (metabase#14644)
!_.isEqual(chartSettings, {}) && (chartSettings || {}).virtual_card == null
? handleResetSettings
: null;
const showSectionPicker =
// don't show section tabs for a single section
sectionNames.length > 1 &&
......@@ -334,7 +239,7 @@ export const ChartSettings = ({
);
return (
<ChartSettingsRoot className={className}>
<>
<ChartSettingsMenu data-testid="chartsettings-sidebar">
{showSectionPicker && (
<SectionContainer isDashboard={false}>
......@@ -356,31 +261,6 @@ export const ChartSettings = ({
/>
</ChartSettingsListContainer>
</ChartSettingsMenu>
{!noPreview && (
<ChartSettingsPreview>
<SectionWarnings warnings={warnings} size={20} />
<ChartSettingsVisualizationContainer>
<Visualization
className={CS.spread}
rawSeries={chartSettingsRawSeries}
showTitle
isEditing
isDashboard
dashboard={dashboard}
dashcard={dashcard}
isSettings
showWarnings
onUpdateVisualizationSettings={handleChangeSettings}
onUpdateWarnings={setWarnings}
/>
</ChartSettingsVisualizationContainer>
<ChartSettingsFooter
onDone={handleDone}
onCancel={handleCancel}
onReset={onResetToDefault}
/>
</ChartSettingsPreview>
)}
<ChartSettingsWidgetPopover
anchor={popoverRef as HTMLElement}
widgets={[styleWidget, formattingWidget].filter(
......@@ -388,28 +268,6 @@ export const ChartSettings = ({
)}
handleEndShowWidget={handleEndShowWidget}
/>
</ChartSettingsRoot>
);
};
export const ChartSettingsWithState = (props: ChartSettingsWithStateProps) => {
const [tempSettings, setTempSettings] = useState(props.settings);
useEffect(() => {
if (props.settings) {
setTempSettings(props.settings);
}
}, [props.settings]);
const onDone = (settings: VisualizationSettings) =>
props.onChange?.(settings ?? tempSettings);
return (
<ChartSettings
{...props}
onChange={setTempSettings}
onDone={onDone}
settings={tempSettings}
/>
</>
);
};
import userEvent from "@testing-library/user-event";
import { fireEvent, renderWithProviders, screen } from "__support__/ui";
import registerVisualizations from "metabase/visualizations/register";
import {
createMockCard,
createMockDashboardCard,
createMockDataset,
createMockVisualizationSettings,
} from "metabase-types/api/mocks";
import { createMockCard, createMockDataset } from "metabase-types/api/mocks";
import type { Widget } from "../types";
import { ChartSettings } from "./ChartSettings";
import type { ChartSettingsProps, Widget } from "./types";
import { BaseChartSettings } from "./BaseChartSettings";
import type { BaseChartSettingsProps } from "./types";
registerVisualizations();
const DEFAULT_PROPS = {
widgets: [],
series: [
{
card: createMockCard({ visualization_settings: {} }),
......@@ -35,10 +31,12 @@ function widget(widget: Partial<Widget> = {}): Widget {
};
}
type SetupOpts = Partial<ChartSettingsProps>;
type SetupOpts = Partial<BaseChartSettingsProps>;
const setup = (props: SetupOpts) => {
return renderWithProviders(<ChartSettings {...DEFAULT_PROPS} {...props} />);
return renderWithProviders(
<BaseChartSettings {...DEFAULT_PROPS} {...props} />,
);
};
describe("ChartSettings", () => {
......@@ -134,42 +132,4 @@ describe("ChartSettings", () => {
expect(screen.queryByText("Foo")).not.toBeInTheDocument();
});
it("reset settings should revert to the original card settings with click behavior", async () => {
const onChange = jest.fn();
const originalVizSettings = createMockVisualizationSettings({
"graph.goal_value": 100,
"graph.show_goal": true,
"graph.goal_label": "foo",
});
const modifiedSettings = createMockVisualizationSettings({
"graph.show_goal": false,
"graph.goal_label": "bar",
click_behavior: {
type: "link",
linkType: "url",
},
});
setup({
dashcard: createMockDashboardCard({
card: createMockCard({ visualization_settings: originalVizSettings }),
}),
settings: modifiedSettings,
widgets: [],
onChange,
});
await userEvent.click(screen.getByText("Reset to defaults"));
expect(onChange).toHaveBeenCalledWith({
...originalVizSettings,
click_behavior: {
type: "link",
linkType: "url",
},
});
});
});
export * from "./BaseChartSettings";
import type { ComputedVisualizationSettings } from "metabase/visualizations/types";
import type Question from "metabase-lib/v1/Question";
import type { QueryBuilderUIControls } from "metabase-types/store";
import type { UseChartSettingsStateReturned } from "../hooks";
import type { CommonChartSettingsProps, Widget } from "../types";
export type BaseChartSettingsProps = {
initial?: QueryBuilderUIControls["initialChartSetting"];
computedSettings?: ComputedVisualizationSettings;
question?: Question;
widgets: Widget[];
} & CommonChartSettingsProps &
Pick<UseChartSettingsStateReturned, "chartSettings" | "transformedSeries">;
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import Button from "metabase/core/components/Button";
import Radio from "metabase/core/components/Radio";
import { color } from "metabase/lib/colors";
import Warnings from "metabase/query_builder/components/Warnings";
interface SectionContainerProps {
isDashboard: boolean;
}
export const SectionContainer = styled.div<SectionContainerProps>`
${({ isDashboard }) =>
isDashboard &&
css`
margin-top: 1rem;
`}
width: 100%;
${Radio.RadioGroupVariants.join(", ")} {
border-bottom: 1px solid var(--mb-color-border);
}
${Radio.RadioContainerVariants.join(", ")} {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
${Radio.RadioLabelVariants.join(", ")} {
flex-grow: 1;
margin-right: 0;
display: flex;
justify-content: center;
&:not(:last-child) {
margin-right: 0;
}
}
`;
export const SectionWarnings = styled(Warnings)`
color: ${() => color("accent4")};
position: absolute;
top: 2rem;
right: 2rem;
z-index: 2;
`;
export const ChartSettingsRoot = styled.div`
display: flex;
flex-grow: 1;
height: 100%;
`;
export const ChartSettingsMenu = styled.div`
flex: 1 0 0;
display: flex;
flex-direction: column;
overflow-y: auto;
`;
export const ChartSettingsListContainer = styled.div`
position: relative;
padding: 1.5rem 0;
`;
export const ChartSettingsPreview = styled.div`
flex: 2 0 0;
display: flex;
flex-direction: column;
border-left: 1px solid var(--mb-color-border);
padding-top: 1.5rem;
`;
export const ChartSettingsVisualizationContainer = styled.div`
position: relative;
margin: 0 2rem;
flex-grow: 1;
`;
export const ChartSettingsFooterRoot = styled.div`
display: flex;
justify-content: end;
padding: 1rem 2rem;
${Button} {
margin-left: 1rem;
}
`;
import styled from "@emotion/styled";
import Button from "metabase/core/components/Button";
export const ChartSettingsFooterRoot = styled.div`
display: flex;
justify-content: end;
padding: 1rem 2rem;
${Button} {
margin-left: 1rem;
}
`;
......@@ -2,9 +2,9 @@ import { t } from "ttag";
import Button from "metabase/core/components/Button";
import { ChartSettingsFooterRoot } from "./ChartSettings.styled";
import { ChartSettingsFooterRoot } from "./ChartSettingsFooter.styled";
type ChartSettingsFooterProps = {
export type ChartSettingsFooterProps = {
onDone: () => void;
onCancel: () => void;
onReset: (() => void) | null;
......
export * from "./ChartSettingsFooter";
import styled from "@emotion/styled";
import { color } from "metabase/lib/colors";
import Warnings from "metabase/query_builder/components/Warnings";
export const SectionWarnings = styled(Warnings)`
color: ${() => color("accent4")};
position: absolute;
top: 2rem;
right: 2rem;
z-index: 2;
`;
export const ChartSettingsPreview = styled.div`
flex: 2 0 0;
display: flex;
flex-direction: column;
border-left: 1px solid var(--mb-color-border);
padding-top: 1.5rem;
`;
export const ChartSettingsVisualizationContainer = styled.div`
position: relative;
margin: 0 2rem;
flex-grow: 1;
`;
import { useState } from "react";
import CS from "metabase/css/core/index.css";
import Visualization from "metabase/visualizations/components/Visualization";
import { ChartSettingsFooter } from "../ChartSettingsFooter";
import {
ChartSettingsPreview,
ChartSettingsVisualizationContainer,
SectionWarnings,
} from "./ChartSettingsVisualization.styled";
import type { ChartSettingsVisualizationProps } from "./types";
export const ChartSettingsVisualization = ({
dashboard,
dashcard,
onCancel,
onDone,
onReset,
onUpdateVisualizationSettings,
rawSeries,
}: ChartSettingsVisualizationProps) => {
const [warnings, setWarnings] = useState<string[]>();
return (
<ChartSettingsPreview>
<SectionWarnings warnings={warnings} size={20} />
<ChartSettingsVisualizationContainer>
<Visualization
className={CS.spread}
rawSeries={rawSeries}
showTitle
isEditing
isDashboard
dashboard={dashboard}
dashcard={dashcard}
isSettings
showWarnings
onUpdateVisualizationSettings={onUpdateVisualizationSettings}
onUpdateWarnings={setWarnings}
/>
</ChartSettingsVisualizationContainer>
<ChartSettingsFooter
onDone={onDone}
onCancel={onCancel}
onReset={onReset}
/>
</ChartSettingsPreview>
);
};
export * from "./ChartSettingsVisualization";
import type Question from "metabase-lib/v1/Question";
import type {
Dashboard,
DashboardCard,
RawSeries,
VisualizationSettings,
} from "metabase-types/api";
import type { ChartSettingsFooterProps } from "../ChartSettingsFooter";
export type ChartSettingsVisualizationProps = {
rawSeries: RawSeries;
dashboard?: Dashboard;
dashcard?: DashboardCard;
onUpdateVisualizationSettings: (
changedSettings: VisualizationSettings,
question: Question,
) => void;
} & ChartSettingsFooterProps;
import { useCallback, useMemo, useState } from "react";
import _ from "underscore";
import CS from "metabase/css/core/index.css";
import { getClickBehaviorSettings } from "metabase/visualizations/lib/settings";
import { getSettingsWidgetsForSeries } from "metabase/visualizations/lib/settings/visualization";
import type { VisualizationSettings } from "metabase-types/api";
import { BaseChartSettings } from "../BaseChartSettings";
import { ChartSettingsRoot } from "../ChartSettings.styled";
import { ChartSettingsVisualization } from "../ChartSettingsVisualization";
import { useChartSettingsState } from "../hooks";
import type { DashboardChartSettingsProps } from "./types";
export const DashboardChartSettings = ({
dashboard,
dashcard,
onChange,
series,
onClose,
widgets: propWidgets,
settings,
}: DashboardChartSettingsProps) => {
const [tempSettings, setTempSettings] = useState<
VisualizationSettings | undefined
>(settings);
const {
chartSettings,
handleChangeSettings,
chartSettingsRawSeries,
transformedSeries,
} = useChartSettingsState({
series,
settings: tempSettings,
onChange: setTempSettings,
});
const handleDone = useCallback(() => {
onChange?.(chartSettings ?? tempSettings ?? {});
onClose?.();
}, [chartSettings, onChange, onClose, tempSettings]);
const handleResetSettings = useCallback(() => {
const originalCardSettings = dashcard?.card.visualization_settings;
const clickBehaviorSettings = getClickBehaviorSettings(chartSettings);
onChange?.({
...originalCardSettings,
...clickBehaviorSettings,
});
}, [chartSettings, dashcard?.card.visualization_settings, onChange]);
const onResetToDefault =
// resetting virtual cards wipes the text and broke the UI (metabase#14644)
!_.isEqual(chartSettings, {}) && (chartSettings || {}).virtual_card == null
? handleResetSettings
: null;
const widgets = useMemo(
() =>
propWidgets ||
getSettingsWidgetsForSeries(
transformedSeries,
handleChangeSettings,
true,
{ dashboardId: dashboard?.id },
),
[propWidgets, transformedSeries, handleChangeSettings, dashboard?.id],
);
return (
<ChartSettingsRoot className={CS.spread}>
<BaseChartSettings
series={series}
onChange={setTempSettings}
chartSettings={chartSettings}
widgets={widgets}
transformedSeries={transformedSeries}
/>
<ChartSettingsVisualization
rawSeries={chartSettingsRawSeries}
dashboard={dashboard}
dashcard={dashcard}
onUpdateVisualizationSettings={handleChangeSettings}
onDone={handleDone}
onCancel={() => onClose?.()}
onReset={onResetToDefault}
/>
</ChartSettingsRoot>
);
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment