Skip to content

Commit 6461840

Browse files
authored
Convert access token input in GH authentication form to a password field (#154)
1 parent 9aa2372 commit 6461840

File tree

2 files changed

+55
-17
lines changed

2 files changed

+55
-17
lines changed

src/components/GitHubAuth/GitHubAuthForm.test.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe(GitHubAuthForm, () => {
1313

1414
render(<GitHubAuthForm open={true} onClose={onClose} />);
1515

16-
const textBox = screen.getByRole("textbox") as HTMLInputElement;
16+
const textBox = screen.getByTestId("access-token-input").firstChild as HTMLInputElement;
1717
const loginBtn = screen.getByRole("button", { name: "Login" });
1818
const closeBtn = screen.getByRole("button", { name: "Close" });
1919
const checkBox = screen.getByRole("checkbox");
@@ -121,4 +121,19 @@ describe(GitHubAuthForm, () => {
121121
expect(textBox.value).toBe("");
122122
});
123123
});
124+
125+
it("Show or hide access token", async () => {
126+
const { textBox } = setup(true);
127+
const showAccessTokenButton = screen.getByLabelText(/Access token hidden/i);
128+
129+
expect(textBox).toHaveAttribute("type", "password");
130+
131+
fireEvent.click(showAccessTokenButton);
132+
133+
expect(textBox).toHaveAttribute("type", "text");
134+
135+
fireEvent.click(showAccessTokenButton);
136+
137+
expect(textBox).toHaveAttribute("type", "password");
138+
});
124139
});

src/components/GitHubAuth/GitHubAuthForm.tsx

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ import {
77
DialogContent,
88
DialogContentText,
99
DialogTitle,
10-
TextField,
10+
FormControl,
11+
FormHelperText,
12+
IconButton,
13+
InputAdornment,
14+
InputLabel,
15+
OutlinedInput,
16+
// TextField,
1117
} from "@mui/material";
1218
import Checkbox from "@mui/material/Checkbox";
1319
import FormControlLabel from "@mui/material/FormControlLabel";
1420
import React from "react";
1521
import { StorageType, validateAndStoreAccessToken } from "../../utils/GitHubUtils";
22+
import { Visibility, VisibilityOff } from "@mui/icons-material";
1623

1724
enum TokenValidationStatus {
1825
Init = "init",
@@ -31,6 +38,7 @@ export default function GitHubAuthForm({ open, onClose }: GitHubAuthFormProps) {
3138
TokenValidationStatus.Init,
3239
);
3340
const [storageType, setStorageType] = React.useState<StorageType>(StorageType.SessionStorage);
41+
const [showAccessToken, setShowAccessToken] = React.useState(false);
3442

3543
const handleStorageTypeCheckChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
3644
setStorageType(event.target.checked ? StorageType.LocalStorage : StorageType.SessionStorage);
@@ -60,6 +68,8 @@ export default function GitHubAuthForm({ open, onClose }: GitHubAuthFormProps) {
6068
: "These credentials aren't stored in any server.";
6169
};
6270

71+
const handleClickShowAccessToken = () => setShowAccessToken((show) => !show);
72+
6373
React.useEffect(() => {
6474
if (accessTokenValid === TokenValidationStatus.Valid) {
6575
handleClose();
@@ -98,21 +108,34 @@ export default function GitHubAuthForm({ open, onClose }: GitHubAuthFormProps) {
98108
</a>
99109
.
100110
</DialogContentText>
101-
<TextField
102-
autoFocus
103-
margin="dense"
104-
id="name"
105-
label="GitHub Access Token"
106-
value={accessTokenValue || ""}
107-
fullWidth
108-
variant="standard"
109-
required
110-
error={accessTokenValid === TokenValidationStatus.Invalid}
111-
helperText={textFieldHelperText()}
112-
onChange={(e) => {
113-
setAccessTokenValue(e.target.value);
114-
}}
115-
/>
111+
<FormControl sx={{ marginTop: 2 }} fullWidth required variant="outlined">
112+
<InputLabel htmlFor="access-token-input">GitHub Access Token</InputLabel>
113+
<OutlinedInput
114+
autoFocus
115+
margin="dense"
116+
id="access-token-input"
117+
data-testid="access-token-input"
118+
label="GitHub Access Token"
119+
type={showAccessToken ? "text" : "password"}
120+
value={accessTokenValue || ""}
121+
error={accessTokenValid === TokenValidationStatus.Invalid}
122+
onChange={(e) => {
123+
setAccessTokenValue(e.target.value);
124+
}}
125+
endAdornment={
126+
<InputAdornment position="end">
127+
<IconButton
128+
onClick={handleClickShowAccessToken}
129+
aria-label={showAccessToken ? "Access token shown" : "Access token hidden"}
130+
edge="end"
131+
>
132+
{showAccessToken ? <VisibilityOff /> : <Visibility />}
133+
</IconButton>
134+
</InputAdornment>
135+
}
136+
/>
137+
<FormHelperText id="access-token-input">{textFieldHelperText()}</FormHelperText>
138+
</FormControl>
116139
<FormControlLabel
117140
control={<Checkbox onChange={handleStorageTypeCheckChanged} />}
118141
label="Save access token in local storage"

0 commit comments

Comments
 (0)