Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reorder to dashboard parameter widgets #5267

Merged
merged 36 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
65b9e17
added paramOrder prop
rafawendel Nov 13, 2020
41c7ff4
minor refactor
rafawendel Nov 13, 2020
5a53e62
Merge branch 'master' into reorder-dashboard-parameters
rafawendel Nov 14, 2020
8b8ad55
moved logic to widget
rafawendel Nov 15, 2020
6fbbacf
Added paramOrder to widget API call
rafawendel Nov 17, 2020
21e4b53
Update client/app/components/dashboards/dashboard-widget/Visualizatio…
rafawendel Nov 19, 2020
7c5f135
Merge branch 'master' into reorder-dashboard-parameters
rafawendel Nov 22, 2020
c82b8bb
Merge branch 'master' into reorder-dashboard-parameters
rafawendel Nov 22, 2020
233dff9
experimental removal of helper element
rafawendel Nov 24, 2020
0b7e123
cleaner comment
rafawendel Nov 24, 2020
dec8cfe
Added dashboard global params logic
rafawendel Nov 24, 2020
41ae2ce
Added backend logic for dashboard options
rafawendel Nov 24, 2020
9e01b2d
Removed testing leftovers
rafawendel Nov 24, 2020
163fb6f
removed appending sortable to parent component behavior
rafawendel Dec 22, 2020
026608a
Revert "Added backend logic for dashboard options"
rafawendel Dec 22, 2020
19acbeb
Merge branch 'master' of github.com:getredash/redash into reorder-das…
rafawendel Dec 22, 2020
036b9ed
Re-structured backend options
rafawendel Dec 23, 2020
6071bef
removed temporary edits
rafawendel Dec 28, 2020
232d828
Added dashboard/widget param reorder cypress tests
rafawendel Dec 28, 2020
ee94809
Separated edit and sorting permission
rafawendel Dec 28, 2020
ada5ccf
added options to public dashboard serializer
rafawendel Dec 28, 2020
16275c1
Merge branch 'master' of github.com:getredash/redash into reorder-das…
rafawendel Jan 6, 2021
019883a
Removed undesirable events from drag
rafawendel Jan 6, 2021
975ddec
Bring back attaching sortable to its parent
rafawendel Jan 6, 2021
4952605
Added prop to control draggable destination parent
rafawendel Jan 6, 2021
a0c1c0e
Removed paramOrder fallback
rafawendel Jan 6, 2021
433e11e
WIP (for Netflify preview)
rafawendel Jan 6, 2021
5986743
fixup! Added prop to control draggable destination parent
rafawendel Jan 6, 2021
027ee86
Better drag and drop styling and fix for the padding
rafawendel Jan 6, 2021
8689e0e
Revert "WIP (for Netflify preview)"
rafawendel Jan 6, 2021
4a4cc0a
Improved dashboard parameter Cypress test
rafawendel Jan 7, 2021
03d806a
Standardized reorder styling
rafawendel Jan 7, 2021
2c68d18
Changed dashboard param reorder to edit mode only
rafawendel Jan 7, 2021
af0d2af
fixup! Improved dashboard parameter Cypress test
rafawendel Jan 7, 2021
83d3eab
fixup! Improved dashboard parameter Cypress test
rafawendel Jan 7, 2021
e53aa8d
Fix for Cypress CI error
rafawendel Jan 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions client/app/components/Parameters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export default class Parameters extends React.Component {
if (oldIndex !== newIndex) {
this.setState(({ parameters }) => {
parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]);
onParametersEdit();
onParametersEdit(parameters);
return { parameters };
});
}
Expand All @@ -110,7 +110,7 @@ export default class Parameters extends React.Component {
this.setState(({ parameters }) => {
const updatedParameter = extend(parameter, updated);
parameters[index] = createParameter(updatedParameter, updatedParameter.parentQueryId);
onParametersEdit();
onParametersEdit(parameters);
return { parameters };
});
});
Expand All @@ -122,6 +122,7 @@ export default class Parameters extends React.Component {
<div key={param.name} className="di-block" data-test={`ParameterName-${param.name}`}>
<div className="parameter-heading">
<label>{param.title || toHuman(param.name)}</label>
{/* TODO: add a prop to allow conditional show of this decoupled from sort */}
{editable && (
<button
className="btn btn-default btn-xs m-l-5"
Expand All @@ -148,6 +149,7 @@ export default class Parameters extends React.Component {
const { parameters } = this.state;
const { editable } = this.props;
const dirtyParamCount = size(filter(parameters, "hasPendingValue"));

return (
<SortableContainer
disabled={!editable}
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/dashboards/DashboardGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ class DashboardGrid extends React.Component {
return (
<div className={className}>
<ResponsiveGridLayout
draggableCancel="input"
draggableCancel="input,.sortable-container"
className={cx("layout", { "disable-animations": this.state.disableAnimations })}
cols={{ [MULTI]: cfg.columns, [SINGLE]: 1 }}
rowHeight={cfg.rowHeight - cfg.margins}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { compact, isEmpty, invoke } from "lodash";
import { compact, isEmpty, invoke, map } from "lodash";
import { markdown } from "markdown";
import cx from "classnames";
import Menu from "antd/lib/menu";
Expand Down Expand Up @@ -84,7 +84,14 @@ function RefreshIndicator({ refreshStartedAt }) {
RefreshIndicator.propTypes = { refreshStartedAt: Moment };
RefreshIndicator.defaultProps = { refreshStartedAt: null };

function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onParametersUpdate }) {
function VisualizationWidgetHeader({
widget,
refreshStartedAt,
parameters,
canEdit,
onParametersUpdate,
onParametersEdit,
}) {
const canViewQuery = currentUser.hasPermission("view_query");

return (
Expand All @@ -104,7 +111,12 @@ function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onPar
</div>
{!isEmpty(parameters) && (
<div className="m-b-10">
<Parameters parameters={parameters} onValuesChange={onParametersUpdate} />
<Parameters
parameters={parameters}
editable={canEdit}
onValuesChange={onParametersUpdate}
onParametersEdit={onParametersEdit}
/>
</div>
)}
</>
Expand All @@ -115,12 +127,16 @@ VisualizationWidgetHeader.propTypes = {
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
refreshStartedAt: Moment,
parameters: PropTypes.arrayOf(PropTypes.object),
canEdit: PropTypes.bool,
onParametersUpdate: PropTypes.func,
onParametersEdit: PropTypes.func,
};

VisualizationWidgetHeader.defaultProps = {
refreshStartedAt: null,
onParametersUpdate: () => {},
onParametersEdit: () => {},
canEdit: false,
parameters: [],
};

Expand Down Expand Up @@ -288,6 +304,11 @@ class VisualizationWidget extends React.Component {
const { localParameters } = this.state;
const widgetQueryResult = widget.getQueryResult();
const isRefreshing = isLoading && !!(widgetQueryResult && widgetQueryResult.getStatus());
const onParametersEdit = parameters => {
const paramOrder = map(parameters, "name");
widget.options.paramOrder = paramOrder;
widget.save("options", { paramOrder });
};

return (
<Widget
Expand All @@ -303,7 +324,9 @@ class VisualizationWidget extends React.Component {
widget={widget}
refreshStartedAt={isRefreshing ? widget.refreshStartedAt : null}
parameters={localParameters}
canEdit={canEdit}
onParametersUpdate={onRefresh}
onParametersEdit={onParametersEdit}
/>
}
footer={
Expand Down
45 changes: 31 additions & 14 deletions client/app/pages/dashboards/DashboardPage.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEmpty } from "lodash";
import { isEmpty, map } from "lodash";
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
Expand All @@ -24,8 +24,8 @@ import DashboardHeader from "./components/DashboardHeader";

import "./DashboardPage.less";

function DashboardSettings({ dashboardOptions }) {
const { dashboard, updateDashboard } = dashboardOptions;
function DashboardSettings({ dashboardConfiguration }) {
const { dashboard, updateDashboard } = dashboardConfiguration;
return (
<div className="m-b-10 p-15 bg-white tiled">
<Checkbox
Expand All @@ -39,11 +39,11 @@ function DashboardSettings({ dashboardOptions }) {
}

DashboardSettings.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};

function AddWidgetContainer({ dashboardOptions, className, ...props }) {
const { showAddTextboxDialog, showAddWidgetDialog } = dashboardOptions;
function AddWidgetContainer({ dashboardConfiguration, className, ...props }) {
const { showAddTextboxDialog, showAddWidgetDialog } = dashboardConfiguration;
return (
<div className={cx("add-widget-container", className)} {...props}>
<h2>
Expand All @@ -66,29 +66,35 @@ function AddWidgetContainer({ dashboardOptions, className, ...props }) {
}

AddWidgetContainer.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
className: PropTypes.string,
};

function DashboardComponent(props) {
const dashboardOptions = useDashboard(props.dashboard);
const dashboardConfiguration = useDashboard(props.dashboard);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed this to avoid confusion

const {
dashboard,
canEditDashboard,
filters,
setFilters,
loadDashboard,
loadWidget,
removeWidget,
saveDashboardLayout,
globalParameters,
updateDashboard,
refreshDashboard,
refreshWidget,
editingLayout,
setGridDisabled,
} = dashboardOptions;
} = dashboardConfiguration;

const [pageContainer, setPageContainer] = useState(null);
const [bottomPanelStyles, setBottomPanelStyles] = useState({});
const onParametersEdit = parameters => {
const paramOrder = map(parameters, "name");
updateDashboard({ options: { globalParamOrder: paramOrder } });
};

useEffect(() => {
if (pageContainer) {
Expand All @@ -114,22 +120,31 @@ function DashboardComponent(props) {
return (
<div className="container" ref={setPageContainer} data-test={`DashboardId${dashboard.id}Container`}>
<DashboardHeader
dashboardOptions={dashboardOptions}
dashboardConfiguration={dashboardConfiguration}
headerExtra={
<DynamicComponent name="Dashboard.HeaderExtra" dashboard={dashboard} dashboardOptions={dashboardOptions} />
<DynamicComponent
name="Dashboard.HeaderExtra"
dashboard={dashboard}
dashboardConfiguration={dashboardConfiguration}
/>
}
/>
{!isEmpty(globalParameters) && (
<div className="dashboard-parameters m-b-10 p-15 bg-white tiled" data-test="DashboardParameters">
<Parameters parameters={globalParameters} onValuesChange={refreshDashboard} />
<Parameters
parameters={globalParameters}
onValuesChange={refreshDashboard}
editable={canEditDashboard}
onParametersEdit={onParametersEdit}
/>
</div>
)}
{!isEmpty(filters) && (
<div className="m-b-10 p-15 bg-white tiled" data-test="DashboardFilters">
<Filters filters={filters} onChange={setFilters} />
</div>
)}
{editingLayout && <DashboardSettings dashboardOptions={dashboardOptions} />}
{editingLayout && <DashboardSettings dashboardConfiguration={dashboardConfiguration} />}
<div id="dashboard-container">
<DashboardGrid
dashboard={dashboard}
Expand All @@ -144,7 +159,9 @@ function DashboardComponent(props) {
onParameterMappingsChange={loadDashboard}
/>
</div>
{editingLayout && <AddWidgetContainer dashboardOptions={dashboardOptions} style={bottomPanelStyles} />}
{editingLayout && (
<AddWidgetContainer dashboardConfiguration={dashboardConfiguration} style={bottomPanelStyles} />
)}
</div>
);
}
Expand Down
49 changes: 27 additions & 22 deletions client/app/pages/dashboards/components/DashboardHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ function buttonType(value) {
return value ? "primary" : "default";
}

function DashboardPageTitle({ dashboardOptions }) {
const { dashboard, canEditDashboard, updateDashboard, editingLayout } = dashboardOptions;
function DashboardPageTitle({ dashboardConfiguration }) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All changes in this file are renamings, no need to go through it

const { dashboard, canEditDashboard, updateDashboard, editingLayout } = dashboardConfiguration;
return (
<div className="title-with-tags">
<div className="page-title">
Expand Down Expand Up @@ -58,11 +58,11 @@ function DashboardPageTitle({ dashboardOptions }) {
}

DashboardPageTitle.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};

function RefreshButton({ dashboardOptions }) {
const { refreshRate, setRefreshRate, disableRefreshRate, refreshing, refreshDashboard } = dashboardOptions;
function RefreshButton({ dashboardConfiguration }) {
const { refreshRate, setRefreshRate, disableRefreshRate, refreshing, refreshDashboard } = dashboardConfiguration;
const allowedIntervals = policy.getDashboardRefreshIntervals();
const refreshRateOptions = clientConfig.dashboardRefreshIntervals;
const onRefreshRateSelected = ({ key }) => {
Expand Down Expand Up @@ -105,10 +105,10 @@ function RefreshButton({ dashboardOptions }) {
}

RefreshButton.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};

function DashboardMoreOptionsButton({ dashboardOptions }) {
function DashboardMoreOptionsButton({ dashboardConfiguration }) {
const {
dashboard,
setEditingLayout,
Expand All @@ -117,7 +117,7 @@ function DashboardMoreOptionsButton({ dashboardOptions }) {
managePermissions,
gridDisabled,
isDashboardOwnerOrAdmin,
} = dashboardOptions;
} = dashboardConfiguration;

const archive = () => {
Modal.confirm({
Expand Down Expand Up @@ -163,18 +163,18 @@ function DashboardMoreOptionsButton({ dashboardOptions }) {
}

DashboardMoreOptionsButton.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};

function DashboardControl({ dashboardOptions, headerExtra }) {
function DashboardControl({ dashboardConfiguration, headerExtra }) {
const {
dashboard,
togglePublished,
canEditDashboard,
fullscreen,
toggleFullscreen,
showShareDashboardDialog,
} = dashboardOptions;
} = dashboardConfiguration;
const showPublishButton = dashboard.is_draft;
const showRefreshButton = true;
const showFullscreenButton = !dashboard.is_draft;
Expand All @@ -190,7 +190,7 @@ function DashboardControl({ dashboardOptions, headerExtra }) {
<span className="fa fa-paper-plane m-r-5" /> Publish
</Button>
)}
{showRefreshButton && <RefreshButton dashboardOptions={dashboardOptions} />}
{showRefreshButton && <RefreshButton dashboardConfiguration={dashboardConfiguration} />}
{showFullscreenButton && (
<Tooltip className="hidden-xs" title="Enable/Disable Fullscreen display">
<Button type={buttonType(fullscreen)} className="icon-button m-l-5" onClick={toggleFullscreen}>
Expand All @@ -210,20 +210,25 @@ function DashboardControl({ dashboardOptions, headerExtra }) {
</Button>
</Tooltip>
)}
{showMoreOptionsButton && <DashboardMoreOptionsButton dashboardOptions={dashboardOptions} />}
{showMoreOptionsButton && <DashboardMoreOptionsButton dashboardConfiguration={dashboardConfiguration} />}
</span>
)}
</div>
);
}

DashboardControl.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
headerExtra: PropTypes.node,
};

function DashboardEditControl({ dashboardOptions, headerExtra }) {
const { setEditingLayout, doneBtnClickedWhileSaving, dashboardStatus, retrySaveDashboardLayout } = dashboardOptions;
function DashboardEditControl({ dashboardConfiguration, headerExtra }) {
const {
setEditingLayout,
doneBtnClickedWhileSaving,
dashboardStatus,
retrySaveDashboardLayout,
} = dashboardConfiguration;
let status;
if (dashboardStatus === DashboardStatusEnum.SAVED) {
status = <span className="save-status">Saved</span>;
Expand Down Expand Up @@ -258,23 +263,23 @@ function DashboardEditControl({ dashboardOptions, headerExtra }) {
}

DashboardEditControl.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
headerExtra: PropTypes.node,
};

export default function DashboardHeader({ dashboardOptions, headerExtra }) {
const { editingLayout } = dashboardOptions;
export default function DashboardHeader({ dashboardConfiguration, headerExtra }) {
const { editingLayout } = dashboardConfiguration;
const DashboardControlComponent = editingLayout ? DashboardEditControl : DashboardControl;

return (
<div className="dashboard-header">
<DashboardPageTitle dashboardOptions={dashboardOptions} />
<DashboardControlComponent dashboardOptions={dashboardOptions} headerExtra={headerExtra} />
<DashboardPageTitle dashboardConfiguration={dashboardConfiguration} />
<DashboardControlComponent dashboardConfiguration={dashboardConfiguration} headerExtra={headerExtra} />
</div>
);
}

DashboardHeader.propTypes = {
dashboardOptions: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dashboardConfiguration: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
headerExtra: PropTypes.node,
};
Loading