diff --git a/.changeset/four-states-shine.md b/.changeset/four-states-shine.md new file mode 100644 index 0000000000..064435dc68 --- /dev/null +++ b/.changeset/four-states-shine.md @@ -0,0 +1,5 @@ +--- + +--- + + diff --git a/packages/agentic/components/src/components/input/input.test.tsx b/packages/agentic/components/src/components/input/input.test.tsx index 0cb64d3508..00e7a06a4d 100644 --- a/packages/agentic/components/src/components/input/input.test.tsx +++ b/packages/agentic/components/src/components/input/input.test.tsx @@ -26,6 +26,77 @@ function flattenStyle(style: unknown) { } describe('Input', () => { + it('INP-002 retains internally owned edits across defaultValue changes and reports each edit once', async () => { + const onChangeText = jest.fn(); + const component = await render(); + + expect(getTextbox(component)).toHaveDisplayValue('Seed'); + await fireEvent.changeText(getTextbox(component), 'Edited'); + expect(getTextbox(component)).toHaveDisplayValue('Edited'); + expect(onChangeText.mock.calls).toEqual([['Edited']]); + + await component.rerender(); + expect(getTextbox(component)).toHaveDisplayValue('Edited'); + expect(onChangeText).toHaveBeenCalledTimes(1); + + await fireEvent.changeText(getTextbox(component), ''); + expect(getTextbox(component)).toHaveDisplayValue(''); + expect(onChangeText.mock.calls).toEqual([['Edited'], ['']]); + }); + + it('INP-002 leaves controlled text unchanged until the owner writes back, including an empty string', async () => { + const onChangeText = jest.fn(); + const component = await render(); + + expect(getTextbox(component)).toHaveDisplayValue(''); + await fireEvent.changeText(getTextbox(component), 'Proposed'); + expect(onChangeText.mock.calls).toEqual([['Proposed']]); + expect(getTextbox(component)).toHaveDisplayValue(''); + + await component.rerender(); + expect(getTextbox(component)).toHaveDisplayValue('Accepted'); + expect(onChangeText).toHaveBeenCalledTimes(1); + + await component.rerender(); + expect(getTextbox(component)).toHaveDisplayValue(''); + expect(onChangeText).toHaveBeenCalledTimes(1); + }); + + it('INP-001 INP-005 merges root and editor accessibility state without overriding owned semantics', async () => { + const component = await render( + , + ); + const textbox = component.getByRole('textbox', { name: 'Editor name' }); + + expect(textbox.props.accessibilityHint).toBe('Editor hint'); + expect(textbox.props.accessibilityState).toEqual({ + busy: true, + expanded: true, + selected: true, + disabled: false, + invalid: true, + readOnly: true, + }); + expect(textbox.props.testID).toBe('editor-identifier'); + expect(component.queryByTestId('root-identifier')).toBeNull(); + expect(component.getByTestId('input-root').props.accessible).toBe(false); + expect(textbox.props.editable).toBe(false); + expect(textbox.props.focusable).toBe(true); + }); + it('forwards its ref prop through phased rendering to the native root', async () => { const ref = React.createRef>();