Skip to content

Commit 76dffc6

Browse files
[ui-core] add formInput component for all input types (#4193)
1 parent 8d94bf0 commit 76dffc6

File tree

2 files changed

+463
-0
lines changed

2 files changed

+463
-0
lines changed
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
// Licensed to Cloudera, Inc. under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. Cloudera, Inc. licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import React from 'react';
18+
import { render, screen, fireEvent } from '@testing-library/react';
19+
import userEvent from '@testing-library/user-event';
20+
import '@testing-library/jest-dom';
21+
import FormInput, { FieldType, FieldConfig } from './FormInput';
22+
23+
interface MockContext {
24+
hideField: boolean;
25+
}
26+
27+
describe('FormInput Component', () => {
28+
const mockOnChange = jest.fn();
29+
const defaultProps = {
30+
loading: false,
31+
onChange: mockOnChange,
32+
className: 'test-class'
33+
};
34+
35+
beforeEach(() => {
36+
mockOnChange.mockClear();
37+
});
38+
39+
describe('INPUT field type', () => {
40+
const inputField: FieldConfig = {
41+
name: 'testInput',
42+
type: FieldType.INPUT,
43+
label: 'Test Input',
44+
placeholder: 'Enter text'
45+
};
46+
47+
it('should render input field with correct props', () => {
48+
render(<FormInput field={inputField} value="test value" {...defaultProps} />);
49+
50+
const input = screen.getByDisplayValue('test value');
51+
expect(input).toBeInTheDocument();
52+
expect(input).toHaveAttribute('placeholder', 'Enter text');
53+
expect(input).toHaveClass('test-class');
54+
});
55+
56+
it('should call onChange when input value changes', () => {
57+
render(<FormInput field={inputField} value="" {...defaultProps} />);
58+
59+
const input = screen.getByPlaceholderText('Enter text');
60+
fireEvent.change(input, { target: { value: 'new value' } });
61+
62+
expect(mockOnChange).toHaveBeenCalledWith('testInput', 'new value');
63+
});
64+
65+
it('should render label for input field', () => {
66+
render(<FormInput field={inputField} value="" {...defaultProps} />);
67+
68+
expect(screen.getByText('Test Input')).toBeInTheDocument();
69+
});
70+
71+
it('should render tooltip icon when tooltip is provided', () => {
72+
const inputWithTooltip: FieldConfig = {
73+
name: 'testInputTooltip',
74+
type: FieldType.INPUT,
75+
label: 'Input with Tooltip',
76+
tooltip: 'This is an input tooltip'
77+
};
78+
79+
render(<FormInput field={inputWithTooltip} value="" {...defaultProps} />);
80+
81+
expect(screen.getByRole('img', { name: /info-circle/i })).toBeInTheDocument();
82+
});
83+
84+
it('should handle visibility conditions - render when not hidden', () => {
85+
const visibilityField: FieldConfig<MockContext> = {
86+
name: 'conditionalField',
87+
type: FieldType.INPUT,
88+
label: 'Conditional Field',
89+
isHidden: (context?: MockContext) => context?.hideField === true
90+
};
91+
92+
render(
93+
<FormInput
94+
field={visibilityField}
95+
context={{ hideField: false }}
96+
value=""
97+
{...defaultProps}
98+
/>
99+
);
100+
101+
expect(screen.getByDisplayValue('')).toBeInTheDocument();
102+
expect(screen.getByText('Conditional Field')).toBeInTheDocument();
103+
});
104+
105+
it('should handle visibility conditions - not render when hidden', () => {
106+
const visibilityField: FieldConfig<MockContext> = {
107+
name: 'conditionalField',
108+
type: FieldType.INPUT,
109+
label: 'Conditional Field',
110+
isHidden: (context?: MockContext) => context?.hideField === true
111+
};
112+
113+
render(
114+
<FormInput
115+
field={visibilityField}
116+
context={{ hideField: true }}
117+
value=""
118+
{...defaultProps}
119+
/>
120+
);
121+
122+
expect(screen.queryByText('Conditional Field')).not.toBeInTheDocument();
123+
});
124+
});
125+
126+
describe('SELECT field type', () => {
127+
const selectField: FieldConfig = {
128+
name: 'testSelect',
129+
type: FieldType.SELECT,
130+
label: 'Test Select',
131+
placeholder: 'Choose option',
132+
options: [
133+
{ value: 'option1', label: 'Option 1' },
134+
{ value: 'option2', label: 'Option 2' }
135+
]
136+
};
137+
138+
it('should render select field with options', () => {
139+
render(<FormInput field={selectField} value="option1" {...defaultProps} />);
140+
141+
const select = screen.getByRole('combobox');
142+
expect(select).toBeInTheDocument();
143+
expect(screen.getByText('Option 1')).toBeInTheDocument();
144+
});
145+
146+
it('should call onChange when select value changes', async () => {
147+
const user = userEvent.setup();
148+
const onChangeSpy = jest.fn();
149+
render(
150+
<FormInput
151+
field={selectField}
152+
value="option1"
153+
onChange={onChangeSpy}
154+
loading={false}
155+
className="test-class"
156+
/>
157+
);
158+
159+
const select = screen.getByRole('combobox');
160+
expect(select).toBeInTheDocument();
161+
162+
expect(screen.getByText('Option 1')).toBeInTheDocument();
163+
await user.click(select);
164+
165+
const option2 = screen.getByText('Option 2');
166+
await user.click(option2);
167+
168+
expect(onChangeSpy).toHaveBeenCalledWith('testSelect', 'option2');
169+
});
170+
171+
it('should render tooltip icon when tooltip is provided', () => {
172+
const selectWithTooltip: FieldConfig = {
173+
name: 'testSelectTooltip',
174+
type: FieldType.SELECT,
175+
label: 'Select with Tooltip',
176+
tooltip: 'This is a select tooltip',
177+
options: [{ value: 'option1', label: 'Option 1' }]
178+
};
179+
180+
render(<FormInput field={selectWithTooltip} value="" {...defaultProps} />);
181+
182+
expect(screen.getByRole('img', { name: /info-circle/i })).toBeInTheDocument();
183+
});
184+
});
185+
186+
describe('CHECKBOX field type', () => {
187+
const checkboxField: FieldConfig = {
188+
name: 'testCheckbox',
189+
type: FieldType.CHECKBOX,
190+
label: 'Test Checkbox',
191+
tooltip: 'This is a tooltip'
192+
};
193+
194+
it('should render checkbox field', () => {
195+
render(<FormInput field={checkboxField} value={true} {...defaultProps} />);
196+
197+
const checkbox = screen.getByRole('checkbox');
198+
expect(checkbox).toBeInTheDocument();
199+
expect(checkbox).toBeChecked();
200+
expect(screen.getByText('Test Checkbox')).toBeInTheDocument();
201+
});
202+
203+
it('should call onChange when checkbox is toggled', () => {
204+
render(<FormInput field={checkboxField} value={false} {...defaultProps} />);
205+
206+
const checkbox = screen.getByRole('checkbox');
207+
fireEvent.click(checkbox);
208+
209+
expect(mockOnChange).toHaveBeenCalledWith('testCheckbox', true);
210+
});
211+
212+
it('should render when default value is true', () => {
213+
render(<FormInput field={checkboxField} defaultValue={true} {...defaultProps} />);
214+
215+
const checkbox = screen.getByRole('checkbox');
216+
expect(checkbox).toBeInTheDocument();
217+
expect(checkbox).toBeChecked();
218+
});
219+
220+
it('should render when default value is false', () => {
221+
render(<FormInput field={checkboxField} defaultValue={false} {...defaultProps} />);
222+
223+
const checkbox = screen.getByRole('checkbox');
224+
expect(checkbox).toBeInTheDocument();
225+
expect(checkbox).not.toBeChecked();
226+
});
227+
228+
it('should render tooltip icon when tooltip is provided', () => {
229+
render(<FormInput field={checkboxField} value={false} {...defaultProps} />);
230+
231+
expect(screen.getByRole('img', { name: /info-circle/i })).toBeInTheDocument();
232+
});
233+
});
234+
235+
describe('RADIO field type', () => {
236+
const radioField: FieldConfig = {
237+
name: 'testRadio',
238+
type: FieldType.RADIO,
239+
label: 'Test Radio',
240+
options: [
241+
{ value: 'radio1', label: 'Radio 1' },
242+
{ value: 'radio2', label: 'Radio 2' }
243+
]
244+
};
245+
246+
it('should render radio group with options', () => {
247+
render(<FormInput field={radioField} value="radio1" {...defaultProps} />);
248+
249+
expect(screen.getByText('Radio 1')).toBeInTheDocument();
250+
expect(screen.getByText('Radio 2')).toBeInTheDocument();
251+
252+
const radio1 = screen.getByDisplayValue('radio1');
253+
const radio2 = screen.getByDisplayValue('radio2');
254+
255+
expect(radio1).toBeChecked();
256+
expect(radio2).not.toBeChecked();
257+
});
258+
259+
it('should call onChange when radio option changes', () => {
260+
render(<FormInput field={radioField} value="radio1" {...defaultProps} />);
261+
262+
const radio2 = screen.getByDisplayValue('radio2');
263+
fireEvent.click(radio2);
264+
265+
expect(mockOnChange).toHaveBeenCalledWith('testRadio', 'radio2');
266+
});
267+
268+
it('should render with default value', () => {
269+
render(
270+
<FormInput field={radioField} defaultValue="radio2" value={undefined} {...defaultProps} />
271+
);
272+
273+
const radio2 = screen.getByDisplayValue('radio2');
274+
expect(radio2).toBeChecked();
275+
});
276+
277+
it('should render tooltip icon when tooltip is provided', () => {
278+
const radioWithTooltip: FieldConfig = {
279+
name: 'testRadioTooltip',
280+
type: FieldType.RADIO,
281+
label: 'Radio with Tooltip',
282+
tooltip: 'This is a radio tooltip',
283+
options: [{ value: 'radio1', label: 'Radio 1' }]
284+
};
285+
286+
render(<FormInput field={radioWithTooltip} value="" {...defaultProps} />);
287+
288+
expect(screen.getByRole('img', { name: /info-circle/i })).toBeInTheDocument();
289+
});
290+
});
291+
});

0 commit comments

Comments
 (0)