Skip to content

Commit 197ad68

Browse files
author
Ashot Harutyunyan
committed
feat: used new component from gravity for number input
1 parent de3e41c commit 197ad68

File tree

12 files changed

+374
-234
lines changed

12 files changed

+374
-234
lines changed

src/lib/core/components/Form/types/number.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ export type NumberLayoutProps<
4444
NumberSpec<undefined, InputComponentProps, LayoutComponentProps>
4545
>;
4646

47-
export type NumberInput<InputComponentProps extends Record<string, any> | undefined = undefined> =
48-
InputType<
49-
number,
50-
InputComponentProps,
51-
undefined,
52-
NumberSpec<undefined, InputComponentProps, undefined>
53-
>;
47+
export type NumberInputType<
48+
InputComponentProps extends Record<string, any> | undefined = undefined,
49+
> = InputType<
50+
number,
51+
InputComponentProps,
52+
undefined,
53+
NumberSpec<undefined, InputComponentProps, undefined>
54+
>;
5455

5556
export type NumberIndependentInput<
5657
InputComponentProps extends Record<string, any> | undefined = undefined,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
3+
import type {NumberInputProps as NumberInputBaseProps} from '@gravity-ui/uikit';
4+
import {NumberInput as CommonNumberInput} from '@gravity-ui/uikit';
5+
import isNil from 'lodash/isNil';
6+
7+
import type {FieldRenderProps, NumberInputProps, StringInputProps} from '../../../../core';
8+
9+
export interface NumberProps
10+
extends Omit<
11+
NumberInputBaseProps,
12+
'value' | 'onBlur' | 'onFocus' | 'onUpdate' | 'disabled' | 'placeholder' | 'qa'
13+
> {}
14+
15+
export const NumberInput = <
16+
T extends NumberInputProps<NumberProps> | StringInputProps<NumberProps>,
17+
>({
18+
name,
19+
input: {value, onBlur, onChange, onFocus},
20+
spec,
21+
inputProps,
22+
}: T) => {
23+
const props = {
24+
hasClear: true,
25+
...inputProps,
26+
value: isNil(value) ? null : Number(value),
27+
defaultValue: isNil(inputProps?.defaultValue) ? null : Number(inputProps?.defaultValue),
28+
onBlur: onBlur,
29+
onFocus: onFocus,
30+
onUpdate: onChange as FieldRenderProps<string | undefined>['input']['onChange'],
31+
disabled: spec.viewSpec.disabled,
32+
placeholder: spec.viewSpec.placeholder,
33+
qa: name,
34+
};
35+
36+
const handleUpdate = (value: number | null) => {
37+
props.onUpdate(value !== null ? `${value}` : undefined);
38+
};
39+
40+
return <CommonNumberInput {...props} onUpdate={handleUpdate} allowDecimal />;
41+
};
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import React from 'react';
2+
3+
import {ThemeProvider} from '@gravity-ui/uikit';
4+
import {render, screen} from '@testing-library/react';
5+
import userEvent from '@testing-library/user-event';
6+
import cloneDeep from 'lodash/cloneDeep';
7+
import noop from 'lodash/noop';
8+
import {Form} from 'react-final-form';
9+
10+
import type {NumberSpec, Spec} from '../../../../../core';
11+
import {DynamicField, SpecTypes} from '../../../../../core';
12+
import {dynamicConfig} from '../../../../constants';
13+
14+
const NAME = 'input';
15+
16+
const PLACEHOLDER = 'placeholder text';
17+
18+
const SPEC_NUMBER: NumberSpec = {
19+
type: SpecTypes.Number,
20+
required: true,
21+
viewSpec: {
22+
type: 'base',
23+
layout: 'row',
24+
layoutTitle: 'Age',
25+
placeholder: PLACEHOLDER,
26+
},
27+
};
28+
29+
const DynamicForm = ({spec}: {spec: Spec}) => (
30+
<ThemeProvider>
31+
<Form initialValues={{}} onSubmit={noop}>
32+
{() => <DynamicField name={NAME} spec={spec} config={dynamicConfig} />}
33+
</Form>
34+
</ThemeProvider>
35+
);
36+
37+
beforeEach(() => {
38+
window.matchMedia = () => ({
39+
media: '',
40+
matches: false,
41+
onchange: () => {},
42+
addListener: () => {},
43+
removeListener: () => {},
44+
addEventListener: () => {},
45+
removeEventListener: () => {},
46+
dispatchEvent: (_) => true,
47+
});
48+
});
49+
50+
describe('Text input', () => {
51+
test('input display check', () => {
52+
render(<DynamicForm spec={SPEC_NUMBER} />);
53+
54+
const input = screen.getByPlaceholderText(PLACEHOLDER);
55+
56+
expect(input).toBeVisible();
57+
});
58+
59+
test('checking value change, focus, deleting input value in Number spec', async () => {
60+
render(<DynamicForm spec={SPEC_NUMBER} />);
61+
62+
const user = userEvent.setup();
63+
const input = screen.getByPlaceholderText(PLACEHOLDER);
64+
65+
expect(input).not.toHaveFocus();
66+
67+
await user.click(input);
68+
69+
expect(input).toHaveFocus();
70+
71+
await user.keyboard('1');
72+
73+
expect(input).toHaveValue('1');
74+
75+
await user.type(input, '{backspace}');
76+
77+
expect(input).toHaveValue('');
78+
});
79+
80+
test('check button clear', async () => {
81+
render(<DynamicForm spec={SPEC_NUMBER} />);
82+
83+
const user = userEvent.setup();
84+
const input = screen.getByPlaceholderText(PLACEHOLDER);
85+
86+
let clearButton = screen.queryByRole('button', {name: 'Clear'});
87+
88+
expect(clearButton).not.toBeInTheDocument();
89+
90+
await user.click(input);
91+
await user.keyboard('1');
92+
93+
clearButton = screen.queryByRole('button', {name: 'Clear'});
94+
95+
expect(clearButton).toBeInTheDocument();
96+
97+
if (clearButton) {
98+
await user.click(clearButton);
99+
}
100+
101+
expect(input).toHaveValue('');
102+
expect(clearButton).not.toBeInTheDocument();
103+
});
104+
105+
test('checking default values Number spec', async () => {
106+
const spec = cloneDeep(SPEC_NUMBER);
107+
spec.defaultValue = 123;
108+
109+
render(<DynamicForm spec={spec} />);
110+
111+
const input = screen.getByPlaceholderText(PLACEHOLDER);
112+
113+
expect(input).toHaveValue('123');
114+
});
115+
116+
test('disabled input check', async () => {
117+
const spec = cloneDeep(SPEC_NUMBER);
118+
spec.viewSpec.disabled = true;
119+
120+
render(<DynamicForm spec={spec} />);
121+
122+
const user = userEvent.setup();
123+
const input = screen.getByPlaceholderText(PLACEHOLDER);
124+
125+
await user.click(input);
126+
127+
expect(input).not.toHaveFocus();
128+
expect(input).toBeDisabled();
129+
});
130+
131+
test('error message check in Number spec', async () => {
132+
const {container} = render(<DynamicForm spec={SPEC_NUMBER} />);
133+
134+
const user = userEvent.setup();
135+
const input = screen.getByPlaceholderText(PLACEHOLDER);
136+
137+
await user.click(input);
138+
await user.keyboard('1');
139+
140+
expect(input).toHaveValue('1');
141+
142+
const clearButton = screen.queryByRole('button', {name: 'Clear'});
143+
144+
expect(clearButton).toBeInTheDocument();
145+
146+
if (clearButton) {
147+
await user.click(clearButton);
148+
}
149+
150+
expect(input).toHaveValue('');
151+
expect(container.querySelector('.df-error-wrapper__error-text')).toBeInTheDocument();
152+
expect(screen.getByText('Empty field')).toBeVisible();
153+
154+
await user.keyboard('1');
155+
156+
expect(input).toHaveValue('1');
157+
expect(container.querySelector('.df-error-wrapper__error-text')).not.toBeInTheDocument();
158+
});
159+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
3+
import {test} from '~playwright/core';
4+
import {DynamicForm} from '~playwright/core/DynamicForm';
5+
6+
import {NUMBER_SPEC} from './helpers';
7+
8+
test.describe('Text Input', () => {
9+
test.describe('Number spec', () => {
10+
test('default', async ({mount, expectScreenshot}) => {
11+
await mount(<DynamicForm spec={NUMBER_SPEC.default} />);
12+
13+
await expectScreenshot();
14+
});
15+
16+
test('required', async ({mount, expectScreenshot}) => {
17+
await mount(<DynamicForm spec={NUMBER_SPEC.required} />);
18+
19+
await expectScreenshot();
20+
});
21+
22+
test('disabled', async ({mount, expectScreenshot}) => {
23+
await mount(<DynamicForm spec={NUMBER_SPEC.disabled} />);
24+
25+
await expectScreenshot();
26+
});
27+
28+
test('description', async ({mount, expectScreenshot}) => {
29+
await mount(<DynamicForm spec={NUMBER_SPEC.description} />);
30+
31+
await expectScreenshot();
32+
});
33+
34+
test('hidden', async ({mount, expectScreenshot}) => {
35+
await mount(<DynamicForm spec={NUMBER_SPEC.hidden} />);
36+
37+
await expectScreenshot();
38+
});
39+
40+
test('layout row verbose', async ({mount, expectScreenshot}) => {
41+
await mount(<DynamicForm spec={NUMBER_SPEC.layoutRowVerbose} />);
42+
43+
await expectScreenshot();
44+
});
45+
46+
test('layout table item', async ({mount, expectScreenshot}) => {
47+
await mount(<DynamicForm spec={NUMBER_SPEC.layoutTableItem} />);
48+
49+
await expectScreenshot();
50+
});
51+
52+
test('layout transperant', async ({mount, expectScreenshot}) => {
53+
await mount(<DynamicForm spec={NUMBER_SPEC.layoutTransparent} />);
54+
55+
await expectScreenshot();
56+
});
57+
58+
test('default value', async ({mount, expectScreenshot}) => {
59+
await mount(<DynamicForm spec={NUMBER_SPEC.defaultValue} />);
60+
61+
await expectScreenshot();
62+
});
63+
});
64+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import type {NumberSpec} from '../../../../../core';
2+
import {SpecTypes} from '../../../../../core';
3+
4+
export const NUMBER_SPEC: Record<string, NumberSpec> = {
5+
default: {
6+
type: SpecTypes.Number,
7+
viewSpec: {
8+
type: 'base',
9+
layout: 'row',
10+
layoutTitle: 'Age',
11+
placeholder: 'placeholder text',
12+
},
13+
},
14+
required: {
15+
type: SpecTypes.Number,
16+
required: true,
17+
viewSpec: {
18+
type: 'base',
19+
layout: 'row',
20+
layoutTitle: 'Age',
21+
placeholder: 'placeholder text',
22+
},
23+
},
24+
disabled: {
25+
type: SpecTypes.Number,
26+
viewSpec: {
27+
type: 'base',
28+
layout: 'row',
29+
layoutTitle: 'Age',
30+
placeholder: 'placeholder text',
31+
disabled: true,
32+
},
33+
},
34+
description: {
35+
type: SpecTypes.Number,
36+
viewSpec: {
37+
type: 'base',
38+
layout: 'row',
39+
layoutTitle: 'Age',
40+
placeholder: 'placeholder text',
41+
layoutDescription: 'description',
42+
},
43+
},
44+
hidden: {
45+
type: SpecTypes.Number,
46+
viewSpec: {
47+
type: 'base',
48+
layout: 'row',
49+
layoutTitle: 'Age',
50+
placeholder: 'placeholder text',
51+
hidden: true,
52+
},
53+
},
54+
layoutRowVerbose: {
55+
type: SpecTypes.Number,
56+
viewSpec: {
57+
type: 'base',
58+
layout: 'row_verbose',
59+
layoutTitle: 'Age',
60+
placeholder: 'placeholder text',
61+
layoutDescription: 'description',
62+
},
63+
},
64+
layoutTableItem: {
65+
type: SpecTypes.Number,
66+
viewSpec: {
67+
type: 'base',
68+
layout: 'table_item',
69+
layoutTitle: 'Age',
70+
placeholder: 'placeholder text',
71+
},
72+
},
73+
layoutTransparent: {
74+
type: SpecTypes.Number,
75+
viewSpec: {
76+
type: 'base',
77+
layout: 'transparent',
78+
layoutTitle: 'Age',
79+
placeholder: 'placeholder text',
80+
},
81+
},
82+
defaultValue: {
83+
defaultValue: 12,
84+
type: SpecTypes.Number,
85+
viewSpec: {
86+
type: 'base',
87+
layout: 'transparent',
88+
layoutTitle: 'Age',
89+
placeholder: 'placeholder text',
90+
},
91+
},
92+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Number';

0 commit comments

Comments
 (0)