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 { isRequired, Plugin } from '@superset-ui/core';
|
||||||
import { QueryFormData } from '@superset-ui/query';
|
import { QueryFormData } from '@superset-ui/query';
|
||||||
import ChartMetadata from './ChartMetadata';
|
import ChartMetadata from './ChartMetadata';
|
||||||
@ -18,7 +18,7 @@ const EMPTY = {};
|
|||||||
|
|
||||||
export type PromiseOrValue<T> = Promise<T> | T;
|
export type PromiseOrValue<T> = Promise<T> | T;
|
||||||
export type PromiseOrValueLoader<T> = () => PromiseOrValue<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 };
|
type ValueOrModuleWithValue<T> = T | { default: T };
|
||||||
|
|
||||||
interface ChartPluginConfig<T extends QueryFormData> {
|
interface ChartPluginConfig<T extends QueryFormData> {
|
||||||
|
@ -4,16 +4,21 @@ import ChartClient from '../../src/clients/ChartClient';
|
|||||||
import ChartDataProvider, { Props } from '../../src/components/ChartDataProvider';
|
import ChartDataProvider, { Props } from '../../src/components/ChartDataProvider';
|
||||||
import { bigNumberFormData } from '../fixtures/formData';
|
import { bigNumberFormData } from '../fixtures/formData';
|
||||||
|
|
||||||
// Note: the mock implentatino of these function directly affects the expected results below
|
// Note: the mock implementation of these function directly affects the expected results below
|
||||||
const defaultMockLoadFormData = jest.fn(allProps => Promise.resolve(allProps.formData));
|
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[]>'
|
type MockLoadFormData = typeof defaultMockLoadFormData | jest.Mock<Promise<unknown>, unknown[]>;
|
||||||
let mockLoadFormData = defaultMockLoadFormData as jest.Mock<Promise<any>, any>;
|
|
||||||
const mockLoadDatasource = jest.fn(datasource => Promise.resolve(datasource)) as jest.Mock<
|
let mockLoadFormData: MockLoadFormData = defaultMockLoadFormData;
|
||||||
Promise<any>,
|
|
||||||
any
|
function createPromise<T>(input: T, ..._args: unknown[]) {
|
||||||
>;
|
return Promise.resolve(input);
|
||||||
const mockLoadQueryData = jest.fn(input => Promise.resolve(input)) as jest.Mock<Promise<any>, any>;
|
}
|
||||||
|
|
||||||
|
const mockLoadDatasource = jest.fn<Promise<unknown>, unknown[]>(createPromise);
|
||||||
|
const mockLoadQueryData = jest.fn<Promise<unknown>, unknown[]>(createPromise);
|
||||||
|
|
||||||
// ChartClient is now a mock
|
// ChartClient is now a mock
|
||||||
jest.mock('../../src/clients/ChartClient', () =>
|
jest.mock('../../src/clients/ChartClient', () =>
|
||||||
@ -177,7 +182,7 @@ describe('ChartDataProvider', () => {
|
|||||||
|
|
||||||
describe('children', () => {
|
describe('children', () => {
|
||||||
it('calls children({ loading: true }) when loading', () => {
|
it('calls children({ loading: true }) when loading', () => {
|
||||||
const children = jest.fn();
|
const children = jest.fn<React.ReactNode, unknown[]>();
|
||||||
setup({ children });
|
setup({ children });
|
||||||
|
|
||||||
// during the first tick (before more promises resolve) loading is true
|
// during the first tick (before more promises resolve) loading is true
|
||||||
@ -188,7 +193,7 @@ describe('ChartDataProvider', () => {
|
|||||||
it('calls children({ payload }) when loaded', () => {
|
it('calls children({ payload }) when loaded', () => {
|
||||||
return new Promise(done => {
|
return new Promise(done => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const children = jest.fn();
|
const children = jest.fn<React.ReactNode, unknown[]>();
|
||||||
setup({ children, loadDatasource: true });
|
setup({ children, loadDatasource: true });
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -208,7 +213,7 @@ describe('ChartDataProvider', () => {
|
|||||||
it('calls children({ error }) upon request error', () => {
|
it('calls children({ error }) upon request error', () => {
|
||||||
return new Promise(done => {
|
return new Promise(done => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const children = jest.fn();
|
const children = jest.fn<React.ReactNode, unknown[]>();
|
||||||
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
|
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
|
||||||
|
|
||||||
setup({ children });
|
setup({ children });
|
||||||
@ -224,7 +229,7 @@ describe('ChartDataProvider', () => {
|
|||||||
it('calls children({ error }) upon JS error', () => {
|
it('calls children({ error }) upon JS error', () => {
|
||||||
return new Promise(done => {
|
return new Promise(done => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const children = jest.fn();
|
const children = jest.fn<React.ReactNode, unknown[]>();
|
||||||
|
|
||||||
mockLoadFormData = jest.fn(() => {
|
mockLoadFormData = jest.fn(() => {
|
||||||
throw new Error('non-async error');
|
throw new Error('non-async error');
|
||||||
@ -245,7 +250,7 @@ describe('ChartDataProvider', () => {
|
|||||||
it('calls onLoad(payload) when loaded', () => {
|
it('calls onLoad(payload) when loaded', () => {
|
||||||
return new Promise(done => {
|
return new Promise(done => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const onLoaded = jest.fn();
|
const onLoaded = jest.fn<void, unknown[]>();
|
||||||
setup({ onLoaded, loadDatasource: true });
|
setup({ onLoaded, loadDatasource: true });
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -263,7 +268,7 @@ describe('ChartDataProvider', () => {
|
|||||||
it('calls onError(error) upon request error', () => {
|
it('calls onError(error) upon request error', () => {
|
||||||
return new Promise(done => {
|
return new Promise(done => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const onError = jest.fn();
|
const onError = jest.fn<void, unknown[]>();
|
||||||
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
|
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
|
||||||
|
|
||||||
setup({ onError });
|
setup({ onError });
|
||||||
@ -278,7 +283,7 @@ describe('ChartDataProvider', () => {
|
|||||||
it('calls onError(error) upon JS error', () => {
|
it('calls onError(error) upon JS error', () => {
|
||||||
return new Promise(done => {
|
return new Promise(done => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const onError = jest.fn();
|
const onError = jest.fn<void, unknown[]>();
|
||||||
|
|
||||||
mockLoadFormData = jest.fn(() => {
|
mockLoadFormData = jest.fn(() => {
|
||||||
throw new Error('non-async error');
|
throw new Error('non-async error');
|
||||||
|
@ -17,7 +17,7 @@ export const TestComponent = ({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
}: {
|
}: {
|
||||||
formData?: any;
|
formData?: unknown;
|
||||||
message?: string;
|
message?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: 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> {
|
export class LazyChartPlugin extends ChartPlugin<QueryFormData> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
@ -77,7 +81,7 @@ export class LazyChartPlugin extends ChartPlugin<QueryFormData> {
|
|||||||
// this mirrors `() => import(module)` syntax
|
// this mirrors `() => import(module)` syntax
|
||||||
loadChart: () => Promise.resolve({ default: TestComponent }),
|
loadChart: () => Promise.resolve({ default: TestComponent }),
|
||||||
// promise without .default
|
// 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>;
|
return <div className="test-component">test</div>;
|
||||||
}
|
}
|
||||||
let loadChartSuccess = jest.fn(() => Promise.resolve(TestComponent));
|
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 loading: () => JSX.Element;
|
||||||
let LoadableRenderer: LoadableRendererType<{}, {}>;
|
let LoadableRenderer: LoadableRendererType<{}, {}>;
|
||||||
let restoreConsole: RestoreConsole;
|
let restoreConsole: RestoreConsole;
|
||||||
|
@ -79,6 +79,7 @@ export default function callApi({
|
|||||||
Object.keys(postPayload).forEach(key => {
|
Object.keys(postPayload).forEach(key => {
|
||||||
const value = postPayload[key];
|
const value = postPayload[key];
|
||||||
if (typeof value !== 'undefined') {
|
if (typeof value !== 'undefined') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
formData.append(key, stringify ? JSON.stringify(value) : value);
|
formData.append(key, stringify ? JSON.stringify(value) : value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -11,8 +11,10 @@ export type FetchRetryOptions = {
|
|||||||
};
|
};
|
||||||
export type Headers = { [k: string]: string };
|
export type Headers = { [k: string]: string };
|
||||||
export type Host = string;
|
export type Host = string;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type Json = { [k: string]: any };
|
export type Json = { [k: string]: any };
|
||||||
export type Method = RequestInit['method'];
|
export type Method = RequestInit['method'];
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type PostPayload = { [key: string]: any };
|
export type PostPayload = { [key: string]: any };
|
||||||
export type Mode = RequestInit['mode'];
|
export type Mode = RequestInit['mode'];
|
||||||
export type Redirect = RequestInit['redirect'];
|
export type Redirect = RequestInit['redirect'];
|
||||||
|
@ -13,14 +13,14 @@ describe('SupersetClient', () => {
|
|||||||
afterEach(SupersetClient.reset);
|
afterEach(SupersetClient.reset);
|
||||||
|
|
||||||
it('exposes reset, configure, init, get, post, isAuthenticated, and reAuthenticate methods', () => {
|
it('exposes reset, configure, init, get, post, isAuthenticated, and reAuthenticate methods', () => {
|
||||||
expect(SupersetClient.configure).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.configure).toBe('function');
|
||||||
expect(SupersetClient.init).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.init).toBe('function');
|
||||||
expect(SupersetClient.get).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.get).toBe('function');
|
||||||
expect(SupersetClient.post).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.post).toBe('function');
|
||||||
expect(SupersetClient.isAuthenticated).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.isAuthenticated).toBe('function');
|
||||||
expect(SupersetClient.reAuthenticate).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.reAuthenticate).toBe('function');
|
||||||
expect(SupersetClient.request).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.request).toBe('function');
|
||||||
expect(SupersetClient.reset).toEqual(expect.any(Function));
|
expect(typeof SupersetClient.reset).toBe('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if you call init, get, post, isAuthenticated, or reAuthenticate before configure', () => {
|
it('throws if you call init, get, post, isAuthenticated, or reAuthenticate before configure', () => {
|
||||||
|
@ -100,7 +100,7 @@ describe('SupersetClientClass', () => {
|
|||||||
return new SupersetClientClass({})
|
return new SupersetClientClass({})
|
||||||
.init()
|
.init()
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: { status: number }) => {
|
||||||
expect(error.status).toBe(403);
|
expect(error.status).toBe(403);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -114,7 +114,7 @@ describe('SupersetClientClass', () => {
|
|||||||
return new SupersetClientClass({})
|
return new SupersetClientClass({})
|
||||||
.init()
|
.init()
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: unknown) => {
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -129,7 +129,7 @@ describe('SupersetClientClass', () => {
|
|||||||
return new SupersetClientClass({})
|
return new SupersetClientClass({})
|
||||||
.init()
|
.init()
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: unknown) => {
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -171,8 +171,10 @@ describe('SupersetClientClass', () => {
|
|||||||
return client
|
return client
|
||||||
.ensureAuth()
|
.ensureAuth()
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: { error: string }) => {
|
||||||
expect(error).toEqual(expect.objectContaining({ error: expect.any(String) }));
|
expect(error).toEqual(
|
||||||
|
expect.objectContaining({ error: expect.any(String) }) as typeof error,
|
||||||
|
);
|
||||||
expect(client.isAuthenticated()).toBe(false);
|
expect(client.isAuthenticated()).toBe(false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -209,14 +211,14 @@ describe('SupersetClientClass', () => {
|
|||||||
return client
|
return client
|
||||||
.init()
|
.init()
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: unknown) => {
|
||||||
expect(error).toEqual(expect.objectContaining(rejectValue));
|
expect(error).toEqual(expect.objectContaining(rejectValue) as unknown);
|
||||||
|
|
||||||
return client
|
return client
|
||||||
.ensureAuth()
|
.ensureAuth()
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error2 => {
|
.catch((error2: unknown) => {
|
||||||
expect(error2).toEqual(expect.objectContaining(rejectValue));
|
expect(error2).toEqual(expect.objectContaining(rejectValue) as unknown);
|
||||||
expect(client.isAuthenticated()).toBe(false);
|
expect(client.isAuthenticated()).toBe(false);
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
@ -305,7 +307,7 @@ describe('SupersetClientClass', () => {
|
|||||||
expect(fetchRequest.mode).toBe(clientConfig.mode);
|
expect(fetchRequest.mode).toBe(clientConfig.mode);
|
||||||
expect(fetchRequest.credentials).toBe(clientConfig.credentials);
|
expect(fetchRequest.credentials).toBe(clientConfig.credentials);
|
||||||
expect(fetchRequest.headers).toEqual(
|
expect(fetchRequest.headers).toEqual(
|
||||||
expect.objectContaining(clientConfig.headers as Object),
|
expect.objectContaining(clientConfig.headers) as typeof fetchRequest.headers,
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -379,7 +381,7 @@ describe('SupersetClientClass', () => {
|
|||||||
expect(fetchRequest.mode).toBe(overrideConfig.mode);
|
expect(fetchRequest.mode).toBe(overrideConfig.mode);
|
||||||
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
|
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
|
||||||
expect(fetchRequest.headers).toEqual(
|
expect(fetchRequest.headers).toEqual(
|
||||||
expect.objectContaining(overrideConfig.headers as Object),
|
expect.objectContaining(overrideConfig.headers) as typeof fetchRequest.headers,
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -431,7 +433,7 @@ describe('SupersetClientClass', () => {
|
|||||||
expect(fetchRequest.mode).toBe(overrideConfig.mode);
|
expect(fetchRequest.mode).toBe(overrideConfig.mode);
|
||||||
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
|
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
|
||||||
expect(fetchRequest.headers).toEqual(
|
expect(fetchRequest.headers).toEqual(
|
||||||
expect.objectContaining(overrideConfig.headers as Object),
|
expect.objectContaining(overrideConfig.headers) as typeof fetchRequest.headers,
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -456,15 +458,15 @@ describe('SupersetClientClass', () => {
|
|||||||
it('passes postPayload key,values in the body', () => {
|
it('passes postPayload key,values in the body', () => {
|
||||||
expect.assertions(3);
|
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 });
|
const client = new SupersetClientClass({ protocol, host });
|
||||||
|
|
||||||
return client.init().then(() =>
|
return client.init().then(() =>
|
||||||
client.post({ url: mockPostUrl, postPayload }).then(() => {
|
client.post({ url: mockPostUrl, postPayload }).then(() => {
|
||||||
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData;
|
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData;
|
||||||
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
|
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(formData.get(key)).toBe(JSON.stringify(postPayload[key]));
|
expect(formData.get(key)).toBe(JSON.stringify(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -474,15 +476,15 @@ describe('SupersetClientClass', () => {
|
|||||||
|
|
||||||
it('respects the stringify parameter for postPayload key,values', () => {
|
it('respects the stringify parameter for postPayload key,values', () => {
|
||||||
expect.assertions(3);
|
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 });
|
const client = new SupersetClientClass({ protocol, host });
|
||||||
|
|
||||||
return client.init().then(() =>
|
return client.init().then(() =>
|
||||||
client.post({ url: mockPostUrl, postPayload, stringify: false }).then(() => {
|
client.post({ url: mockPostUrl, postPayload, stringify: false }).then(() => {
|
||||||
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData;
|
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData;
|
||||||
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
|
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(formData.get(key)).toBe(String(postPayload[key]));
|
expect(formData.get(key)).toBe(String(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -88,7 +88,9 @@ describe('callApi()', () => {
|
|||||||
expect(fetchParams.mode).toBe(mockRequest.mode);
|
expect(fetchParams.mode).toBe(mockRequest.mode);
|
||||||
expect(fetchParams.cache).toBe(mockRequest.cache);
|
expect(fetchParams.cache).toBe(mockRequest.cache);
|
||||||
expect(fetchParams.credentials).toBe(mockRequest.credentials);
|
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.redirect).toBe(mockRequest.redirect);
|
||||||
expect(fetchParams.signal).toBe(mockRequest.signal);
|
expect(fetchParams.signal).toBe(mockRequest.signal);
|
||||||
expect(fetchParams.body).toBe(mockRequest.body);
|
expect(fetchParams.body).toBe(mockRequest.body);
|
||||||
@ -101,7 +103,7 @@ describe('callApi()', () => {
|
|||||||
describe('POST requests', () => {
|
describe('POST requests', () => {
|
||||||
it('encodes key,value pairs from postPayload', () => {
|
it('encodes key,value pairs from postPayload', () => {
|
||||||
expect.assertions(3);
|
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(() => {
|
return callApi({ url: mockPostUrl, method: 'POST', postPayload }).then(() => {
|
||||||
const calls = fetchMock.calls(mockPostUrl);
|
const calls = fetchMock.calls(mockPostUrl);
|
||||||
@ -110,8 +112,8 @@ describe('callApi()', () => {
|
|||||||
const fetchParams = calls[0][1];
|
const fetchParams = calls[0][1];
|
||||||
const body = fetchParams.body as FormData;
|
const body = fetchParams.body as FormData;
|
||||||
|
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(body.get(key)).toBe(JSON.stringify(postPayload[key]));
|
expect(body.get(key)).toBe(JSON.stringify(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -144,7 +146,7 @@ describe('callApi()', () => {
|
|||||||
object: { a: 'a', 1: 1 },
|
object: { a: 'a', 1: 1 },
|
||||||
null: null,
|
null: null,
|
||||||
emptyString: '',
|
emptyString: '',
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
expect.assertions(1 + 2 * Object.keys(postPayload).length);
|
expect.assertions(1 + 2 * Object.keys(postPayload).length);
|
||||||
|
|
||||||
@ -158,9 +160,9 @@ describe('callApi()', () => {
|
|||||||
const stringified = calls[0][1].body as FormData;
|
const stringified = calls[0][1].body as FormData;
|
||||||
const unstringified = calls[1][1].body as FormData;
|
const unstringified = calls[1][1].body as FormData;
|
||||||
|
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(stringified.get(key)).toBe(JSON.stringify(postPayload[key]));
|
expect(stringified.get(key)).toBe(JSON.stringify(value));
|
||||||
expect(unstringified.get(key)).toBe(String(postPayload[key]));
|
expect(unstringified.get(key)).toBe(String(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -171,7 +173,7 @@ describe('callApi()', () => {
|
|||||||
describe('PUT requests', () => {
|
describe('PUT requests', () => {
|
||||||
it('encodes key,value pairs from postPayload', () => {
|
it('encodes key,value pairs from postPayload', () => {
|
||||||
expect.assertions(3);
|
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(() => {
|
return callApi({ url: mockPutUrl, method: 'PUT', postPayload }).then(() => {
|
||||||
const calls = fetchMock.calls(mockPutUrl);
|
const calls = fetchMock.calls(mockPutUrl);
|
||||||
@ -180,8 +182,8 @@ describe('callApi()', () => {
|
|||||||
const fetchParams = calls[0][1];
|
const fetchParams = calls[0][1];
|
||||||
const body = fetchParams.body as FormData;
|
const body = fetchParams.body as FormData;
|
||||||
|
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(body.get(key)).toBe(JSON.stringify(postPayload[key]));
|
expect(body.get(key)).toBe(JSON.stringify(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -214,7 +216,7 @@ describe('callApi()', () => {
|
|||||||
object: { a: 'a', 1: 1 },
|
object: { a: 'a', 1: 1 },
|
||||||
null: null,
|
null: null,
|
||||||
emptyString: '',
|
emptyString: '',
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
expect.assertions(1 + 2 * Object.keys(postPayload).length);
|
expect.assertions(1 + 2 * Object.keys(postPayload).length);
|
||||||
|
|
||||||
@ -228,9 +230,9 @@ describe('callApi()', () => {
|
|||||||
const stringified = calls[0][1].body as FormData;
|
const stringified = calls[0][1].body as FormData;
|
||||||
const unstringified = calls[1][1].body as FormData;
|
const unstringified = calls[1][1].body as FormData;
|
||||||
|
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(stringified.get(key)).toBe(JSON.stringify(postPayload[key]));
|
expect(stringified.get(key)).toBe(JSON.stringify(value));
|
||||||
expect(unstringified.get(key)).toBe(String(postPayload[key]));
|
expect(unstringified.get(key)).toBe(String(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -241,7 +243,7 @@ describe('callApi()', () => {
|
|||||||
describe('PATCH requests', () => {
|
describe('PATCH requests', () => {
|
||||||
it('encodes key,value pairs from postPayload', () => {
|
it('encodes key,value pairs from postPayload', () => {
|
||||||
expect.assertions(3);
|
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(() => {
|
return callApi({ url: mockPatchUrl, method: 'PATCH', postPayload }).then(() => {
|
||||||
const calls = fetchMock.calls(mockPatchUrl);
|
const calls = fetchMock.calls(mockPatchUrl);
|
||||||
@ -250,8 +252,8 @@ describe('callApi()', () => {
|
|||||||
const fetchParams = calls[0][1];
|
const fetchParams = calls[0][1];
|
||||||
const body = fetchParams.body as FormData;
|
const body = fetchParams.body as FormData;
|
||||||
|
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(body.get(key)).toBe(JSON.stringify(postPayload[key]));
|
expect(body.get(key)).toBe(JSON.stringify(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -284,7 +286,7 @@ describe('callApi()', () => {
|
|||||||
object: { a: 'a', 1: 1 },
|
object: { a: 'a', 1: 1 },
|
||||||
null: null,
|
null: null,
|
||||||
emptyString: '',
|
emptyString: '',
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
expect.assertions(1 + 2 * Object.keys(postPayload).length);
|
expect.assertions(1 + 2 * Object.keys(postPayload).length);
|
||||||
|
|
||||||
@ -298,9 +300,9 @@ describe('callApi()', () => {
|
|||||||
const stringified = calls[0][1].body as FormData;
|
const stringified = calls[0][1].body as FormData;
|
||||||
const unstringified = calls[1][1].body as FormData;
|
const unstringified = calls[1][1].body as FormData;
|
||||||
|
|
||||||
Object.keys(postPayload).forEach(key => {
|
Object.entries(postPayload).forEach(([key, value]) => {
|
||||||
expect(stringified.get(key)).toBe(JSON.stringify(postPayload[key]));
|
expect(stringified.get(key)).toBe(JSON.stringify(value));
|
||||||
expect(unstringified.get(key)).toBe(String(postPayload[key]));
|
expect(unstringified.get(key)).toBe(String(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -389,7 +391,9 @@ describe('callApi()', () => {
|
|||||||
const fetchParams = calls[1][1];
|
const fetchParams = calls[1][1];
|
||||||
const headers = { 'If-None-Match': 'etag' };
|
const headers = { 'If-None-Match': 'etag' };
|
||||||
expect(calls).toHaveLength(2);
|
expect(calls).toHaveLength(2);
|
||||||
expect(fetchParams.headers).toEqual(expect.objectContaining(headers));
|
expect(fetchParams.headers).toEqual(
|
||||||
|
expect.objectContaining(headers) as typeof fetchParams.headers,
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -417,11 +421,13 @@ describe('callApi()', () => {
|
|||||||
const mockCachedPayload = { status: 304 };
|
const mockCachedPayload = { status: 304 };
|
||||||
fetchMock.get(mockUncachedUrl, mockCachedPayload);
|
fetchMock.get(mockUncachedUrl, mockCachedPayload);
|
||||||
|
|
||||||
return callApi({ url: mockUncachedUrl, method: 'GET' }).catch(error => {
|
return callApi({ url: mockUncachedUrl, method: 'GET' }).catch(
|
||||||
|
(error: { message: string }) => {
|
||||||
const calls = fetchMock.calls(mockUncachedUrl);
|
const calls = fetchMock.calls(mockUncachedUrl);
|
||||||
expect(calls).toHaveLength(1);
|
expect(calls).toHaveLength(1);
|
||||||
expect(error.message).toEqual('Received 304 but no content is cached!');
|
expect(error.message).toEqual('Received 304 but no content is cached!');
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns original response if no Etag', async () => {
|
it('returns original response if no Etag', async () => {
|
||||||
@ -431,7 +437,7 @@ describe('callApi()', () => {
|
|||||||
expect(calls).toHaveLength(1);
|
expect(calls).toHaveLength(1);
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200);
|
||||||
const body = await response.json();
|
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 () => {
|
it('returns original response if status not 304 or 200', async () => {
|
||||||
@ -452,7 +458,7 @@ describe('callApi()', () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
})
|
})
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: { status: number; statusText: string }) => {
|
||||||
expect(fetchMock.calls(mockErrorUrl)).toHaveLength(4);
|
expect(fetchMock.calls(mockErrorUrl)).toHaveLength(4);
|
||||||
expect(error.status).toBe(mockErrorPayload.status);
|
expect(error.status).toBe(mockErrorPayload.status);
|
||||||
expect(error.statusText).toBe(mockErrorPayload.statusText);
|
expect(error.statusText).toBe(mockErrorPayload.statusText);
|
||||||
@ -468,7 +474,7 @@ describe('callApi()', () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
})
|
})
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: { status: number; statusText: string }) => {
|
||||||
expect(fetchMock.calls(mockErrorUrl)).toHaveLength(1);
|
expect(fetchMock.calls(mockErrorUrl)).toHaveLength(1);
|
||||||
expect(error.status).toBe(mockErrorPayload.status);
|
expect(error.status).toBe(mockErrorPayload.status);
|
||||||
expect(error.statusText).toBe(mockErrorPayload.statusText);
|
expect(error.statusText).toBe(mockErrorPayload.statusText);
|
||||||
|
@ -76,9 +76,9 @@ describe('callApiAndParseWithTimeout()', () => {
|
|||||||
|
|
||||||
callApiAndParseWithTimeout({ url: mockTimeoutUrl, method: 'GET', timeout: 1 })
|
callApiAndParseWithTimeout({ url: mockTimeoutUrl, method: 'GET', timeout: 1 })
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: { error: string; statusText: string }) => {
|
||||||
expect(fetchMock.calls(mockTimeoutUrl)).toHaveLength(1);
|
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');
|
expect(error.statusText).toBe('timeout');
|
||||||
|
|
||||||
return done(); // eslint-disable-line promise/no-callback-in-promise
|
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(
|
return callApiAndParseWithTimeout({ url: mockGetUrl, method: 'GET', timeout: 100 }).then(
|
||||||
(response: Json) => {
|
(response: Json) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
expect(response.json).toEqual(expect.objectContaining(mockGetPayload));
|
expect(response.json).toEqual(expect.objectContaining(mockGetPayload));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -32,17 +32,19 @@ describe('parseResponse()', () => {
|
|||||||
it('returns a Promise', () => {
|
it('returns a Promise', () => {
|
||||||
const apiPromise = callApi({ url: mockGetUrl, method: 'GET' });
|
const apiPromise = callApi({ url: mockGetUrl, method: 'GET' });
|
||||||
const parsedResponsePromise = parseResponse(apiPromise);
|
const parsedResponsePromise = parseResponse(apiPromise);
|
||||||
expect(parsedResponsePromise).toEqual(expect.any(Promise));
|
expect(parsedResponsePromise).toBeInstanceOf(Promise);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resolves to { json, response } if the request succeeds', () => {
|
it('resolves to { json, response } if the request succeeds', () => {
|
||||||
expect.assertions(3);
|
expect.assertions(4);
|
||||||
const apiPromise = callApi({ url: mockGetUrl, method: 'GET' });
|
const apiPromise = callApi({ url: mockGetUrl, method: 'GET' });
|
||||||
|
|
||||||
return parseResponse(apiPromise).then(args => {
|
return parseResponse(apiPromise).then(args => {
|
||||||
expect(fetchMock.calls(mockGetUrl)).toHaveLength(1);
|
expect(fetchMock.calls(mockGetUrl)).toHaveLength(1);
|
||||||
expect(Object.keys(args)).toEqual(expect.arrayContaining(['response', 'json']));
|
const keys = Object.keys(args);
|
||||||
expect(args.json).toEqual(expect.objectContaining(mockGetPayload));
|
expect(keys).toContain('response');
|
||||||
|
expect(keys).toContain('json');
|
||||||
|
expect(args.json).toEqual(expect.objectContaining(mockGetPayload) as typeof args.json);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -60,7 +62,7 @@ describe('parseResponse()', () => {
|
|||||||
|
|
||||||
return parseResponse(apiPromise, 'json')
|
return parseResponse(apiPromise, 'json')
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: { stack: unknown; message: string }) => {
|
||||||
expect(fetchMock.calls(mockTextUrl)).toHaveLength(1);
|
expect(fetchMock.calls(mockTextUrl)).toHaveLength(1);
|
||||||
expect(error.stack).toBeDefined();
|
expect(error.stack).toBeDefined();
|
||||||
expect(error.message).toContain('Unexpected token');
|
expect(error.message).toContain('Unexpected token');
|
||||||
@ -70,7 +72,7 @@ describe('parseResponse()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('resolves to { text, response } if the `parseMethod=text`', () => {
|
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
|
// test with json + bigint to ensure that it was not first parsed as json
|
||||||
const mockTextParseUrl = '/mock/textparse/url';
|
const mockTextParseUrl = '/mock/textparse/url';
|
||||||
@ -81,7 +83,9 @@ describe('parseResponse()', () => {
|
|||||||
|
|
||||||
return parseResponse(apiPromise, 'text').then(args => {
|
return parseResponse(apiPromise, 'text').then(args => {
|
||||||
expect(fetchMock.calls(mockTextParseUrl)).toHaveLength(1);
|
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);
|
expect(args.text).toBe(mockTextJsonResponse);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -118,7 +122,7 @@ describe('parseResponse()', () => {
|
|||||||
|
|
||||||
return parseResponse(apiPromise)
|
return parseResponse(apiPromise)
|
||||||
.then(throwIfCalled)
|
.then(throwIfCalled)
|
||||||
.catch(error => {
|
.catch((error: { ok: boolean; status: number }) => {
|
||||||
expect(fetchMock.calls(mockNotOkayUrl)).toHaveLength(1);
|
expect(fetchMock.calls(mockNotOkayUrl)).toHaveLength(1);
|
||||||
expect(error.ok).toBe(false);
|
expect(error.ok).toBe(false);
|
||||||
expect(error.status).toBe(404);
|
expect(error.status).toBe(404);
|
||||||
|
@ -4,7 +4,7 @@ import { TranslatorConfig } from './types';
|
|||||||
interface Jed {
|
interface Jed {
|
||||||
translate(input: string): Jed;
|
translate(input: string): Jed;
|
||||||
ifPlural(value: number, plural: string): Jed;
|
ifPlural(value: number, plural: string): Jed;
|
||||||
fetch(...args: any[]): string;
|
fetch(...args: unknown[]): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_LANGUAGE_PACK = {
|
const DEFAULT_LANGUAGE_PACK = {
|
||||||
@ -25,14 +25,20 @@ export default class Translator {
|
|||||||
|
|
||||||
constructor(config: TranslatorConfig = {}) {
|
constructor(config: TranslatorConfig = {}) {
|
||||||
const { languagePack = DEFAULT_LANGUAGE_PACK } = config;
|
const { languagePack = DEFAULT_LANGUAGE_PACK } = config;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
this.i18n = new UntypedJed(languagePack) as Jed;
|
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);
|
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
|
return this.i18n
|
||||||
.translate(singular)
|
.translate(singular)
|
||||||
.ifPlural(num, plural)
|
.ifPlural(num, plural)
|
||||||
|
@ -25,12 +25,12 @@ function getInstance() {
|
|||||||
return singleton;
|
return singleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
function t(input: string, ...args: any[]) {
|
function t(input: string, ...args: unknown[]) {
|
||||||
return getInstance().translate(input, ...args);
|
return getInstance().translate(input, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tn(singular: string, plural: string, ...args: any[]) {
|
function tn(singular: string, plural: string, num?: number, ...args: unknown[]) {
|
||||||
return getInstance().translateWithNumber(singular, plural, ...args);
|
return getInstance().translateWithNumber(singular, plural, num, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { configure, t, tn };
|
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,
|
// 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
|
// or the DataTable class itself. In case it is the former, we'd need to tell it
|
||||||
// where is jQuery.
|
// where is jQuery.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
if (!dt.$) {
|
if (!dt.$) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
dt(window, $);
|
dt(window, $);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +288,7 @@ export default function ReactDataTable(props: DataTableProps) {
|
|||||||
<td
|
<td
|
||||||
key={key}
|
key={key}
|
||||||
// only set innerHTML for actual html content, this saves time
|
// only set innerHTML for actual html content, this saves time
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
dangerouslySetInnerHTML={isHtml ? { __html: text } : undefined}
|
dangerouslySetInnerHTML={isHtml ? { __html: text } : undefined}
|
||||||
data-key={key}
|
data-key={key}
|
||||||
data-sort={val}
|
data-sort={val}
|
||||||
@ -307,6 +310,7 @@ export default function ReactDataTable(props: DataTableProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
dangerouslySetInnerHTML={{ __html: ReactDOMServer.renderToStaticMarkup(tableElement) }}
|
dangerouslySetInnerHTML={{ __html: ReactDOMServer.renderToStaticMarkup(tableElement) }}
|
||||||
ref={rootElem}
|
ref={rootElem}
|
||||||
className="superset-legacy-chart-table"
|
className="superset-legacy-chart-table"
|
||||||
|
@ -73,7 +73,7 @@ const NO_DATA_RENDER_DATA = [
|
|||||||
|
|
||||||
// Override the noData render function to make a prettier UX
|
// Override the noData render function to make a prettier UX
|
||||||
// Code adapted from https://github.com/novus/nvd3/blob/master/src/utils.js#L653
|
// 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 opt = chart.options();
|
||||||
const margin = opt.margin();
|
const margin = opt.margin();
|
||||||
const height = nv.utils.availableHeight(null, container, margin);
|
const height = nv.utils.availableHeight(null, container, margin);
|
||||||
|
@ -16,15 +16,29 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* 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';
|
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 { width, height, datasource, formData, queryData } = chartProps;
|
||||||
const { verboseMap = {} } = datasource;
|
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,
|
label,
|
||||||
min: values.whisker_low,
|
min: values.whisker_low,
|
||||||
max: values.whisker_high,
|
max: values.whisker_high,
|
||||||
@ -35,7 +49,14 @@ export default function transformProps(chartProps: ChartProps) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const xAxisLabel = groupby.join('/');
|
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) => {
|
const boxPlotValues = data.reduce((r: number[], e: BoxPlotDataRow) => {
|
||||||
r.push(e.min, e.max, ...e.outliers);
|
r.push(e.min, e.max, ...e.outliers);
|
||||||
|
@ -2,10 +2,12 @@ import { pick } from 'lodash';
|
|||||||
import { ChartProps } from '@superset-ui/chart';
|
import { ChartProps } from '@superset-ui/chart';
|
||||||
import { BoxPlotDataRow, RawBoxPlotDataRow } from '../components/BoxPlot/types';
|
import { BoxPlotDataRow, RawBoxPlotDataRow } from '../components/BoxPlot/types';
|
||||||
import { HookProps } from '../components/BoxPlot/BoxPlot';
|
import { HookProps } from '../components/BoxPlot/BoxPlot';
|
||||||
|
import { BoxPlotEncoding } from '../components/BoxPlot/Encoder';
|
||||||
|
|
||||||
export default function transformProps(chartProps: ChartProps) {
|
export default function transformProps(chartProps: ChartProps) {
|
||||||
const { width, height, formData, queryData } = 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 }) => ({
|
const data = (queryData.data as RawBoxPlotDataRow[]).map(({ label, values }) => ({
|
||||||
label,
|
label,
|
||||||
@ -33,9 +35,15 @@ export default function transformProps(chartProps: ChartProps) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (isHorizontal) {
|
if (isHorizontal) {
|
||||||
|
if (encoding.x.scale) {
|
||||||
encoding.x.scale.domain = valueDomain;
|
encoding.x.scale.domain = valueDomain;
|
||||||
} else {
|
} else {
|
||||||
|
encoding.x.scale = { domain: valueDomain };
|
||||||
|
}
|
||||||
|
} else if (encoding.y.scale) {
|
||||||
encoding.y.scale.domain = valueDomain;
|
encoding.y.scale.domain = valueDomain;
|
||||||
|
} else {
|
||||||
|
encoding.y.scale = { domain: valueDomain };
|
||||||
}
|
}
|
||||||
|
|
||||||
const hooks = chartProps.hooks as HookProps;
|
const hooks = chartProps.hooks as HookProps;
|
||||||
|
@ -1,35 +1,39 @@
|
|||||||
import { ChartProps } from '@superset-ui/chart';
|
import { ChartProps } from '@superset-ui/chart';
|
||||||
import { flatMap } from 'lodash';
|
import { flatMap } from 'lodash';
|
||||||
|
|
||||||
|
interface Value {
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Key = keyof Value;
|
||||||
|
|
||||||
interface DataRow {
|
interface DataRow {
|
||||||
key: string[];
|
key: string[];
|
||||||
values: {
|
values: Value[];
|
||||||
[key: string]: any;
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function transformProps(chartProps: ChartProps) {
|
export default function transformProps(chartProps: ChartProps) {
|
||||||
const { width, height, formData, queryData } = chartProps;
|
const { width, height, formData, queryData } = chartProps;
|
||||||
const {
|
const {
|
||||||
colorScheme,
|
colorScheme,
|
||||||
entity,
|
|
||||||
maxBubbleSize,
|
maxBubbleSize,
|
||||||
series,
|
|
||||||
showLegend,
|
showLegend,
|
||||||
size,
|
|
||||||
x,
|
|
||||||
xAxisFormat,
|
xAxisFormat,
|
||||||
xAxisLabel,
|
xAxisLabel,
|
||||||
// TODO: These fields are not supported yet
|
// TODO: These fields are not supported yet
|
||||||
// xAxisShowminmax,
|
// xAxisShowminmax,
|
||||||
// xLogScale,
|
// xLogScale,
|
||||||
y,
|
|
||||||
yAxisLabel,
|
yAxisLabel,
|
||||||
yAxisFormat,
|
yAxisFormat,
|
||||||
// TODO: These fields are not supported yet
|
// TODO: These fields are not supported yet
|
||||||
// yAxisShowminmax,
|
// yAxisShowminmax,
|
||||||
// yLogScale,
|
// yLogScale,
|
||||||
} = formData;
|
} = 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[];
|
const data = queryData.data as DataRow[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -85,7 +85,9 @@ export default class BoxPlot extends React.PureComponent<Props> {
|
|||||||
<TooltipRenderer datum={datum} color={color} encoder={encoder} />
|
<TooltipRenderer datum={datum} color={color} encoder={encoder} />
|
||||||
)}
|
)}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
|
||||||
xScale={convertScaleToDataUIScale(channels.x.definition.scale as 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)}
|
yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)}
|
||||||
>
|
>
|
||||||
{layout.renderXAxis()}
|
{layout.renderXAxis()}
|
||||||
|
@ -225,9 +225,9 @@ export default class LineChart extends PureComponent<Props> {
|
|||||||
onMouseMove,
|
onMouseMove,
|
||||||
tooltipData,
|
tooltipData,
|
||||||
}: {
|
}: {
|
||||||
onMouseLeave: (...args: any[]) => void;
|
onMouseLeave: (...args: unknown[]) => void;
|
||||||
onMouseMove: (...args: any[]) => void;
|
onMouseMove: (...args: unknown[]) => void;
|
||||||
tooltipData: any;
|
tooltipData: { datum: { y?: number } };
|
||||||
}) => (
|
}) => (
|
||||||
<XYChart
|
<XYChart
|
||||||
showYGrid
|
showYGrid
|
||||||
@ -240,7 +240,9 @@ export default class LineChart extends PureComponent<Props> {
|
|||||||
renderTooltip={null}
|
renderTooltip={null}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
tooltipData={tooltipData}
|
tooltipData={tooltipData}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
|
||||||
xScale={convertScaleToDataUIScale(channels.x.definition.scale as 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)}
|
yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)}
|
||||||
onMouseMove={onMouseMove}
|
onMouseMove={onMouseMove}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
|
@ -87,7 +87,9 @@ export default class ScatterPlot extends PureComponent<Props> {
|
|||||||
<TooltipRenderer datum={datum} encoder={encoder} />
|
<TooltipRenderer datum={datum} encoder={encoder} />
|
||||||
)}
|
)}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
|
||||||
xScale={convertScaleToDataUIScale(channels.x.definition.scale as 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)}
|
yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)}
|
||||||
>
|
>
|
||||||
{layout.renderXAxis()}
|
{layout.renderXAxis()}
|
||||||
|
Loading…
Reference in New Issue
Block a user