Skip to content

Commit 6cfc5eb

Browse files
committed
fix: Material UI renaming of props
feat: Dialog components with demo feat: .disabled property for AppIconButton update: npom dependencies
1 parent 48dbd2f commit 6cfc5eb

File tree

19 files changed

+10692
-31140
lines changed

19 files changed

+10692
-31140
lines changed

package-lock.json

Lines changed: 148 additions & 20564 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-typescript-material",
3-
"version": "1.0.3",
3+
"version": "1.1.5",
44
"description": "React + Material UI + Auth starter using TypeScript",
55
"author": {
66
"name": "Anton Karpenko",
@@ -19,28 +19,28 @@
1919
"url": "https://github.com/karpolan/react-typescript-material-ui-with-auth-starter.git"
2020
},
2121
"dependencies": {
22-
"@material-ui/core": "^4.11.3",
22+
"@material-ui/core": "^4.12.3",
2323
"@material-ui/icons": "^4.11.2",
24-
"@material-ui/lab": "^4.0.0-alpha.57",
25-
"date-fns": "^2.21.1",
24+
"@material-ui/lab": "^4.0.0-alpha.60",
25+
"date-fns": "^2.23.0",
2626
"react": "^17.0.2",
2727
"react-dom": "^17.0.2",
2828
"react-router-dom": "^5.2.0",
2929
"react-scripts": "4.0.3",
30-
"typescript": "^4.1.2",
30+
"typescript": "^4.3.5",
3131
"validate.js": "^0.13.1",
32-
"web-vitals": "^1.0.1"
32+
"web-vitals": "^1.1.2"
3333
},
3434
"devDependencies": {
35-
"@testing-library/jest-dom": "^5.11.4",
36-
"@testing-library/react": "^11.1.0",
35+
"@testing-library/jest-dom": "^5.14.1",
36+
"@testing-library/react": "^11.2.7",
3737
"@testing-library/user-event": "^12.1.10",
38-
"@types/jest": "^26.0.15",
39-
"@types/node": "^12.0.0",
40-
"@types/react": "^17.0.0",
41-
"@types/react-dom": "^17.0.0",
42-
"@types/react-router-dom": "^5.1.7",
43-
"prettier": "^2.2.1"
38+
"@types/jest": "^26.0.24",
39+
"@types/node": "^12.20.19",
40+
"@types/react": "^17.0.19",
41+
"@types/react-dom": "^17.0.9",
42+
"@types/react-router-dom": "^5.1.8",
43+
"prettier": "^2.3.2"
4444
},
4545
"scripts": {
4646
"start": "react-scripts start",

src/components/AppButton/AppButton.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ interface Props extends Omit<ButtonProps, 'color'> {
3131
component?: React.ElementType; // Could be RouterLink, AppLink, etc.
3232
to?: string; // Link prop
3333
href?: string; // Link prop
34+
openInNewTab?: boolean; // Link prop
3435
underline?: 'none' | 'hover' | 'always'; // Link prop
3536
}
3637

src/components/AppIconButton/AppIconButton.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,25 @@ interface Props extends Omit<IconButtonProps, 'color'> {
3030
to?: string; // Link prop
3131
href?: string; // Link prop
3232
}
33-
const AppIconButton: React.FC<Props> = ({ color, className, children, icon, title, ...restOfProps }) => {
33+
const AppIconButton: React.FC<Props> = ({ color, className, children, disabled, icon, title, ...restOfProps }) => {
3434
const classes = useStyles();
3535
const classButton = clsx(classes[color as ColorName], className);
3636
const colorButton = getValidMuiColor(color);
3737

3838
const renderIcon = () => (
39-
<IconButton className={classButton} color={colorButton} {...restOfProps}>
39+
<IconButton className={classButton} color={colorButton} disabled={disabled} {...restOfProps}>
4040
<AppIcon icon={icon} />
4141
{children}
4242
</IconButton>
4343
);
4444

45-
return title ? <Tooltip title={title}>{renderIcon()}</Tooltip> : renderIcon();
45+
// When title is set, wrap te IconButton with Tooltip.
46+
// Note: when IconButton is disabled the Tooltip is not working, so we don't need it
47+
if (title && !disabled) {
48+
return <Tooltip title={title}>{renderIcon()}</Tooltip>;
49+
}
50+
51+
return title && !disabled ? <Tooltip title={title}>{renderIcon()}</Tooltip> : renderIcon();
4652
};
4753

4854
export default AppIconButton;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { ReactNode, useCallback } from 'react';
2+
import { makeStyles, createStyles, Dialog, DialogActions, DialogContent, DialogProps } from '@material-ui/core';
3+
import { AppButton } from '..';
4+
import { AppDialogTitle } from './components';
5+
import { ColorName, dialogStyles } from '../../utils/style';
6+
7+
const useStyles = makeStyles((theme) =>
8+
createStyles({
9+
root: {},
10+
...dialogStyles(theme),
11+
})
12+
);
13+
14+
/**
15+
* Shows generic "Common" dialog
16+
* @param {function} props.onConfirm - event for Confirm button, called as onConfirm(data)
17+
* @param {function} props.onClose - event for Close and Cancel buttons and the backdrop
18+
*/
19+
interface Props extends DialogProps {
20+
data?: unknown;
21+
title?: string;
22+
text?: string;
23+
body?: ReactNode;
24+
hideCancelButton?: boolean;
25+
confirmButtonText?: string;
26+
confirmButtonColor?: ColorName;
27+
onConfirm?: (data: unknown) => void;
28+
onClose?: (event: {}) => void;
29+
}
30+
const CommonDialog: React.FC<Props> = ({
31+
open = false, // Don't show dialog by default
32+
data, // optional data passed to onConfirm callback
33+
title = 'Missing title...',
34+
text = 'Text is missing...',
35+
body, // JSX to render instead of .text
36+
hideCancelButton = false,
37+
confirmButtonText = 'Confirm',
38+
confirmButtonColor = 'primary',
39+
onConfirm,
40+
onClose,
41+
...props
42+
}) => {
43+
const classes = useStyles();
44+
45+
const handleOnConfirm = useCallback(() => {
46+
if (onConfirm && typeof onConfirm === 'function') {
47+
onConfirm(data);
48+
}
49+
}, [data, onConfirm]);
50+
51+
return (
52+
<Dialog
53+
className={classes.root}
54+
classes={{ paper: classes.paper }}
55+
open={open}
56+
onClose={onClose}
57+
aria-labelledby="form-dialog-title"
58+
{...props}
59+
>
60+
<AppDialogTitle id="form-dialog-title" onClose={onClose}>
61+
{title}
62+
</AppDialogTitle>
63+
<DialogContent>{body || text}</DialogContent>
64+
<DialogActions className={classes.actions}>
65+
{!hideCancelButton && <AppButton onClick={onClose}>Cancel</AppButton>}
66+
<AppButton onClick={handleOnConfirm} color={confirmButtonColor} mr={0}>
67+
{confirmButtonText}
68+
</AppButton>
69+
</DialogActions>
70+
</Dialog>
71+
);
72+
};
73+
74+
export default CommonDialog;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { ReactNode } from 'react';
2+
import { makeStyles, createStyles, Dialog, DialogActions, DialogContent, DialogProps } from '@material-ui/core';
3+
import { AppDialogTitle } from './components';
4+
import { dialogStyles } from '../../utils/style';
5+
6+
const useStyles = makeStyles((theme) =>
7+
createStyles({
8+
...dialogStyles(theme),
9+
})
10+
);
11+
12+
/**
13+
* Makes composition of Content and Actions inside the Dialog.
14+
*/
15+
interface Props extends DialogProps {
16+
title?: string;
17+
content?: ReactNode;
18+
actions?: ReactNode;
19+
onClose?: (event: {}) => void;
20+
}
21+
const CompositionDialog: React.FC<Props> = ({
22+
actions,
23+
open = false, // Don't show dialog by default
24+
children = null,
25+
content = null,
26+
title = 'Missing title...',
27+
onClose,
28+
...props
29+
}) => {
30+
const classes = useStyles();
31+
return (
32+
<Dialog
33+
classes={{ paper: classes.paper }}
34+
open={open}
35+
onClose={onClose}
36+
aria-labelledby="form-dialog-title"
37+
{...props}
38+
>
39+
<AppDialogTitle id="form-dialog-title" onClose={onClose}>
40+
{title}
41+
</AppDialogTitle>
42+
<DialogContent className={classes.content}>
43+
{content}
44+
{children}
45+
</DialogContent>
46+
<DialogActions className={classes.actions}>{actions}</DialogActions>
47+
</Dialog>
48+
);
49+
};
50+
51+
export default CompositionDialog;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { makeStyles, createStyles, DialogTitle, Theme, DialogTitleProps } from '@material-ui/core';
2+
import { AppIconButton } from '../../';
3+
import { dialogStyles } from '../../../utils/style';
4+
5+
const useStyles = makeStyles((theme: Theme) =>
6+
createStyles({
7+
titleContainer: {
8+
display: 'flex',
9+
maxWidth: `calc(100% - ${theme.spacing(4)}px)`,
10+
},
11+
title: {
12+
textOverflow: 'ellipsis',
13+
whiteSpace: 'nowrap',
14+
overflow: 'hidden',
15+
},
16+
xButton: dialogStyles(theme).xButton,
17+
})
18+
);
19+
20+
/**
21+
* Renders Material UI Dialog Title with optional (x) button to close the dialog
22+
* @param {function} [onClose] - when set the (x) button aded to Dialog Title and event called on button click
23+
*/
24+
interface Props extends DialogTitleProps {
25+
onClose?: (event: {}) => void;
26+
}
27+
const AppDialogTitle: React.FC<Props> = ({ children, onClose, ...props }) => {
28+
const classes = useStyles();
29+
return (
30+
<DialogTitle {...props}>
31+
<div className={classes.titleContainer}>
32+
<span className={classes.title}>{children}</span>
33+
</div>
34+
{Boolean(onClose) ? (
35+
<AppIconButton className={classes.xButton} icon="close" aria-label="close" title="Close" onClick={onClose} />
36+
) : null}
37+
</DialogTitle>
38+
);
39+
};
40+
41+
export default AppDialogTitle;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import AppDialogTitle from './AppDialogTitle';
2+
3+
export { AppDialogTitle };

src/components/dialogs/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import CommonDialog from './CommonDialog';
2+
import CompositionDialog from './CompositionDialog';
3+
4+
export { CommonDialog, CompositionDialog };

src/routes/Routes.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,38 @@
22
import { useAppStore } from '../store';
33
import PublicRoutes from './PublicRoutes';
44
import PrivateRoutes from './PrivateRoutes';
5+
// import { isUserStillLoggedIn } from '../api/auth/utils';
6+
// import { api } from '../api';
57

68
/**
79
* Renders routes depending on Authenticated or Anonymous users
810
*/
911
const Routes = () => {
1012
const [state /*, dispatch*/] = useAppStore();
1113

14+
// Re-login or logout the user if needed
1215
// useEffect(() => {
13-
// // Check isn't token expired?
14-
// const isLogged = isUserStillLoggedIn();
15-
// if (state.isAuthenticated && !isLogged) {
16-
// // Token was expired, logout immediately!
17-
// console.warn('Token was expired, logout immediately!');
18-
// api.auth.logout();
19-
// dispatch({ type: 'LOG_OUT' });
20-
// return; // Thats all for now, the App will be completely re-rendered soon
21-
// }
22-
// if (isLogged && !state.isAuthenticated) {
23-
// // Valid token is present but we are not logged in somehow, lets fix it
24-
// dispatch({ type: 'LOG_IN' });
25-
// }
26-
// }, [state.isAuthenticated, dispatch]); // Effect for every state.isAuthenticated change
16+
// // Check isn't token expired?
17+
// const isLogged = isUserStillLoggedIn();
18+
19+
// if (state.isAuthenticated && !isLogged) {
20+
// // Token was expired, logout immediately!
21+
// console.warn('Token was expired, logout immediately!');
22+
// api?.auth?.logout();
23+
// // dispatch({ type: 'LOG_OUT' }); // Not needed due to reloading App in api.auth.logout()
24+
// return; // Thats all for now, the App will be completely re-rendered soon
25+
// }
26+
27+
// if (isLogged && !state.isAuthenticated) {
28+
// // Valid token is present but we are not logged in somehow, lets fix it
29+
// console.warn('Token found, lets try to auto login');
30+
// api?.auth?.refresh().then(() => {
31+
// dispatch({ type: 'LOG_IN' }); // Update global store only if token refresh was successful.
32+
// });
33+
// }
34+
// }, [state.isAuthenticated, dispatch]); // Effect for every state.isAuthenticated change actually
2735

2836
console.log('Routes() - isAuthenticated:', state.isAuthenticated);
2937
return state.isAuthenticated ? <PrivateRoutes /> : <PublicRoutes />;
3038
};
31-
3239
export default Routes;

0 commit comments

Comments
 (0)