diff --git a/frontend/src/metabase/visualizations/components/ChartSettings.jsx b/frontend/src/metabase/visualizations/components/ChartSettings.jsx index 2befb59091455c3069e536f2441e7fb7a9284a9b..bf9491543aa7ee3b4cc0d5a1fbf890d37c0da11e 100644 --- a/frontend/src/metabase/visualizations/components/ChartSettings.jsx +++ b/frontend/src/metabase/visualizations/components/ChartSettings.jsx @@ -25,6 +25,7 @@ import { import { getSettingDefintionsForColumn } from "metabase/visualizations/lib/settings/column"; import ChartSettingsWidget from "./ChartSettingsWidget"; +import ChartSettingsWidgetList from "./ChartSettingsWidgetList"; import ChartSettingsWidgetPopover from "./ChartSettingsWidgetPopover"; import { SectionContainer, SectionWarnings } from "./ChartSettings.styled"; @@ -195,16 +196,8 @@ class ChartSettings extends Component { } render() { - const { - className, - question, - addField, - noPreview, - children, - setSidebarPropsOverride, - dashboard, - isDashboard, - } = this.props; + const { className, question, addField, noPreview, dashboard, isDashboard } = + this.props; const { currentWidget, popoverRef } = this.state; const settings = this._getSettings(); @@ -236,7 +229,6 @@ class ChartSettings extends Component { "data", "display", "axes", - "labels", // include all section names so any forgotten sections are sorted to the end ...sectionNames.map(x => x.toLowerCase()), ]; @@ -286,32 +278,11 @@ class ChartSettings extends Component { </SectionContainer> ); - const widgetList = visibleWidgets.map(widget => ( - <ChartSettingsWidget - key={widget.id} - {...widget} - {...extraWidgetProps} - setSidebarPropsOverride={setSidebarPropsOverride} - /> - )); - const onReset = !_.isEqual(settings, {}) && (settings || {}).virtual_card == null // resetting virtual cards wipes the text and broke the UI (metabase#14644) ? this.handleResetSettings : null; - // custom render prop layout: - if (children) { - return children({ - sectionNames, - sectionPicker, - widgetList, - onDone: this.handleDone, - onCancel: this.handleCancel, - onReset: onReset, - }); - } - const showSectionPicker = // don't show section tabs for a single section sectionNames.length > 1 && @@ -337,7 +308,10 @@ class ChartSettings extends Component { )} {noPreview ? ( <div className="full-height relative scroll-y scroll-show pt2 pb4"> - {widgetList} + <ChartSettingsWidgetList + widgets={visibleWidgets} + extraWidgetProps={extraWidgetProps} + /> </div> ) : ( <div className="Grid flex-full"> @@ -345,7 +319,10 @@ class ChartSettings extends Component { className="Grid-cell Cell--1of3 scroll-y scroll-show border-right py4" data-testid="chartsettings-sidebar" > - {widgetList} + <ChartSettingsWidgetList + widgets={visibleWidgets} + extraWidgetProps={extraWidgetProps} + /> </div> <div className="Grid-cell flex flex-column pt2"> <div className="mx4 flex flex-column"> diff --git a/frontend/src/metabase/visualizations/components/ChartSettingsWidgetList.styled.tsx b/frontend/src/metabase/visualizations/components/ChartSettingsWidgetList.styled.tsx new file mode 100644 index 0000000000000000000000000000000000000000..32dcb1b056b4e372abfb914e50632d9e9bd6f5f1 --- /dev/null +++ b/frontend/src/metabase/visualizations/components/ChartSettingsWidgetList.styled.tsx @@ -0,0 +1,21 @@ +import styled from "@emotion/styled"; + +import { color } from "metabase/lib/colors"; + +import ChartSettingsWidget from "./ChartSettingsWidget"; + +export const ChartSettingsWidgetListHeader = styled.h4` + margin-left: 2rem; + margin-bottom: 1rem; + color: ${color("bg-dark")}; + text-transform: uppercase; +`; + +export const ChartSettingsWidgetListDivider = styled.div` + background-color: ${color("border")}; + height: 1px; + display: block; + margin-bottom: 1.5rem; + margin-left: 2rem; + margin-right: 2rem; +`; diff --git a/frontend/src/metabase/visualizations/components/ChartSettingsWidgetList.tsx b/frontend/src/metabase/visualizations/components/ChartSettingsWidgetList.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8b152afebc9fdf6d05c0856e23e915a8ecb558bd --- /dev/null +++ b/frontend/src/metabase/visualizations/components/ChartSettingsWidgetList.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import _ from "underscore"; +import ChartSettingsWidget from "./ChartSettingsWidget"; + +import { + ChartSettingsWidgetListHeader, + ChartSettingsWidgetListDivider, +} from "./ChartSettingsWidgetList.styled"; + +interface ChartSettingsWidgetListProps { + widgets: { id: string; group?: string }[]; + extraWidgetProps: Record<string, unknown>; +} + +const ChartSettingsWidgetList = ({ + widgets, + extraWidgetProps, +}: ChartSettingsWidgetListProps) => { + const widgetsAreGrouped = widgets.some(widget => widget.group); + + if (!widgetsAreGrouped) { + return widgets.map(widget => ( + <ChartSettingsWidget key={widget.id} {...widget} {...extraWidgetProps} /> + )); + } else { + const groupedWidgets = widgets.reduce<Record<string, any[]>>( + (memo, widget) => { + const group = widget.group || ""; + (memo[group] = memo[group] || []).push(widget); + return memo; + }, + {}, + ); + + return Object.keys(groupedWidgets).map((group, groupIndex, groups) => { + const lastGroup = groupIndex === groups.length - 1; + return ( + <div key={`group-${groupIndex}`}> + {group && ( + <ChartSettingsWidgetListHeader> + {group} + </ChartSettingsWidgetListHeader> + )} + <div> + {_.sortBy(groupedWidgets[group], "index").map(widget => ( + <ChartSettingsWidget + key={widget.id} + {...widget} + {...extraWidgetProps} + /> + ))} + {!lastGroup && <ChartSettingsWidgetListDivider />} + </div> + </div> + ); + }); + } +}; + +export default ChartSettingsWidgetList; diff --git a/frontend/src/metabase/visualizations/lib/settings/graph.js b/frontend/src/metabase/visualizations/lib/settings/graph.js index d8c6bc668c10537fc5733be35fb8084dc412ed6d..0d3c38707c3be00b396bbd7217493e9593aab2f5 100644 --- a/frontend/src/metabase/visualizations/lib/settings/graph.js +++ b/frontend/src/metabase/visualizations/lib/settings/graph.js @@ -479,7 +479,9 @@ export const GRAPH_AXIS_SETTINGS = { }, "graph.x_axis.scale": { section: t`Axes`, - title: t`X-axis scale`, + group: t`X-axis`, + title: t`Scale`, + index: 4, widget: "select", readDependencies: [ "graph.x_axis._is_timeseries", @@ -513,7 +515,9 @@ export const GRAPH_AXIS_SETTINGS = { }, "graph.y_axis.scale": { section: t`Axes`, - title: t`Y-axis scale`, + title: t`Scale`, + index: 7, + group: t`Y-axis`, widget: "select", default: "linear", getProps: (series, vizSettings) => ({ @@ -526,7 +530,9 @@ export const GRAPH_AXIS_SETTINGS = { }, "graph.x_axis.axis_enabled": { section: t`Axes`, - title: t`Show x-axis line and marks`, + group: t`X-axis`, + title: t`Show lines and marks`, + index: 3, widget: "select", props: { options: [ @@ -541,7 +547,9 @@ export const GRAPH_AXIS_SETTINGS = { }, "graph.y_axis.axis_enabled": { section: t`Axes`, - title: t`Show y-axis line and marks`, + title: t`Show lines and marks`, + index: 8, + group: t`Y-axis`, widget: "select", props: { options: [ @@ -553,12 +561,17 @@ export const GRAPH_AXIS_SETTINGS = { }, "graph.y_axis.auto_range": { section: t`Axes`, + group: t`Y-axis`, + index: 4, title: t`Auto y-axis range`, + inline: true, widget: "toggle", default: true, }, "graph.y_axis.min": { section: t`Axes`, + group: t`Y-axis`, + index: 5, title: t`Min`, widget: "number", default: 0, @@ -567,6 +580,8 @@ export const GRAPH_AXIS_SETTINGS = { }, "graph.y_axis.max": { section: t`Axes`, + group: t`Y-axis`, + index: 6, title: t`Max`, widget: "number", default: 100, @@ -597,35 +612,52 @@ export const GRAPH_AXIS_SETTINGS = { */ "graph.y_axis.auto_split": { section: t`Axes`, - title: t`Use a split y-axis when necessary`, + group: t`Y-axis`, + index: 3, + title: t`Split y-axis when necessary`, widget: "toggle", + inline: true, default: true, getHidden: series => series.length < 2, }, "graph.x_axis.labels_enabled": { - section: t`Labels`, - title: t`Show label on x-axis`, + section: t`Axes`, + group: t`X-axis`, + index: 1, + title: t`Show label`, + inline: true, widget: "toggle", default: true, }, "graph.x_axis.title_text": { - section: t`Labels`, - title: t`X-axis label`, + section: t`Axes`, + title: t`Label`, + index: 2, + group: t`X-axis`, widget: "input", getHidden: (series, vizSettings) => vizSettings["graph.x_axis.labels_enabled"] === false, getDefault: (series, vizSettings) => - series.length === 1 ? getFriendlyName(series[0].data.cols[0]) : null, + series.length > 1 ? getFriendlyName(series[0].data.cols[0]) : null, + getProps: series => ({ + placeholder: + series.length > 1 ? getFriendlyName(series[0].data.cols[0]) : null, + }), }, "graph.y_axis.labels_enabled": { - section: t`Labels`, - title: t`Show label on y-axis`, + section: t`Axes`, + title: t`Show label`, + index: 1, + group: t`Y-axis`, widget: "toggle", + inline: true, default: true, }, "graph.y_axis.title_text": { - section: t`Labels`, - title: t`Y-axis label`, + section: t`Axes`, + title: t`Label`, + index: 2, + group: t`Y-axis`, widget: "input", getHidden: (series, vizSettings) => vizSettings["graph.y_axis.labels_enabled"] === false,