fix: hundreds lint warning (#447)

* fix: lint

* fix: more lints

* fix: more lints

* fix: comment

* fix: any

* fix: ChartDataProvider test lint

* fix: lints

* fix: chartprops
This commit is contained in:
Krist Wongsuphasawat 2020-05-07 12:53:36 -07:00 committed by Yongjie Zhao
parent 6d7de01abe
commit 67e15d011a
21 changed files with 188 additions and 114 deletions

View File

@ -1,4 +1,4 @@
import { FunctionComponent, ComponentType } from 'react';
import { ComponentType } from 'react';
import { isRequired, Plugin } from '@superset-ui/core';
import { QueryFormData } from '@superset-ui/query';
import ChartMetadata from './ChartMetadata';
@ -18,7 +18,7 @@ const EMPTY = {};
export type PromiseOrValue<T> = Promise<T> | T;
export type PromiseOrValueLoader<T> = () => PromiseOrValue<T>;
export type ChartType = ComponentType<any> | FunctionComponent<any>;
export type ChartType = ComponentType<any>;
type ValueOrModuleWithValue<T> = T | { default: T };
interface ChartPluginConfig<T extends QueryFormData> {

View File

@ -4,16 +4,21 @@ import ChartClient from '../../src/clients/ChartClient';
import ChartDataProvider, { Props } from '../../src/components/ChartDataProvider';
import { bigNumberFormData } from '../fixtures/formData';
// Note: the mock implentatino of these function directly affects the expected results below
const defaultMockLoadFormData = jest.fn(allProps => Promise.resolve(allProps.formData));
// Note: the mock implementation of these function directly affects the expected results below
const defaultMockLoadFormData = jest.fn(({ formData }: { formData: unknown }) =>
Promise.resolve(formData),
);
// coerce here else get: Type 'Mock<Promise<any>, []>' is not assignable to type 'Mock<Promise<any>, any[]>'
let mockLoadFormData = defaultMockLoadFormData as jest.Mock<Promise<any>, any>;
const mockLoadDatasource = jest.fn(datasource => Promise.resolve(datasource)) as jest.Mock<
Promise<any>,
any
>;
const mockLoadQueryData = jest.fn(input => Promise.resolve(input)) as jest.Mock<Promise<any>, any>;
type MockLoadFormData = typeof defaultMockLoadFormData | jest.Mock<Promise<unknown>, unknown[]>;
let mockLoadFormData: MockLoadFormData = defaultMockLoadFormData;
function createPromise<T>(input: T, ..._args: unknown[]) {
return Promise.resolve(input);
}
const mockLoadDatasource = jest.fn<Promise<unknown>, unknown[]>(createPromise);
const mockLoadQueryData = jest.fn<Promise<unknown>, unknown[]>(createPromise);
// ChartClient is now a mock
jest.mock('../../src/clients/ChartClient', () =>
@ -177,7 +182,7 @@ describe('ChartDataProvider', () => {
describe('children', () => {
it('calls children({ loading: true }) when loading', () => {
const children = jest.fn();
const children = jest.fn<React.ReactNode, unknown[]>();
setup({ children });
// during the first tick (before more promises resolve) loading is true
@ -188,7 +193,7 @@ describe('ChartDataProvider', () => {
it('calls children({ payload }) when loaded', () => {
return new Promise(done => {
expect.assertions(2);
const children = jest.fn();
const children = jest.fn<React.ReactNode, unknown[]>();
setup({ children, loadDatasource: true });
setTimeout(() => {
@ -208,7 +213,7 @@ describe('ChartDataProvider', () => {
it('calls children({ error }) upon request error', () => {
return new Promise(done => {
expect.assertions(2);
const children = jest.fn();
const children = jest.fn<React.ReactNode, unknown[]>();
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
setup({ children });
@ -224,7 +229,7 @@ describe('ChartDataProvider', () => {
it('calls children({ error }) upon JS error', () => {
return new Promise(done => {
expect.assertions(2);
const children = jest.fn();
const children = jest.fn<React.ReactNode, unknown[]>();
mockLoadFormData = jest.fn(() => {
throw new Error('non-async error');
@ -245,7 +250,7 @@ describe('ChartDataProvider', () => {
it('calls onLoad(payload) when loaded', () => {
return new Promise(done => {
expect.assertions(2);
const onLoaded = jest.fn();
const onLoaded = jest.fn<void, unknown[]>();
setup({ onLoaded, loadDatasource: true });
setTimeout(() => {
@ -263,7 +268,7 @@ describe('ChartDataProvider', () => {
it('calls onError(error) upon request error', () => {
return new Promise(done => {
expect.assertions(2);
const onError = jest.fn();
const onError = jest.fn<void, unknown[]>();
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
setup({ onError });
@ -278,7 +283,7 @@ describe('ChartDataProvider', () => {
it('calls onError(error) upon JS error', () => {
return new Promise(done => {
expect.assertions(2);
const onError = jest.fn();
const onError = jest.fn<void, unknown[]>();
mockLoadFormData = jest.fn(() => {
throw new Error('non-async error');

View File

@ -17,7 +17,7 @@ export const TestComponent = ({
width,
height,
}: {
formData?: any;
formData?: unknown;
message?: string;
width?: number;
height?: number;
@ -67,6 +67,10 @@ export class DiligentChartPlugin extends ChartPlugin<QueryFormData> {
}
}
function identity<T>(x: T) {
return x;
}
export class LazyChartPlugin extends ChartPlugin<QueryFormData> {
constructor() {
super({
@ -77,7 +81,7 @@ export class LazyChartPlugin extends ChartPlugin<QueryFormData> {
// this mirrors `() => import(module)` syntax
loadChart: () => Promise.resolve({ default: TestComponent }),
// promise without .default
loadTransformProps: () => Promise.resolve((x: any) => x),
loadTransformProps: () => Promise.resolve(identity),
});
}
}

View File

@ -10,7 +10,7 @@ describe('createLoadableRenderer', () => {
return <div className="test-component">test</div>;
}
let loadChartSuccess = jest.fn(() => Promise.resolve(TestComponent));
let render: (loaded: { [key: string]: any }) => JSX.Element;
let render: (loaded: { Chart: React.ComponentType }) => JSX.Element;
let loading: () => JSX.Element;
let LoadableRenderer: LoadableRendererType<{}, {}>;
let restoreConsole: RestoreConsole;

View File

@ -79,6 +79,7 @@ export default function callApi({
Object.keys(postPayload).forEach(key => {
const value = postPayload[key];
if (typeof value !== 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
formData.append(key, stringify ? JSON.stringify(value) : value);
}
});

View File

@ -11,8 +11,10 @@ export type FetchRetryOptions = {
};
export type Headers = { [k: string]: string };
export type Host = string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Json = { [k: string]: any };
export type Method = RequestInit['method'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type PostPayload = { [key: string]: any };
export type Mode = RequestInit['mode'];
export type Redirect = RequestInit['redirect'];

View File

@ -13,14 +13,14 @@ describe('SupersetClient', () => {
afterEach(SupersetClient.reset);
it('exposes reset, configure, init, get, post, isAuthenticated, and reAuthenticate methods', () => {
expect(SupersetClient.configure).toEqual(expect.any(Function));
expect(SupersetClient.init).toEqual(expect.any(Function));
expect(SupersetClient.get).toEqual(expect.any(Function));
expect(SupersetClient.post).toEqual(expect.any(Function));
expect(SupersetClient.isAuthenticated).toEqual(expect.any(Function));
expect(SupersetClient.reAuthenticate).toEqual(expect.any(Function));
expect(SupersetClient.request).toEqual(expect.any(Function));
expect(SupersetClient.reset).toEqual(expect.any(Function));
expect(typeof SupersetClient.configure).toBe('function');
expect(typeof SupersetClient.init).toBe('function');
expect(typeof SupersetClient.get).toBe('function');
expect(typeof SupersetClient.post).toBe('function');
expect(typeof SupersetClient.isAuthenticated).toBe('function');
expect(typeof SupersetClient.reAuthenticate).toBe('function');
expect(typeof SupersetClient.request).toBe('function');
expect(typeof SupersetClient.reset).toBe('function');
});
it('throws if you call init, get, post, isAuthenticated, or reAuthenticate before configure', () => {

View File

@ -100,7 +100,7 @@ describe('SupersetClientClass', () => {
return new SupersetClientClass({})
.init()
.then(throwIfCalled)
.catch(error => {
.catch((error: { status: number }) => {
expect(error.status).toBe(403);
return true;
@ -114,7 +114,7 @@ describe('SupersetClientClass', () => {
return new SupersetClientClass({})
.init()
.then(throwIfCalled)
.catch(error => {
.catch((error: unknown) => {
expect(error).toBeDefined();
return true;
@ -129,7 +129,7 @@ describe('SupersetClientClass', () => {
return new SupersetClientClass({})
.init()
.then(throwIfCalled)
.catch(error => {
.catch((error: unknown) => {
expect(error).toBeDefined();
return true;
@ -171,8 +171,10 @@ describe('SupersetClientClass', () => {
return client
.ensureAuth()
.then(throwIfCalled)
.catch(error => {
expect(error).toEqual(expect.objectContaining({ error: expect.any(String) }));
.catch((error: { error: string }) => {
expect(error).toEqual(
expect.objectContaining({ error: expect.any(String) }) as typeof error,
);
expect(client.isAuthenticated()).toBe(false);
return true;
@ -209,14 +211,14 @@ describe('SupersetClientClass', () => {
return client
.init()
.then(throwIfCalled)
.catch(error => {
expect(error).toEqual(expect.objectContaining(rejectValue));
.catch((error: unknown) => {
expect(error).toEqual(expect.objectContaining(rejectValue) as unknown);
return client
.ensureAuth()
.then(throwIfCalled)
.catch(error2 => {
expect(error2).toEqual(expect.objectContaining(rejectValue));
.catch((error2: unknown) => {
expect(error2).toEqual(expect.objectContaining(rejectValue) as unknown);
expect(client.isAuthenticated()).toBe(false);
// reset
@ -305,7 +307,7 @@ describe('SupersetClientClass', () => {
expect(fetchRequest.mode).toBe(clientConfig.mode);
expect(fetchRequest.credentials).toBe(clientConfig.credentials);
expect(fetchRequest.headers).toEqual(
expect.objectContaining(clientConfig.headers as Object),
expect.objectContaining(clientConfig.headers) as typeof fetchRequest.headers,
);
return true;
@ -379,7 +381,7 @@ describe('SupersetClientClass', () => {
expect(fetchRequest.mode).toBe(overrideConfig.mode);
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
expect(fetchRequest.headers).toEqual(
expect.objectContaining(overrideConfig.headers as Object),
expect.objectContaining(overrideConfig.headers) as typeof fetchRequest.headers,
);
return true;
@ -431,7 +433,7 @@ describe('SupersetClientClass', () => {
expect(fetchRequest.mode).toBe(overrideConfig.mode);
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
expect(fetchRequest.headers).toEqual(
expect.objectContaining(overrideConfig.headers as Object),
expect.objectContaining(overrideConfig.headers) as typeof fetchRequest.headers,
);
return true;
@ -456,15 +458,15 @@ describe('SupersetClientClass', () => {
it('passes postPayload key,values in the body', () => {
expect.assertions(3);
const postPayload = { number: 123, array: [1, 2, 3] } as any;
const postPayload = { number: 123, array: [1, 2, 3] };
const client = new SupersetClientClass({ protocol, host });
return client.init().then(() =>
client.post({ url: mockPostUrl, postPayload }).then(() => {
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData;
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
Object.keys(postPayload).forEach(key => {
expect(formData.get(key)).toBe(JSON.stringify(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(formData.get(key)).toBe(JSON.stringify(value));
});
return true;
@ -474,15 +476,15 @@ describe('SupersetClientClass', () => {
it('respects the stringify parameter for postPayload key,values', () => {
expect.assertions(3);
const postPayload = { number: 123, array: [1, 2, 3] } as any;
const postPayload = { number: 123, array: [1, 2, 3] };
const client = new SupersetClientClass({ protocol, host });
return client.init().then(() =>
client.post({ url: mockPostUrl, postPayload, stringify: false }).then(() => {
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData;
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
Object.keys(postPayload).forEach(key => {
expect(formData.get(key)).toBe(String(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(formData.get(key)).toBe(String(value));
});
return true;

View File

@ -88,7 +88,9 @@ describe('callApi()', () => {
expect(fetchParams.mode).toBe(mockRequest.mode);
expect(fetchParams.cache).toBe(mockRequest.cache);
expect(fetchParams.credentials).toBe(mockRequest.credentials);
expect(fetchParams.headers).toEqual(expect.objectContaining(mockRequest.headers as Object));
expect(fetchParams.headers).toEqual(
expect.objectContaining(mockRequest.headers) as typeof fetchParams.headers,
);
expect(fetchParams.redirect).toBe(mockRequest.redirect);
expect(fetchParams.signal).toBe(mockRequest.signal);
expect(fetchParams.body).toBe(mockRequest.body);
@ -101,7 +103,7 @@ describe('callApi()', () => {
describe('POST requests', () => {
it('encodes key,value pairs from postPayload', () => {
expect.assertions(3);
const postPayload = { key: 'value', anotherKey: 1237 } as any;
const postPayload = { key: 'value', anotherKey: 1237 };
return callApi({ url: mockPostUrl, method: 'POST', postPayload }).then(() => {
const calls = fetchMock.calls(mockPostUrl);
@ -110,8 +112,8 @@ describe('callApi()', () => {
const fetchParams = calls[0][1];
const body = fetchParams.body as FormData;
Object.keys(postPayload).forEach(key => {
expect(body.get(key)).toBe(JSON.stringify(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(body.get(key)).toBe(JSON.stringify(value));
});
return true;
@ -144,7 +146,7 @@ describe('callApi()', () => {
object: { a: 'a', 1: 1 },
null: null,
emptyString: '',
} as any;
};
expect.assertions(1 + 2 * Object.keys(postPayload).length);
@ -158,9 +160,9 @@ describe('callApi()', () => {
const stringified = calls[0][1].body as FormData;
const unstringified = calls[1][1].body as FormData;
Object.keys(postPayload).forEach(key => {
expect(stringified.get(key)).toBe(JSON.stringify(postPayload[key]));
expect(unstringified.get(key)).toBe(String(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(stringified.get(key)).toBe(JSON.stringify(value));
expect(unstringified.get(key)).toBe(String(value));
});
return true;
@ -171,7 +173,7 @@ describe('callApi()', () => {
describe('PUT requests', () => {
it('encodes key,value pairs from postPayload', () => {
expect.assertions(3);
const postPayload = { key: 'value', anotherKey: 1237 } as any;
const postPayload = { key: 'value', anotherKey: 1237 };
return callApi({ url: mockPutUrl, method: 'PUT', postPayload }).then(() => {
const calls = fetchMock.calls(mockPutUrl);
@ -180,8 +182,8 @@ describe('callApi()', () => {
const fetchParams = calls[0][1];
const body = fetchParams.body as FormData;
Object.keys(postPayload).forEach(key => {
expect(body.get(key)).toBe(JSON.stringify(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(body.get(key)).toBe(JSON.stringify(value));
});
return true;
@ -214,7 +216,7 @@ describe('callApi()', () => {
object: { a: 'a', 1: 1 },
null: null,
emptyString: '',
} as any;
};
expect.assertions(1 + 2 * Object.keys(postPayload).length);
@ -228,9 +230,9 @@ describe('callApi()', () => {
const stringified = calls[0][1].body as FormData;
const unstringified = calls[1][1].body as FormData;
Object.keys(postPayload).forEach(key => {
expect(stringified.get(key)).toBe(JSON.stringify(postPayload[key]));
expect(unstringified.get(key)).toBe(String(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(stringified.get(key)).toBe(JSON.stringify(value));
expect(unstringified.get(key)).toBe(String(value));
});
return true;
@ -241,7 +243,7 @@ describe('callApi()', () => {
describe('PATCH requests', () => {
it('encodes key,value pairs from postPayload', () => {
expect.assertions(3);
const postPayload = { key: 'value', anotherKey: 1237 } as any;
const postPayload = { key: 'value', anotherKey: 1237 };
return callApi({ url: mockPatchUrl, method: 'PATCH', postPayload }).then(() => {
const calls = fetchMock.calls(mockPatchUrl);
@ -250,8 +252,8 @@ describe('callApi()', () => {
const fetchParams = calls[0][1];
const body = fetchParams.body as FormData;
Object.keys(postPayload).forEach(key => {
expect(body.get(key)).toBe(JSON.stringify(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(body.get(key)).toBe(JSON.stringify(value));
});
return true;
@ -284,7 +286,7 @@ describe('callApi()', () => {
object: { a: 'a', 1: 1 },
null: null,
emptyString: '',
} as any;
};
expect.assertions(1 + 2 * Object.keys(postPayload).length);
@ -298,9 +300,9 @@ describe('callApi()', () => {
const stringified = calls[0][1].body as FormData;
const unstringified = calls[1][1].body as FormData;
Object.keys(postPayload).forEach(key => {
expect(stringified.get(key)).toBe(JSON.stringify(postPayload[key]));
expect(unstringified.get(key)).toBe(String(postPayload[key]));
Object.entries(postPayload).forEach(([key, value]) => {
expect(stringified.get(key)).toBe(JSON.stringify(value));
expect(unstringified.get(key)).toBe(String(value));
});
return true;
@ -389,7 +391,9 @@ describe('callApi()', () => {
const fetchParams = calls[1][1];
const headers = { 'If-None-Match': 'etag' };
expect(calls).toHaveLength(2);
expect(fetchParams.headers).toEqual(expect.objectContaining(headers));
expect(fetchParams.headers).toEqual(
expect.objectContaining(headers) as typeof fetchParams.headers,
);
return true;
});
@ -417,11 +421,13 @@ describe('callApi()', () => {
const mockCachedPayload = { status: 304 };
fetchMock.get(mockUncachedUrl, mockCachedPayload);
return callApi({ url: mockUncachedUrl, method: 'GET' }).catch(error => {
const calls = fetchMock.calls(mockUncachedUrl);
expect(calls).toHaveLength(1);
expect(error.message).toEqual('Received 304 but no content is cached!');
});
return callApi({ url: mockUncachedUrl, method: 'GET' }).catch(
(error: { message: string }) => {
const calls = fetchMock.calls(mockUncachedUrl);
expect(calls).toHaveLength(1);
expect(error.message).toEqual('Received 304 but no content is cached!');
},
);
});
it('returns original response if no Etag', async () => {
@ -431,7 +437,7 @@ describe('callApi()', () => {
expect(calls).toHaveLength(1);
expect(response.status).toEqual(200);
const body = await response.json();
expect(body).toEqual(mockGetPayload);
expect(body as typeof mockGetPayload).toEqual(mockGetPayload);
});
it('returns original response if status not 304 or 200', async () => {
@ -452,7 +458,7 @@ describe('callApi()', () => {
method: 'GET',
})
.then(throwIfCalled)
.catch(error => {
.catch((error: { status: number; statusText: string }) => {
expect(fetchMock.calls(mockErrorUrl)).toHaveLength(4);
expect(error.status).toBe(mockErrorPayload.status);
expect(error.statusText).toBe(mockErrorPayload.statusText);
@ -468,7 +474,7 @@ describe('callApi()', () => {
method: 'GET',
})
.then(throwIfCalled)
.catch(error => {
.catch((error: { status: number; statusText: string }) => {
expect(fetchMock.calls(mockErrorUrl)).toHaveLength(1);
expect(error.status).toBe(mockErrorPayload.status);
expect(error.statusText).toBe(mockErrorPayload.statusText);

View File

@ -76,9 +76,9 @@ describe('callApiAndParseWithTimeout()', () => {
callApiAndParseWithTimeout({ url: mockTimeoutUrl, method: 'GET', timeout: 1 })
.then(throwIfCalled)
.catch(error => {
.catch((error: { error: string; statusText: string }) => {
expect(fetchMock.calls(mockTimeoutUrl)).toHaveLength(1);
expect(Object.keys(error)).toEqual(expect.arrayContaining(['error', 'statusText']));
expect(Object.keys(error)).toEqual(['error', 'statusText']);
expect(error.statusText).toBe('timeout');
return done(); // eslint-disable-line promise/no-callback-in-promise
@ -93,6 +93,7 @@ describe('callApiAndParseWithTimeout()', () => {
return callApiAndParseWithTimeout({ url: mockGetUrl, method: 'GET', timeout: 100 }).then(
(response: Json) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
expect(response.json).toEqual(expect.objectContaining(mockGetPayload));
return true;

View File

@ -32,17 +32,19 @@ describe('parseResponse()', () => {
it('returns a Promise', () => {
const apiPromise = callApi({ url: mockGetUrl, method: 'GET' });
const parsedResponsePromise = parseResponse(apiPromise);
expect(parsedResponsePromise).toEqual(expect.any(Promise));
expect(parsedResponsePromise).toBeInstanceOf(Promise);
});
it('resolves to { json, response } if the request succeeds', () => {
expect.assertions(3);
expect.assertions(4);
const apiPromise = callApi({ url: mockGetUrl, method: 'GET' });
return parseResponse(apiPromise).then(args => {
expect(fetchMock.calls(mockGetUrl)).toHaveLength(1);
expect(Object.keys(args)).toEqual(expect.arrayContaining(['response', 'json']));
expect(args.json).toEqual(expect.objectContaining(mockGetPayload));
const keys = Object.keys(args);
expect(keys).toContain('response');
expect(keys).toContain('json');
expect(args.json).toEqual(expect.objectContaining(mockGetPayload) as typeof args.json);
return true;
});
@ -60,7 +62,7 @@ describe('parseResponse()', () => {
return parseResponse(apiPromise, 'json')
.then(throwIfCalled)
.catch(error => {
.catch((error: { stack: unknown; message: string }) => {
expect(fetchMock.calls(mockTextUrl)).toHaveLength(1);
expect(error.stack).toBeDefined();
expect(error.message).toContain('Unexpected token');
@ -70,7 +72,7 @@ describe('parseResponse()', () => {
});
it('resolves to { text, response } if the `parseMethod=text`', () => {
expect.assertions(3);
expect.assertions(4);
// test with json + bigint to ensure that it was not first parsed as json
const mockTextParseUrl = '/mock/textparse/url';
@ -81,7 +83,9 @@ describe('parseResponse()', () => {
return parseResponse(apiPromise, 'text').then(args => {
expect(fetchMock.calls(mockTextParseUrl)).toHaveLength(1);
expect(Object.keys(args)).toEqual(expect.arrayContaining(['response', 'text']));
const keys = Object.keys(args);
expect(keys).toContain('response');
expect(keys).toContain('text');
expect(args.text).toBe(mockTextJsonResponse);
return true;
@ -118,7 +122,7 @@ describe('parseResponse()', () => {
return parseResponse(apiPromise)
.then(throwIfCalled)
.catch(error => {
.catch((error: { ok: boolean; status: number }) => {
expect(fetchMock.calls(mockNotOkayUrl)).toHaveLength(1);
expect(error.ok).toBe(false);
expect(error.status).toBe(404);

View File

@ -4,7 +4,7 @@ import { TranslatorConfig } from './types';
interface Jed {
translate(input: string): Jed;
ifPlural(value: number, plural: string): Jed;
fetch(...args: any[]): string;
fetch(...args: unknown[]): string;
}
const DEFAULT_LANGUAGE_PACK = {
@ -25,14 +25,20 @@ export default class Translator {
constructor(config: TranslatorConfig = {}) {
const { languagePack = DEFAULT_LANGUAGE_PACK } = config;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
this.i18n = new UntypedJed(languagePack) as Jed;
}
translate(input: string, ...args: any[]): string {
translate(input: string, ...args: unknown[]): string {
return this.i18n.translate(input).fetch(...args);
}
translateWithNumber(singular: string, plural: string, num: number = 0, ...args: any[]): string {
translateWithNumber(
singular: string,
plural: string,
num: number = 0,
...args: unknown[]
): string {
return this.i18n
.translate(singular)
.ifPlural(num, plural)

View File

@ -25,12 +25,12 @@ function getInstance() {
return singleton;
}
function t(input: string, ...args: any[]) {
function t(input: string, ...args: unknown[]) {
return getInstance().translate(input, ...args);
}
function tn(singular: string, plural: string, ...args: any[]) {
return getInstance().translateWithNumber(singular, plural, ...args);
function tn(singular: string, plural: string, num?: number, ...args: unknown[]) {
return getInstance().translateWithNumber(singular, plural, num, ...args);
}
export { configure, t, tn };

View File

@ -35,7 +35,9 @@ import { DataTableProps } from './transformProps';
// Depending on how the modules are imported, `dt` may be a CommonJS init function,
// or the DataTable class itself. In case it is the former, we'd need to tell it
// where is jQuery.
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (!dt.$) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
dt(window, $);
}
@ -286,6 +288,7 @@ export default function ReactDataTable(props: DataTableProps) {
<td
key={key}
// only set innerHTML for actual html content, this saves time
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={isHtml ? { __html: text } : undefined}
data-key={key}
data-sort={val}
@ -307,6 +310,7 @@ export default function ReactDataTable(props: DataTableProps) {
return (
<div
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: ReactDOMServer.renderToStaticMarkup(tableElement) }}
ref={rootElem}
className="superset-legacy-chart-table"

View File

@ -73,7 +73,7 @@ const NO_DATA_RENDER_DATA = [
// Override the noData render function to make a prettier UX
// Code adapted from https://github.com/novus/nvd3/blob/master/src/utils.js#L653
nv.utils.noData = function (chart, container) {
nv.utils.noData = function noData(chart, container) {
const opt = chart.options();
const margin = opt.margin();
const height = nv.utils.availableHeight(null, container, margin);

View File

@ -16,15 +16,29 @@
* specific language governing permissions and limitations
* under the License.
*/
import { ChartProps } from '@superset-ui/chart';
import { ChartProps, QueryData } from '@superset-ui/chart';
import { QueryFormData, QueryFormDataMetric } from '@superset-ui/query';
import { RawBoxPlotDataRow, BoxPlotDataRow } from '../../components/BoxPlot/types';
export default function transformProps(chartProps: ChartProps) {
export type LegacyBoxPlotFormData = {
groupby?: QueryFormData['groupby'];
metrics?: QueryFormData['metrics'];
colorScheme?: string;
};
export type LegacyBoxPlotChartProps = ChartProps & {
formData: LegacyBoxPlotFormData;
queryData: QueryData & {
data?: RawBoxPlotDataRow[];
};
};
export default function transformProps(chartProps: LegacyBoxPlotChartProps) {
const { width, height, datasource, formData, queryData } = chartProps;
const { verboseMap = {} } = datasource;
const { colorScheme, groupby, metrics } = formData;
const { colorScheme, groupby = [], metrics = [] } = formData;
const data = (queryData.data as RawBoxPlotDataRow[]).map(({ label, values }) => ({
const data = (queryData.data || []).map(({ label, values }) => ({
label,
min: values.whisker_low,
max: values.whisker_high,
@ -35,7 +49,14 @@ export default function transformProps(chartProps: ChartProps) {
}));
const xAxisLabel = groupby.join('/');
const yAxisLabel = metrics.length > 0 ? verboseMap[metrics[0]] || metrics[0] : '';
let metric: QueryFormDataMetric = '';
if (Array.isArray(metrics)) {
metric = metrics.length > 0 ? metrics[0] : '';
} else {
metric = metrics;
}
const yAxisLabel = typeof metric === 'string' ? verboseMap[metric] || metric : metric.label;
const boxPlotValues = data.reduce((r: number[], e: BoxPlotDataRow) => {
r.push(e.min, e.max, ...e.outliers);

View File

@ -2,10 +2,12 @@ import { pick } from 'lodash';
import { ChartProps } from '@superset-ui/chart';
import { BoxPlotDataRow, RawBoxPlotDataRow } from '../components/BoxPlot/types';
import { HookProps } from '../components/BoxPlot/BoxPlot';
import { BoxPlotEncoding } from '../components/BoxPlot/Encoder';
export default function transformProps(chartProps: ChartProps) {
const { width, height, formData, queryData } = chartProps;
const { encoding, margin, theme } = formData;
const { margin, theme } = formData;
const encoding = formData.encoding as BoxPlotEncoding;
const data = (queryData.data as RawBoxPlotDataRow[]).map(({ label, values }) => ({
label,
@ -33,9 +35,15 @@ export default function transformProps(chartProps: ChartProps) {
];
if (isHorizontal) {
encoding.x.scale.domain = valueDomain;
} else {
if (encoding.x.scale) {
encoding.x.scale.domain = valueDomain;
} else {
encoding.x.scale = { domain: valueDomain };
}
} else if (encoding.y.scale) {
encoding.y.scale.domain = valueDomain;
} else {
encoding.y.scale = { domain: valueDomain };
}
const hooks = chartProps.hooks as HookProps;

View File

@ -1,35 +1,39 @@
import { ChartProps } from '@superset-ui/chart';
import { flatMap } from 'lodash';
interface Value {
[key: string]: unknown;
}
type Key = keyof Value;
interface DataRow {
key: string[];
values: {
[key: string]: any;
}[];
values: Value[];
}
export default function transformProps(chartProps: ChartProps) {
const { width, height, formData, queryData } = chartProps;
const {
colorScheme,
entity,
maxBubbleSize,
series,
showLegend,
size,
x,
xAxisFormat,
xAxisLabel,
// TODO: These fields are not supported yet
// xAxisShowminmax,
// xLogScale,
y,
yAxisLabel,
yAxisFormat,
// TODO: These fields are not supported yet
// yAxisShowminmax,
// yLogScale,
} = formData;
const x = formData.x as Key;
const y = formData.y as Key;
const series = formData.series as Key;
const size = formData.size as Key;
const entity = formData.entity as Key;
const data = queryData.data as DataRow[];
return {

View File

@ -85,7 +85,9 @@ export default class BoxPlot extends React.PureComponent<Props> {
<TooltipRenderer datum={datum} color={color} encoder={encoder} />
)}
theme={theme}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
xScale={convertScaleToDataUIScale(channels.x.definition.scale as any)}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)}
>
{layout.renderXAxis()}

View File

@ -225,9 +225,9 @@ export default class LineChart extends PureComponent<Props> {
onMouseMove,
tooltipData,
}: {
onMouseLeave: (...args: any[]) => void;
onMouseMove: (...args: any[]) => void;
tooltipData: any;
onMouseLeave: (...args: unknown[]) => void;
onMouseMove: (...args: unknown[]) => void;
tooltipData: { datum: { y?: number } };
}) => (
<XYChart
showYGrid
@ -240,7 +240,9 @@ export default class LineChart extends PureComponent<Props> {
renderTooltip={null}
theme={theme}
tooltipData={tooltipData}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
xScale={convertScaleToDataUIScale(channels.x.definition.scale as any)}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}

View File

@ -87,7 +87,9 @@ export default class ScatterPlot extends PureComponent<Props> {
<TooltipRenderer datum={datum} encoder={encoder} />
)}
theme={theme}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
xScale={convertScaleToDataUIScale(channels.x.definition.scale as any)}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)}
>
{layout.renderXAxis()}