diff --git a/package/src/elicitation/ElicitationForm.test.tsx b/package/src/elicitation/ElicitationForm.test.tsx index 1aed438..5a245cf 100644 --- a/package/src/elicitation/ElicitationForm.test.tsx +++ b/package/src/elicitation/ElicitationForm.test.tsx @@ -122,3 +122,45 @@ describe('elicitation/ElicitationForm', () => { expect(screen.queryByRole('link')).not.toBeInTheDocument(); }); }); + +describe('elicitation/a string over several lines', () => { + const multiline: ElicitationRequestedSchema = { + type: 'object', + properties: { + story: { type: 'string', title: 'What happened?', multiline: true, maxLength: 8000 }, + short: { type: 'string', title: 'Order number' }, + }, + required: ['story'], + }; + + it('gives a multiline string a text area and keeps the plain one an input', () => { + render(); + + expect(screen.getByLabelText(/What happened\?/).tagName).toBe('TEXTAREA'); + expect(screen.getByLabelText('Order number').tagName).toBe('INPUT'); + }); + + it('keeps the line breaks the person typed', async () => { + const onAccept = jest.fn(); + render(); + + const field = screen.getByLabelText(/What happened\?/); + await userEvent.type(field, 'First line{Enter}second line'); + await userEvent.click(screen.getByRole('button', { name: 'Submit' })); + + expect(onAccept).toHaveBeenCalledWith( + expect.objectContaining({ story: 'First line\nsecond line' }) + ); + }); + + it('keeps the required mark and the limit of the schema', async () => { + const onAccept = jest.fn(); + render(); + + expect(screen.getByLabelText(/What happened\?/)).toHaveAttribute('maxlength', '8000'); + + await userEvent.click(screen.getByRole('button', { name: 'Submit' })); + expect(screen.getByText('Required')).toBeInTheDocument(); + expect(onAccept).not.toHaveBeenCalled(); + }); +}); diff --git a/package/src/elicitation/ElicitationForm.tsx b/package/src/elicitation/ElicitationForm.tsx index ac17c06..1dbeaf2 100644 --- a/package/src/elicitation/ElicitationForm.tsx +++ b/package/src/elicitation/ElicitationForm.tsx @@ -13,6 +13,7 @@ import { Stack, Switch, Text, + Textarea, TextInput, } from '@mantine/core'; import { IconExternalLink, IconForms } from '@tabler/icons-react'; @@ -101,11 +102,17 @@ function isCompactField(field: ElicitationField): boolean { return true; } if (field.kind === 'text') { + if (field.schema.multiline) { + return false; + } return field.inputType !== 'text' || (field.schema.maxLength ?? Infinity) <= 120; } return field.kind === 'select' && field.options.length > 3; } +const MIN_TEXTAREA_ROWS = 3; +const MAX_TEXTAREA_ROWS = 8; + const EMPTY_SCHEMA: ElicitationRequestedSchema = { type: 'object', properties: {} }; type FieldControlProps = { @@ -128,6 +135,23 @@ function FieldControl({ field, value, error, disabled, onChange, onBlur }: Field switch (field.kind) { case 'text': + if (field.schema.multiline) { + return ( +