frontend phase 2: onboarding & profiles — customer/patient, nurse profile & bank
Turns a logged-in user into a usable account, consuming the b3 identity-profiles
contract behind the services/{domain} seam.
Services (mock default true; real HTTP clients wired for a one-line flip):
- services/patients: rewritten to b3 PatientDto + client-augmented relation/conditions;
full CRUD, optimistic soft-archive, cache-splice on create, age<->birthDate helper.
- services/profiles: customer + nurse profile get/upsert + avatar (404->null mapping).
- services/nurse: payout bank accounts + IBAN(Sheba) util + pending-only polling.
Screens: A3->A4 onboarding wizard, E1 patients list/CRUD, A5 home (first-login gate +
nudge), customer profile (no national-ID), nurse profile bootstrap (unverified
placeholder), nurse bank settings (pending/verified/mismatch + make-primary).
Shared composites (each tested): GenderToggle, ConditionChips, RelationSelect,
PatientForm, PatientCard, BankStatusPanel; reuses f0 StepperHeader/StatusChip/PhoneField.
Adds onboarding/home/profile/nurseProfile/bank i18n namespaces (both locales, in sync),
the --bal-primary-soft token, and nurse sidebar Profile + Bank entries.
Contract gaps filed: REQ-005 (patient relation/conditions), REQ-006 (avatar route),
REQ-007 (customer name/language). Gate: check + 112 tests + build all green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ThemeProvider } from '../../theme';
|
||||
import GenderToggle from './GenderToggle';
|
||||
|
||||
function renderToggle(value: 'male' | 'female' | null) {
|
||||
const onChange = jest.fn();
|
||||
const utils = render(
|
||||
<ThemeProvider>
|
||||
<GenderToggle value={value} onChange={onChange} maleLabel="Male" femaleLabel="Female" />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
return { ...utils, onChange };
|
||||
}
|
||||
|
||||
describe('<GenderToggle/> component', () => {
|
||||
it('renders both options', () => {
|
||||
renderToggle(null);
|
||||
expect(screen.getByText('Male')).toBeInTheDocument();
|
||||
expect(screen.getByText('Female')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('marks the selected value as pressed', () => {
|
||||
const { container } = renderToggle('female');
|
||||
expect(container.querySelector('[data-gender="female"]')).toHaveAttribute('aria-pressed', 'true');
|
||||
expect(container.querySelector('[data-gender="male"]')).toHaveAttribute('aria-pressed', 'false');
|
||||
});
|
||||
|
||||
it('calls onChange with the picked gender', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { onChange } = renderToggle(null);
|
||||
await user.click(screen.getByText('Male'));
|
||||
expect(onChange).toHaveBeenCalledWith('male');
|
||||
});
|
||||
|
||||
it('does not fire onChange when the active value is clicked again (no deselect)', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { onChange } = renderToggle('male');
|
||||
await user.click(screen.getByText('Male'));
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user