mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-03-28 08:15:55 +00:00
Merge pull request #3 from Skidragon/view-plain-text-password-field
Toggle Plain Text on Password Fields
This commit is contained in:
commit
0e2816f57e
1655
package-lock.json
generated
1655
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -7,12 +7,16 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"test": "vitest",
|
||||||
|
"coverage": "vitest run --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/material": "^5.15.14",
|
"@mui/material": "^5.15.14",
|
||||||
|
"@testing-library/jest-dom": "^6.4.6",
|
||||||
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@types/chrome": "^0.0.263",
|
"@types/chrome": "^0.0.263",
|
||||||
"asmcrypto.js": "2.3.2",
|
"asmcrypto.js": "2.3.2",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
@ -25,6 +29,8 @@
|
|||||||
"react-dropzone": "^14.2.3"
|
"react-dropzone": "^14.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@testing-library/dom": "^10.3.0",
|
||||||
|
"@testing-library/react": "^16.0.0",
|
||||||
"@types/react": "^18.2.64",
|
"@types/react": "^18.2.64",
|
||||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||||
"@types/react-dom": "^18.2.21",
|
"@types/react-dom": "^18.2.21",
|
||||||
@ -35,6 +41,7 @@
|
|||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
"eslint-plugin-react-refresh": "^0.4.5",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^5.1.6"
|
"vite": "^5.1.6",
|
||||||
|
"vitest": "^1.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
src/App.tsx
16
src/App.tsx
@ -41,6 +41,7 @@ import {
|
|||||||
} from "./App-styles";
|
} from "./App-styles";
|
||||||
import { Spacer } from "./common/Spacer";
|
import { Spacer } from "./common/Spacer";
|
||||||
import { Loader } from "./components/Loader";
|
import { Loader } from "./components/Loader";
|
||||||
|
import { PasswordField } from "./components";
|
||||||
|
|
||||||
type extStates =
|
type extStates =
|
||||||
| "not-authenticated"
|
| "not-authenticated"
|
||||||
@ -741,8 +742,7 @@ function App() {
|
|||||||
Confirm Wallet Password
|
Confirm Wallet Password
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
<CustomInput
|
<PasswordField
|
||||||
type="password"
|
|
||||||
id="standard-adornment-password"
|
id="standard-adornment-password"
|
||||||
value={paymentPassword}
|
value={paymentPassword}
|
||||||
onChange={(e) => setPaymentPassword(e.target.value)}
|
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||||
@ -808,8 +808,7 @@ function App() {
|
|||||||
Confirm Wallet Password
|
Confirm Wallet Password
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
<CustomInput
|
<PasswordField
|
||||||
type="password"
|
|
||||||
id="standard-adornment-password"
|
id="standard-adornment-password"
|
||||||
value={paymentPassword}
|
value={paymentPassword}
|
||||||
onChange={(e) => setPaymentPassword(e.target.value)}
|
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||||
@ -1000,8 +999,7 @@ function App() {
|
|||||||
Confirm Wallet Password
|
Confirm Wallet Password
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
<CustomInput
|
<PasswordField
|
||||||
type="password"
|
|
||||||
id="standard-adornment-password"
|
id="standard-adornment-password"
|
||||||
value={walletToBeDownloadedPassword}
|
value={walletToBeDownloadedPassword}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@ -1073,8 +1071,7 @@ function App() {
|
|||||||
Wallet Password
|
Wallet Password
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
<CustomInput
|
<PasswordField
|
||||||
type="password"
|
|
||||||
id="standard-adornment-password"
|
id="standard-adornment-password"
|
||||||
value={walletToBeDownloadedPassword}
|
value={walletToBeDownloadedPassword}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@ -1086,8 +1083,7 @@ function App() {
|
|||||||
Confirm Wallet Password
|
Confirm Wallet Password
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
<CustomInput
|
<PasswordField
|
||||||
type="password"
|
|
||||||
id="standard-adornment-password"
|
id="standard-adornment-password"
|
||||||
value={walletToBeDownloadedPasswordConfirm}
|
value={walletToBeDownloadedPasswordConfirm}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
28
src/components/PasswordField/PasswordField.spec.tsx
Normal file
28
src/components/PasswordField/PasswordField.spec.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
test
|
||||||
|
} from 'vitest';
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import userEvent from '@testing-library/user-event'
|
||||||
|
import {
|
||||||
|
PasswordField
|
||||||
|
} from './PasswordField'
|
||||||
|
|
||||||
|
|
||||||
|
describe('PasswordField', () => {
|
||||||
|
test('it renders', () => {
|
||||||
|
const { queryByTestId } = render(<PasswordField data-testid="test-id" value="test-value" />)
|
||||||
|
expect(queryByTestId('test-id')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('User can toggle between plain text view and password view', async () => {
|
||||||
|
const { getByTestId } = render(<PasswordField data-testid="test-id" value="test-value" />)
|
||||||
|
const user = userEvent.setup();
|
||||||
|
expect(getByTestId("password-text-indicator").textContent).toBe('👁️🗨️');
|
||||||
|
await user.click(getByTestId('toggle-view-password-btn'));
|
||||||
|
expect(getByTestId("plain-text-indicator").textContent).toBe('👁️');
|
||||||
|
await user.click(getByTestId('toggle-view-password-btn'));
|
||||||
|
expect(getByTestId("password-text-indicator").textContent).toBe('👁️🗨️');
|
||||||
|
})
|
||||||
|
})
|
61
src/components/PasswordField/PasswordField.tsx
Normal file
61
src/components/PasswordField/PasswordField.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { Button, InputAdornment, TextField, TextFieldProps, styled } from "@mui/material";
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
export const CustomInput = styled(TextField)({
|
||||||
|
width: "183px", // Adjust the width as needed
|
||||||
|
borderRadius: "5px",
|
||||||
|
// backgroundColor: "rgba(30, 30, 32, 1)",
|
||||||
|
outline: "none",
|
||||||
|
input: {
|
||||||
|
fontSize: 10,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: 400,
|
||||||
|
color: "white",
|
||||||
|
"&::placeholder": {
|
||||||
|
fontSize: 16,
|
||||||
|
color: "rgba(255, 255, 255, 0.2)",
|
||||||
|
},
|
||||||
|
outline: "none",
|
||||||
|
padding: "10px",
|
||||||
|
},
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
"& fieldset": {
|
||||||
|
border: '0.5px solid rgba(255, 255, 255, 0.5)',
|
||||||
|
},
|
||||||
|
"&:hover fieldset": {
|
||||||
|
border: '0.5px solid rgba(255, 255, 255, 0.5)',
|
||||||
|
},
|
||||||
|
"&.Mui-focused fieldset": {
|
||||||
|
border: '0.5px solid rgba(255, 255, 255, 0.5)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"& .MuiInput-underline:before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"& .MuiInput-underline:hover:not(.Mui-disabled):before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"& .MuiInput-underline:after": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const PasswordField: React.FunctionComponent<TextFieldProps> = ({ ...props }) => {
|
||||||
|
const [canViewPassword, setCanViewPassword] = useState(false);
|
||||||
|
return (
|
||||||
|
<CustomInput
|
||||||
|
type={canViewPassword ? 'text' : 'password'}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end" data-testid="toggle-view-password-btn" onClick={() => {
|
||||||
|
setCanViewPassword((prevState) => !prevState)
|
||||||
|
}}>
|
||||||
|
{canViewPassword ? <Button data-testid="plain-text-indicator" sx={{ minWidth: 0, p: 0 }}>👁️</Button> : <Button data-testid="password-text-indicator" sx={{ minWidth: 0, p: 0 }}>👁️🗨️</Button>}
|
||||||
|
</InputAdornment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/index.ts
Normal file
1
src/components/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './PasswordField/PasswordField';
|
1
src/test/setup.ts
Normal file
1
src/test/setup.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
import '@testing-library/jest-dom'
|
@ -1,9 +1,15 @@
|
|||||||
|
/// <reference types="vitest" />
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
// Import path module for resolving file paths
|
// Import path module for resolving file paths
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: 'jsdom',
|
||||||
|
globals: true,
|
||||||
|
setupFiles: ['./src/test/setup.ts']
|
||||||
|
},
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user