From 04280351ab31fdf0c02130c55e64a18a6389c240 Mon Sep 17 00:00:00 2001 From: Clement Mwimo Date: Thu, 16 Jul 2026 17:56:54 -0600 Subject: [PATCH 1/8] feat: use MXUI Select component for property type manual account --- src/components/Select.tsx | 73 +++++++++++++++++++ src/privacy/input.ts | 3 + src/views/manualAccount/ManualAccountForm.tsx | 7 +- 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/components/Select.tsx diff --git a/src/components/Select.tsx b/src/components/Select.tsx new file mode 100644 index 0000000000..da3e153de1 --- /dev/null +++ b/src/components/Select.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import FormControl from '@mui/material/FormControl' +import FormHelperText from '@mui/material/FormHelperText' +import InputLabel from '@mui/material/InputLabel' +import MenuItem from '@mui/material/MenuItem' +import MuiSelect, { SelectChangeEvent } from '@mui/material/Select' + +export interface SelectOption { + label: string + value: string +} + +export interface SelectProps { + 'data-test'?: string + error?: boolean + fullWidth?: boolean + helperText?: string + label: string + name: string + onChange: (e: { target: { name: string; value: string } }) => void + options?: SelectOption[] + placeholder?: string + value: string +} + +export const Select = ({ + 'data-test': dataTest, + error, + fullWidth = true, + helperText, + label, + name, + onChange, + options = [], + placeholder = 'Select a value', + value, +}: SelectProps) => { + const handleChange = (e: SelectChangeEvent) => { + onChange({ target: { name, value: e.target.value as string } }) + } + + return ( + + {label} + { + if (selected === '') { + return placeholder + } + const option = options.find((opt) => opt.value === selected) + return option ? option.label : selected + }} + value={value} + > + + {placeholder} + + {options.map((option) => ( + + {option.label} + + ))} + + {helperText && {helperText}} + + ) +} diff --git a/src/privacy/input.ts b/src/privacy/input.ts index 687b07b5d9..6b9729046a 100644 --- a/src/privacy/input.ts +++ b/src/privacy/input.ts @@ -2,6 +2,7 @@ // This is the ONLY file that @kyper related inputs should be directly imported import { Radio, PASSWORD_VALIDATIONS } from '@kyper/input' +import { Select } from 'src/components/Select' import { SelectionBox } from '@mxenabled/mxui' import { UserFeedback } from '@kyper/userfeedback' import { withProtection } from 'src/privacy/withProtection' @@ -20,12 +21,14 @@ import { TextField } from '@mxenabled/mxui' const ProtectedTextField = withProtection(TextField) const ProtectedRadio = withProtection(Radio) +const ProtectedSelect = withProtection(Select) const ProtectedSelectionBox = withProtection(SelectionBox) const ProtectedUserFeedback = withProtection(UserFeedback) export { ProtectedTextField as TextField, ProtectedRadio as Radio, + ProtectedSelect as Select, ProtectedSelectionBox as SelectionBox, ProtectedUserFeedback as UserFeedback, PASSWORD_VALIDATIONS as PasswordValidations, diff --git a/src/views/manualAccount/ManualAccountForm.tsx b/src/views/manualAccount/ManualAccountForm.tsx index 645cc31f77..11772c2b32 100644 --- a/src/views/manualAccount/ManualAccountForm.tsx +++ b/src/views/manualAccount/ManualAccountForm.tsx @@ -309,12 +309,13 @@ export const ManualAccountForm = React.forwardRef ) + + expect(screen.getByLabelText('Account type')).toBeInTheDocument() + expect(screen.getByText('Select a value')).toBeInTheDocument() + }) + + it('calls onChange with the selected value', async () => { + const user = userEvent.setup() + const handleChange = vi.fn() + + render() - - expect(screen.getByLabelText('Account type')).toBeInTheDocument() - expect(screen.getByText('Select a value')).toBeInTheDocument() - }) - - it('calls onChange with the selected value', async () => { - const user = userEvent.setup() - const handleChange = vi.fn() - - render( + > + {field.options?.map((option) => ( + + {option.label} + + ))} + ) } else { From cf3e5e4a4c0ffae41861c5aeeb516fe4dbdd9965 Mon Sep 17 00:00:00 2001 From: Clement Mwimo Date: Fri, 17 Jul 2026 11:03:42 -0600 Subject: [PATCH 6/8] test if selecting an option works --- src/views/manualAccount/ManualAccountForm-test.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/views/manualAccount/ManualAccountForm-test.tsx b/src/views/manualAccount/ManualAccountForm-test.tsx index 5816bbe3b5..c50a5afd98 100644 --- a/src/views/manualAccount/ManualAccountForm-test.tsx +++ b/src/views/manualAccount/ManualAccountForm-test.tsx @@ -113,10 +113,15 @@ describe('', () => { expect(screen.getByLabelText(/interest rate/i)).toBeInTheDocument() }) - it('renders the property type select field for property accounts', async () => { - await renderManualAccountForm({ accountType: AccountTypes.PROPERTY }) + it('renders the property type select field and allows selecting a value', async () => { + const { user } = await renderManualAccountForm({ accountType: AccountTypes.PROPERTY }) expect(screen.getByLabelText(/property type/i)).toBeInTheDocument() + + await user.click(screen.getByLabelText(/property type/i)) + await user.click(await screen.findByRole('option', { name: 'Vehicle' })) + + expect(screen.getByRole('combobox', { name: /property type/i })).toHaveTextContent('Vehicle') }) }) From 8c7b3718303630816c42a0e969c02cc5855a3540 Mon Sep 17 00:00:00 2001 From: Clement Mwimo Date: Fri, 17 Jul 2026 11:22:34 -0600 Subject: [PATCH 7/8] add required error validation test --- src/views/manualAccount/ManualAccountForm-test.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/views/manualAccount/ManualAccountForm-test.tsx b/src/views/manualAccount/ManualAccountForm-test.tsx index c50a5afd98..a9efee2c8d 100644 --- a/src/views/manualAccount/ManualAccountForm-test.tsx +++ b/src/views/manualAccount/ManualAccountForm-test.tsx @@ -184,6 +184,18 @@ describe('', () => { expect((await screen.findAllByText(/is required/i)).length).toBeGreaterThan(0) expect(mockApi.createAccount).not.toHaveBeenCalled() }) + + it('shows a required error for property type when saving without selecting a value', async () => { + const { user, mockApi } = await renderManualAccountForm({ + accountType: AccountTypes.PROPERTY, + }) + + await user.type(screen.getByLabelText(/account name/i), 'My Property') + await user.click(screen.getByTestId('save-manual-account-button')) + + expect(await screen.findByText('Property type is required')).toBeInTheDocument() + expect(mockApi.createAccount).not.toHaveBeenCalled() + }) }) describe('Form Submission', () => { From a834c846f53a099a8f3b4d950f9ac78f08534d5e Mon Sep 17 00:00:00 2001 From: Clement Mwimo Date: Fri, 17 Jul 2026 11:47:08 -0600 Subject: [PATCH 8/8] add required prop to select --- src/views/manualAccount/ManualAccountForm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/manualAccount/ManualAccountForm.tsx b/src/views/manualAccount/ManualAccountForm.tsx index 376b26cf10..52d144dfd4 100644 --- a/src/views/manualAccount/ManualAccountForm.tsx +++ b/src/views/manualAccount/ManualAccountForm.tsx @@ -316,6 +316,7 @@ export const ManualAccountForm = React.forwardRef