refactor: typing for explore Control and messageToasts (#11416)

This commit is contained in:
Jesse Yang 2020-10-24 21:40:36 -07:00 committed by GitHub
parent d02a61f21c
commit 8aecffd83b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 226 additions and 265 deletions

View File

@ -40,7 +40,7 @@ import {
import { setUnsavedChanges } from 'src/dashboard/actions/dashboardState';
import * as dashboardFilters from 'src/dashboard/actions/dashboardFilters';
import { addWarningToast, ADD_TOAST } from 'src/messageToasts/actions';
import { ADD_TOAST } from 'src/messageToasts/actions';
import {
DASHBOARD_GRID_TYPE,
@ -349,24 +349,27 @@ describe('dashboardLayout actions', () => {
const { getState, dispatch } = setup({
dashboardLayout: {
present: {
source: { type: ROW_TYPE },
destination: { type: ROW_TYPE, children: ['rowChild'] },
dragging: { type: CHART_TYPE, meta: { width: 1 } },
rowChild: { type: CHART_TYPE, meta: { width: 12 } },
source: { id: 'source', type: ROW_TYPE, children: ['dragging'] },
destination: {
id: 'destination',
type: ROW_TYPE,
children: ['rowChild'],
},
dragging: { id: 'dragging', type: CHART_TYPE, meta: { width: 1 } },
rowChild: { id: 'rowChild', type: CHART_TYPE, meta: { width: 12 } },
},
},
});
const dropResult = {
source: { id: 'source', type: ROW_TYPE },
destination: { id: 'destination', type: ROW_TYPE },
dragging: { id: 'dragging', type: CHART_TYPE },
dragging: { id: 'dragging', type: CHART_TYPE, meta: { width: 1 } },
};
const thunk = handleComponentDrop(dropResult);
thunk(dispatch, getState);
expect(dispatch.getCall(0).args[0].type).toEqual(
addWarningToast('').type,
);
expect(dispatch.getCall(0).args[0].type).toEqual(ADD_TOAST);
expect(dispatch.callCount).toBe(1);
});
@ -479,13 +482,9 @@ describe('dashboardLayout actions', () => {
},
};
const thunk1 = handleComponentDrop(dropResult);
thunk1(dispatch, getState);
handleComponentDrop(dropResult)(dispatch, getState);
const thunk2 = dispatch.getCall(0).args[0];
thunk2(dispatch, getState);
expect(dispatch.getCall(1).args[0].type).toEqual(ADD_TOAST);
expect(dispatch.getCall(0).args[0].type).toEqual(ADD_TOAST);
});
});

View File

@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import { INFO_TOAST, DANGER_TOAST } from 'src/messageToasts/constants';
import { ToastType } from 'src/messageToasts/constants';
export default [
{ id: 'info_id', toastType: INFO_TOAST, text: 'info toast' },
{ id: 'danger_id', toastType: DANGER_TOAST, text: 'danger toast' },
{ id: 'info_id', toastType: ToastType.INFO, text: 'info toast' },
{ id: 'danger_id', toastType: ToastType.DANGER, text: 'danger toast' },
];

View File

@ -16,18 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
DANGER_TOAST,
INFO_TOAST,
SUCCESS_TOAST,
} from 'src/messageToasts/constants';
import { ToastType } from 'src/messageToasts/constants';
import getToastsFromPyFlashMessages from 'src/messageToasts/utils/getToastsFromPyFlashMessages';
describe('getToastsFromPyFlashMessages', () => {
it('should return an info toast', () => {
const toast = getToastsFromPyFlashMessages([['info', 'info test']])[0];
expect(toast).toMatchObject({ toastType: INFO_TOAST, text: 'info test' });
expect(toast).toMatchObject({
toastType: ToastType.INFO,
text: 'info test',
});
});
it('should return a success toast', () => {
@ -35,7 +34,7 @@ describe('getToastsFromPyFlashMessages', () => {
['success', 'success test'],
])[0];
expect(toast).toMatchObject({
toastType: SUCCESS_TOAST,
toastType: ToastType.SUCCESS,
text: 'success test',
});
});
@ -43,7 +42,7 @@ describe('getToastsFromPyFlashMessages', () => {
it('should return a danger toast', () => {
const toast = getToastsFromPyFlashMessages([['danger', 'danger test']])[0];
expect(toast).toMatchObject({
toastType: DANGER_TOAST,
toastType: ToastType.DANGER,
text: 'danger test',
});
});

View File

@ -49,7 +49,9 @@ const propTypes = {
timeout: PropTypes.number,
vizType: PropTypes.string.isRequired,
triggerRender: PropTypes.bool,
owners: PropTypes.arrayOf(PropTypes.string),
owners: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
),
// state
chartAlert: PropTypes.string,
chartStatus: PropTypes.string,

View File

@ -618,6 +618,7 @@ class DatasourceEditor extends React.PureComponent {
<div className="m-l-10 m-t-20 m-b-10">
{DATASOURCE_TYPES_ARR.map(type => (
<Radio
key={type.key}
value={type.key}
inline
onChange={this.onDatasourceTypeChange.bind(this, type.key)}

View File

@ -17,23 +17,31 @@
* under the License.
*/
/* eslint camelcase: 0 */
import { t, SupersetClient } from '@superset-ui/core';
import { addDangerToast } from '../../messageToasts/actions';
import { DatasourceMeta } from '@superset-ui/chart-controls';
import {
t,
SupersetClient,
DatasourceType,
QueryFormData,
} from '@superset-ui/core';
import { Dispatch } from 'redux';
import { addDangerToast } from 'src/messageToasts/actions';
import { Slice } from 'src/types/Chart';
const FAVESTAR_BASE_URL = '/superset/favstar/slice';
export const SET_DATASOURCE_TYPE = 'SET_DATASOURCE_TYPE';
export function setDatasourceType(datasourceType) {
export function setDatasourceType(datasourceType: DatasourceType) {
return { type: SET_DATASOURCE_TYPE, datasourceType };
}
export const SET_DATASOURCE = 'SET_DATASOURCE';
export function setDatasource(datasource) {
export function setDatasource(datasource: DatasourceMeta) {
return { type: SET_DATASOURCE, datasource };
}
export const SET_DATASOURCES = 'SET_DATASOURCES';
export function setDatasources(datasources) {
export function setDatasources(datasources: DatasourceMeta[]) {
return { type: SET_DATASOURCES, datasources };
}
@ -53,29 +61,19 @@ export function fetchDatasourcesSucceeded() {
return { type: FETCH_DATASOURCES_SUCCEEDED };
}
export const FETCH_DATASOURCES_FAILED = 'FETCH_DATASOURCES_FAILED';
export function fetchDatasourcesFailed(error) {
return { type: FETCH_DATASOURCES_FAILED, error };
}
export const POST_DATASOURCES_FAILED = 'POST_DATASOURCES_FAILED';
export function postDatasourcesFailed(error) {
return { type: POST_DATASOURCES_FAILED, error };
}
export const RESET_FIELDS = 'RESET_FIELDS';
export function resetControls() {
return { type: RESET_FIELDS };
}
export const TOGGLE_FAVE_STAR = 'TOGGLE_FAVE_STAR';
export function toggleFaveStar(isStarred) {
export function toggleFaveStar(isStarred: boolean) {
return { type: TOGGLE_FAVE_STAR, isStarred };
}
export const FETCH_FAVE_STAR = 'FETCH_FAVE_STAR';
export function fetchFaveStar(sliceId) {
return function (dispatch) {
export function fetchFaveStar(sliceId: string) {
return function (dispatch: Dispatch<ReturnType<typeof toggleFaveStar>>) {
SupersetClient.get({
endpoint: `${FAVESTAR_BASE_URL}/${sliceId}/count`,
}).then(({ json }) => {
@ -87,33 +85,32 @@ export function fetchFaveStar(sliceId) {
}
export const SAVE_FAVE_STAR = 'SAVE_FAVE_STAR';
export function saveFaveStar(sliceId, isStarred) {
return function (dispatch) {
export function saveFaveStar(sliceId: string, isStarred: boolean) {
return function (dispatch: Dispatch<ReturnType<typeof addDangerToast>>) {
const urlSuffix = isStarred ? 'unselect' : 'select';
SupersetClient.get({
endpoint: `${FAVESTAR_BASE_URL}/${sliceId}/${urlSuffix}/`,
})
.then(() => dispatch(toggleFaveStar(!isStarred)))
.catch(() =>
.catch(() => {
dispatch(
addDangerToast(t('An error occurred while starring this chart')),
),
);
);
});
};
}
export const SET_FIELD_VALUE = 'SET_FIELD_VALUE';
export function setControlValue(controlName, value, validationErrors) {
export function setControlValue(
controlName: string,
value: any,
validationErrors: any[],
) {
return { type: SET_FIELD_VALUE, controlName, value, validationErrors };
}
export const UPDATE_EXPLORE_ENDPOINTS = 'UPDATE_EXPLORE_ENDPOINTS';
export function updateExploreEndpoints(jsonUrl, csvUrl, standaloneUrl) {
return { type: UPDATE_EXPLORE_ENDPOINTS, jsonUrl, csvUrl, standaloneUrl };
}
export const SET_EXPLORE_CONTROLS = 'UPDATE_EXPLORE_CONTROLS';
export function setExploreControls(formData) {
export function setExploreControls(formData: QueryFormData) {
return { type: SET_EXPLORE_CONTROLS, formData };
}
@ -123,17 +120,17 @@ export function removeControlPanelAlert() {
}
export const UPDATE_CHART_TITLE = 'UPDATE_CHART_TITLE';
export function updateChartTitle(sliceName) {
export function updateChartTitle(sliceName: string) {
return { type: UPDATE_CHART_TITLE, sliceName };
}
export const CREATE_NEW_SLICE = 'CREATE_NEW_SLICE';
export function createNewSlice(
can_add,
can_download,
can_overwrite,
slice,
form_data,
can_add: boolean,
can_download: boolean,
can_overwrite: boolean,
slice: Slice,
form_data: QueryFormData,
) {
return {
type: CREATE_NEW_SLICE,
@ -146,6 +143,26 @@ export function createNewSlice(
}
export const SLICE_UPDATED = 'SLICE_UPDATED';
export function sliceUpdated(slice) {
export function sliceUpdated(slice: Slice) {
return { type: SLICE_UPDATED, slice };
}
export const exploreActions = {
setDatasourceType,
setDatasource,
setDatasources,
fetchDatasourcesStarted,
fetchDatasourcesSucceeded,
resetControls,
toggleFaveStar,
fetchFaveStar,
saveFaveStar,
setControlValue,
setExploreControls,
removeControlPanelAlert,
updateChartTitle,
createNewSlice,
sliceUpdated,
};
export type ExploreActions = typeof exploreActions;

View File

@ -16,52 +16,41 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import './Control.less';
import React, { ReactNode } from 'react';
import { ControlType } from '@superset-ui/chart-controls';
import { JsonValue, QueryFormData } from '@superset-ui/core';
import { ExploreActions } from '../actions/exploreActions';
import controlMap from './controls';
const controlTypes = Object.keys(controlMap);
import './Control.less';
const propTypes = {
actions: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.oneOfType([
PropTypes.oneOf(controlTypes).isRequired,
PropTypes.func.isRequired,
]),
hidden: PropTypes.bool,
label: PropTypes.string.isRequired,
choices: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.array),
PropTypes.func,
]),
description: PropTypes.string,
tooltipOnClick: PropTypes.func,
places: PropTypes.number,
validationErrors: PropTypes.array,
renderTrigger: PropTypes.bool,
rightNode: PropTypes.node,
formData: PropTypes.object,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.object,
PropTypes.bool,
PropTypes.array,
PropTypes.func,
]),
export type ControlProps = {
// the actual action dispatcher (via bindActionCreators) has identical
// signature to the original action factory.
actions: ExploreActions;
type: ControlType;
label: string;
name: string;
description?: string;
tooltipOnClick?: () => ReactNode;
places?: number;
rightNode?: ReactNode;
formData?: QueryFormData | null;
value?: JsonValue;
validationErrors?: any[];
hidden?: boolean;
renderTrigger?: boolean;
};
const defaultProps = {
renderTrigger: false,
hidden: false,
validationErrors: [],
};
export default class Control extends React.PureComponent<
ControlProps,
{ hovered: boolean }
> {
onMouseEnter: () => void;
export default class Control extends React.PureComponent {
constructor(props) {
onMouseLeave: () => void;
constructor(props: ControlProps) {
super(props);
this.state = { hovered: false };
this.onChange = this.onChange.bind(this);
@ -69,11 +58,11 @@ export default class Control extends React.PureComponent {
this.onMouseLeave = this.setHover.bind(this, false);
}
onChange(value, errors) {
onChange(value: any, errors: any[]) {
this.props.actions.setControlValue(this.props.name, value, errors);
}
setHover(hovered) {
setHover(hovered: boolean) {
this.setState({ hovered });
}
@ -98,6 +87,3 @@ export default class Control extends React.PureComponent {
);
}
}
Control.propTypes = propTypes;
Control.defaultProps = defaultProps;

View File

@ -28,7 +28,7 @@ import ControlPanelSection from './ControlPanelSection';
import ControlRow from './ControlRow';
import Control from './Control';
import { sectionsToRender } from '../controlUtils';
import * as exploreActions from '../actions/exploreActions';
import { exploreActions } from '../actions/exploreActions';
const propTypes = {
actions: PropTypes.object.isRequired,
@ -106,8 +106,8 @@ class ControlPanelsContainer extends React.Component {
return (
<Control
name={name}
key={`control-${name}`}
name={name}
validationErrors={validationErrors}
actions={actions}
formData={provideFormDataToProps ? formData : null}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import {
Modal,
Row,
@ -31,18 +31,10 @@ import { OptionsType } from 'react-select/src/types';
import { AsyncSelect } from 'src/components/Select';
import rison from 'rison';
import { t, SupersetClient } from '@superset-ui/core';
import Chart from 'src/types/Chart';
import Chart, { Slice } from 'src/types/Chart';
import FormLabel from 'src/components/FormLabel';
import getClientErrorObject from '../../utils/getClientErrorObject';
export type Slice = {
id?: number;
slice_id: number;
slice_name: string;
description: string | null;
cache_timeout: number | null;
};
type InternalProps = {
slice: Slice;
onHide: () => void;
@ -81,28 +73,31 @@ function PropertiesModal({ slice, onHide, onSave }: InternalProps) {
});
}
async function fetchChartData() {
try {
const response = await SupersetClient.get({
endpoint: `/api/v1/chart/${slice.slice_id}`,
});
const chart = response.json.result;
setOwners(
chart.owners.map((owner: any) => ({
value: owner.id,
label: `${owner.first_name} ${owner.last_name}`,
})),
);
} catch (response) {
const clientError = await getClientErrorObject(response);
showError(clientError);
}
}
const fetchChartData = useCallback(
async function fetchChartData() {
try {
const response = await SupersetClient.get({
endpoint: `/api/v1/chart/${slice.slice_id}`,
});
const chart = response.json.result;
setOwners(
chart.owners.map((owner: any) => ({
value: owner.id,
label: `${owner.first_name} ${owner.last_name}`,
})),
);
} catch (response) {
const clientError = await getClientErrorObject(response);
showError(clientError);
}
},
[slice.slice_id],
);
// get the owners of this slice
useEffect(() => {
fetchChartData();
}, []);
}, [fetchChartData]);
const loadOptions = (input = '') => {
const query = rison.encode({

View File

@ -72,19 +72,6 @@ export default function exploreReducer(state = {}, action) {
isDatasourcesLoading: true,
};
},
[actions.FETCH_DATASOURCES_SUCCEEDED]() {
return {
...state,
isDatasourcesLoading: false,
};
},
[actions.FETCH_DATASOURCES_FAILED]() {
return {
...state,
isDatasourcesLoading: false,
controlPanelAlert: action.error,
};
},
[actions.SET_DATASOURCES]() {
return {
...state,

View File

@ -17,20 +17,18 @@
* under the License.
*/
import shortid from 'shortid';
import { ToastType, ToastMeta } from '../types';
import {
INFO_TOAST,
SUCCESS_TOAST,
WARNING_TOAST,
DANGER_TOAST,
} from '../constants';
export function getToastUuid(type) {
export function getToastUuid(type: ToastType) {
return `${type}-${shortid.generate()}`;
}
export const ADD_TOAST = 'ADD_TOAST';
export function addToast({ toastType, text, duration = 8000 }) {
export function addToast({
toastType,
text,
duration = 8000,
}: Omit<ToastMeta, 'id'>) {
return {
type: ADD_TOAST,
payload: {
@ -43,7 +41,7 @@ export function addToast({ toastType, text, duration = 8000 }) {
}
export const REMOVE_TOAST = 'REMOVE_TOAST';
export function removeToast(id) {
export function removeToast(id: string) {
return {
type: REMOVE_TOAST,
payload: {
@ -54,25 +52,21 @@ export function removeToast(id) {
// Different types of toasts
export const ADD_INFO_TOAST = 'ADD_INFO_TOAST';
export function addInfoToast(text) {
return dispatch =>
dispatch(addToast({ text, toastType: INFO_TOAST, duration: 4000 }));
export function addInfoToast(text: string) {
return addToast({ text, toastType: ToastType.INFO, duration: 4000 });
}
export const ADD_SUCCESS_TOAST = 'ADD_SUCCESS_TOAST';
export function addSuccessToast(text) {
return dispatch =>
dispatch(addToast({ text, toastType: SUCCESS_TOAST, duration: 4000 }));
export function addSuccessToast(text: string) {
return addToast({ text, toastType: ToastType.SUCCESS, duration: 4000 });
}
export const ADD_WARNING_TOAST = 'ADD_WARNING_TOAST';
export function addWarningToast(text) {
return dispatch =>
dispatch(addToast({ text, toastType: WARNING_TOAST, duration: 6000 }));
export function addWarningToast(text: string) {
return addToast({ text, toastType: ToastType.WARNING, duration: 6000 });
}
export const ADD_DANGER_TOAST = 'ADD_DANGER_TOAST';
export function addDangerToast(text) {
return dispatch =>
dispatch(addToast({ text, toastType: DANGER_TOAST, duration: 8000 }));
export function addDangerToast(text: string) {
return addToast({ text, toastType: ToastType.DANGER, duration: 8000 });
}

View File

@ -20,11 +20,10 @@ import { Alert } from 'react-bootstrap';
import { styled } from '@superset-ui/core';
import cx from 'classnames';
import Interweave from 'interweave';
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import Icon from 'src/components/Icon';
import { ToastType } from 'src/messageToasts/types';
import { SUCCESS_TOAST, WARNING_TOAST, DANGER_TOAST } from '../constants';
import { ToastType } from 'src/messageToasts/constants';
import { ToastMeta } from '../types';
const ToastContianer = styled.div`
display: flex;
@ -37,19 +36,21 @@ const ToastContianer = styled.div`
`;
interface ToastPresenterProps {
toast: { id: string; toastType: ToastType; text: string; duration: number };
toast: ToastMeta;
onCloseToast: (id: string) => void;
}
export default function Toast({ toast, onCloseToast }: ToastPresenterProps) {
let hideTimer: ReturnType<typeof setTimeout>;
const hideTimer = useRef<ReturnType<typeof setTimeout>>();
const [visible, setVisible] = useState(false);
const showToast = () => {
setVisible(true);
};
const handleClosePress = () => {
clearTimeout(hideTimer);
const handleClosePress = useCallback(() => {
if (hideTimer.current) {
clearTimeout(hideTimer.current);
}
// Wait for the transition
setVisible(() => {
setTimeout(() => {
@ -57,18 +58,20 @@ export default function Toast({ toast, onCloseToast }: ToastPresenterProps) {
}, 150);
return false;
});
};
}, [onCloseToast, toast.id]);
useEffect(() => {
setTimeout(showToast);
if (toast.duration > 0) {
hideTimer = setTimeout(handleClosePress, toast.duration);
hideTimer.current = setTimeout(handleClosePress, toast.duration);
}
return () => {
clearTimeout(hideTimer);
if (hideTimer.current) {
clearTimeout(hideTimer.current);
}
};
}, []);
}, [handleClosePress, toast.duration]);
return (
<Alert
@ -77,17 +80,17 @@ export default function Toast({ toast, onCloseToast }: ToastPresenterProps) {
'alert',
'toast',
visible && 'toast--visible',
toast.toastType === SUCCESS_TOAST && 'toast--success',
toast.toastType === WARNING_TOAST && 'toast--warning',
toast.toastType === DANGER_TOAST && 'toast--danger',
toast.toastType === ToastType.SUCCESS && 'toast--success',
toast.toastType === ToastType.WARNING && 'toast--warning',
toast.toastType === ToastType.DANGER && 'toast--danger',
)}
>
<ToastContianer>
{toast.toastType === SUCCESS_TOAST && (
{toast.toastType === ToastType.SUCCESS && (
<Icon name="circle-check-solid" />
)}
{toast.toastType === WARNING_TOAST ||
(toast.toastType === DANGER_TOAST && <Icon name="error-solid" />)}
{toast.toastType === ToastType.WARNING ||
(toast.toastType === ToastType.DANGER && <Icon name="error-solid" />)}
<Interweave content={toast.text} />
</ToastContianer>
</Alert>

View File

@ -18,7 +18,7 @@
*/
import React from 'react';
import { styled } from '@superset-ui/core';
import { ToastType } from 'src/messageToasts/types';
import { ToastMeta } from 'src/messageToasts/types';
import Toast from './Toast';
const StyledToastPresenter = styled.div`
@ -67,25 +67,22 @@ const StyledToastPresenter = styled.div`
}
`;
type ToastShape = {
id: string;
toastType: ToastType;
text: string;
duration: number;
type ToastPresenterProps = {
toasts: Array<ToastMeta>;
removeToast: () => void;
};
interface ToastPresenterProps {
toasts: Array<ToastShape>;
removeToast: () => void;
}
const ToastPresenter = ({ toasts, removeToast }: ToastPresenterProps) =>
toasts.length > 0 && (
<StyledToastPresenter id="toast-presenter">
{toasts.map(toast => (
<Toast key={toast.id} toast={toast} onCloseToast={removeToast} />
))}
</StyledToastPresenter>
export default function ToastPresenter({
toasts,
removeToast,
}: ToastPresenterProps) {
return (
toasts.length > 0 && (
<StyledToastPresenter id="toast-presenter">
{toasts.map(toast => (
<Toast key={toast.id} toast={toast} onCloseToast={removeToast} />
))}
</StyledToastPresenter>
)
);
export default ToastPresenter;
}

View File

@ -17,7 +17,12 @@
* under the License.
*/
// Toast types
export const INFO_TOAST = 'INFO_TOAST';
export const SUCCESS_TOAST = 'SUCCESS_TOAST';
export const WARNING_TOAST = 'WARNING_TOAST';
export const DANGER_TOAST = 'DANGER_TOAST';
import { ToastType } from './types';
export { ToastType } from './types';
// for backward compatibility
export const INFO_TOAST = ToastType.INFO;
export const SUCCES_TOAST = ToastType.SUCCESS;
export const WARNING_TOAST = ToastType.WARNING;
export const DANGER_TOAST = ToastType.DANGER;

View File

@ -1,39 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import {
INFO_TOAST,
SUCCESS_TOAST,
WARNING_TOAST,
DANGER_TOAST,
} from './constants';
// eslint-disable-next-line import/prefer-default-export
export const toastShape = PropTypes.shape({
id: PropTypes.string.isRequired,
toastType: PropTypes.oneOf([
INFO_TOAST,
SUCCESS_TOAST,
WARNING_TOAST,
DANGER_TOAST,
]).isRequired,
text: PropTypes.string.isRequired,
duration: PropTypes.number,
});

View File

@ -16,9 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
export enum ToastType {
INFO = 'INFO_TOAST',
SUCCESS = 'SUCCESS_TOAST',
WARNING = 'WARNING_TOAST',
DANGER = 'DANGER_TOAST',
}
export type ToastType =
| 'INFO_TOAST'
| 'SUCCESS_TOAST'
| 'WARNING_TOAST'
| 'DANGER_TOAST';
export interface ToastMeta {
id: string;
toastType: ToastType;
text: string;
duration: number;
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
import { addToast } from '../actions';
import { INFO_TOAST, SUCCESS_TOAST, DANGER_TOAST } from '../constants';
import { ToastType } from '../constants';
export default function toastsFromPyFlashMessages(flashMessages = []) {
const toasts = [];
@ -25,8 +25,8 @@ export default function toastsFromPyFlashMessages(flashMessages = []) {
flashMessages.forEach(([messageType, message]) => {
const toastType =
messageType === 'danger'
? DANGER_TOAST
: (messageType === 'success' && SUCCESS_TOAST) || INFO_TOAST;
? ToastType.DANGER
: (messageType === 'success' && ToastType.SUCCESS) || ToastType.INFO;
const toast = addToast({
text: message,

View File

@ -37,3 +37,11 @@ export default interface Chart {
owners?: Owner[];
datasource_name_text?: string;
}
export type Slice = {
id?: number;
slice_id: number;
slice_name: string;
description: string | null;
cache_timeout: number | null;
};

View File

@ -34,8 +34,8 @@ import ListView, {
SelectOption,
} from 'src/components/ListView';
import withToasts from 'src/messageToasts/enhancers/withToasts';
import PropertiesModal, { Slice } from 'src/explore/components/PropertiesModal';
import Chart from 'src/types/Chart';
import PropertiesModal from 'src/explore/components/PropertiesModal';
import Chart, { Slice } from 'src/types/Chart';
import ListViewCard from 'src/components/ListViewCard';
import Label from 'src/components/Label';
import { Dropdown, Menu } from 'src/common/components';