Mono repo -> Master (#22553)
Combines the following repositories into one: https://github.com/mattermost/mattermost-server https://github.com/mattermost/mattermost-webapp https://github.com/mattermost/focalboard https://github.com/mattermost/mattermost-plugin-playbooks
Этот коммит содержится в:
@@ -0,0 +1,31 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/ModalController component should match snapshot without any modals 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<Connect(ModalController)>
|
||||
<ModalController
|
||||
actions={
|
||||
Object {
|
||||
"closeModal": [Function],
|
||||
}
|
||||
}
|
||||
modals={
|
||||
Object {
|
||||
"modalState": Object {},
|
||||
}
|
||||
}
|
||||
/>
|
||||
</Connect(ModalController)>
|
||||
</Provider>
|
||||
`;
|
||||
33
webapp/channels/src/components/modal_controller/index.ts
Обычный файл
33
webapp/channels/src/components/modal_controller/index.ts
Обычный файл
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux';
|
||||
|
||||
import {GlobalState} from 'types/store/index.js';
|
||||
|
||||
import {Action, GenericAction} from 'mattermost-redux/types/actions.js';
|
||||
|
||||
import {closeModal} from 'actions/views/modals';
|
||||
|
||||
import ModalController from './modal_controller';
|
||||
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
return {
|
||||
modals: state.views.modals,
|
||||
};
|
||||
}
|
||||
|
||||
type Actions = {
|
||||
closeModal: (modalId: string) => void;
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
||||
return {
|
||||
actions: bindActionCreators<ActionCreatorsMapObject<Action>, Actions>({
|
||||
closeModal,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ModalController);
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {Provider} from 'react-redux';
|
||||
import {mount} from 'enzyme';
|
||||
|
||||
import {closeModal} from 'actions/views/modals';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
|
||||
import ModalController from '.';
|
||||
|
||||
type TestModalProps = {
|
||||
onExited: () => void;
|
||||
}
|
||||
|
||||
type TestModalState = {
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
class TestModal extends React.PureComponent<TestModalProps, TestModalState> {
|
||||
constructor(props: TestModalProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
show: true,
|
||||
};
|
||||
}
|
||||
|
||||
hideModal = () => {
|
||||
this.setState({show: false});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal
|
||||
show={this.state.show}
|
||||
onHide={this.hideModal}
|
||||
onExited={this.props.onExited}
|
||||
>
|
||||
<Modal.Header closeButton={true}/>
|
||||
<Modal.Body/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('components/ModalController', () => {
|
||||
const modalId = 'test_modal';
|
||||
|
||||
test('component should match snapshot without any modals', () => {
|
||||
const state = {
|
||||
views: {
|
||||
modals: {
|
||||
modalState: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ModalController/>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('ModalController > *').length).toBe(0);
|
||||
expect(document.getElementsByClassName('modal-dialog').length).toBeFalsy();
|
||||
});
|
||||
|
||||
test('test model should be open', () => {
|
||||
const state = {
|
||||
views: {
|
||||
modals: {
|
||||
modalState: {
|
||||
[modalId]: {
|
||||
open: true,
|
||||
dialogProps: {},
|
||||
dialogType: TestModal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
mount(
|
||||
<Provider store={store}>
|
||||
<ModalController/>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(document.getElementsByClassName('modal-dialog').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should pass onExited to modal to allow a modal to remove itself', () => {
|
||||
const state = {
|
||||
views: {
|
||||
modals: {
|
||||
modalState: {
|
||||
[modalId]: {
|
||||
open: true,
|
||||
dialogProps: {},
|
||||
dialogType: TestModal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ModalController/>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(TestModal).exists()).toBe(true);
|
||||
expect(wrapper.find(TestModal).prop('onExited')).toBeDefined();
|
||||
expect(wrapper.find(Modal).prop('onExited')).toBeDefined();
|
||||
|
||||
wrapper.find(TestModal).prop('onExited')!();
|
||||
|
||||
expect(store.getActions()).toEqual([
|
||||
closeModal(modalId),
|
||||
]);
|
||||
});
|
||||
|
||||
test('should call a provided onExited in addition to removing the modal', () => {
|
||||
const onExited = jest.fn();
|
||||
|
||||
const state = {
|
||||
views: {
|
||||
modals: {
|
||||
modalState: {
|
||||
[modalId]: {
|
||||
open: true,
|
||||
dialogProps: {
|
||||
onExited,
|
||||
},
|
||||
dialogType: TestModal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ModalController/>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(TestModal).exists()).toBe(true);
|
||||
expect(wrapper.find(TestModal).prop('onExited')).toBeDefined();
|
||||
expect(wrapper.find(Modal).prop('onExited')).toBeDefined();
|
||||
|
||||
expect(onExited).not.toBeCalled();
|
||||
|
||||
wrapper.find(TestModal).prop('onExited')!();
|
||||
|
||||
expect(onExited).toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Modal = {
|
||||
open: boolean;
|
||||
dialogProps: Record<string, any>;
|
||||
dialogType: React.ComponentType;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
|
||||
/*
|
||||
* Object that has map of modal's id and element
|
||||
*/
|
||||
modals: {
|
||||
modalState: {
|
||||
[modalId: string]: Modal;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Object with action creators
|
||||
*/
|
||||
actions: {
|
||||
|
||||
/*
|
||||
* Action creator to close modal
|
||||
*/
|
||||
closeModal: (modalId: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export default class ModalController extends React.PureComponent<Props> {
|
||||
public render(): React.ReactNode {
|
||||
const {modals, ...props} = this.props;
|
||||
const {modalState} = modals;
|
||||
|
||||
if (!modals) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const modalOutput = [];
|
||||
|
||||
for (const modalId in modalState) {
|
||||
if (modalState.hasOwnProperty(modalId)) {
|
||||
const modal = modalState[modalId];
|
||||
if (modal.open) {
|
||||
const modalComponent = React.createElement(modal.dialogType, Object.assign({}, modal.dialogProps, {
|
||||
onExited: () => {
|
||||
props.actions.closeModal(modalId);
|
||||
|
||||
// Call any onExited prop provided by whoever opened the modal, if one was provided
|
||||
modal.dialogProps?.onExited?.();
|
||||
},
|
||||
onHide: props.actions.closeModal.bind(this, modalId),
|
||||
key: `${modalId}_modal`,
|
||||
}));
|
||||
|
||||
modalOutput.push(modalComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>{modalOutput}</>
|
||||
);
|
||||
}
|
||||
}
|
||||
Ссылка в новой задаче
Block a user