Skip to content

Commit 351220f

Browse files
committed
Add toggle options for dashboard widget headers
This adds three toggle controls under a submenu Display Options - Show Query Description - Show Query Name - Show Visualization Name
1 parent 03f8a56 commit 351220f

File tree

3 files changed

+123
-9
lines changed

3 files changed

+123
-9
lines changed

client/app/components/QueryLink.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import VisualizationName from "@/components/visualizations/VisualizationName";
66

77
import "./QueryLink.less";
88

9-
function QueryLink({ query, visualization, readOnly }) {
9+
function QueryLink({ query, visualization, readOnly, showQueryName, showVisualizationName }) {
1010
const getUrl = () => {
1111
let hash = null;
1212
if (visualization) {
@@ -21,11 +21,14 @@ function QueryLink({ query, visualization, readOnly }) {
2121
return query.getUrl(false, hash);
2222
};
2323

24-
const QueryLinkWrapper = props => (readOnly ? <span {...props} /> : <Link href={getUrl()} {...props} />);
24+
const QueryLinkWrapper = (props) => (readOnly ? <span {...props} /> : <Link href={getUrl()} {...props} />);
2525

2626
return (
2727
<QueryLinkWrapper className="query-link">
28-
<VisualizationName visualization={visualization} /> <span>{query.name}</span>
28+
{(showVisualizationName & showQueryName && <VisualizationName visualization={visualization} />) || (
29+
<span className="visualization-name" />
30+
)}
31+
{(showQueryName && <span>{query.name}</span>) || <span>{visualization.name}</span>}
2932
</QueryLinkWrapper>
3033
);
3134
}

client/app/components/dashboards/dashboard-widget/VisualizationWidget.jsx

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { compact, isEmpty, invoke, map } from "lodash";
44
import { markdown } from "markdown";
55
import cx from "classnames";
66
import Menu from "antd/lib/menu";
7+
import Switch from "antd/lib/switch";
78
import HtmlContent from "@redash/viz/lib/components/HtmlContent";
89
import { currentUser } from "@/services/auth";
910
import recordEvent from "@/services/recordEvent";
@@ -22,7 +23,17 @@ import VisualizationRenderer from "@/components/visualizations/VisualizationRend
2223

2324
import Widget from "./Widget";
2425

25-
function visualizationWidgetMenuOptions({ widget, canEditDashboard, onParametersEdit }) {
26+
function visualizationWidgetMenuOptions({
27+
widget,
28+
canEditDashboard,
29+
onParametersEdit,
30+
onToggleShowQueryDescription,
31+
onToggleShowQueryName,
32+
onToggleShowVisualizationName,
33+
showQueryDescription,
34+
showQueryName,
35+
showVisualizationName,
36+
}) {
2637
const canViewQuery = currentUser.hasPermission("view_query");
2738
const canEditParameters = canEditDashboard && !isEmpty(invoke(widget, "query.getParametersDefs"));
2839
const widgetQueryResult = widget.getQueryResult();
@@ -62,6 +73,21 @@ function visualizationWidgetMenuOptions({ widget, canEditDashboard, onParameters
6273
"Download as Excel File"
6374
)}
6475
</Menu.Item>,
76+
<Menu.Divider key="divider_before_display_options" />,
77+
<Menu.SubMenu key="display_options" title="Display Options" data-test="DisplayOptionsSubMenu">
78+
<Menu.Item key="show_query_description" disabled={true}>
79+
<Switch size="small" checked={showQueryDescription} onClick={onToggleShowQueryDescription} /> Show Query
80+
Description
81+
</Menu.Item>
82+
<Menu.Item key="show_query_name" disabled={true}>
83+
<Switch size="small" checked={showQueryName} onClick={onToggleShowQueryName} /> Show Query Name
84+
</Menu.Item>
85+
<Menu.Item key="show_viz_name" disabled={true}>
86+
<Switch size="small" checked={showVisualizationName} onClick={onToggleShowVisualizationName} /> Show
87+
Visualization Name
88+
</Menu.Item>
89+
</Menu.SubMenu>,
90+
6591
(canViewQuery || canEditParameters) && <Menu.Divider key="divider" />,
6692
canViewQuery && (
6793
<Menu.Item key="view_query">
@@ -98,6 +124,9 @@ function VisualizationWidgetHeader({
98124
isEditing,
99125
onParametersUpdate,
100126
onParametersEdit,
127+
showVisualizationName,
128+
showQueryName,
129+
showQueryDescription,
101130
}) {
102131
const canViewQuery = currentUser.hasPermission("view_query");
103132

@@ -106,10 +135,19 @@ function VisualizationWidgetHeader({
106135
<RefreshIndicator refreshStartedAt={refreshStartedAt} />
107136
<div className="t-header widget clearfix">
108137
<div className="th-title">
109-
<p>
110-
<QueryLink query={widget.getQuery()} visualization={widget.visualization} readOnly={!canViewQuery} />
111-
</p>
112-
{!isEmpty(widget.getQuery().description) && (
138+
{(showQueryName | showVisualizationName && (
139+
<p>
140+
<QueryLink
141+
query={widget.getQuery()}
142+
visualization={widget.visualization}
143+
readOnly={!canViewQuery}
144+
showQueryName={showQueryName}
145+
showVisualizationName={showVisualizationName}
146+
/>
147+
</p>
148+
)) ||
149+
""}
150+
{showQueryDescription && (
113151
<HtmlContent className="text-muted markdown query--description">
114152
{markdown.toHTML(widget.getQuery().description || "")}
115153
</HtmlContent>
@@ -138,6 +176,9 @@ VisualizationWidgetHeader.propTypes = {
138176
isEditing: PropTypes.bool,
139177
onParametersUpdate: PropTypes.func,
140178
onParametersEdit: PropTypes.func,
179+
showQueryDescription: PropTypes.bool,
180+
showQueryName: PropTypes.bool,
181+
showVisualizationName: PropTypes.bool,
141182
};
142183

143184
VisualizationWidgetHeader.defaultProps = {
@@ -146,6 +187,9 @@ VisualizationWidgetHeader.defaultProps = {
146187
onParametersEdit: () => {},
147188
isEditing: false,
148189
parameters: [],
190+
showQueryDescription: true,
191+
showQueryName: true,
192+
showVisualizationName: true,
149193
};
150194

151195
function VisualizationWidgetFooter({ widget, isPublic, onRefresh, onExpand }) {
@@ -227,6 +271,7 @@ class VisualizationWidget extends React.Component {
227271
onRefresh: PropTypes.func,
228272
onDelete: PropTypes.func,
229273
onParameterMappingsChange: PropTypes.func,
274+
onOptionsChange: PropTypes.func,
230275
};
231276

232277
static defaultProps = {
@@ -239,13 +284,17 @@ class VisualizationWidget extends React.Component {
239284
onRefresh: () => {},
240285
onDelete: () => {},
241286
onParameterMappingsChange: () => {},
287+
onOptionsChange: () => {},
242288
};
243289

244290
constructor(props) {
245291
super(props);
246292
this.state = {
247293
localParameters: props.widget.getLocalParameters(),
248294
localFilters: props.filters,
295+
showVisualizationName: props.widget.options?.showVisualizationName ?? true,
296+
showQueryName: props.widget.options?.showQueryName ?? true,
297+
showQueryDescription: props.widget.options?.showQueryDescription ?? true,
249298
};
250299
}
251300

@@ -279,10 +328,51 @@ class VisualizationWidget extends React.Component {
279328
});
280329
};
281330

331+
toggleShowQueryDescription = () => {
332+
const { widget } = this.props;
333+
const { showQueryDescription } = this.state;
334+
const newOptions = {
335+
...widget.options,
336+
showQueryDescription: !showQueryDescription,
337+
};
338+
339+
widget.save("options", newOptions).then(() => {
340+
this.setState({ showQueryDescription: !showQueryDescription });
341+
});
342+
};
343+
344+
toggleShowQueryName = () => {
345+
const { widget } = this.props;
346+
const { showQueryName } = this.state;
347+
const newOptions = {
348+
...widget.options,
349+
showQueryName: !showQueryName,
350+
};
351+
352+
widget.save("options", newOptions).then(() => {
353+
this.setState({ showQueryName: !showQueryName });
354+
});
355+
};
356+
357+
toggleShowVisualizationName = () => {
358+
const { widget } = this.props;
359+
const { showVisualizationName } = this.state;
360+
361+
const newOptions = {
362+
...widget.options,
363+
showVisualizationName: !showVisualizationName,
364+
};
365+
366+
widget.save("options", newOptions).then(() => {
367+
this.setState({ showVisualizationName: !showVisualizationName });
368+
});
369+
};
370+
282371
renderVisualization() {
283372
const { widget, filters } = this.props;
284373
const widgetQueryResult = widget.getQueryResult();
285374
const widgetStatus = widgetQueryResult && widgetQueryResult.getStatus();
375+
286376
switch (widgetStatus) {
287377
case "failed":
288378
return (
@@ -325,7 +415,7 @@ class VisualizationWidget extends React.Component {
325415

326416
render() {
327417
const { widget, isLoading, isPublic, canEdit, isEditing, onRefresh } = this.props;
328-
const { localParameters } = this.state;
418+
const { localParameters, showQueryDescription, showQueryName, showVisualizationName } = this.state;
329419
const widgetQueryResult = widget.getQueryResult();
330420
const isRefreshing = isLoading && !!(widgetQueryResult && widgetQueryResult.getStatus());
331421
const onParametersEdit = (parameters) => {
@@ -342,6 +432,12 @@ class VisualizationWidget extends React.Component {
342432
widget,
343433
canEditDashboard: canEdit,
344434
onParametersEdit: this.editParameterMappings,
435+
onToggleShowVisualizationName: this.toggleShowVisualizationName,
436+
onToggleShowQueryName: this.toggleShowQueryName,
437+
onToggleShowQueryDescription: this.toggleShowQueryDescription,
438+
showQueryDescription,
439+
showQueryName,
440+
showVisualizationName,
345441
})}
346442
header={
347443
<VisualizationWidgetHeader
@@ -351,6 +447,9 @@ class VisualizationWidget extends React.Component {
351447
isEditing={isEditing}
352448
onParametersUpdate={onRefresh}
353449
onParametersEdit={onParametersEdit}
450+
showQueryDescription={showQueryDescription}
451+
showQueryName={showQueryName}
452+
showVisualizationName={showVisualizationName}
354453
/>
355454
}
356455
footer={

client/cypress/integration/dashboard/widget_spec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ describe("Widget", () => {
4444
});
4545
});
4646

47+
it("try display options menu", function() {
48+
createQueryAndAddWidget(this.dashboardId).then(elTestId => {
49+
cy.visit(this.dashboardUrl);
50+
cy.getByTestId(elTestId).within(() => {
51+
cy.getByTestId("WidgetDropdownButton").click();
52+
});
53+
cy.getByTestId("WidgetDropdownButtonMenu")
54+
.contains("Display Options")
55+
.click();
56+
});
57+
});
58+
4759
describe("Auto height for table visualization", () => {
4860
it("renders correct height for 2 table rows", function() {
4961
const queryData = {

0 commit comments

Comments
 (0)