mirror of
https://github.com/apache/superset.git
synced 2024-09-19 20:19:37 -04:00
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:
parent
6d7de01abe
commit
67e15d011a
@ -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> {
|
||||
|
@ -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');
|
||||
|
@ -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),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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'];
|
||||
|
@ -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', () => {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 };
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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()}
|
||||
|
@ -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}
|
||||
|
@ -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()}
|
||||
|
Loading…
Reference in New Issue
Block a user