Skip to content
Snippets Groups Projects
Unverified Commit 836bb7d0 authored by Oisin Coveney's avatar Oisin Coveney Committed by GitHub
Browse files

Split ChartType components into files (#47737)

parent da106c72
Branches
Tags
No related merge requests found
Showing
with 560 additions and 260 deletions
......@@ -210,11 +210,11 @@ describe("scenarios > visualizations > maps", () => {
cy.findByText("Visualization").click();
// Ensure the Map visualization is sensible
cy.findByTestId("Map-button").should(
"have.attr",
"data-is-sensible",
"true",
);
cy.findByTestId("display-options-sensible").as("sensibleOptions");
cy.get("@sensibleOptions").within(() => {
cy.findByTestId("Map-button").should("be.visible");
});
});
it("should apply brush filters by dragging map", () => {
......
......@@ -14,13 +14,11 @@ import { useValidatedEntityId } from "metabase/lib/entity-id/hooks/use-validated
import type { GenericErrorResponse } from "metabase/lib/errors";
import { getResponseErrorMessage } from "metabase/lib/errors";
import { useSelector } from "metabase/lib/redux";
import {
onCloseChartType,
onOpenChartSettings,
setUIControls,
} from "metabase/query_builder/actions";
import QueryVisualization from "metabase/query_builder/components/QueryVisualization";
import ChartTypeSidebar from "metabase/query_builder/components/view/sidebars/ChartTypeSidebar";
import {
ChartTypeSettings,
useChartTypeVisualizations,
} from "metabase/query_builder/components/chart-type-selector";
import { getMetadata } from "metabase/selectors/metadata";
import { Box, Group } from "metabase/ui";
import { PublicMode } from "metabase/visualizations/click-actions/modes/PublicMode";
......@@ -41,6 +39,40 @@ type State = {
error: GenericErrorResponse | null;
};
type StaticQuestionVisualizationSelectorProps = {
question: Question;
result: Dataset | null;
onUpdateQuestion: (question: Question) => void;
};
const StaticQuestionVisualizationSelector = ({
question,
result,
onUpdateQuestion,
}: StaticQuestionVisualizationSelectorProps) => {
const {
selectedVisualization,
updateQuestionVisualization,
sensibleVisualizations,
nonSensibleVisualizations,
} = useChartTypeVisualizations({
question,
result,
onUpdateQuestion,
});
return (
<Box w="355px">
<ChartTypeSettings
selectedVisualization={selectedVisualization}
onSelectVisualization={updateQuestionVisualization}
sensibleVisualizations={sensibleVisualizations}
nonSensibleVisualizations={nonSensibleVisualizations}
/>
</Box>
);
};
const StaticQuestionInner = ({
questionId: initId,
showVisualizationSelector,
......@@ -130,10 +162,6 @@ const StaticQuestionInner = ({
const question = new Question(card, metadata);
const defaultHeight = card ? getDefaultVizHeight(card.display) : undefined;
const legacyQuery = question.legacyQuery({
useStructuredQuery: true,
});
return (
<Box
className={cx(CS.flexFull, CS.fullWidth)}
......@@ -142,17 +170,11 @@ const StaticQuestionInner = ({
>
<Group h="100%" pos="relative" align="flex-start">
{showVisualizationSelector && (
<Box w="355px">
<ChartTypeSidebar
question={question}
result={result}
onOpenChartSettings={onOpenChartSettings}
onCloseChartType={onCloseChartType}
query={legacyQuery}
setUIControls={setUIControls}
updateQuestion={changeVisualization}
/>
</Box>
<StaticQuestionVisualizationSelector
question={question}
result={result}
onUpdateQuestion={changeVisualization}
/>
)}
<QueryVisualization
className={cx(CS.flexFull, CS.fullWidth, CS.fullHeight)}
......
......@@ -133,19 +133,19 @@ describe("StaticQuestion", () => {
it("should render a visualization selector if showVisualizationSelector is true", async () => {
setup({ showVisualizationSelector: true });
await waitForLoaderToBeRemoved();
expect(screen.getByTestId("chart-type-sidebar")).toBeInTheDocument();
expect(screen.getByTestId("chart-type-settings")).toBeInTheDocument();
});
it("should not render a visualization selector if showVisualizationSelector is false", async () => {
setup();
await waitForLoaderToBeRemoved();
expect(screen.queryByTestId("chart-type-sidebar")).not.toBeInTheDocument();
expect(screen.queryByTestId("chart-type-settings")).not.toBeInTheDocument();
});
it("should change the visualization if a different visualization is selected", async () => {
setup({ showVisualizationSelector: true });
await waitForLoaderToBeRemoved();
expect(screen.getByTestId("chart-type-sidebar")).toBeInTheDocument();
expect(screen.getByTestId("chart-type-settings")).toBeInTheDocument();
for (const visType of Object.keys(VISUALIZATION_TYPES)) {
await userEvent.click(
......
import styled from "@emotion/styled";
export const OptionList = styled.div`
display: flex;
margin: 1rem 1rem 3rem 1rem;
flex-wrap: wrap;
`;
import { ChartTypeOption, type ChartTypeOptionProps } from "../ChartTypeOption";
import { OptionList } from "./ChartTypeList.styled";
export type ChartTypeListProps = {
visualizationList: ChartTypeOptionProps["visualizationType"][];
"data-testid"?: string;
} & Pick<
ChartTypeOptionProps,
"selectedVisualization" | "onSelectVisualization"
>;
export const ChartTypeList = ({
visualizationList,
onSelectVisualization,
selectedVisualization,
"data-testid": dataTestId,
}: ChartTypeListProps) => (
<OptionList data-testid={dataTestId}>
{visualizationList.map(type => (
<ChartTypeOption
key={type}
visualizationType={type}
selectedVisualization={selectedVisualization}
onSelectVisualization={onSelectVisualization}
/>
))}
</OptionList>
);
import { renderWithProviders, screen, within } from "__support__/ui";
import { checkNotNull } from "metabase/lib/types";
import visualizations from "metabase/visualizations";
import registerVisualizations from "metabase/visualizations/register";
import { DEFAULT_VIZ_ORDER } from "../viz-order";
import { ChartTypeList, type ChartTypeListProps } from "./ChartTypeList";
registerVisualizations();
const VISUALIZATION_TEST_LABELS = DEFAULT_VIZ_ORDER.map(
display => visualizations.get(display)?.uiName,
);
const setup = ({
onSelectVisualization = jest.fn(),
selectedVisualization = "table",
visualizationList = DEFAULT_VIZ_ORDER,
}: Partial<ChartTypeListProps> = {}) => {
renderWithProviders(
<ChartTypeList
visualizationList={visualizationList}
selectedVisualization={selectedVisualization}
onSelectVisualization={onSelectVisualization}
/>,
);
};
describe("ChartTypeList", () => {
it("should display a given list of visualizations", () => {
setup();
screen.getAllByRole("option").map((element, index) => {
const withinElement = within(element);
expect(
withinElement.getByText(checkNotNull(VISUALIZATION_TEST_LABELS[index])),
).toBeInTheDocument();
expect(
withinElement.getByTestId(`${VISUALIZATION_TEST_LABELS[index]}-button`),
).toBeInTheDocument();
});
});
});
export * from "./ChartTypeList";
......@@ -82,17 +82,3 @@ export const OptionIconContainer = styled.div<OptionIconContainerProps>`
}
}
`;
export const OptionLabel = styled.h4`
color: var(--mb-color-text-medium);
font-weight: bold;
font-size: 0.75rem;
text-transform: uppercase;
margin: 1rem 0 1rem 1.5rem;
`;
export const OptionList = styled.div`
display: flex;
margin: 1rem 1rem 3rem 1rem;
flex-wrap: wrap;
`;
import { checkNotNull } from "metabase/lib/types";
import { Icon } from "metabase/ui";
import visualizations from "metabase/visualizations";
import type { CardDisplayType } from "metabase-types/api";
import {
OptionIconContainer,
OptionRoot,
OptionText,
SettingsButton,
} from "./ChartTypeOption.styled";
export type ChartTypeOptionProps = {
onSelectVisualization: (display: CardDisplayType) => void;
visualizationType: CardDisplayType;
selectedVisualization: CardDisplayType;
};
export const ChartTypeOption = ({
visualizationType,
selectedVisualization,
onSelectVisualization,
}: ChartTypeOptionProps) => {
const visualization = checkNotNull(visualizations.get(visualizationType));
const isSelected = selectedVisualization === visualizationType;
return (
<OptionRoot
isSelected={isSelected}
data-testid={`${visualization.uiName}-container`}
role="option"
aria-selected={isSelected}
>
<OptionIconContainer
onClick={() => onSelectVisualization(visualizationType)}
data-testid={`${visualization.uiName}-button`}
>
<Icon name={visualization.iconName} size={20} />
{isSelected && (
<SettingsButton
onlyIcon
icon="gear"
iconSize={16}
onClick={() => onSelectVisualization(visualizationType)}
/>
)}
</OptionIconContainer>
<OptionText data-testid="chart-type-option-label">
{visualization.uiName}
</OptionText>
</OptionRoot>
);
};
import userEvent from "@testing-library/user-event";
import {
getIcon,
queryIcon,
renderWithProviders,
screen,
} from "__support__/ui";
import type { IconName } from "metabase/ui";
import registerVisualizations from "metabase/visualizations/register";
import type { CardDisplayType } from "metabase-types/api";
import { ChartTypeOption, type ChartTypeOptionProps } from "./ChartTypeOption";
registerVisualizations();
const EXPECTED_VISUALIZATION_VALUES: Record<
CardDisplayType,
{
key: CardDisplayType;
displayName: string;
iconName: IconName | (string & unknown);
}
> = {
scalar: { key: "scalar", displayName: "Number", iconName: "number" },
smartscalar: {
key: "smartscalar",
displayName: "Trend",
iconName: "smartscalar",
},
progress: { key: "progress", displayName: "Progress", iconName: "progress" },
gauge: { key: "gauge", displayName: "Gauge", iconName: "gauge" },
table: { key: "table", displayName: "Table", iconName: "table2" },
line: { key: "line", displayName: "Line", iconName: "line" },
area: { key: "area", displayName: "Area", iconName: "area" },
bar: { key: "bar", displayName: "Bar", iconName: "bar" },
waterfall: {
key: "waterfall",
displayName: "Waterfall",
iconName: "waterfall",
},
combo: { key: "combo", displayName: "Combo", iconName: "lineandbar" },
row: { key: "row", displayName: "Row", iconName: "horizontal_bar" },
scatter: { key: "scatter", displayName: "Scatter", iconName: "bubble" },
pie: { key: "pie", displayName: "Pie", iconName: "pie" },
map: { key: "map", displayName: "Map", iconName: "pinmap" },
funnel: { key: "funnel", displayName: "Funnel", iconName: "funnel" },
object: { key: "object", displayName: "Detail", iconName: "document" },
pivot: { key: "pivot", displayName: "Pivot Table", iconName: "pivot_table" },
};
const setup = ({
selectedVisualization = "table",
onSelectVisualization = jest.fn(),
visualizationType = "bar",
}: Partial<ChartTypeOptionProps> = {}) => {
renderWithProviders(
<ChartTypeOption
selectedVisualization={selectedVisualization}
onSelectVisualization={onSelectVisualization}
visualizationType={visualizationType}
/>,
);
return { onSelectVisualization };
};
describe("ChartTypeOption", () => {
Object.entries(EXPECTED_VISUALIZATION_VALUES).forEach(
([key, { iconName, displayName }]) => {
it(`should display a label and icon for ${key} visualization type`, () => {
setup({
visualizationType: key as CardDisplayType,
});
expect(
screen.getByTestId(`${displayName}-container`),
).toBeInTheDocument();
expect(getIcon(iconName)).toBeInTheDocument();
expect(screen.getByTestId("chart-type-option-label")).toHaveTextContent(
displayName,
);
});
it("should call 'onClick' when the button is clicked", async () => {
const { onSelectVisualization } = setup({
visualizationType: key as CardDisplayType,
});
await userEvent.click(screen.getByTestId(`${displayName}-button`));
expect(onSelectVisualization).toHaveBeenCalledWith(key);
});
// TODO: include styles in the tests when component is migrated to Mantine
it("should have aria-selected attribute if selectedVisualization=visualizationType", () => {
setup({
selectedVisualization: key as CardDisplayType,
visualizationType: key as CardDisplayType,
});
expect(screen.getByTestId(`${displayName}-container`)).toHaveAttribute(
"aria-selected",
"true",
);
});
},
);
it("should display a gear icon when hovering display type when selected", async () => {
const { onSelectVisualization } = setup({
visualizationType: "bar",
selectedVisualization: "bar",
});
expect(screen.getByTestId("Bar-button")).toBeInTheDocument();
await userEvent.hover(screen.getByTestId("Bar-button"));
await userEvent.click(getIcon("gear"));
expect(onSelectVisualization).toHaveBeenCalled();
});
it("should not display a gear icon when hovering display type when not selected", async () => {
setup({
visualizationType: "bar",
selectedVisualization: "table",
});
expect(screen.getByTestId("Bar-button")).toBeInTheDocument();
await userEvent.hover(screen.getByTestId("Bar-button"));
expect(queryIcon("gear")).not.toBeInTheDocument();
});
});
export * from "./ChartTypeOption";
import styled from "@emotion/styled";
export const OptionLabel = styled.h4`
color: var(--mb-color-text-medium);
font-weight: bold;
font-size: 0.75rem;
text-transform: uppercase;
margin: 1rem 0 1rem 1.5rem;
`;
import { t } from "ttag";
import { Box } from "metabase/ui";
import { ChartTypeList, type ChartTypeListProps } from "../ChartTypeList";
import { OptionLabel } from "./ChartTypeSettings.styled";
export type ChartTypeSettingsProps = {
sensibleVisualizations: ChartTypeListProps["visualizationList"];
nonSensibleVisualizations: ChartTypeListProps["visualizationList"];
} & Pick<ChartTypeListProps, "selectedVisualization" | "onSelectVisualization">;
export const ChartTypeSettings = ({
selectedVisualization,
onSelectVisualization,
sensibleVisualizations,
nonSensibleVisualizations,
}: ChartTypeSettingsProps) => (
<Box display="contents" data-testid="chart-type-settings">
<ChartTypeList
data-testid="display-options-sensible"
visualizationList={sensibleVisualizations}
onSelectVisualization={onSelectVisualization}
selectedVisualization={selectedVisualization}
/>
<OptionLabel>{t`Other charts`}</OptionLabel>
<ChartTypeList
data-testid="display-options-not-sensible"
visualizationList={nonSensibleVisualizations}
onSelectVisualization={onSelectVisualization}
selectedVisualization={selectedVisualization}
/>
</Box>
);
import userEvent from "@testing-library/user-event";
import { getIcon, render, screen } from "__support__/ui";
import registerVisualizations from "metabase/visualizations/register";
import {
ChartTypeSettings,
type ChartTypeSettingsProps,
} from "./ChartTypeSettings";
registerVisualizations();
const setup = ({
selectedVisualization = "bar",
}: Partial<ChartTypeSettingsProps> = {}) => {
const onSelectVisualization = jest.fn();
const sensibleVisualizations: ChartTypeSettingsProps["sensibleVisualizations"] =
["bar", "line"];
const nonSensibleVisualizations: ChartTypeSettingsProps["nonSensibleVisualizations"] =
["pie", "scatter"];
render(
<ChartTypeSettings
sensibleVisualizations={sensibleVisualizations}
nonSensibleVisualizations={nonSensibleVisualizations}
selectedVisualization={selectedVisualization}
onSelectVisualization={onSelectVisualization}
/>,
);
return {
onSelectVisualization,
};
};
describe("ChartTypeSettings", () => {
it("renders the sensible and non-sensible visualizations with an `Other charts` label", () => {
setup();
expect(screen.getByTestId("display-options-sensible")).toBeInTheDocument();
expect(screen.getByText("Other charts")).toBeInTheDocument();
expect(
screen.getByTestId("display-options-not-sensible"),
).toBeInTheDocument();
});
it("passes correct props to ChartTypeLists", () => {
setup();
const sensibleList = screen.getByTestId("display-options-sensible");
const nonSensibleList = screen.getByTestId("display-options-not-sensible");
expect(sensibleList).toHaveAttribute(
"data-testid",
"display-options-sensible",
);
expect(nonSensibleList).toHaveAttribute(
"data-testid",
"display-options-not-sensible",
);
});
it("calls onSelectVisualization when a sensible visualization is selected", async () => {
const { onSelectVisualization } = setup();
await userEvent.click(getIcon("bar"));
expect(onSelectVisualization).toHaveBeenCalledWith("bar");
});
it("calls onSelectVisualization when a non-sensible visualization is selected", async () => {
const { onSelectVisualization } = setup();
await userEvent.click(getIcon("pie"));
expect(onSelectVisualization).toHaveBeenCalledWith("pie");
});
});
export * from "./ChartTypeSettings";
export * from "./ChartTypeList";
export * from "./ChartTypeOption";
export * from "./ChartTypeSettings";
export * from "./use-chart-type-visualizations";
export * from "./viz-order";
import { useCallback, useMemo } from "react";
import _ from "underscore";
import visualizations from "metabase/visualizations";
import { sanatizeResultData } from "metabase/visualizations/shared/utils/data";
import type Question from "metabase-lib/v1/Question";
import {
type CardDisplayType,
type Dataset,
type VisualizationDisplay,
isCardDisplayType,
} from "metabase-types/api";
import { DEFAULT_VIZ_ORDER } from "./viz-order";
export type UseChartTypeVisualizationsProps = {
question: Question;
onUpdateQuestion: (question: Question) => void;
};
export const useChartTypeVisualizations = ({
question,
onUpdateQuestion,
result,
}: UseChartTypeVisualizationsProps & GetSensibleVisualizationsProps) => {
const selectedVisualization = question.display();
const updateQuestionVisualization = useCallback(
(display: CardDisplayType) => {
let newQuestion = question.setDisplay(display).lockDisplay(); // prevent viz auto-selection
const visualization = visualizations.get(display);
if (visualization?.onDisplayUpdate) {
const updatedSettings = visualization.onDisplayUpdate(
newQuestion.settings(),
);
newQuestion = newQuestion.setSettings(updatedSettings);
}
onUpdateQuestion(newQuestion);
},
[onUpdateQuestion, question],
);
const { sensibleVisualizations, nonSensibleVisualizations } = useMemo(
() => getSensibleVisualizations({ result }),
[result],
);
return {
selectedVisualization,
updateQuestionVisualization,
sensibleVisualizations,
nonSensibleVisualizations,
};
};
type IsSensibleVisualizationProps = {
result: Dataset | null;
vizType: VisualizationDisplay;
};
const isSensibleVisualization = ({
result,
vizType,
}: IsSensibleVisualizationProps) => {
const visualization = visualizations.get(vizType);
return (
(result?.data &&
visualization?.isSensible?.(sanatizeResultData(result.data))) ||
false
);
};
export type GetSensibleVisualizationsProps = {
result: Dataset | null;
};
export const getSensibleVisualizations = ({
result,
}: GetSensibleVisualizationsProps) => {
const availableVizTypes = Array.from(visualizations.entries())
.filter(([_, config]) => !config.hidden)
.map(([vizType]) => vizType)
.filter(isCardDisplayType);
const orderedVizTypes = _.union(DEFAULT_VIZ_ORDER, availableVizTypes);
const [sensibleVisualizations, nonSensibleVisualizations] = _.partition(
orderedVizTypes,
vizType => isSensibleVisualization({ result, vizType }),
);
return { sensibleVisualizations, nonSensibleVisualizations };
};
import type { CardDisplayType } from "metabase-types/api";
export const DEFAULT_VIZ_ORDER: CardDisplayType[] = [
"table",
"bar",
"line",
"pie",
"scalar",
"row",
"area",
"combo",
"pivot",
"smartscalar",
"gauge",
"progress",
"funnel",
"object",
"map",
"scatter",
"waterfall",
] as const;
......@@ -46,7 +46,7 @@ import {
import { ViewFooter } from "./ViewFooter";
import ViewSidebar from "./ViewSidebar";
import ChartSettingsSidebar from "./sidebars/ChartSettingsSidebar";
import ChartTypeSidebar from "./sidebars/ChartTypeSidebar";
import { ChartTypeSidebar } from "./sidebars/ChartTypeSidebar";
import { QuestionInfoSidebar } from "./sidebars/QuestionInfoSidebar";
import { SummarizeSidebar } from "./sidebars/SummarizeSidebar";
import TimelineSidebar from "./sidebars/TimelineSidebar";
......@@ -60,10 +60,11 @@ const fadeIn = {
class View extends Component {
getLeftSidebar = () => {
const {
question,
result,
isShowingChartSettingsSidebar,
isShowingChartTypeSidebar,
onCloseChartSettings,
onCloseChartType,
} = this.props;
if (isShowingChartSettingsSidebar) {
......@@ -93,7 +94,7 @@ class View extends Component {
}
if (isShowingChartTypeSidebar) {
return <ChartTypeSidebar {...this.props} onClose={onCloseChartType} />;
return <ChartTypeSidebar question={question} result={result} />;
}
return null;
......
import cx from "classnames";
import type * as React from "react";
import { useCallback, useMemo } from "react";
import { t } from "ttag";
import _ from "underscore";
import CS from "metabase/css/core/index.css";
import type { UpdateQuestionOpts } from "metabase/query_builder/actions";
import SidebarContent from "metabase/query_builder/components/SidebarContent";
import { Icon } from "metabase/ui";
import visualizations from "metabase/visualizations";
import { sanatizeResultData } from "metabase/visualizations/shared/utils/data";
import type { Visualization } from "metabase/visualizations/types";
import * as Lib from "metabase-lib";
import type Question from "metabase-lib/v1/Question";
import type Query from "metabase-lib/v1/queries/Query";
import { type CardDisplayType, isCardDisplayType } from "metabase-types/api";
import {
OptionIconContainer,
OptionLabel,
OptionList,
OptionRoot,
OptionText,
SettingsButton,
} from "./ChartTypeSidebar.styled";
const DEFAULT_ORDER: CardDisplayType[] = [
"table",
"bar",
"line",
"pie",
"scalar",
"row",
"area",
"combo",
"pivot",
"smartscalar",
"gauge",
"progress",
"funnel",
"object",
"map",
"scatter",
"waterfall",
] as const;
interface ChartTypeSidebarProps {
question: Question;
result: any;
onOpenChartSettings: (props: {
initialChartSettings: { section: string };
showSidebarTitle: boolean;
}) => void;
onCloseChartType: () => void;
updateQuestion: (question: Question, props: UpdateQuestionOpts) => void;
setUIControls: (props: { isShowingRawTable: boolean }) => void;
query: Query;
}
const ChartTypeSidebar = ({
question,
result,
onOpenChartSettings,
onCloseChartType,
updateQuestion,
setUIControls,
query,
}: ChartTypeSidebarProps) => {
const [makesSense, nonSense]: [CardDisplayType[], CardDisplayType[]] =
useMemo(
() =>
_.partition(
_.union(
DEFAULT_ORDER,
Array.from(visualizations)
.map(([vizType]) => vizType)
.filter(isCardDisplayType),
).filter(vizType => !visualizations?.get(vizType)?.hidden),
vizType => {
const visualization = visualizations.get(vizType);
return (
result &&
result.data &&
visualization?.isSensible &&
visualization?.isSensible(sanatizeResultData(result.data), query)
);
},
),
[result, query],
);
const openChartSettings = useCallback(
(e: React.MouseEvent) => {
e.stopPropagation();
onOpenChartSettings({
initialChartSettings: { section: t`Data` },
showSidebarTitle: true,
});
},
[onOpenChartSettings],
);
const handleClick = useCallback(
(display: CardDisplayType, e: React.MouseEvent) => {
if (display === question.display()) {
openChartSettings(e);
} else {
let newQuestion = question.setDisplay(display).lockDisplay(); // prevent viz auto-selection
const visualization = visualizations.get(display);
if (visualization?.onDisplayUpdate) {
const updatedSettings = visualization.onDisplayUpdate(
newQuestion.settings(),
);
newQuestion = newQuestion.setSettings(updatedSettings);
}
updateQuestion(newQuestion, {
shouldUpdateUrl: Lib.queryDisplayInfo(question.query()).isEditable,
});
setUIControls({ isShowingRawTable: false });
}
},
[question, updateQuestion, setUIControls, openChartSettings],
);
return (
<SidebarContent
className={cx(CS.fullHeight, CS.px1)}
onDone={() => onCloseChartType()}
data-testid="chart-type-sidebar"
>
<OptionList data-testid="display-options-sensible">
{makesSense.map(type => {
const visualization = visualizations.get(type);
return (
visualization && (
<ChartTypeOption
key={type}
visualization={visualization}
isSelected={type === question.display()}
isSensible
onClick={e => handleClick(type, e)}
onSettingsClick={openChartSettings}
/>
)
);
})}
</OptionList>
<OptionLabel>{t`Other charts`}</OptionLabel>
<OptionList data-testid="display-options-not-sensible">
{nonSense.map(type => {
const visualization = visualizations.get(type);
return (
visualization && (
<ChartTypeOption
key={type}
visualization={visualization}
isSelected={type === question.display()}
isSensible={false}
onClick={e => handleClick(type, e)}
onSettingsClick={openChartSettings}
/>
)
);
})}
</OptionList>
</SidebarContent>
);
};
interface ChartTypeOptionProps {
isSelected: boolean;
isSensible: boolean;
onClick: (e: React.MouseEvent) => void;
onSettingsClick: (e: React.MouseEvent) => void;
visualization: Visualization;
}
const ChartTypeOption = ({
visualization,
isSelected,
isSensible,
onClick,
onSettingsClick,
}: ChartTypeOptionProps) => (
<OptionRoot
isSelected={isSelected}
data-testid={`${visualization.uiName}-container`}
role="option"
aria-selected={isSelected}
>
<OptionIconContainer
onClick={onClick}
data-is-sensible={isSensible}
data-testid={`${visualization.uiName}-button`}
>
<Icon name={visualization.iconName} size={20} />
{isSelected && (
<SettingsButton
onlyIcon
icon="gear"
iconSize={16}
onClick={onSettingsClick}
/>
)}
</OptionIconContainer>
<OptionText>{visualization.uiName}</OptionText>
</OptionRoot>
);
// eslint-disable-next-line import/no-default-export -- deprecated usage
export default ChartTypeSidebar;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment