Skip to content
This repository was archived by the owner on Dec 9, 2021. It is now read-only.

Commit 1aded54

Browse files
authored
Modal (#26)
* Change HOC modal to render props * Rename GeneralModal to GenericModal * fix modal background
1 parent 03bc974 commit 1aded54

9 files changed

+246
-211
lines changed

src/constants/KeyCode.ts

-16
This file was deleted.

src/views/home/Home.tsx

+20-27
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import IStore from '../../stores/IStore';
77
import {Dispatch} from 'redux';
88
import IMetaReducerState from '../../stores/meta/IMetaReducerState';
99
import IUserReducerState from '../../stores/user/IUserReducerState';
10-
import GeneralModalAsync from '../modals/GeneralModalAsync';
10+
import GenericModalAsync from '../modals/GenericModalAsync';
1111
import ModalAction from '../../stores/modal/ModalAction';
1212
import ExampleFormModalAsync from '../modals/ExampleFormModalAsync';
1313
import IAction from '../../stores/IAction';
14+
import {IProps as GenericModalProps} from '../modals/GenericModal';
1415

1516
interface IState {}
1617
interface IProps {}
@@ -39,7 +40,7 @@ class Home extends React.Component<IStateToProps & IDispatchToProps & IProps, IS
3940
private _onClickPushExampleHandler: (event: React.MouseEvent<HTMLButtonElement>) => void = this._onClickPushExample.bind(this);
4041
private _onClickOpenModalHandler: (event: React.MouseEvent<HTMLButtonElement>) => void = this._onClickOpenModal.bind(this);
4142
private _onClickFormModalHandler: (event: React.MouseEvent<HTMLButtonElement>) => void = this._onClickFormModal.bind(this);
42-
private _onAcceptHandler: (event: React.MouseEvent<HTMLButtonElement>) => void = this._onAccept.bind(this);
43+
private _onAcceptHandler: (modalProps: GenericModalProps) => void = this._onAccept.bind(this);
4344

4445
public componentWillMount(): void {
4546
this.props.setMeta({
@@ -88,46 +89,38 @@ class Home extends React.Component<IStateToProps & IDispatchToProps & IProps, IS
8889
event.preventDefault();
8990

9091
const genericModal: JSX.Element = (
91-
<GeneralModalAsync
92-
modalData={{
93-
message: (
94-
<div>
95-
<h3>{'Generic Modal'}</h3>
96-
<p>{'Example of a generic modal. Used for simple messages.'}</p>
97-
</div>
98-
),
99-
acceptLabel: 'Open Another Modal',
100-
rejectLabel: 'Close',
101-
}}
92+
<GenericModalAsync
93+
message={(
94+
<div>
95+
<h3>{'Generic Modal'}</h3>
96+
<p>{'Example of a generic modal. Used for simple messages.'}</p>
97+
</div>
98+
)}
99+
acceptLabel={'Open Another Modal'}
100+
rejectLabel={'Close'}
102101
onAccept={this._onAcceptHandler}
103102
/>
104103
);
105104

106105
this.props.addModal(genericModal);
107106
}
108107

109-
private _onAccept(event: React.MouseEvent<HTMLButtonElement>): void {
110-
event.preventDefault();
111-
108+
private _onAccept(modalProps: GenericModalProps): void {
112109
const genericModal: JSX.Element = (
113-
<GeneralModalAsync
114-
modalData={{
115-
message: (
116-
<div>
117-
<p>{'Handles opening multiple modals.'}</p>
118-
</div>
119-
),
120-
acceptLabel: 'Ok',
121-
}}
110+
<GenericModalAsync
111+
message={(
112+
<div>
113+
<p>{'Handles opening multiple modals.'}</p>
114+
</div>
115+
)}
116+
acceptLabel={'Ok'}
122117
/>
123118
);
124119

125120
this.props.addModal(genericModal);
126121
}
127122

128123
private _onClickFormModal(event: React.MouseEvent<HTMLButtonElement>): void {
129-
event.preventDefault();
130-
131124
const formModal: JSX.Element = (
132125
<ExampleFormModalAsync isRequired={true} />
133126
);

src/views/modals/BaseModal.tsx

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import * as React from 'react';
2+
import {connect} from 'react-redux';
3+
import {Dispatch} from 'redux';
4+
import IAction from '../../stores/IAction';
5+
import IStore from '../../stores/IStore';
6+
import ModalAction from '../../stores/modal/ModalAction';
7+
import KeyboardKeyEnum from '../../constants/KeyboardKeyEnum';
8+
import * as classNames from 'classnames';
9+
10+
interface IProps {
11+
isRequired?: boolean;
12+
}
13+
interface IState {}
14+
interface IStateToProps {}
15+
interface IDispatchToProps {
16+
dispatch: (action: IAction<any>) => void;
17+
}
18+
19+
const mapStateToProps = (state: IStore) => ({});
20+
const mapDispatchToProps = (dispatch: Dispatch<IAction<any>>): IDispatchToProps => ({
21+
dispatch,
22+
});
23+
24+
type PropsUnion = IStateToProps & IDispatchToProps & IProps;
25+
26+
class BaseModal extends React.Component<PropsUnion, IState> {
27+
28+
public static defaultProps: Partial<PropsUnion> = {
29+
isRequired: false,
30+
};
31+
32+
public componentDidMount(): void {
33+
if (!this.props.isRequired) {
34+
global.window.addEventListener('keydown', this._onKeyDownModal);
35+
}
36+
}
37+
38+
public componentWillUnmount(): void {
39+
if (!this.props.isRequired) {
40+
global.window.removeEventListener('keydown', this._onKeyDownModal);
41+
}
42+
}
43+
44+
public render(): JSX.Element {
45+
return (
46+
<div
47+
className="modal"
48+
role="alert"
49+
aria-live="polite"
50+
>
51+
<div
52+
className={this._buildModalOverlayClassNames()}
53+
onClick={this._onClickOverlay}
54+
/>
55+
{this.props.children}
56+
</div>
57+
);
58+
}
59+
60+
private _onClickOverlay = (event: React.MouseEvent<HTMLElement>): void => {
61+
if (!this.props.isRequired) {
62+
this.props.dispatch(ModalAction.closeModal());
63+
}
64+
}
65+
66+
private _onKeyDownModal = (event: KeyboardEvent): void => {
67+
if (event.key === KeyboardKeyEnum.ESCAPE) {
68+
event.preventDefault();
69+
70+
this.props.dispatch(ModalAction.closeModal());
71+
}
72+
}
73+
74+
private _buildModalOverlayClassNames = (): string => {
75+
return classNames({
76+
'modal-overlay': true,
77+
'modal-overlay_required': this.props.isRequired,
78+
});
79+
}
80+
81+
}
82+
83+
export default connect<IStateToProps, IDispatchToProps, IProps>(mapStateToProps, mapDispatchToProps)(BaseModal);

src/views/modals/ExampleFormModal.tsx

+48-20
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,58 @@
11
import * as React from 'react';
22
import {form2js} from 'form2js';
3-
import withBaseModal, {TProps as BaseModelProps} from './withBaseModal';
43
import InputField from '../components/InputField';
4+
import {connect} from 'react-redux';
5+
import IAction from '../../stores/IAction';
6+
import IStore from '../../stores/IStore';
7+
import {Dispatch} from 'redux';
8+
import ModalAction from '../../stores/modal/ModalAction';
9+
import BaseModal from './BaseModal';
510

6-
interface IProps extends BaseModelProps {
7-
modalData: {};
11+
export interface IProps {
12+
isRequired: boolean;
13+
data: any;
814
}
915
interface IState {}
16+
interface IStateToProps {}
17+
interface IDispatchToProps {
18+
dispatch: (action: IAction<any>) => void;
19+
}
20+
21+
const mapStateToProps = (state: IStore) => ({});
22+
const mapDispatchToProps = (dispatch: Dispatch<IAction<any>>): IDispatchToProps => ({
23+
dispatch,
24+
});
25+
26+
type PropsUnion = IStateToProps & IDispatchToProps & IProps;
1027

11-
class ExampleFormModal extends React.Component<IProps, IState> {
28+
class ExampleFormModal extends React.Component<PropsUnion, IState> {
29+
30+
public static defaultProps: Partial<PropsUnion> = {
31+
isRequired: false,
32+
};
1233

1334
private _formElement: HTMLFormElement = null;
1435
private _onClickAcceptHandler: (event: React.MouseEvent<HTMLButtonElement>) => void = this._onClickAccept.bind(this);
36+
private _onClickCloseHandler: (event: React.MouseEvent<HTMLButtonElement>) => void = this._onClickClose.bind(this);
1537

1638
public render(): JSX.Element {
1739
return (
18-
<section className="modal-content">
19-
<h2 className="modal-header modal-header_left">{'Modal Form Title'}</h2>
20-
<div className="modal-body">
21-
{this._buildFormJsx()}
22-
</div>
23-
<div className="modal-footer modal-footer_stack">
24-
<button onClick={this.props.closeModal}>
25-
{'Cancel'}
26-
</button>
27-
<button onClick={this._onClickAcceptHandler}>
28-
{'Accept'}
29-
</button>
30-
</div>
31-
</section>
40+
<BaseModal isRequired={this.props.isRequired}>
41+
<section className="modal-content">
42+
<h2 className="modal-header modal-header_left">{'Modal Form Title'}</h2>
43+
<div className="modal-body">
44+
{this._buildFormJsx()}
45+
</div>
46+
<div className="modal-footer modal-footer_stack">
47+
<button onClick={this._onClickCloseHandler}>
48+
{'Cancel'}
49+
</button>
50+
<button onClick={this._onClickAcceptHandler}>
51+
{'Accept'}
52+
</button>
53+
</div>
54+
</section>
55+
</BaseModal>
3256
);
3357
}
3458

@@ -119,12 +143,16 @@ class ExampleFormModal extends React.Component<IProps, IState> {
119143

120144
console.info(formData);
121145

122-
this.props.closeModal();
146+
this._onClickClose();
123147
} else {
124148
this._formElement.classList.remove('u-validate');
125149
}
126150
}
127151

152+
private _onClickClose(event: React.MouseEvent<HTMLButtonElement> = null): void {
153+
this.props.dispatch(ModalAction.closeModal());
154+
}
155+
128156
}
129157

130-
export default withBaseModal(ExampleFormModal);
158+
export default connect<IStateToProps, IDispatchToProps, IProps>(mapStateToProps, mapDispatchToProps)(ExampleFormModal);

src/views/modals/GeneralModal.tsx

-43
This file was deleted.

src/views/modals/GeneralModalAsync.tsx

-11
This file was deleted.

0 commit comments

Comments
 (0)