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

Migrate Parameters component to React #4006

Merged
merged 42 commits into from
Aug 18, 2019
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0408f28
Start Parameters Migration
gabrieldutra Jul 22, 2019
3cdc232
Merge branch 'master' into react-parameters
gabrieldutra Jul 22, 2019
15419f6
Add dirtyCount
gabrieldutra Jul 24, 2019
61541d4
Use workaround with setState
gabrieldutra Jul 24, 2019
d9dbef5
Apply Changes
gabrieldutra Jul 25, 2019
2fe9a16
Add EditSettingsDialog
gabrieldutra Jul 25, 2019
1d51a66
Add Cmd/Ctrl + Enter behavior
gabrieldutra Jul 26, 2019
0ede83a
Remove isApplying
gabrieldutra Jul 26, 2019
cf49952
Delete Angular version of parameters
gabrieldutra Jul 26, 2019
f2ab142
Update tests
gabrieldutra Jul 26, 2019
33db3bc
Remove angular stuff
gabrieldutra Jul 26, 2019
cdac798
Merge branch 'master' into react-parameters
gabrieldutra Jul 26, 2019
5f81f3f
Merge branch 'master' into react-parameters
gabrieldutra Jul 26, 2019
3fb3e3f
Update jest
gabrieldutra Jul 26, 2019
01bca17
Drag placeholder
gabrieldutra Jul 29, 2019
2c73726
Update events
gabrieldutra Jul 29, 2019
9f0d1ab
Use old button styling and move css
gabrieldutra Jul 29, 2019
7499226
Merge branch 'master' into react-parameters
gabrieldutra Jul 29, 2019
0b928c0
Reviewing code
gabrieldutra Jul 29, 2019
00d633d
Add parameter rearrange test
gabrieldutra Jul 29, 2019
95f1a42
Add Parameter Settings title change test
gabrieldutra Jul 29, 2019
9598d95
Update Parameter Settings button styling
gabrieldutra Jul 29, 2019
dab94d5
Move parameter url logic back to Parameters
gabrieldutra Jul 30, 2019
dfa60b6
Disable url update when query is new
gabrieldutra Jul 30, 2019
2082493
Merge branch 'master' into react-parameters
gabrieldutra Jul 30, 2019
0c0bf6e
Styling changes (#4019)
ranbena Jul 31, 2019
649aaf5
Merge branch 'master' into react-parameters
gabrieldutra Jul 31, 2019
ed848ab
Ran's title width styling
gabrieldutra Jul 31, 2019
aab7058
Update drag test
gabrieldutra Jul 31, 2019
9e679b9
Improve sizing for Number inputs
gabrieldutra Aug 1, 2019
6cfb271
Fix issue with dragged parameter wrapping
gabrieldutra Aug 1, 2019
5baf71e
Don't reevaluate dirtyParamCount
gabrieldutra Aug 1, 2019
69fe667
Merge branch 'master' into react-parameters
gabrieldutra Aug 1, 2019
efc987f
Merge branch 'react-parameters' of github.com:getredash/redash into r…
gabrieldutra Aug 1, 2019
8b7eec1
Merge branch 'master' into react-parameters
gabrieldutra Aug 5, 2019
cad5ff0
Allow multiple values :)
gabrieldutra Aug 5, 2019
542f650
Fix parameter alignments
ranbena Aug 5, 2019
f2be99b
Fix Select width on search
gabrieldutra Aug 5, 2019
58b1b72
Update client/app/components/Parameters.less
gabrieldutra Aug 6, 2019
aa097e3
Merge branch 'master' into react-parameters
gabrieldutra Aug 6, 2019
5d22e49
Humanize param.name
gabrieldutra Aug 7, 2019
2a28a18
Make sure angular updates Execute disabled status
gabrieldutra Aug 7, 2019
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
1 change: 1 addition & 0 deletions client/app/components/EditParameterSettingsDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ function EditParameterSettingsDialog(props) {
<Input
value={isNull(param.title) ? getDefaultTitle(param.name) : param.title}
onChange={e => setParam({ ...param, title: e.target.value })}
data-test="ParameterTitleInput"
/>
</Form.Item>
<Form.Item label="Type" {...formItemProps}>
Expand Down
14 changes: 4 additions & 10 deletions client/app/components/ParameterApplyButton.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import Button from 'antd/lib/button';
import Badge from 'antd/lib/badge';
import Tooltip from 'antd/lib/tooltip';
import { KeyboardShortcuts } from '@/services/keyboard-shortcuts';

function ParameterApplyButton({ paramCount, onClick, isApplying }) {
// show spinner when applying (also when count is empty so the fade out is consistent)
const icon = isApplying || !paramCount ? 'spinner fa-pulse' : 'check';
function ParameterApplyButton({ paramCount, onClick }) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Removed isApplying as it was not affecting anything.

// show spinner when count is empty so the fade out is consistent
const icon = !paramCount ? 'spinner fa-pulse' : 'check';

return (
<div className="parameter-apply-button" data-show={!!paramCount} data-test="ParameterApplyButton">
Expand All @@ -28,11 +27,6 @@ function ParameterApplyButton({ paramCount, onClick, isApplying }) {
ParameterApplyButton.propTypes = {
onClick: PropTypes.func.isRequired,
paramCount: PropTypes.number.isRequired,
isApplying: PropTypes.bool.isRequired,
};

export default function init(ngModule) {
ngModule.component('parameterApplyButton', react2angular(ParameterApplyButton));
}

init.init = true;
export default ParameterApplyButton;
2 changes: 1 addition & 1 deletion client/app/components/ParameterMappingInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Input from 'antd/lib/input';
import Radio from 'antd/lib/radio';
import Form from 'antd/lib/form';
import Tooltip from 'antd/lib/tooltip';
import { ParameterValueInput } from '@/components/ParameterValueInput';
import ParameterValueInput from '@/components/ParameterValueInput';
import { ParameterMappingType } from '@/services/widget';
import { Parameter } from '@/services/query';
import { HelpTrigger } from '@/components/HelpTrigger';
Expand Down
35 changes: 2 additions & 33 deletions client/app/components/ParameterValueInput.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import Select from 'antd/lib/select';
import Input from 'antd/lib/input';
import InputNumber from 'antd/lib/input-number';
Expand All @@ -19,7 +18,7 @@ const multipleValuesProps = {
maxTagPlaceholder: num => `+${num.length} more`,
};

export class ParameterValueInput extends React.Component {
class ParameterValueInput extends React.Component {
static propTypes = {
type: PropTypes.string,
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
Expand Down Expand Up @@ -193,34 +192,4 @@ export class ParameterValueInput extends React.Component {
}
}

export default function init(ngModule) {
ngModule.component('parameterValueInput', {
template: `
<parameter-value-input-impl
type="$ctrl.param.type"
value="$ctrl.param.normalizedValue"
parameter="$ctrl.param"
enum-options="$ctrl.param.enumOptions"
query-id="$ctrl.param.queryId"
allow-multiple-values="!!$ctrl.param.multiValuesOptions"
on-select="$ctrl.setValue"
></parameter-value-input-impl>
`,
bindings: {
param: '<',
},
controller($scope) {
this.setValue = (value, isDirty) => {
if (isDirty) {
this.param.setPendingValue(value);
} else {
this.param.clearPendingValue();
}
$scope.$apply();
};
},
});
ngModule.component('parameterValueInputImpl', react2angular(ParameterValueInput));
}

init.init = true;
export default ParameterValueInput;
69 changes: 5 additions & 64 deletions client/app/components/ParameterValueInput.less
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
.parameter-input {
display: inline-block;
position: relative;
width: 100%;

.@{ant-prefix}-input[type="text"] {
width: 195px;
.@{ant-prefix}-input,
.@{ant-prefix}-input-number,
.@{ant-prefix}-select {
ranbena marked this conversation as resolved.
Show resolved Hide resolved
min-width: 100% !important;
}

&[data-dirty] {
Expand All @@ -18,65 +21,3 @@
}
}
}

.parameter-container {
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to Parameters.less.

position: relative;

.parameter-apply-button {
display: none; // default for mobile

// "floating" on desktop
@media (min-width: 768px) {
position: absolute;
bottom: -42px;
left: -15px;
border-radius: 2px;
z-index: 1;
transition: opacity 150ms ease-out;
box-shadow: 0 4px 9px -3px rgba(102, 136, 153, 0.15);
background-color: #ffffff;
padding: 4px;
padding-left: 16px;
opacity: 0;
display: block;
pointer-events: none; // so tooltip doesn't remain after button hides
}

&[data-show="true"] {
opacity: 1;
display: block;
pointer-events: auto;
}

button {
padding: 0 8px 0 6px;
color: #2096f3;
border-color: #50acf6;

// smaller on desktop
@media (min-width: 768px) {
font-size: 12px;
height: 27px;
}

&:hover, &:focus, &:active {
background-color: #eef7fe;
}

i {
margin-right: 3px;
}
}

.ant-badge-count {
min-width: 15px;
height: 15px;
padding: 0 5px;
font-size: 10px;
line-height: 15px;
background: #f77b74;
border-radius: 7px;
box-shadow: 0px 0px 0 1px white, -1px 1px 0 1px #5d6f7d85;
}
}
}
202 changes: 202 additions & 0 deletions client/app/components/Parameters.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import React from 'react';
import PropTypes from 'prop-types';
import { size, filter, forEach, extend } from 'lodash';
import { react2angular } from 'react2angular';
import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc';
import { $location } from '@/services/ng';
import { Parameter } from '@/services/query';
import ParameterApplyButton from '@/components/ParameterApplyButton';
import ParameterValueInput from '@/components/ParameterValueInput';
import EditParameterSettingsDialog from './EditParameterSettingsDialog';

import './Parameters.less';

const DragHandle = sortableHandle(({ parameterName }) => (
<div className="drag-handle" data-test={`DragHandle-${parameterName}`} />
));

const SortableItem = sortableElement(({ className, parameterName, disabled, children }) => (
<div className={className} data-editable={!disabled || null}>
{!disabled && <DragHandle parameterName={parameterName} />}
{children}
</div>
));
const SortableContainer = sortableContainer(({ children }) => children);

function updateUrl(parameters) {
const params = extend({}, $location.search());
parameters.forEach((param) => {
extend(params, param.toUrlParams());
});
Object.keys(params).forEach(key => params[key] == null && delete params[key]);
$location.search(params);
}

export class Parameters extends React.Component {
static propTypes = {
parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)),
editable: PropTypes.bool,
disableUrlUpdate: PropTypes.bool,
onValuesChange: PropTypes.func,
onParametersEdit: PropTypes.func,
};

static defaultProps = {
parameters: [],
editable: false,
disableUrlUpdate: false,
onValuesChange: () => {},
onParametersEdit: () => {},
}

constructor(props) {
super(props);
const { parameters } = props;
this.state = { parameters, dragging: false };
if (!props.disableUrlUpdate) {
updateUrl(parameters);
}
}

componentDidUpdate = (prevProps) => {
const { parameters, disableUrlUpdate } = this.props;
if (prevProps.parameters !== parameters) {
this.setState({ parameters });
if (!disableUrlUpdate) {
updateUrl(parameters);
}
}
};

handleKeyDown = (e) => {
// Cmd/Ctrl/Alt + Enter
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey || e.altKey)) {
e.stopPropagation();
gabrieldutra marked this conversation as resolved.
Show resolved Hide resolved
this.applyChanges();
}
};

setPendingValue = (param, value, isDirty) => {
this.setState(({ parameters }) => {
if (isDirty) {
param.setPendingValue(value);
} else {
param.clearPendingValue();
}
return { parameters };
});
};

moveParameter = ({ oldIndex, newIndex }) => {
const { onParametersEdit } = this.props;
if (oldIndex !== newIndex) {
this.setState(({ parameters }) => {
parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]);
onParametersEdit();
return { parameters };
});
}
this.setState({ dragging: false });
};

onBeforeSortStart = () => {
this.setState({ dragging: true });
};

applyChanges = () => {
const { onValuesChange, disableUrlUpdate } = this.props;
this.setState(({ parameters }) => {
forEach(parameters, p => p.applyPendingValue());
onValuesChange();
if (!disableUrlUpdate) {
updateUrl(parameters);
}
return { parameters };
});
};

showParameterSettings = (parameter, index) => {
const { onParametersEdit } = this.props;
EditParameterSettingsDialog
.showModal({ parameter })
.result.then((updated) => {
this.setState(({ parameters }) => {
const updatedParameter = extend(parameter, updated);
parameters[index] = new Parameter(updatedParameter, updatedParameter.parentQueryId);
Copy link
Member Author

Choose a reason for hiding this comment

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

Same idea here, but reconstructing the Parameter instead.

onParametersEdit();
return { parameters };
});
});
};

renderParameter(param, index) {
const { editable } = this.props;
return (
<div
key={param.name}
className="di-block"
data-test={`ParameterName-${param.name}`}
>
<div className="parameter-heading">
<label>{param.title || param.name}</label>
{editable && (
<button
className="btn btn-default btn-xs m-l-5"
onClick={() => this.showParameterSettings(param, index)}
data-test={`ParameterSettings-${param.name}`}
type="button"
>
<i className="fa fa-cog" />
</button>
)}
</div>
<ParameterValueInput
type={param.type}
value={param.normalizedValue}
parameter={param}
enumOptions={param.enumOptions}
queryId={param.queryId}
allowMultipleValues={!!param.multiValuesOptions}
onSelect={(value, isDirty) => this.setPendingValue(param, value, isDirty)}
/>
</div>
);
}

render() {
const { parameters, dragging } = this.state;
const { editable } = this.props;
const dirtyParamCount = size(filter(parameters, 'hasPendingValue'));
return (
<SortableContainer
axis="xy"
useDragHandle
lockToContainerEdges
helperClass="parameter-dragged"
updateBeforeSortStart={this.onBeforeSortStart}
onSortEnd={this.moveParameter}
>
<div
className="parameter-container"
onKeyDown={dirtyParamCount ? this.handleKeyDown : null}
data-draggable={editable || null}
data-dragging={dragging || null}
>
{parameters.map((param, index) => (
<SortableItem className="parameter-block" key={param.name} index={index} parameterName={param.name} disabled={!editable}>
{this.renderParameter(param, index)}
</SortableItem>
))}

<ParameterApplyButton onClick={this.applyChanges} paramCount={dirtyParamCount} />
</div>
</SortableContainer>
);
}
}

export default function init(ngModule) {
ngModule.component('parameters', react2angular(Parameters));
}

init.init = true;
Loading