Skip to content

Commit b427ae3

Browse files
authored
fix: Update ch connection form to use password field (#929)
Fixes: HDX-1877
1 parent 8ab3b42 commit b427ae3

File tree

3 files changed

+131
-3
lines changed

3 files changed

+131
-3
lines changed

packages/app/src/components/ConnectionForm.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { Box, Button, Flex, Group, Stack, Text, Tooltip } from '@mantine/core';
55
import { notifications } from '@mantine/notifications';
66

77
import api from '@/api';
8-
import { InputControlled } from '@/components/InputControlled';
8+
import {
9+
InputControlled,
10+
PasswordInputControlled,
11+
} from '@/components/InputControlled';
912
import { IS_LOCAL_MODE } from '@/config';
1013
import {
1114
Connection,
@@ -252,7 +255,7 @@ export function ConnectionForm({
252255
)}
253256
{(showUpdatePassword || isNew) && (
254257
<Flex align="center" gap="sm">
255-
<InputControlled
258+
<PasswordInputControlled
256259
style={{ flexGrow: 1 }}
257260
name="password"
258261
control={control}

packages/app/src/components/InputControlled.tsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import React from 'react';
22
import { Control, Controller, FieldValues, Path } from 'react-hook-form';
3-
import { Input, InputProps } from '@mantine/core';
3+
import {
4+
Input,
5+
InputProps,
6+
PasswordInput,
7+
PasswordInputProps,
8+
} from '@mantine/core';
49

510
interface InputControlledProps<T extends FieldValues>
611
extends Omit<InputProps, 'name' | 'style'>,
@@ -10,6 +15,14 @@ interface InputControlledProps<T extends FieldValues>
1015
rules?: Parameters<Control<T>['register']>[1];
1116
}
1217

18+
interface PasswordInputControlledProps<T extends FieldValues>
19+
extends Omit<PasswordInputProps, 'name' | 'style'>,
20+
Omit<React.InputHTMLAttributes<HTMLInputElement>, 'name' | 'size'> {
21+
name: Path<T>;
22+
control: Control<T>;
23+
rules?: Parameters<Control<T>['register']>[1];
24+
}
25+
1326
export function InputControlled<T extends FieldValues>({
1427
name,
1528
control,
@@ -27,3 +40,21 @@ export function InputControlled<T extends FieldValues>({
2740
/>
2841
);
2942
}
43+
44+
export function PasswordInputControlled<T extends FieldValues>({
45+
name,
46+
control,
47+
rules,
48+
...props
49+
}: PasswordInputControlledProps<T>) {
50+
return (
51+
<Controller
52+
name={name}
53+
control={control}
54+
rules={rules}
55+
render={({ field, fieldState: { error } }) => (
56+
<PasswordInput {...props} {...field} error={error?.message} />
57+
)}
58+
/>
59+
);
60+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import React from 'react';
2+
import { useForm } from 'react-hook-form';
3+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
4+
5+
import { InputControlled, PasswordInputControlled } from '../InputControlled';
6+
7+
// Test wrapper component that provides form context
8+
function TestForm({ children }: { children: React.ReactNode }) {
9+
const { control } = useForm({
10+
defaultValues: {
11+
testInput: '',
12+
testPassword: '',
13+
},
14+
});
15+
16+
return (
17+
<form>
18+
{React.cloneElement(children as React.ReactElement, { control })}
19+
</form>
20+
);
21+
}
22+
23+
describe('InputControlled', () => {
24+
it('renders input with correct props', () => {
25+
renderWithMantine(
26+
<TestForm>
27+
<InputControlled
28+
name="testInput"
29+
placeholder="Test input"
30+
control={{} as any}
31+
/>
32+
</TestForm>,
33+
);
34+
35+
const input = screen.getByPlaceholderText('Test input');
36+
expect(input).toBeInTheDocument();
37+
});
38+
39+
it('handles input changes', async () => {
40+
const { container } = renderWithMantine(
41+
<TestForm>
42+
<InputControlled
43+
name="testInput"
44+
placeholder="Test input"
45+
control={{} as any}
46+
/>
47+
</TestForm>,
48+
);
49+
50+
const input = screen.getByPlaceholderText('Test input');
51+
fireEvent.change(input, { target: { value: 'test value' } });
52+
53+
await waitFor(() => {
54+
expect(input).toHaveValue('test value');
55+
});
56+
});
57+
});
58+
59+
describe('PasswordInputControlled', () => {
60+
it('renders password input with correct props', () => {
61+
renderWithMantine(
62+
<TestForm>
63+
<PasswordInputControlled
64+
name="testPassword"
65+
placeholder="Enter password"
66+
control={{} as any}
67+
/>
68+
</TestForm>,
69+
);
70+
71+
const input = screen.getByPlaceholderText('Enter password');
72+
expect(input).toBeInTheDocument();
73+
expect(input).toHaveAttribute('type', 'password');
74+
});
75+
76+
it('handles password input changes', async () => {
77+
const { container } = renderWithMantine(
78+
<TestForm>
79+
<PasswordInputControlled
80+
name="testPassword"
81+
placeholder="Enter password"
82+
control={{} as any}
83+
/>
84+
</TestForm>,
85+
);
86+
87+
const input = screen.getByPlaceholderText('Enter password');
88+
fireEvent.change(input, { target: { value: 'testpassword123' } });
89+
90+
await waitFor(() => {
91+
expect(input).toHaveValue('testpassword123');
92+
});
93+
});
94+
});

0 commit comments

Comments
 (0)