Skip to content

Commit

Permalink
Merge remote-tracking branch 'fork/master' into user-edit
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrieldutra committed Feb 2, 2019
2 parents d891df1 + 10b5c03 commit 0262e08
Show file tree
Hide file tree
Showing 116 changed files with 1,338 additions and 993 deletions.
2 changes: 1 addition & 1 deletion .circleci/Dockerfile.cypress
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM cypress/browsers:chrome67
ENV APP /usr/src/app
WORKDIR $APP

RUN npm install --no-save cypress @percy/cypress > /dev/null
RUN npm install --no-save puppeteer@1.10.0 cypress@^3.1.5 @percy/cypress@^0.2.3 > /dev/null

COPY cypress $APP/cypress
COPY cypress.json $APP/cypress.json
Expand Down
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ jobs:
command: |
npm run cypress start
docker-compose run cypress node ./cypress/cypress.js db-seed
# Make sure the API key is the same so Percy snapshots are consistent
docker-compose -p cypress run postgres psql -U postgres -h postgres -c "update users set api_key = 'secret' where email ='admin@redash.io';"
- run:
name: Execute Cypress tests
command: npm run cypress run-ci
Expand Down
42 changes: 29 additions & 13 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
engines:
version: "2"
checks:
complex-logic:
enabled: false
file-lines:
enabled: false
method-complexity:
enabled: false
method-count:
enabled: false
method-lines:
config:
threshold: 100
nested-control-flow:
enabled: false
identical-code:
enabled: false
plugins:
pep8:
enabled: true
eslint:
enabled: true
channel: "eslint-3"
channel: "eslint-5"
config:
config: client/.eslintrc.js
checks:
import/no-unresolved:
enabled: false
ratings:
paths:
- "redash/**/*.py"
- "client/**/*.js"
exclude_paths:
- tests/**/*.py
- migrations/**/*.py
- old_migrations/**/*.py
- setup/**/*
- bin/**/*

no-multiple-empty-lines: # TODO: Enable
enabled: false
exclude_patterns:
- "tests/**/*.py"
- "migrations/**/*.py"
- "setup/**/*"
- "bin/**/*"
- "**/node_modules/"
- "client/dist/"
- "**/*.pyc"
13 changes: 12 additions & 1 deletion client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,24 @@ module.exports = {
"no-lonely-if": "off",
"consistent-return": "off",
"no-control-regex": "off",
'no-multiple-empty-lines': 'warn',
"no-script-url": "off", // some <a> tags should have href="javascript:void(0)"
'operator-linebreak': 'off',
'react/destructuring-assignment': 'off',
"react/jsx-filename-extension": "off",
'react/jsx-one-expression-per-line': 'off',
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
'react/jsx-wrap-multilines': 'warn',
'react/no-access-state-in-setstate': 'warn',
"react/prefer-stateless-function": "warn",
"react/forbid-prop-types": "warn",
"react/prop-types": "warn",
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/label-has-associated-control": ["warn", {
"controlComponents": true
}],
"jsx-a11y/label-has-for": "off",
"jsx-a11y/no-static-element-interactions": "off",
"max-len": ['error', 120, 2, {
Expand All @@ -43,6 +52,8 @@ module.exports = {
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
}]
}],
"no-else-return": ["error", {"allowElseIf": true}],
"object-curly-newline": ["error", {"consistent": true}],
}
};
16 changes: 15 additions & 1 deletion client/app/assets/less/redash/redash-newstyle.less
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ body {
.label-tag-archived,
.label-tag {
margin-right: 3px;
display: inline-block;
display: inline;
margin-top: 2px;
max-width: 24ch;
.text-overflow();
Expand Down Expand Up @@ -940,3 +940,17 @@ text.slicetext {
}
}

.ui-select-choices-row > span {
background-color: inherit !important;
}

.list-group-item.inactive,
.ui-select-choices-row.disabled {
background-color: #eee !important;
border-color: transparent;
opacity: 0.5;
box-shadow: none;
color: #333;
pointer-events: none;
cursor: not-allowed;
}
7 changes: 6 additions & 1 deletion client/app/assets/less/redash/tags-control.less
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

&.inline-tags-control {
display: inline-block;
vertical-align: middle;
}
}

// This is for using .inline-tags-control in Angular which renders
// a little differently than React (e.g. in Alert.html)
.inline-tags-control .tags-control {
display: inline-block;
}
1 change: 0 additions & 1 deletion client/app/components/DateTimeRangeInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,3 @@ export default function init(ngModule) {
}

init.init = true;

16 changes: 8 additions & 8 deletions client/app/components/EditInPlace.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class EditInPlace extends React.Component {
placeholder: '',
value: '',
};

constructor(props) {
super(props);
this.state = {
Expand Down Expand Up @@ -67,14 +68,13 @@ export class EditInPlace extends React.Component {
</span>
);

renderEdit = () =>
React.createElement(this.props.editor, {
ref: this.inputRef,
className: 'rd-form-control',
defaultValue: this.props.value,
onBlur: this.stopEditing,
onKeyDown: this.keyDown,
});
renderEdit = () => React.createElement(this.props.editor, {
ref: this.inputRef,
className: 'rd-form-control',
defaultValue: this.props.value,
onBlur: this.stopEditing,
onKeyDown: this.keyDown,
});

render() {
return (
Expand Down
77 changes: 77 additions & 0 deletions client/app/components/FavoritesControl.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import { $rootScope } from '@/services/ng';

function toggleItem(event, item, callback) {
event.preventDefault();
event.stopPropagation();

const action = item.is_favorite ? item.$unfavorite.bind(item) : item.$favorite.bind(item);
const savedIsFavorite = item.is_favorite;

action().then(() => {
item.is_favorite = !savedIsFavorite;
$rootScope.$broadcast('reloadFavorites');
callback();
});
}

export function FavoritesControl({ item, onChange }) {
const icon = item.is_favorite ? 'fa fa-star' : 'fa fa-star-o';
const title = item.is_favorite ? 'Remove from favorites' : 'Add to favorites';
return (
<a
href="javascript:void(0)"
title={title}
className="btn-favourite"
onClick={event => toggleItem(event, item, onChange)}
>
<i className={icon} aria-hidden="true" />
</a>
);
}

FavoritesControl.propTypes = {
item: PropTypes.shape({
is_favorite: PropTypes.bool.isRequired,
}).isRequired,
onChange: PropTypes.func,
// Force component update when `item` changes.
// Remove this when `react2angular` will finally go to hell
forceUpdate: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
};

FavoritesControl.defaultProps = {
onChange: () => {},
};

export default function init(ngModule) {
ngModule.component('favoritesControlImpl', react2angular(FavoritesControl));
ngModule.component('favoritesControl', {
template: `
<favorites-control-impl
ng-if="$ctrl.item"
item="$ctrl.item"
on-change="$ctrl.onChange"
force-update="$ctrl.forceUpdateTag"
></favorites-control-impl>
`,
bindings: {
item: '=',
},
controller($scope) {
// See comment for FavoritesControl.propTypes.forceUpdate
this.forceUpdateTag = 'force' + Date.now();
$scope.$on('reloadFavorites', () => {
this.forceUpdateTag = 'force' + Date.now();
});

this.onChange = () => {
$scope.$applyAsync();
};
},
});
}

init.init = true;
24 changes: 13 additions & 11 deletions client/app/components/ParameterMappingInput.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint react/no-multi-comp: 0 */

import { extend, map, includes, findIndex, find, fromPairs, isNull, isUndefined } from 'lodash';
import { extend, map, includes, findIndex, find, fromPairs } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import Select from 'antd/lib/select';
Expand Down Expand Up @@ -127,11 +127,11 @@ export class ParameterMappingInput extends React.Component {
value={mapping.mapTo}
onChange={event => this.updateParamMapping(mapping, { mapTo: event.target.value })}
/>
{ alreadyExists &&
<div className="help-block">
Dashboard parameter with this name already exists
</div>
}
{ alreadyExists && (
<div className="help-block">
Dashboard parameter with this name already exists
</div>
)}
</div>
);
}
Expand Down Expand Up @@ -159,11 +159,12 @@ export class ParameterMappingInput extends React.Component {
const { mapping } = this.props;
return (
<div className="m-t-10">
<label>Change parameter value:</label>
<label htmlFor="parameter-value-input">Change parameter value:</label>
<ParameterValueInput
id="parameter-value-input"
className="w-100"
type={mapping.param.type}
value={isUndefined(mapping.value) || isNull(mapping.value) ? mapping.param.normalizedValue : mapping.value}
value={mapping.param.normalizedValue}
enumOptions={mapping.param.enumOptions}
queryId={mapping.param.queryId}
onSelect={value => this.updateParamMapping(mapping, { value })}
Expand Down Expand Up @@ -191,8 +192,9 @@ export class ParameterMappingInput extends React.Component {
}
return (
<div className="m-t-10">
<label>Change parameter title (leave empty to use existing):</label>
<label htmlFor="parameter-title">Change parameter title (leave empty to use existing):</label>
<input
id="parameter-title"
type="text"
className="form-control"
value={mapping.title}
Expand Down Expand Up @@ -260,8 +262,8 @@ export class ParameterMappingListInput extends React.Component {
<div>
{this.props.mappings.map((mapping, index) => {
const existingParamsNames = this.props.existingParams
.filter(({ type }) => type === mapping.param.type) // exclude mismatching param types
.map(({ name }) => name); // keep names only
.filter(({ type }) => type === mapping.param.type) // exclude mismatching param types
.map(({ name }) => name); // keep names only

return (
<div key={mapping.name} className={(index === 0 ? '' : ' m-t-15')}>
Expand Down
34 changes: 23 additions & 11 deletions client/app/components/ParameterValueInput.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { isNull, isUndefined } from 'lodash';
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';
import { DateInput } from './DateInput';
import { DateRangeInput } from './DateRangeInput';
import { DateTimeInput } from './DateTimeInput';
Expand Down Expand Up @@ -127,13 +128,23 @@ export class ParameterValueInput extends React.Component {
);
}

renderNumberInput() {
const { value, onSelect, className } = this.props;
return (
<InputNumber
className={'form-control ' + className}
defaultValue={!isNaN(value) && value || 0}
onChange={onSelect}
/>
);
}

renderTextInput() {
const { value, onSelect, type } = this.props;
const { value, onSelect, className } = this.props;
return (
<input
type={type}
className={'form-control ' + this.props.className}
value={isNull(value) || isUndefined(value) ? '' : value}
<Input
className={'form-control ' + className}
defaultValue={value || ''}
onChange={event => onSelect(event.target.value)}
/>
);
Expand All @@ -150,6 +161,7 @@ export class ParameterValueInput extends React.Component {
case 'date-range': return this.renderDateRangeInput();
case 'enum': return this.renderEnumInput();
case 'query': return this.renderQueryBasedInput();
case 'number': return this.renderNumberInput();
default: return this.renderTextInput();
}
}
Expand All @@ -158,12 +170,12 @@ 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"
enum-options="$ctrl.param.enumOptions"
<parameter-value-input-impl
type="$ctrl.param.type"
value="$ctrl.param.normalizedValue"
enum-options="$ctrl.param.enumOptions"
query-id="$ctrl.param.queryId"
on-select="$ctrl.setValue"
on-select="$ctrl.setValue"
></parameter-value-input-impl>
`,
bindings: {
Expand Down
Loading

0 comments on commit 0262e08

Please sign in to comment.