|
1 |
| -import React, { FormEvent } from 'react'; |
| 1 | +import React, { type FormEvent, useState } from 'react'; |
2 | 2 | import { useId } from '../../hooks/hookPolyfills';
|
3 | 3 | import {
|
4 | 4 | type CoderAuthStatus,
|
5 | 5 | useCoderAppConfig,
|
6 | 6 | useCoderAuth,
|
7 | 7 | } from '../CoderProvider';
|
8 | 8 |
|
9 |
| -import { Theme, makeStyles } from '@material-ui/core'; |
10 |
| -import TextField from '@material-ui/core/TextField'; |
11 | 9 | import { CoderLogo } from '../CoderLogo';
|
12 | 10 | import { Link, LinkButton } from '@backstage/core-components';
|
13 | 11 | import { VisuallyHidden } from '../VisuallyHidden';
|
| 12 | +import { makeStyles } from '@material-ui/core'; |
| 13 | +import TextField from '@material-ui/core/TextField'; |
| 14 | +import ErrorIcon from '@material-ui/icons/ErrorOutline'; |
| 15 | +import SyncIcon from '@material-ui/icons/Sync'; |
14 | 16 |
|
15 |
| -type UseStyleInput = Readonly<{ status: CoderAuthStatus }>; |
16 |
| -type StyleKeys = |
17 |
| - | 'formContainer' |
18 |
| - | 'authInputFieldset' |
19 |
| - | 'coderLogo' |
20 |
| - | 'authButton' |
21 |
| - | 'warningBanner' |
22 |
| - | 'warningBannerContainer'; |
23 |
| - |
24 |
| -const useStyles = makeStyles<Theme, UseStyleInput, StyleKeys>(theme => ({ |
| 17 | +const useStyles = makeStyles(theme => ({ |
25 | 18 | formContainer: {
|
26 | 19 | maxWidth: '30em',
|
27 | 20 | marginLeft: 'auto',
|
@@ -50,41 +43,13 @@ const useStyles = makeStyles<Theme, UseStyleInput, StyleKeys>(theme => ({
|
50 | 43 | marginLeft: 'auto',
|
51 | 44 | marginRight: 'auto',
|
52 | 45 | },
|
53 |
| - |
54 |
| - warningBannerContainer: { |
55 |
| - paddingTop: theme.spacing(4), |
56 |
| - paddingLeft: theme.spacing(6), |
57 |
| - paddingRight: theme.spacing(6), |
58 |
| - }, |
59 |
| - |
60 |
| - warningBanner: ({ status }) => { |
61 |
| - let color: string; |
62 |
| - let backgroundColor: string; |
63 |
| - |
64 |
| - if (status === 'invalid') { |
65 |
| - color = theme.palette.error.contrastText; |
66 |
| - backgroundColor = theme.palette.banner.error; |
67 |
| - } else { |
68 |
| - color = theme.palette.text.primary; |
69 |
| - backgroundColor = theme.palette.background.default; |
70 |
| - } |
71 |
| - |
72 |
| - return { |
73 |
| - color, |
74 |
| - backgroundColor, |
75 |
| - borderRadius: theme.shape.borderRadius, |
76 |
| - textAlign: 'center', |
77 |
| - paddingTop: theme.spacing(0.5), |
78 |
| - paddingBottom: theme.spacing(0.5), |
79 |
| - }; |
80 |
| - }, |
81 | 46 | }));
|
82 | 47 |
|
83 | 48 | export const CoderAuthInputForm = () => {
|
84 | 49 | const hookId = useId();
|
| 50 | + const styles = useStyles(); |
85 | 51 | const appConfig = useCoderAppConfig();
|
86 | 52 | const { status, registerNewToken } = useCoderAuth();
|
87 |
| - const styles = useStyles({ status }); |
88 | 53 |
|
89 | 54 | const onSubmit = (event: FormEvent<HTMLFormElement>) => {
|
90 | 55 | event.preventDefault();
|
@@ -161,13 +126,122 @@ export const CoderAuthInputForm = () => {
|
161 | 126 | </fieldset>
|
162 | 127 |
|
163 | 128 | {(status === 'invalid' || status === 'authenticating') && (
|
164 |
| - <div className={styles.warningBannerContainer}> |
165 |
| - <div id={warningBannerId} className={styles.warningBanner}> |
166 |
| - {status === 'invalid' && 'Invalid token'} |
167 |
| - {status === 'authenticating' && <>Authenticating…</>} |
168 |
| - </div> |
169 |
| - </div> |
| 129 | + <InvalidStatusNotifier authStatus={status} bannerId={warningBannerId} /> |
170 | 130 | )}
|
171 | 131 | </form>
|
172 | 132 | );
|
173 | 133 | };
|
| 134 | + |
| 135 | +const useInvalidStatusStyles = makeStyles(theme => ({ |
| 136 | + warningBannerSpacer: { |
| 137 | + paddingTop: theme.spacing(2), |
| 138 | + }, |
| 139 | + |
| 140 | + warningBanner: { |
| 141 | + display: 'flex', |
| 142 | + flexFlow: 'row nowrap', |
| 143 | + alignItems: 'center', |
| 144 | + color: theme.palette.text.primary, |
| 145 | + backgroundColor: theme.palette.background.default, |
| 146 | + borderRadius: theme.shape.borderRadius, |
| 147 | + border: `1.5px solid ${theme.palette.background.default}`, |
| 148 | + padding: 0, |
| 149 | + }, |
| 150 | + |
| 151 | + errorContent: { |
| 152 | + display: 'flex', |
| 153 | + flexFlow: 'row nowrap', |
| 154 | + alignItems: 'center', |
| 155 | + columnGap: theme.spacing(1), |
| 156 | + marginRight: 'auto', |
| 157 | + |
| 158 | + paddingTop: theme.spacing(0.5), |
| 159 | + paddingBottom: theme.spacing(0.5), |
| 160 | + paddingLeft: theme.spacing(2), |
| 161 | + paddingRight: 0, |
| 162 | + }, |
| 163 | + |
| 164 | + icon: { |
| 165 | + fontSize: '16px', |
| 166 | + }, |
| 167 | + |
| 168 | + syncIcon: { |
| 169 | + color: theme.palette.text.primary, |
| 170 | + opacity: 0.6, |
| 171 | + }, |
| 172 | + |
| 173 | + errorIcon: { |
| 174 | + color: theme.palette.error.main, |
| 175 | + fontSize: '16px', |
| 176 | + }, |
| 177 | + |
| 178 | + dismissButton: { |
| 179 | + border: 'none', |
| 180 | + alignSelf: 'stretch', |
| 181 | + padding: `0 ${theme.spacing(1.5)}px 0 ${theme.spacing(2)}px`, |
| 182 | + color: theme.palette.text.primary, |
| 183 | + backgroundColor: 'inherit', |
| 184 | + lineHeight: 1, |
| 185 | + cursor: 'pointer', |
| 186 | + |
| 187 | + '&:hover': { |
| 188 | + backgroundColor: theme.palette.action.hover, |
| 189 | + }, |
| 190 | + }, |
| 191 | + |
| 192 | + '@keyframes spin': { |
| 193 | + '100%': { |
| 194 | + transform: 'rotate(360deg)', |
| 195 | + }, |
| 196 | + }, |
| 197 | +})); |
| 198 | + |
| 199 | +type InvalidStatusProps = Readonly<{ |
| 200 | + authStatus: CoderAuthStatus; |
| 201 | + bannerId: string; |
| 202 | +}>; |
| 203 | + |
| 204 | +function InvalidStatusNotifier({ authStatus, bannerId }: InvalidStatusProps) { |
| 205 | + const [showNotification, setShowNotification] = useState(true); |
| 206 | + const styles = useInvalidStatusStyles(); |
| 207 | + |
| 208 | + if (!showNotification) { |
| 209 | + return null; |
| 210 | + } |
| 211 | + |
| 212 | + return ( |
| 213 | + <div className={styles.warningBannerSpacer}> |
| 214 | + <div id={bannerId} className={styles.warningBanner}> |
| 215 | + <span className={styles.errorContent}> |
| 216 | + {authStatus === 'authenticating' && ( |
| 217 | + <> |
| 218 | + <SyncIcon |
| 219 | + className={`${styles.icon} ${styles.syncIcon}`} |
| 220 | + // Needed to make MUI v4 icons respect sizing values |
| 221 | + fontSize="inherit" |
| 222 | + /> |
| 223 | + Authenticating… |
| 224 | + </> |
| 225 | + )} |
| 226 | + |
| 227 | + {authStatus === 'invalid' && ( |
| 228 | + <> |
| 229 | + <ErrorIcon |
| 230 | + className={`${styles.icon} ${styles.errorIcon}`} |
| 231 | + fontSize="inherit" |
| 232 | + /> |
| 233 | + Invalid token |
| 234 | + </> |
| 235 | + )} |
| 236 | + </span> |
| 237 | + |
| 238 | + <button |
| 239 | + className={styles.dismissButton} |
| 240 | + onClick={() => setShowNotification(false)} |
| 241 | + > |
| 242 | + Dismiss |
| 243 | + </button> |
| 244 | + </div> |
| 245 | + </div> |
| 246 | + ); |
| 247 | +} |
0 commit comments