ESLint: no-restricted-syntax (#10889)

* Enable no-restricted syntax rule

* Fix webpack.config.js

* Remove unused function from utils/common.js

* Refactor triple nested for loop

* Fix loops in src/explore components

* Fix loops in SqlLab components

* Fix loops in AlteredSliceTag

* Fix loops in FilterableTable

* Add fixtures and uinit tests for findControlItem

* Add license
This commit is contained in:
Kamil Gabryjelski 2020-09-18 18:05:57 +02:00 committed by GitHub
parent 2003442b32
commit ccfd293227
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 206 additions and 169 deletions

View File

@ -101,7 +101,6 @@ module.exports = {
'no-multi-spaces': 0,
'no-prototype-builtins': 0,
'no-restricted-properties': 0,
'no-restricted-syntax': 0,
'no-restricted-imports': [
'error',
{
@ -215,7 +214,6 @@ module.exports = {
'no-multi-spaces': 0,
'no-prototype-builtins': 0,
'no-restricted-properties': 0,
'no-restricted-syntax': 0,
'no-restricted-imports': [
'error',
{

View File

@ -17,19 +17,20 @@
* under the License.
*/
import React from 'react';
import {
getChartControlPanelRegistry,
ColumnOption,
t,
} from '@superset-ui/core';
import { getChartControlPanelRegistry, t } from '@superset-ui/core';
import {
getControlConfig,
getControlState,
getFormDataFromControls,
applyMapStateToPropsToControl,
getAllControlsState,
findControlItem,
} from 'src/explore/controlUtils';
import {
controlPanelSectionsChartOptions,
controlPanelSectionsChartOptionsOnlyColorScheme,
controlPanelSectionsChartOptionsTable,
} from 'spec/javascripts/explore/fixtures';
describe('controlUtils', () => {
const state = {
@ -43,56 +44,10 @@ describe('controlUtils', () => {
beforeAll(() => {
getChartControlPanelRegistry()
.registerValue('test-chart', {
controlPanelSections: [
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [
[
'color_scheme',
{
name: 'rose_area_proportion',
config: {
type: 'CheckboxControl',
label: t('Use Area Proportions'),
description: t(
'Check if the Rose Chart should use segment area instead of ' +
'segment radius for proportioning',
),
default: false,
renderTrigger: true,
},
},
],
[
{
name: 'stacked_style',
config: {
type: 'SelectControl',
label: t('Stacked Style'),
renderTrigger: true,
choices: [
['stack', 'stack'],
['stream', 'stream'],
['expand', 'expand'],
],
default: 'stack',
description: '',
},
},
],
],
},
],
controlPanelSections: controlPanelSectionsChartOptions,
})
.registerValue('test-chart-override', {
controlPanelSections: [
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [['color_scheme']],
},
],
controlPanelSections: controlPanelSectionsChartOptionsOnlyColorScheme,
controlOverrides: {
color_scheme: {
label: t('My beautiful colors'),
@ -100,40 +55,7 @@ describe('controlUtils', () => {
},
})
.registerValue('table', {
controlPanelSections: [
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [
[
'metric',
'metrics',
{
name: 'all_columns',
config: {
type: 'SelectControl',
queryField: 'columns',
multi: true,
label: t('Columns'),
default: [],
description: t('Columns to display'),
optionRenderer: c => <ColumnOption column={c} showType />,
valueRenderer: c => <ColumnOption column={c} />,
valueKey: 'column_name',
allowAll: true,
mapStateToProps: stateRef => ({
options: stateRef.datasource
? stateRef.datasource.columns
: [],
}),
commaChoosesOption: false,
freeForm: true,
},
},
],
],
},
],
controlPanelSections: controlPanelSectionsChartOptionsTable,
});
});
@ -287,4 +209,38 @@ describe('controlUtils', () => {
});
});
});
describe('findControlItem', () => {
it('find control as a string', () => {
const controlItem = findControlItem(
controlPanelSectionsChartOptions,
'color_scheme',
);
expect(controlItem).toEqual('color_scheme');
});
it('find control as a control object', () => {
let controlItem = findControlItem(
controlPanelSectionsChartOptions,
'rose_area_proportion',
);
expect(controlItem.name).toEqual('rose_area_proportion');
expect(controlItem).toHaveProperty('config');
controlItem = findControlItem(
controlPanelSectionsChartOptions,
'stacked_style',
);
expect(controlItem.name).toEqual('stacked_style');
expect(controlItem).toHaveProperty('config');
});
it('returns null when key is not found', () => {
const controlItem = findControlItem(
controlPanelSectionsChartOptions,
'non_existing_key',
);
expect(controlItem).toBeNull();
});
});
});

View File

@ -0,0 +1,104 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { ColumnOption, t } from '@superset-ui/core';
export const controlPanelSectionsChartOptions = [
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [
[
'color_scheme',
{
name: 'rose_area_proportion',
config: {
type: 'CheckboxControl',
label: t('Use Area Proportions'),
description: t(
'Check if the Rose Chart should use segment area instead of ' +
'segment radius for proportioning',
),
default: false,
renderTrigger: true,
},
},
],
[
{
name: 'stacked_style',
config: {
type: 'SelectControl',
label: t('Stacked Style'),
renderTrigger: true,
choices: [
['stack', 'stack'],
['stream', 'stream'],
['expand', 'expand'],
],
default: 'stack',
description: '',
},
},
],
],
},
];
export const controlPanelSectionsChartOptionsOnlyColorScheme = [
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [['color_scheme']],
},
];
export const controlPanelSectionsChartOptionsTable = [
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [
[
'metric',
'metrics',
{
name: 'all_columns',
config: {
type: 'SelectControl',
queryField: 'columns',
multi: true,
label: t('Columns'),
default: [],
description: t('Columns to display'),
optionRenderer: c => <ColumnOption column={c} showType />,
valueRenderer: c => <ColumnOption column={c} />,
valueKey: 'column_name',
allowAll: true,
mapStateToProps: stateRef => ({
options: stateRef.datasource ? stateRef.datasource.columns : [],
}),
commaChoosesOption: false,
freeForm: true,
},
},
],
],
},
];

View File

@ -173,12 +173,9 @@ class TabbedSqlEditors extends React.PureComponent {
UNSAFE_componentWillReceiveProps(nextProps) {
const nextActiveQeId =
nextProps.tabHistory[nextProps.tabHistory.length - 1];
const queriesArray = [];
for (const id in nextProps.queries) {
if (nextProps.queries[id].sqlEditorId === nextActiveQeId) {
queriesArray.push(nextProps.queries[id]);
}
}
const queriesArray = Object.values(nextProps.queries).filter(
query => query.sqlEditorId === nextActiveQeId,
);
if (!areArraysShallowEqual(queriesArray, this.state.queriesArray)) {
this.setState({ queriesArray });
}

View File

@ -110,10 +110,9 @@ class TableElement extends React.PureComponent {
/>
);
}
let latest = [];
for (const k in table.partitions.latest) {
latest.push(`${k}=${table.partitions.latest[k]}`);
}
let latest = Object.entries(table.partitions.latest).map(
([key, value]) => `${key}=${value}`,
);
latest = latest.join('/');
header = (
<Well bsSize="small">

View File

@ -493,8 +493,7 @@ export default function sqlLabReducer(state = {}, action) {
// Fetch the updates to the queries present in the store.
let change = false;
let { queriesLastUpdate } = state;
for (const id in action.alteredQueries) {
const changedQuery = action.alteredQueries[id];
Object.entries(action.alteredQueries).forEach(([id, changedQuery]) => {
if (
!state.queries.hasOwnProperty(id) ||
(state.queries[id].state !== 'stopped' &&
@ -506,7 +505,7 @@ export default function sqlLabReducer(state = {}, action) {
newQueries[id] = { ...state.queries[id], ...changedQuery };
change = true;
}
}
});
if (!change) {
newQueries = state.queries;
}

View File

@ -76,19 +76,17 @@ export default class AlteredSliceTag extends React.Component {
const fdKeys = Object.keys(cfd);
const diffs = {};
for (const fdKey of fdKeys) {
// Ignore values that are undefined/nonexisting in either
fdKeys.forEach(fdKey => {
if (!ofd[fdKey] && !cfd[fdKey]) {
continue;
return;
}
// Ignore obsolete legacy filters
if (['filters', 'having', 'having_filters', 'where'].includes(fdKey)) {
continue;
return;
}
if (!this.isEqualish(ofd[fdKey], cfd[fdKey])) {
diffs[fdKey] = { before: ofd[fdKey], after: cfd[fdKey] };
}
}
});
return diffs;
}
@ -149,7 +147,7 @@ export default class AlteredSliceTag extends React.Component {
renderRows() {
const { diffs } = this.state;
const rows = [];
for (const key in diffs) {
Object.entries(diffs).forEach(([key, diff]) => {
rows.push(
<Tr key={key}>
<Td
@ -160,11 +158,11 @@ export default class AlteredSliceTag extends React.Component {
key
}
/>
<Td column="before">{this.formatValue(diffs[key].before, key)}</Td>
<Td column="after">{this.formatValue(diffs[key].after, key)}</Td>
<Td column="before">{this.formatValue(diff.before, key)}</Td>
<Td column="after">{this.formatValue(diff.after, key)}</Td>
</Tr>,
);
}
});
return rows;
}

View File

@ -26,11 +26,11 @@ import {
Grid,
ScrollSync,
SortDirection,
SortDirectionType,
SortIndicator,
Table,
SortDirectionType,
} from 'react-virtualized';
import { t, getMultipleTextDimensions } from '@superset-ui/core';
import { getMultipleTextDimensions, t } from '@superset-ui/core';
import Button from '../Button';
import CopyToClipboard from '../CopyToClipboard';
@ -241,24 +241,22 @@ export default class FilterableTable extends PureComponent<
}
formatTableData(data: Record<string, unknown>[]): Datum[] {
const formattedData = data.map(row => {
return data.map(row => {
const newRow = {};
for (const k in row) {
const val = row[k];
Object.entries(row).forEach(([key, val]) => {
if (['string', 'number'].indexOf(typeof val) >= 0) {
newRow[k] = val;
newRow[key] = val;
} else {
newRow[k] = val === null ? null : JSONbig.stringify(val);
newRow[key] = val === null ? null : JSONbig.stringify(val);
}
}
});
return newRow;
});
return formattedData;
}
hasMatch(text: string, row: Datum) {
const values = [];
for (const key in row) {
const values: string[] = [];
Object.keys(row).forEach(key => {
if (row.hasOwnProperty(key)) {
const cellValue = row[key];
if (typeof cellValue === 'string') {
@ -270,7 +268,7 @@ export default class FilterableTable extends PureComponent<
values.push(cellValue.toString());
}
}
}
});
const lowerCaseText = text.toLowerCase();
return values.some(v => v.includes(lowerCaseText));
}

View File

@ -293,20 +293,18 @@ class ExploreViewContainer extends React.Component {
renderErrorMessage() {
// Returns an error message as a node if any errors are in the store
const errors = [];
const ctrls = this.props.controls;
for (const controlName in this.props.controls) {
const control = this.props.controls[controlName];
if (control.validationErrors && control.validationErrors.length > 0) {
errors.push(
<div key={controlName}>
{t('Control labeled ')}
<strong>{` "${control.label}" `}</strong>
{control.validationErrors.join('. ')}
</div>,
);
}
}
const errors = Object.entries(this.props.controls)
.filter(
([, control]) =>
control.validationErrors && control.validationErrors.length > 0,
)
.map(([key, control]) => (
<div key={key}>
{t('Control labeled ')}
<strong>{` "${control.label}" `}</strong>
{control.validationErrors.join('. ')}
</div>
));
let errorMessage;
if (errors.length > 0) {
errorMessage = <div style={{ textAlign: 'left' }}>{errors}</div>;

View File

@ -103,7 +103,7 @@ export default class SelectControl extends React.PureComponent {
if (opt) {
if (this.props.multi) {
optionValue = [];
for (const o of opt) {
opt.forEach(o => {
// select all options
if (o.meta === true) {
this.props.onChange(
@ -114,7 +114,7 @@ export default class SelectControl extends React.PureComponent {
return;
}
optionValue.push(o[this.props.valueKey] || o);
}
});
} else if (opt.meta === true) {
return;
} else {

View File

@ -51,22 +51,19 @@ export function validateControl(control, processedState) {
/**
* Find control item from control panel config.
*/
function findControlItem(controlPanelSections, controlKey) {
for (const section of controlPanelSections) {
for (const controlArr of section.controlSetRows) {
for (const control of controlArr) {
if (controlKey === control) return control;
if (
control !== null &&
typeof control === 'object' &&
control.name === controlKey
) {
return control;
}
}
}
}
return null;
export function findControlItem(controlPanelSections, controlKey) {
return (
controlPanelSections
.map(section => section.controlSetRows)
.flat(2)
.find(
control =>
controlKey === control ||
(control !== null &&
typeof control === 'object' &&
control.name === controlKey),
) ?? null
);
}
export const getControlConfig = memoizeOne(function getControlConfig(

View File

@ -82,14 +82,6 @@ export function getShortUrl(longUrl) {
);
}
export function supersetURL(rootUrl, getParams = {}) {
const url = new URL(rootUrl, window.location.origin);
for (const k in getParams) {
url.searchParams.set(k, getParams[k]);
}
return url.href;
}
export function optionLabel(opt) {
if (opt === null) {
return NULL_STRING;

View File

@ -78,7 +78,7 @@ const plugins = [
// }
// }
const entryFiles = {};
for (const [entry, chunks] of Object.entries(entrypoints)) {
Object.entries(entrypoints).forEach(([entry, chunks]) => {
entryFiles[entry] = {
css: chunks
.filter(x => x.endsWith('.css'))
@ -87,7 +87,8 @@ const plugins = [
.filter(x => x.endsWith('.js'))
.map(x => path.join(output.publicPath, x)),
};
}
});
return {
...seed,
entrypoints: entryFiles,
@ -430,7 +431,7 @@ if (isDevMode) {
// find all the symlinked plugins and use their source code for imports
let hasSymlink = false;
for (const [pkg, version] of Object.entries(packageConfig.dependencies)) {
Object.entries(packageConfig.dependencies).forEach(([pkg, version]) => {
const srcPath = `./node_modules/${pkg}/src`;
if (/superset-ui/.test(pkg) && fs.existsSync(srcPath)) {
console.log(
@ -441,7 +442,7 @@ if (isDevMode) {
config.resolve.alias[`${pkg}$`] = `${pkg}/src`;
hasSymlink = true;
}
}
});
if (hasSymlink) {
console.log(''); // pure cosmetic new line
}