mirror of
https://github.com/apache/superset.git
synced 2026-06-05 23:59:25 +00:00
chore: Moves messageToasts to the components folder (#14431)
* chore: Moves messageToasts to the components folder * Rebases
This commit is contained in:
committed by
GitHub
parent
b6d78bf4f2
commit
9b17e86b44
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||
import Toast from 'src/components/MessageToasts/Toast';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import mockMessageToasts from './mockMessageToasts';
|
||||
|
||||
const props = {
|
||||
toast: mockMessageToasts[0],
|
||||
onCloseToast() {},
|
||||
};
|
||||
|
||||
const setup = overrideProps =>
|
||||
mount(<Toast {...props} {...overrideProps} />, {
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
});
|
||||
|
||||
describe('Toast', () => {
|
||||
it('should render', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('[data-test="toast-container"]')).toExist();
|
||||
});
|
||||
|
||||
it('should render toastText within the div', () => {
|
||||
const wrapper = setup();
|
||||
const container = wrapper.find('[data-test="toast-container"]');
|
||||
expect(container.hostNodes().childAt(1).text()).toBe(props.toast.text);
|
||||
});
|
||||
|
||||
it('should call onCloseToast upon toast dismissal', async () =>
|
||||
act(
|
||||
() =>
|
||||
new Promise(done => {
|
||||
const onCloseToast = id => {
|
||||
expect(id).toBe(props.toast.id);
|
||||
done();
|
||||
};
|
||||
|
||||
const wrapper = setup({ onCloseToast });
|
||||
wrapper.find('[data-test="close-button"]').props().onClick();
|
||||
}),
|
||||
));
|
||||
});
|
||||
111
superset-frontend/src/components/MessageToasts/Toast.tsx
Normal file
111
superset-frontend/src/components/MessageToasts/Toast.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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 { styled, css, SupersetTheme } from '@superset-ui/core';
|
||||
import cx from 'classnames';
|
||||
import Interweave from 'interweave';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { ToastType, ToastMeta } from './types';
|
||||
|
||||
const ToastContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
padding: 0 11px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIcon = (theme: SupersetTheme) => css`
|
||||
min-width: ${theme.gridUnit * 5}px;
|
||||
color: ${theme.colors.grayscale.base};
|
||||
`;
|
||||
|
||||
interface ToastPresenterProps {
|
||||
toast: ToastMeta;
|
||||
onCloseToast: (id: string) => void;
|
||||
}
|
||||
|
||||
export default function Toast({ toast, onCloseToast }: ToastPresenterProps) {
|
||||
const hideTimer = useRef<ReturnType<typeof setTimeout>>();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const showToast = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const handleClosePress = useCallback(() => {
|
||||
if (hideTimer.current) {
|
||||
clearTimeout(hideTimer.current);
|
||||
}
|
||||
// Wait for the transition
|
||||
setVisible(() => {
|
||||
setTimeout(() => {
|
||||
onCloseToast(toast.id);
|
||||
}, 150);
|
||||
return false;
|
||||
});
|
||||
}, [onCloseToast, toast.id]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(showToast);
|
||||
|
||||
if (toast.duration > 0) {
|
||||
hideTimer.current = setTimeout(handleClosePress, toast.duration);
|
||||
}
|
||||
return () => {
|
||||
if (hideTimer.current) {
|
||||
clearTimeout(hideTimer.current);
|
||||
}
|
||||
};
|
||||
}, [handleClosePress, toast.duration]);
|
||||
|
||||
let className = 'toast--success';
|
||||
let icon = <Icons.CircleCheckSolid css={theme => StyledIcon(theme)} />;
|
||||
|
||||
if (toast.toastType === ToastType.WARNING) {
|
||||
icon = <Icons.WarningSolid css={StyledIcon} />;
|
||||
className = 'toast--warning';
|
||||
} else if (toast.toastType === ToastType.DANGER) {
|
||||
icon = <Icons.ErrorSolid css={StyledIcon} />;
|
||||
className = 'toast--danger';
|
||||
} else if (toast.toastType === ToastType.INFO) {
|
||||
icon = <Icons.InfoSolid css={StyledIcon} />;
|
||||
className = 'toast--info';
|
||||
}
|
||||
|
||||
return (
|
||||
<ToastContainer
|
||||
className={cx('alert', 'toast', visible && 'toast--visible', className)}
|
||||
data-test="toast-container"
|
||||
role="alert"
|
||||
>
|
||||
{icon}
|
||||
<Interweave content={toast.text} />
|
||||
<i
|
||||
className="fa fa-close pull-right pointer"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClosePress}
|
||||
aria-label="Close"
|
||||
data-test="close-button"
|
||||
/>
|
||||
</ToastContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import ToastPresenter from './ToastPresenter';
|
||||
|
||||
import { removeToast } from './actions';
|
||||
|
||||
export default connect(
|
||||
({ messageToasts: toasts }) => ({ toasts }),
|
||||
dispatch => bindActionCreators({ removeToast }, dispatch),
|
||||
)(ToastPresenter);
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import Toast from 'src/components/MessageToasts/Toast';
|
||||
import ToastPresenter from 'src/components/MessageToasts/ToastPresenter';
|
||||
import mockMessageToasts from './mockMessageToasts';
|
||||
|
||||
describe('ToastPresenter', () => {
|
||||
const props = {
|
||||
toasts: mockMessageToasts,
|
||||
removeToast() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
const wrapper = shallow(<ToastPresenter {...props} {...overrideProps} />);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a div with id toast-presenter', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('#toast-presenter')).toExist();
|
||||
});
|
||||
|
||||
it('should render a Toast for each toast object', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(Toast)).toHaveLength(props.toasts.length);
|
||||
});
|
||||
|
||||
it('should pass removeToast to the Toast component', () => {
|
||||
const removeToast = () => {};
|
||||
const wrapper = setup({ removeToast });
|
||||
expect(wrapper.find(Toast).first().prop('onCloseToast')).toBe(removeToast);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { styled } from '@superset-ui/core';
|
||||
import { ToastMeta } from 'src/components/MessageToasts/types';
|
||||
import Toast from './Toast';
|
||||
|
||||
const StyledToastPresenter = styled.div`
|
||||
max-width: 600px;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
margin-right: 50px;
|
||||
margin-bottom: 50px;
|
||||
z-index: ${({ theme }) => theme.zIndex.max};
|
||||
|
||||
.toast {
|
||||
background: ${({ theme }) => theme.colors.grayscale.dark1};
|
||||
border-radius: ${({ theme }) => theme.borderRadius};
|
||||
box-shadow: 0 2px 4px 0
|
||||
fade(
|
||||
${({ theme }) => theme.colors.grayscale.dark2},
|
||||
${({ theme }) => theme.opacity.mediumLight}
|
||||
);
|
||||
color: ${({ theme }) => theme.colors.grayscale.light5};
|
||||
opacity: 0;
|
||||
position: relative;
|
||||
transform: translateY(-100%);
|
||||
white-space: pre-line;
|
||||
will-change: transform, opacity;
|
||||
transition: transform ${({ theme }) => theme.transitionTiming}s,
|
||||
opacity ${({ theme }) => theme.transitionTiming}s;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 6px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.toast > button {
|
||||
color: ${({ theme }) => theme.colors.grayscale.light5};
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toast--visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
`;
|
||||
|
||||
type ToastPresenterProps = {
|
||||
toasts: Array<ToastMeta>;
|
||||
removeToast: () => void;
|
||||
};
|
||||
|
||||
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>
|
||||
)
|
||||
);
|
||||
}
|
||||
103
superset-frontend/src/components/MessageToasts/actions.ts
Normal file
103
superset-frontend/src/components/MessageToasts/actions.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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 shortid from 'shortid';
|
||||
import { ToastType, ToastMeta } from './types';
|
||||
|
||||
type ToastOptions = Partial<Omit<ToastMeta, 'id' | 'toastType' | 'text'>>;
|
||||
|
||||
export function getToastUuid(type: ToastType) {
|
||||
return `${type}-${shortid.generate()}`;
|
||||
}
|
||||
|
||||
export const ADD_TOAST = 'ADD_TOAST';
|
||||
export function addToast({
|
||||
toastType,
|
||||
text,
|
||||
duration = 8000,
|
||||
noDuplicate = false,
|
||||
}: Omit<ToastMeta, 'id'>) {
|
||||
return {
|
||||
type: ADD_TOAST,
|
||||
payload: {
|
||||
id: getToastUuid(toastType),
|
||||
toastType,
|
||||
text,
|
||||
duration,
|
||||
noDuplicate,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const REMOVE_TOAST = 'REMOVE_TOAST';
|
||||
export function removeToast(id: string) {
|
||||
return {
|
||||
type: REMOVE_TOAST,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Different types of toasts
|
||||
export const ADD_INFO_TOAST = 'ADD_INFO_TOAST';
|
||||
export function addInfoToast(text: string, options?: ToastOptions) {
|
||||
return addToast({
|
||||
text,
|
||||
toastType: ToastType.INFO,
|
||||
duration: 4000,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export const ADD_SUCCESS_TOAST = 'ADD_SUCCESS_TOAST';
|
||||
export function addSuccessToast(text: string, options?: ToastOptions) {
|
||||
return addToast({
|
||||
text,
|
||||
toastType: ToastType.SUCCESS,
|
||||
duration: 4000,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export const ADD_WARNING_TOAST = 'ADD_WARNING_TOAST';
|
||||
export function addWarningToast(text: string, options?: ToastOptions) {
|
||||
return addToast({
|
||||
text,
|
||||
toastType: ToastType.WARNING,
|
||||
duration: 6000,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export const ADD_DANGER_TOAST = 'ADD_DANGER_TOAST';
|
||||
export function addDangerToast(text: string, options?: ToastOptions) {
|
||||
return addToast({
|
||||
text,
|
||||
toastType: ToastType.DANGER,
|
||||
duration: 8000,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export const toastActions = {
|
||||
addInfoToast,
|
||||
addSuccessToast,
|
||||
addWarningToast,
|
||||
addDangerToast,
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 { addToast } from './actions';
|
||||
import { ToastType } from './types';
|
||||
|
||||
export default function toastsFromPyFlashMessages(flashMessages = []) {
|
||||
const toasts = [];
|
||||
|
||||
flashMessages.forEach(([messageType, message]) => {
|
||||
const toastType =
|
||||
messageType === 'danger'
|
||||
? ToastType.DANGER
|
||||
: (messageType === 'success' && ToastType.SUCCESS) || ToastType.INFO;
|
||||
|
||||
const toast = addToast({
|
||||
text: message,
|
||||
toastType,
|
||||
}).payload;
|
||||
|
||||
toasts.push(toast);
|
||||
});
|
||||
|
||||
return toasts;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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 { ToastType } from 'src/components/MessageToasts/types';
|
||||
import getToastsFromPyFlashMessages from 'src/components/MessageToasts/getToastsFromPyFlashMessages';
|
||||
|
||||
describe('getToastsFromPyFlashMessages', () => {
|
||||
it('should return an info toast', () => {
|
||||
const toast = getToastsFromPyFlashMessages([['info', 'info test']])[0];
|
||||
expect(toast).toMatchObject({
|
||||
toastType: ToastType.INFO,
|
||||
text: 'info test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a success toast', () => {
|
||||
const toast = getToastsFromPyFlashMessages([
|
||||
['success', 'success test'],
|
||||
])[0];
|
||||
expect(toast).toMatchObject({
|
||||
toastType: ToastType.SUCCESS,
|
||||
text: 'success test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a danger toast', () => {
|
||||
const toast = getToastsFromPyFlashMessages([['danger', 'danger test']])[0];
|
||||
expect(toast).toMatchObject({
|
||||
toastType: ToastType.DANGER,
|
||||
text: 'danger test',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 { ToastType } from 'src/components/MessageToasts/types';
|
||||
|
||||
export default [
|
||||
{ id: 'info_id', toastType: ToastType.INFO, text: 'info toast' },
|
||||
{ id: 'danger_id', toastType: ToastType.DANGER, text: 'danger toast' },
|
||||
];
|
||||
42
superset-frontend/src/components/MessageToasts/reducers.js
Normal file
42
superset-frontend/src/components/MessageToasts/reducers.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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 { ADD_TOAST, REMOVE_TOAST } from './actions';
|
||||
|
||||
export default function messageToastsReducer(toasts = [], action) {
|
||||
switch (action.type) {
|
||||
case ADD_TOAST: {
|
||||
const { payload: toast } = action;
|
||||
const result = toasts.slice();
|
||||
if (!toast.noDuplicate || !result.find(x => x.text === toast.text)) {
|
||||
return [toast, ...toasts];
|
||||
}
|
||||
return toasts;
|
||||
}
|
||||
|
||||
case REMOVE_TOAST: {
|
||||
const {
|
||||
payload: { id },
|
||||
} = action;
|
||||
return [...toasts].filter(toast => toast.id !== id);
|
||||
}
|
||||
|
||||
default:
|
||||
return toasts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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 { ADD_TOAST, REMOVE_TOAST } from 'src/components/MessageToasts/actions';
|
||||
import messageToastsReducer from 'src/components/MessageToasts/reducers';
|
||||
|
||||
describe('messageToasts reducer', () => {
|
||||
it('should return initial state', () => {
|
||||
expect(messageToastsReducer(undefined, {})).toEqual([]);
|
||||
});
|
||||
|
||||
it('should add a toast', () => {
|
||||
expect(
|
||||
messageToastsReducer([], {
|
||||
type: ADD_TOAST,
|
||||
payload: { text: 'test', id: 'id', type: 'test_type' },
|
||||
}),
|
||||
).toEqual([{ text: 'test', id: 'id', type: 'test_type' }]);
|
||||
});
|
||||
|
||||
it('should remove a toast', () => {
|
||||
expect(
|
||||
messageToastsReducer([{ id: 'id' }, { id: 'id2' }], {
|
||||
type: REMOVE_TOAST,
|
||||
payload: { id: 'id' },
|
||||
}),
|
||||
).toEqual([{ id: 'id2' }]);
|
||||
});
|
||||
});
|
||||
34
superset-frontend/src/components/MessageToasts/types.ts
Normal file
34
superset-frontend/src/components/MessageToasts/types.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export enum ToastType {
|
||||
INFO = 'INFO_TOAST',
|
||||
SUCCESS = 'SUCCESS_TOAST',
|
||||
WARNING = 'WARNING_TOAST',
|
||||
DANGER = 'DANGER_TOAST',
|
||||
}
|
||||
|
||||
export interface ToastMeta {
|
||||
id: string;
|
||||
toastType: ToastType;
|
||||
text: string;
|
||||
duration: number;
|
||||
/** Whether to skip displaying this message if there are another toast
|
||||
* with the same message. */
|
||||
noDuplicate?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 { ComponentType, useMemo } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect, useDispatch } from 'react-redux';
|
||||
|
||||
import {
|
||||
addDangerToast,
|
||||
addInfoToast,
|
||||
addSuccessToast,
|
||||
addWarningToast,
|
||||
} from './actions';
|
||||
|
||||
export interface ToastProps {
|
||||
addDangerToast: typeof addDangerToast;
|
||||
addInfoToast: typeof addInfoToast;
|
||||
addSuccessToast: typeof addSuccessToast;
|
||||
addWarningToast: typeof addWarningToast;
|
||||
}
|
||||
|
||||
const toasters = {
|
||||
addInfoToast,
|
||||
addSuccessToast,
|
||||
addWarningToast,
|
||||
addDangerToast,
|
||||
};
|
||||
|
||||
// To work properly the redux state must have a `messageToasts` subtree
|
||||
export default function withToasts(BaseComponent: ComponentType<any>) {
|
||||
return connect(null, dispatch => bindActionCreators(toasters, dispatch))(
|
||||
BaseComponent,
|
||||
) as any;
|
||||
// Redux has some confusing typings that cause problems for consumers of this function.
|
||||
// If someone can fix the types, great, but for now it's just any.
|
||||
}
|
||||
|
||||
export function useToasts() {
|
||||
const dispatch = useDispatch();
|
||||
return useMemo(() => bindActionCreators(toasters, dispatch), [dispatch]);
|
||||
}
|
||||
Reference in New Issue
Block a user