@salutejs/sdds-api-tests 0.2.0-canary.2420.23801489438.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Salute Devices
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@salutejs/sdds-api-tests",
3
+ "version": "0.2.0-canary.2420.23801489438.0",
4
+ "description": "API tests for components",
5
+ "author": "Salute Frontend Team <salute.developers@gmail.com>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "ssh://git@github.com:salute-developers/plasma.git",
10
+ "directory": "utils/api-tests"
11
+ },
12
+ "scripts": {
13
+ "test": "rm -rf tests && node script.mjs && vitest run --config ./vitest.config.ts"
14
+ },
15
+ "devDependencies": {
16
+ "@salutejs/plasma-b2c": "1.613.0-canary.2420.23801489438.0",
17
+ "@salutejs/plasma-giga": "0.340.0-canary.2420.23801489438.0",
18
+ "@salutejs/plasma-icons": "1.234.0-canary.2420.23801489438.0",
19
+ "@salutejs/plasma-web": "1.615.0-canary.2420.23801489438.0",
20
+ "@salutejs/sdds-bizcom": "0.345.0-canary.2420.23801489438.0",
21
+ "@salutejs/sdds-cs": "0.349.0-canary.2420.23801489438.0",
22
+ "@salutejs/sdds-dfa": "0.343.0-canary.2420.23801489438.0",
23
+ "@salutejs/sdds-finai": "0.336.0-canary.2420.23801489438.0",
24
+ "@salutejs/sdds-insol": "0.340.0-canary.2420.23801489438.0",
25
+ "@salutejs/sdds-netology": "0.344.0-canary.2420.23801489438.0",
26
+ "@salutejs/sdds-platform-ai": "0.344.0-canary.2420.23801489438.0",
27
+ "@salutejs/sdds-scan": "0.343.0-canary.2420.23801489438.0",
28
+ "@salutejs/sdds-serv": "0.344.0-canary.2420.23801489438.0",
29
+ "@types/react": "18.0.28",
30
+ "@types/react-dom": "18.0.11",
31
+ "react": "18.2.0",
32
+ "react-dom": "18.2.0"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "gitHead": "f3b2e40fa8039e6ea8e1e6ebfae162910a29dac1"
38
+ }
package/script.mjs ADDED
@@ -0,0 +1,109 @@
1
+ /* Скрипт для подготовки файлов с тестами под разные библиотеки.
2
+ * basicLib - библиотека для исходных файлов. Компоненты для тестов должны браться только из нее.
3
+ * libs - массив с библиотеками, в которых будет проверяться API.
4
+ * Происходит это заменой импорта с basicLib на нужную в данный момент библиотеку.
5
+ * Далее скрипт генерирует файлы на основе этих мета-данных и уже после этого запускается тестирование. */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const config = {
12
+ basicLib: '@salutejs/plasma-b2c',
13
+ libs: [
14
+ {
15
+ name: '@salutejs/plasma-b2c',
16
+ ignoreComponents: ['Combobox', 'TextField'],
17
+ },
18
+ {
19
+ name: '@salutejs/plasma-web',
20
+ ignoreComponents: ['Combobox', 'TextField'],
21
+ },
22
+ {
23
+ name: '@salutejs/plasma-giga',
24
+ },
25
+ {
26
+ name: '@salutejs/sdds-bizcom',
27
+ },
28
+ {
29
+ name: '@salutejs/sdds-cs',
30
+ },
31
+ {
32
+ name: '@salutejs/sdds-dfa',
33
+ },
34
+ {
35
+ name: '@salutejs/sdds-finai',
36
+ },
37
+ {
38
+ name: '@salutejs/sdds-insol',
39
+ },
40
+ {
41
+ name: '@salutejs/sdds-netology',
42
+ },
43
+ {
44
+ name: '@salutejs/sdds-platform-ai',
45
+ },
46
+ {
47
+ name: '@salutejs/sdds-scan',
48
+ },
49
+ {
50
+ name: '@salutejs/sdds-serv',
51
+ },
52
+ ],
53
+ ignoreComponents: [],
54
+ srcDir: 'src',
55
+ outDir: 'tests',
56
+ };
57
+
58
+ (() => {
59
+ function getFiles(dir) {
60
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
61
+ const fullPath = path.join(dir, entry.name);
62
+ return entry.isDirectory() ? getFiles(fullPath) : fullPath;
63
+ });
64
+ }
65
+
66
+ try {
67
+ const filename = fileURLToPath(import.meta.url);
68
+ const dirname = path.dirname(filename);
69
+
70
+ const SRC_DIR = path.resolve(dirname, config.srcDir);
71
+ const OUT_DIR = path.resolve(dirname, config.outDir);
72
+
73
+ const files = getFiles(SRC_DIR);
74
+
75
+ files.forEach((filePath) => {
76
+ if (
77
+ Array.isArray(config.ignoreComponents) &&
78
+ config.ignoreComponents.some((component) => filePath.includes(`src/components/${component}`))
79
+ ) {
80
+ return;
81
+ }
82
+
83
+ const relPath = path.relative(SRC_DIR, filePath);
84
+ const originalContent = fs.readFileSync(filePath, 'utf8');
85
+
86
+ config.libs.forEach(({ name, ignoreComponents }) => {
87
+ if (
88
+ Array.isArray(ignoreComponents) &&
89
+ ignoreComponents.some((component) => filePath.includes(`src/components/${component}`))
90
+ ) {
91
+ return;
92
+ }
93
+
94
+ const libFolder = name.replace('@salutejs/', '');
95
+ const targetPath = path.join(OUT_DIR, libFolder, relPath);
96
+
97
+ const transformedContent = originalContent.replace(new RegExp(config.basicLib, 'g'), name);
98
+
99
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
100
+ fs.writeFileSync(targetPath, transformedContent);
101
+ });
102
+ });
103
+
104
+ console.log('Файлы для тестов сгенерированы успешно');
105
+ } catch (err) {
106
+ console.error('Ошибка генерации тестов: ', err);
107
+ process.exit(1);
108
+ }
109
+ })();
@@ -0,0 +1,88 @@
1
+ import * as React from 'react';
2
+ import type { ComponentProps, ReactNode, CSSProperties, AriaRole } from 'react';
3
+ import { describe, it } from 'vitest';
4
+ import { expectTypeOf } from 'expect-type';
5
+ import { IconDownload } from '@salutejs/plasma-icons';
6
+ import { Button } from '@salutejs/plasma-b2c';
7
+
8
+ type ButtonProps = ComponentProps<typeof Button>;
9
+
10
+ describe('Basics', () => {
11
+ it('Common', () => {
12
+ expectTypeOf<ButtonProps>().toHaveProperty('children').toEqualTypeOf<ReactNode>();
13
+ expectTypeOf<ButtonProps>().toHaveProperty('text').toEqualTypeOf<string | undefined>();
14
+ expectTypeOf<ButtonProps>().toHaveProperty('contentLeft').toEqualTypeOf<ReactNode>();
15
+ expectTypeOf<ButtonProps>().toHaveProperty('contentPlacing').toEqualTypeOf<'default' | 'relaxed' | undefined>();
16
+ expectTypeOf<ButtonProps>().toHaveProperty('additionalContent').toEqualTypeOf<ReactNode>();
17
+ expectTypeOf<ButtonProps>().toHaveProperty('isLoading').toEqualTypeOf<boolean | undefined>();
18
+ expectTypeOf<ButtonProps>().toHaveProperty('loader').toEqualTypeOf<ReactNode>();
19
+ expectTypeOf<ButtonProps>().toHaveProperty('stretch').toEqualTypeOf<boolean | undefined>();
20
+ expectTypeOf<ButtonProps>()
21
+ .toHaveProperty('stretching')
22
+ .toEqualTypeOf<'fixed' | 'filled' | 'auto' | undefined>();
23
+ expectTypeOf<ButtonProps>().toHaveProperty('square').toEqualTypeOf<boolean | undefined>();
24
+ expectTypeOf<ButtonProps>().toHaveProperty('focused').toEqualTypeOf<boolean | undefined>();
25
+ expectTypeOf<ButtonProps>().toHaveProperty('disabled').toEqualTypeOf<boolean | undefined>();
26
+ expectTypeOf<ButtonProps>()
27
+ .toHaveProperty('pin')
28
+ .toEqualTypeOf<
29
+ | 'square-square'
30
+ | 'square-clear'
31
+ | 'clear-square'
32
+ | 'clear-clear'
33
+ | 'clear-circle'
34
+ | 'circle-clear'
35
+ | 'circle-circle'
36
+ | undefined
37
+ >();
38
+ expectTypeOf<ButtonProps>().toHaveProperty('outlined').toEqualTypeOf<boolean | undefined>();
39
+ expectTypeOf<ButtonProps>().toHaveProperty('shiftLeft').toEqualTypeOf<boolean | undefined>();
40
+ expectTypeOf<ButtonProps>().toHaveProperty('shiftRight').toEqualTypeOf<boolean | undefined>();
41
+ expectTypeOf<ButtonProps>().toHaveProperty('blur').toEqualTypeOf<'small' | 'medium' | 'large' | undefined>();
42
+ });
43
+
44
+ it('Variations', () => {
45
+ type View = NonNullable<ButtonProps['view']>;
46
+ expectTypeOf<View>().toExtend<string>();
47
+ expectTypeOf<string>().not.toExtend<View>();
48
+
49
+ type Size = NonNullable<ButtonProps['size']>;
50
+ expectTypeOf<Size>().toExtend<string>();
51
+ expectTypeOf<string>().not.toExtend<Size>();
52
+ });
53
+
54
+ it('HTMLInputElement', () => {
55
+ expectTypeOf<ButtonProps>().toHaveProperty('id').toEqualTypeOf<string | undefined>();
56
+ expectTypeOf<ButtonProps>().toHaveProperty('className').toEqualTypeOf<string | undefined>();
57
+ expectTypeOf<ButtonProps>().toHaveProperty('style').toEqualTypeOf<CSSProperties | undefined>();
58
+ expectTypeOf<ButtonProps>()
59
+ .toHaveProperty('onClick')
60
+ .toEqualTypeOf<React.MouseEventHandler<HTMLElement> | undefined>();
61
+ expectTypeOf<ButtonProps>()
62
+ .toHaveProperty('onMouseEnter')
63
+ .toEqualTypeOf<React.MouseEventHandler<HTMLElement> | undefined>();
64
+ expectTypeOf<ButtonProps>()
65
+ .toHaveProperty('onMouseLeave')
66
+ .toEqualTypeOf<React.MouseEventHandler<HTMLElement> | undefined>();
67
+ expectTypeOf<ButtonProps>().toHaveProperty('aria-label').toEqualTypeOf<string | undefined>();
68
+ expectTypeOf<ButtonProps>().toHaveProperty('role').toEqualTypeOf<AriaRole | undefined>();
69
+ });
70
+ });
71
+
72
+ describe('Unions', () => {
73
+ it('RightContent', () => {
74
+ expectTypeOf<ButtonProps>({ value: 123 });
75
+ expectTypeOf<ButtonProps>({ contentRight: 123 });
76
+ // @ts-expect-error
77
+ expectTypeOf<ButtonProps>({ value: 123, contentRight: 123 });
78
+ });
79
+ });
80
+
81
+ describe('Complex', () => {
82
+ it('Examples', () => {
83
+ expectTypeOf<ButtonProps>({ text: 'Текст', contentLeft: <IconDownload color="inherit" /> });
84
+ expectTypeOf<ButtonProps>({ isLoading: true, loader: <div>Loader...</div> });
85
+ expectTypeOf<ButtonProps>({ text: 'Button', stretching: 'filled' });
86
+ expectTypeOf<ButtonProps>({ text: 'Hello', value: 'Plasma', stretching: 'filled', contentPlacing: 'default' });
87
+ });
88
+ });
@@ -0,0 +1,103 @@
1
+ import * as React from 'react';
2
+ import type { ComponentProps, ReactNode, CSSProperties, AriaRole } from 'react';
3
+ import { useState } from 'react';
4
+ import { describe, it } from 'vitest';
5
+ import { expectTypeOf } from 'expect-type';
6
+ import { Checkbox } from '@salutejs/plasma-b2c';
7
+
8
+ type CheckboxProps = ComponentProps<typeof Checkbox>;
9
+
10
+ describe('Basics', () => {
11
+ it('Common', () => {
12
+ expectTypeOf<CheckboxProps>().toHaveProperty('label').toEqualTypeOf<ReactNode>();
13
+ expectTypeOf<CheckboxProps>().toHaveProperty('description').toEqualTypeOf<ReactNode>();
14
+ expectTypeOf<CheckboxProps>().toHaveProperty('singleLine').toEqualTypeOf<boolean | undefined>();
15
+ expectTypeOf<CheckboxProps>().toHaveProperty('indeterminate').toEqualTypeOf<boolean | undefined>();
16
+ expectTypeOf<CheckboxProps>().toHaveProperty('focused').toEqualTypeOf<boolean | undefined>();
17
+ expectTypeOf<CheckboxProps>().toHaveProperty('disabled').toEqualTypeOf<boolean | undefined>();
18
+ expectTypeOf<CheckboxProps>().toHaveProperty('checked').toEqualTypeOf<boolean | undefined>();
19
+ expectTypeOf<CheckboxProps>().toHaveProperty('readOnly').toEqualTypeOf<boolean | undefined>();
20
+ expectTypeOf<CheckboxProps>().toHaveProperty('required').toEqualTypeOf<boolean | undefined>();
21
+ expectTypeOf<CheckboxProps>().toHaveProperty('name').toEqualTypeOf<string | undefined>();
22
+ expectTypeOf<CheckboxProps>()
23
+ .toHaveProperty('value')
24
+ .toEqualTypeOf<string | number | readonly string[] | undefined>();
25
+ });
26
+
27
+ it('Variations', () => {
28
+ type View = NonNullable<CheckboxProps['view']>;
29
+ expectTypeOf<View>().toExtend<string>();
30
+ expectTypeOf<string>().not.toExtend<View>();
31
+
32
+ type Size = NonNullable<CheckboxProps['size']>;
33
+ expectTypeOf<Size>().toExtend<string>();
34
+ expectTypeOf<string>().not.toExtend<Size>();
35
+ });
36
+
37
+ it('HTMLInputElement', () => {
38
+ expectTypeOf<CheckboxProps>().toHaveProperty('id').toEqualTypeOf<string | undefined>();
39
+ expectTypeOf<CheckboxProps>().toHaveProperty('className').toEqualTypeOf<string | undefined>();
40
+ expectTypeOf<CheckboxProps>().toHaveProperty('style').toEqualTypeOf<CSSProperties | undefined>();
41
+ expectTypeOf<CheckboxProps>()
42
+ .toHaveProperty('onChange')
43
+ .toEqualTypeOf<React.ChangeEventHandler<HTMLInputElement> | undefined>();
44
+ expectTypeOf<CheckboxProps>()
45
+ .toHaveProperty('onFocus')
46
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
47
+ expectTypeOf<CheckboxProps>()
48
+ .toHaveProperty('onBlur')
49
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
50
+ expectTypeOf<CheckboxProps>().toHaveProperty('aria-label').toEqualTypeOf<string | undefined>();
51
+ expectTypeOf<CheckboxProps>().toHaveProperty('role').toEqualTypeOf<AriaRole | undefined>();
52
+ });
53
+ });
54
+
55
+ describe('Complex', () => {
56
+ it('Examples', () => {
57
+ expectTypeOf<CheckboxProps>({ label: 'Согласен с условиями' });
58
+ expectTypeOf<CheckboxProps>({ label: 'Пункт', description: 'Описание пункта' });
59
+ expectTypeOf<CheckboxProps>({ label: 'Пункт', description: 'Описание', singleLine: true });
60
+ expectTypeOf<CheckboxProps>({ label: 'Частично', indeterminate: true });
61
+ expectTypeOf<CheckboxProps>({ label: 'Неактивен', disabled: true, checked: true });
62
+ });
63
+ });
64
+
65
+ describe('Examples', () => {
66
+ it('Controlled', () => {
67
+ () => {
68
+ const [checked, setChecked] = useState(false);
69
+
70
+ return (
71
+ <Checkbox
72
+ label="Согласен с условиями"
73
+ checked={checked}
74
+ onChange={(e) => setChecked(e.target.checked)}
75
+ />
76
+ );
77
+ };
78
+ });
79
+
80
+ it('With description', () => {
81
+ () => {
82
+ return <Checkbox label="Получать уведомления" description="На почту и в приложение" />;
83
+ };
84
+ });
85
+
86
+ it('Indeterminate', () => {
87
+ () => {
88
+ return <Checkbox label="Выбрать все" indeterminate />;
89
+ };
90
+ });
91
+
92
+ it('Disabled', () => {
93
+ () => {
94
+ return <Checkbox label="Неактивный" disabled checked />;
95
+ };
96
+ });
97
+
98
+ it('SingleLine', () => {
99
+ () => {
100
+ return <Checkbox label="Длинный заголовок" description="Длинное описание" singleLine />;
101
+ };
102
+ });
103
+ });
@@ -0,0 +1,468 @@
1
+ import type {
2
+ AriaRole,
3
+ ComponentProps,
4
+ CSSProperties,
5
+ ReactElement,
6
+ RefObject,
7
+ ReactNode,
8
+ ChangeEventHandler,
9
+ } from 'react';
10
+ import React, { useState, useRef } from 'react';
11
+ import { describe, it } from 'vitest';
12
+ import { expectTypeOf } from 'expect-type';
13
+ import { Combobox } from '@salutejs/plasma-b2c';
14
+
15
+ type ComboboxProps = ComponentProps<typeof Combobox>;
16
+
17
+ type ItemOption = {
18
+ value: string;
19
+ label: string;
20
+ placement?:
21
+ | 'top'
22
+ | 'top-start'
23
+ | 'top-end'
24
+ | 'right'
25
+ | 'right-start'
26
+ | 'right-end'
27
+ | 'bottom'
28
+ | 'bottom-start'
29
+ | 'bottom-end'
30
+ | 'left'
31
+ | 'left-start'
32
+ | 'left-end';
33
+ items?: Array<ItemOption>;
34
+ disabled?: boolean;
35
+ contentLeft?: ReactNode;
36
+ contentRight?: ReactNode;
37
+ className?: string;
38
+ listMaxHeight?: CSSProperties['height'];
39
+ };
40
+
41
+ describe('Basics', () => {
42
+ it('Common', () => {
43
+ // @ts-expect-error
44
+ expectTypeOf<ComboboxProps>({});
45
+ // @ts-expect-error
46
+ expectTypeOf<ComboboxProps>({ placement: 'bottom' });
47
+ expectTypeOf<ComboboxProps>({ items: [] });
48
+ expectTypeOf<ComboboxProps>()
49
+ .toHaveProperty('placement')
50
+ .toEqualTypeOf<
51
+ | 'top'
52
+ | 'top-start'
53
+ | 'top-end'
54
+ | 'right'
55
+ | 'right-start'
56
+ | 'right-end'
57
+ | 'bottom'
58
+ | 'bottom-start'
59
+ | 'bottom-end'
60
+ | 'left'
61
+ | 'left-start'
62
+ | 'left-end'
63
+ | undefined
64
+ >();
65
+ expectTypeOf<ComboboxProps>().toHaveProperty('placeholder').toEqualTypeOf<string | undefined>();
66
+ expectTypeOf<ComboboxProps>().toHaveProperty('helperText').toEqualTypeOf<string | undefined>();
67
+ expectTypeOf<ComboboxProps>().toHaveProperty('contentLeft').toEqualTypeOf<ReactElement | undefined>();
68
+ expectTypeOf<ComboboxProps>().toHaveProperty('textBefore').toEqualTypeOf<string | undefined>();
69
+ expectTypeOf<ComboboxProps>().toHaveProperty('textAfter').toEqualTypeOf<string | undefined>();
70
+ expectTypeOf<ComboboxProps>().toHaveProperty('variant').toEqualTypeOf<'normal' | 'tight' | undefined>();
71
+ expectTypeOf<ComboboxProps>().toHaveProperty('zIndex').toEqualTypeOf<CSSProperties['zIndex'] | undefined>();
72
+ expectTypeOf<ComboboxProps>()
73
+ .toHaveProperty('listMaxHeight')
74
+ .toEqualTypeOf<CSSProperties['height'] | undefined>();
75
+ expectTypeOf<ComboboxProps>().toHaveProperty('listWidth').toEqualTypeOf<CSSProperties['width'] | undefined>();
76
+ expectTypeOf<ComboboxProps>()
77
+ .toHaveProperty('portal')
78
+ .toEqualTypeOf<string | RefObject<HTMLElement> | undefined>();
79
+ expectTypeOf<ComboboxProps>().toHaveProperty('closeAfterSelect').toEqualTypeOf<boolean | undefined>();
80
+ expectTypeOf<ComboboxProps>()
81
+ .toHaveProperty('onChangeValue')
82
+ .toEqualTypeOf<((value: string) => void) | undefined>();
83
+ expectTypeOf<ComboboxProps>()
84
+ .toHaveProperty('filterValue')
85
+ .toEqualTypeOf<((value: string) => boolean) | undefined>();
86
+ // TODO: Сузить тип для onScroll
87
+ // expectTypeOf<ComboboxProps>()
88
+ // .toHaveProperty('onScroll')
89
+ // .toEqualTypeOf<((e: UIEvent<HTMLElement>) => void) | undefined>();
90
+ expectTypeOf<ComboboxProps>()
91
+ .toHaveProperty('onToggle')
92
+ .toEqualTypeOf<((isOpen: boolean) => void) | undefined>();
93
+ expectTypeOf<ComboboxProps>().toHaveProperty('beforeList').toEqualTypeOf<ReactNode | undefined>();
94
+ expectTypeOf<ComboboxProps>().toHaveProperty('afterList').toEqualTypeOf<ReactNode | undefined>();
95
+ expectTypeOf<ComboboxProps>().toHaveProperty('virtual').toEqualTypeOf<boolean | undefined>();
96
+ expectTypeOf<ComboboxProps>().toHaveProperty('mode').toEqualTypeOf<'default' | 'radio' | undefined>();
97
+ expectTypeOf<ComboboxProps>().toHaveProperty('emptyStateDescription').toEqualTypeOf<ReactNode | undefined>();
98
+ expectTypeOf<ComboboxProps>().toHaveProperty('treeView').toEqualTypeOf<boolean | undefined>();
99
+ expectTypeOf<ComboboxProps>().toHaveProperty('arrowPlacement').toEqualTypeOf<'left' | 'right' | undefined>();
100
+ expectTypeOf<ComboboxProps>().toHaveProperty('listHeight').toEqualTypeOf<CSSProperties['height'] | undefined>();
101
+ expectTypeOf<ComboboxProps>()
102
+ .toHaveProperty('listOverflow')
103
+ .toEqualTypeOf<CSSProperties['overflow'] | undefined>();
104
+ expectTypeOf<ComboboxProps>().toHaveProperty('label').toEqualTypeOf<string | undefined>();
105
+ expectTypeOf<ComboboxProps>().toHaveProperty('keepPlaceholder').toEqualTypeOf<boolean | undefined>();
106
+ expectTypeOf<ComboboxProps>().toHaveProperty('readOnly').toEqualTypeOf<boolean | undefined>();
107
+ expectTypeOf<ComboboxProps>().toHaveProperty('disabled').toEqualTypeOf<boolean | undefined>();
108
+ expectTypeOf<ComboboxProps>().toHaveProperty('alwaysOpened').toEqualTypeOf<boolean | undefined>();
109
+ expectTypeOf<ComboboxProps>().toHaveProperty('name').toEqualTypeOf<string | undefined>();
110
+ expectTypeOf<ComboboxProps>().toHaveProperty('defaultValue').toEqualTypeOf<string | string[] | undefined>();
111
+ expectTypeOf<ComboboxProps>().toHaveProperty('multiple').toEqualTypeOf<boolean | undefined>();
112
+ expectTypeOf<ComboboxProps>().toHaveProperty('value').toEqualTypeOf<string | string[] | undefined>();
113
+ expectTypeOf<ComboboxProps>().toHaveProperty('isTargetAmount').toEqualTypeOf<boolean | undefined>();
114
+ expectTypeOf<ComboboxProps>().toHaveProperty('targetAmount').toEqualTypeOf<number | undefined>();
115
+ expectTypeOf<ComboboxProps>().toHaveProperty('selectAllOptions').toEqualTypeOf<
116
+ | {
117
+ checked?: boolean;
118
+ indeterminate?: boolean;
119
+ label?: string;
120
+ onClick?: () => void;
121
+ sticky?: boolean;
122
+ }
123
+ | undefined
124
+ >();
125
+ expectTypeOf<ComboboxProps>()
126
+ .toHaveProperty('chipClickArea')
127
+ .toEqualTypeOf<'full' | 'close-icon' | undefined>();
128
+ expectTypeOf<ComboboxProps>().toHaveProperty('required').toEqualTypeOf<boolean | undefined>();
129
+ expectTypeOf<ComboboxProps>().toHaveProperty('requiredPlacement').toEqualTypeOf<'left' | 'right' | undefined>();
130
+ expectTypeOf<ComboboxProps>().toHaveProperty('optional').toEqualTypeOf<boolean | undefined>();
131
+ expectTypeOf<ComboboxProps>().toHaveProperty('optionalText').toEqualTypeOf<string | undefined>();
132
+ expectTypeOf<ComboboxProps>().toHaveProperty('hasRequiredIndicator').toEqualTypeOf<boolean | undefined>();
133
+ expectTypeOf<ComboboxProps>().toHaveProperty('hintText').toEqualTypeOf<string | undefined>();
134
+ expectTypeOf<ComboboxProps>().toHaveProperty('hintTrigger').toEqualTypeOf<'hover' | 'click' | undefined>();
135
+ expectTypeOf<ComboboxProps>().toHaveProperty('hintTargetIcon').toEqualTypeOf<ReactNode | undefined>();
136
+ // TODO: Почему свойства нет в sdds-insol?
137
+ // expectTypeOf<ComboboxProps>()
138
+ // .toHaveProperty('hintTargetPlacement')
139
+ // .toEqualTypeOf<'inner' | 'outer' | undefined>();
140
+ expectTypeOf<ComboboxProps>().toHaveProperty('hintHasArrow').toEqualTypeOf<boolean | undefined>();
141
+ expectTypeOf<ComboboxProps>().toHaveProperty('hintOffset').toEqualTypeOf<[number, number] | undefined>();
142
+ expectTypeOf<ComboboxProps>().toHaveProperty('hintWidth').toEqualTypeOf<string | undefined>();
143
+ expectTypeOf<ComboboxProps>().toHaveProperty('hintContentLeft').toEqualTypeOf<ReactNode | undefined>();
144
+ const items = [{ value: '', label: '', randomProp: '' }];
145
+ // @ts-expect-error
146
+ expectTypeOf<ComboboxProps>({ items: [{ value: '' }] });
147
+ // @ts-expect-error
148
+ expectTypeOf<ComboboxProps>({ items: [{ label: '' }] });
149
+ expectTypeOf<ComboboxProps>({ items: [{ value: '', label: '' }] });
150
+ expectTypeOf<ComboboxProps>({ items });
151
+ expectTypeOf<ComboboxProps>({ items: [{ value: '', label: '', disabled: true }] });
152
+ expectTypeOf<ComboboxProps>({ items: [{ value: '', label: '', disabled: true }] });
153
+ expectTypeOf<ComboboxProps>().toHaveProperty('items').toEqualTypeOf<ItemOption[]>();
154
+ expectTypeOf<ComboboxProps>()
155
+ .toHaveProperty('renderItem')
156
+ .toEqualTypeOf<((item: ItemOption) => ReactNode) | undefined>();
157
+ expectTypeOf<ComboboxProps>()
158
+ .toHaveProperty('filter')
159
+ .toEqualTypeOf<((item: ItemOption, textValue: string) => boolean) | undefined>();
160
+ expectTypeOf<ComboboxProps>()
161
+ .toHaveProperty('onChange')
162
+ .toEqualTypeOf<
163
+ | ((value: string[], item: ItemOption | null) => void)
164
+ | ((value: string, item: ItemOption | null) => void)
165
+ | ChangeEventHandler
166
+ | undefined
167
+ >();
168
+ expectTypeOf<ComboboxProps>()
169
+ .toHaveProperty('renderValue')
170
+ .toEqualTypeOf<((item: ItemOption) => string) | undefined>();
171
+ });
172
+
173
+ it('Variations', () => {
174
+ type View = NonNullable<ComboboxProps['view']>;
175
+ expectTypeOf<View>().toExtend<string>();
176
+ expectTypeOf<string>().not.toExtend<View>();
177
+
178
+ type Size = NonNullable<ComboboxProps['size']>;
179
+ expectTypeOf<Size>().toExtend<string>();
180
+ expectTypeOf<string>().not.toExtend<Size>();
181
+
182
+ type LabelPlacement = NonNullable<ComboboxProps['labelPlacement']>;
183
+ expectTypeOf<LabelPlacement>().toExtend<string>();
184
+ expectTypeOf<string>().not.toExtend<LabelPlacement>();
185
+ // TODO: Почему свойств hintView и hintSize нет в sdds-insol?
186
+ // type HintView = NonNullable<ComboboxProps['hintView']>;
187
+ // expectTypeOf<HintView>().toExtend<string>();
188
+ // expectTypeOf<string>().not.toExtend<HintView>();
189
+ //
190
+ // type HintSize = NonNullable<ComboboxProps['hintSize']>;
191
+ // expectTypeOf<HintSize>().toExtend<string>();
192
+ // expectTypeOf<string>().not.toExtend<HintSize>();
193
+ });
194
+
195
+ it('HTMLInputElement', () => {
196
+ expectTypeOf<ComboboxProps>().toHaveProperty('id').toEqualTypeOf<string | undefined>();
197
+ expectTypeOf<ComboboxProps>().toHaveProperty('className').toEqualTypeOf<string | undefined>();
198
+ expectTypeOf<ComboboxProps>().toHaveProperty('style').toEqualTypeOf<CSSProperties | undefined>();
199
+ expectTypeOf<ComboboxProps>()
200
+ .toHaveProperty('onFocus')
201
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
202
+ expectTypeOf<ComboboxProps>()
203
+ .toHaveProperty('onBlur')
204
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
205
+ expectTypeOf<ComboboxProps>()
206
+ .toHaveProperty('onKeyDown')
207
+ .toEqualTypeOf<React.KeyboardEventHandler<HTMLInputElement> | undefined>();
208
+ expectTypeOf<ComboboxProps>()
209
+ .toHaveProperty('onKeyUp')
210
+ .toEqualTypeOf<React.KeyboardEventHandler<HTMLInputElement> | undefined>();
211
+ expectTypeOf<ComboboxProps>().toHaveProperty('placeholder').toEqualTypeOf<string | undefined>();
212
+ expectTypeOf<ComboboxProps>().toHaveProperty('aria-label').toEqualTypeOf<string | undefined>();
213
+ expectTypeOf<ComboboxProps>().toHaveProperty('role').toEqualTypeOf<AriaRole | undefined>();
214
+ });
215
+ });
216
+
217
+ describe('Unions', () => {
218
+ it('ViewStateProps', () => {
219
+ expectTypeOf<ComboboxProps>({ items: [], readOnly: true, disabled: true, alwaysOpened: false });
220
+ expectTypeOf<ComboboxProps>({ items: [], readOnly: false, disabled: false, alwaysOpened: true });
221
+ // TODO: Неправильная работа юниона ViewStateProps. Должна быть ошибка.
222
+ expectTypeOf<ComboboxProps>({ items: [], readOnly: false, disabled: false, alwaysOpened: false });
223
+ // TODO: Неправильная работа юниона ViewStateProps. Должна быть ошибка.
224
+ expectTypeOf<ComboboxProps>({ items: [], readOnly: true, disabled: true, alwaysOpened: true });
225
+ });
226
+
227
+ it('IsMultiselect', () => {
228
+ expectTypeOf<ComboboxProps>({ items: [], value: '' });
229
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, value: [] });
230
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, value: [''] });
231
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, onChange: (value: string[]) => {} });
232
+ expectTypeOf<ComboboxProps>({ items: [], multiple: false, onChange: (value: string) => {} });
233
+ // @ts-expect-error
234
+ expectTypeOf<ComboboxProps>({ items: [], multiple: false, value: [] });
235
+ // @ts-expect-error
236
+ expectTypeOf<ComboboxProps>({ items: [], multiple: false, onChange: (value: string[]) => {} });
237
+ // @ts-expect-error
238
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, value: '' });
239
+ // @ts-expect-error
240
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, onChange: (value: string) => {} });
241
+ expectTypeOf<ComboboxProps>({ items: [], isTargetAmount: false });
242
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, isTargetAmount: true });
243
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, targetAmount: 1 });
244
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, renderValue: () => '' });
245
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, selectAllOptions: { checked: true } });
246
+ expectTypeOf<ComboboxProps>({ items: [], multiple: true, chipClickArea: 'full' });
247
+ // @ts-expect-error
248
+ expectTypeOf<ComboboxProps>({ items: [], isTargetAmount: true });
249
+ // @ts-expect-error
250
+ expectTypeOf<ComboboxProps>({ items: [], targetAmount: 1 });
251
+ // @ts-expect-error
252
+ expectTypeOf<ComboboxProps>({ items: [], renderValue: () => '' });
253
+ // @ts-expect-error
254
+ expectTypeOf<ComboboxProps>({ items: [], selectAllOptions: { checked: true } });
255
+ // @ts-expect-error
256
+ expectTypeOf<ComboboxProps>({ items: [], chipClickArea: 'full' });
257
+ expectTypeOf<ComboboxProps>({ items: [], name: '', defaultValue: '' });
258
+ expectTypeOf<ComboboxProps>({ items: [], name: '', multiple: true, defaultValue: [''] });
259
+ // @ts-expect-error
260
+ expectTypeOf<ComboboxProps>({ items: [], name: '', value: '' });
261
+ expectTypeOf<ComboboxProps>({ items: [], name: '', multiple: true, isTargetAmount: true });
262
+ expectTypeOf<ComboboxProps>({ items: [], name: '', multiple: true, targetAmount: 1 });
263
+ expectTypeOf<ComboboxProps>({ items: [], name: '', multiple: true, renderValue: () => '' });
264
+ expectTypeOf<ComboboxProps>({ items: [], name: '', multiple: true, selectAllOptions: { checked: true } });
265
+ expectTypeOf<ComboboxProps>({ items: [], name: '', multiple: true, chipClickArea: 'full' });
266
+ // @ts-expect-error
267
+ expectTypeOf<ComboboxProps>({ items: [], name: '', isTargetAmount: true });
268
+ // @ts-expect-error
269
+ expectTypeOf<ComboboxProps>({ items: [], name: '', targetAmount: 1 });
270
+ // @ts-expect-error
271
+ expectTypeOf<ComboboxProps>({ items: [], name: '', renderValue: () => '' });
272
+ // @ts-expect-error
273
+ expectTypeOf<ComboboxProps>({ items: [], name: '', selectAllOptions: { checked: true } });
274
+ // @ts-expect-error
275
+ expectTypeOf<ComboboxProps>({ items: [], name: '', chipClickArea: 'full' });
276
+ });
277
+ });
278
+
279
+ describe('Generics', () => {
280
+ it('ItemOption', () => {
281
+ const items = [{ value: '', label: '', randomProp: '', boolProp: true }];
282
+
283
+ void (<Combobox items={items} />);
284
+ void (
285
+ <Combobox
286
+ multiple
287
+ items={items}
288
+ renderItem={(item) => {
289
+ return item.randomProp;
290
+ }}
291
+ filter={(item) => item.boolProp}
292
+ onChange={(value: string[], item) => {
293
+ return item && item.randomProp;
294
+ }}
295
+ renderValue={(item) => {
296
+ return item.randomProp;
297
+ }}
298
+ />
299
+ );
300
+ });
301
+ });
302
+
303
+ describe('Examples', () => {
304
+ const items = [
305
+ {
306
+ value: 'north_america',
307
+ label: 'Северная Америка',
308
+ },
309
+ {
310
+ value: 'south_america',
311
+ label: 'Южная Америка',
312
+ items: [
313
+ {
314
+ value: 'brazil',
315
+ label: 'Бразилия',
316
+ disabled: true,
317
+ },
318
+ {
319
+ value: 'argentina',
320
+ label: 'Аргентина',
321
+ },
322
+ ],
323
+ },
324
+ ];
325
+
326
+ it('Single', () => {
327
+ () => {
328
+ const [value, setValue] = useState<string>('');
329
+
330
+ return (
331
+ <Combobox
332
+ items={items}
333
+ value={value}
334
+ onChange={setValue}
335
+ placeholder="Placeholder"
336
+ label="Label"
337
+ helperText="Helper text"
338
+ />
339
+ );
340
+ };
341
+ });
342
+
343
+ it('Multiple', () => {
344
+ () => {
345
+ const [value, setValue] = useState<string[]>([]);
346
+
347
+ return (
348
+ <Combobox
349
+ multiple
350
+ items={items}
351
+ value={value}
352
+ onChange={setValue}
353
+ placeholder="Placeholder"
354
+ label="Label"
355
+ helperText="Helper text"
356
+ />
357
+ );
358
+ };
359
+ });
360
+
361
+ it('Predefined', () => {
362
+ () => {
363
+ const [multipleValue, setMultipleValue] = useState<string[]>(['brazil', 'north_america']);
364
+
365
+ return (
366
+ <Combobox
367
+ multiple
368
+ items={items}
369
+ value={multipleValue}
370
+ onChange={setMultipleValue}
371
+ placeholder="Placeholder"
372
+ label="Label"
373
+ helperText="Helper text"
374
+ />
375
+ );
376
+ };
377
+ });
378
+
379
+ it('Portal', () => {
380
+ () => {
381
+ const [value, setValue] = useState<string>('');
382
+
383
+ const ref = useRef(null);
384
+
385
+ return (
386
+ <Combobox
387
+ items={items}
388
+ value={value}
389
+ onChange={setValue}
390
+ placeholder="Placeholder"
391
+ label="Label"
392
+ helperText="Helper text"
393
+ portal={ref}
394
+ listWidth="300px"
395
+ />
396
+ );
397
+ };
398
+ });
399
+
400
+ it('Uncontrolled', () => {
401
+ () => {
402
+ return <Combobox items={items} placeholder="Placeholder" label="Label" helperText="Helper text" />;
403
+ };
404
+ });
405
+
406
+ it('Uncontrolled', () => {
407
+ () => {
408
+ const items = Array(5000)
409
+ .fill(1)
410
+ .map((_, i) => ({ value: i.toString(), label: i.toString() }));
411
+
412
+ return (
413
+ <Combobox
414
+ items={items}
415
+ virtual
416
+ listMaxHeight="200px"
417
+ placeholder="Placeholder"
418
+ label="Label"
419
+ helperText="Helper text"
420
+ />
421
+ );
422
+ };
423
+ });
424
+
425
+ it('Disabled elements', () => {
426
+ () => {
427
+ const [value, setValue] = useState(['brazil']);
428
+
429
+ return (
430
+ <Combobox
431
+ multiple
432
+ label="Label"
433
+ placeholder="Placeholder"
434
+ items={items}
435
+ value={value}
436
+ onChange={setValue}
437
+ isTargetAmount
438
+ />
439
+ );
440
+ };
441
+ });
442
+
443
+ it('Treeview', () => {
444
+ () => {
445
+ return (
446
+ <Combobox
447
+ multiple
448
+ treeView
449
+ label="Введите 'Токио'"
450
+ placeholder="Placeholder"
451
+ items={items}
452
+ listMaxHeight="300px"
453
+ />
454
+ );
455
+ };
456
+ });
457
+
458
+ it('Native form', () => {
459
+ () => {
460
+ return (
461
+ <form>
462
+ <Combobox label="Combobox" name="combobox" defaultValue="brazil" items={items} />
463
+ <Combobox label="Combobox" name="comboboxMulti" defaultValue={['brazil']} items={items} multiple />
464
+ </form>
465
+ );
466
+ };
467
+ });
468
+ });
@@ -0,0 +1,283 @@
1
+ import * as React from 'react';
2
+ import type { ComponentProps, ReactNode, CSSProperties, AriaRole, ReactElement, KeyboardEvent } from 'react';
3
+ import { useState } from 'react';
4
+ import { describe, it } from 'vitest';
5
+ import { expectTypeOf } from 'expect-type';
6
+ import { TextField } from '@salutejs/plasma-b2c';
7
+
8
+ type TextFieldProps = ComponentProps<typeof TextField>;
9
+
10
+ describe('Basics', () => {
11
+ it('Common', () => {
12
+ expectTypeOf<TextFieldProps>().toHaveProperty('label').toEqualTypeOf<string | undefined>();
13
+ expectTypeOf<TextFieldProps>().toHaveProperty('placeholder').toEqualTypeOf<string | undefined>();
14
+ expectTypeOf<TextFieldProps>().toHaveProperty('leftHelper').toEqualTypeOf<ReactNode>();
15
+ expectTypeOf<TextFieldProps>().toHaveProperty('titleCaption').toEqualTypeOf<ReactNode>();
16
+ expectTypeOf<TextFieldProps>().toHaveProperty('contentLeft').toEqualTypeOf<ReactElement | undefined>();
17
+ expectTypeOf<TextFieldProps>().toHaveProperty('contentRight').toEqualTypeOf<ReactElement | undefined>();
18
+ expectTypeOf<TextFieldProps>().toHaveProperty('textBefore').toEqualTypeOf<string | undefined>();
19
+ expectTypeOf<TextFieldProps>().toHaveProperty('textAfter').toEqualTypeOf<string | undefined>();
20
+ expectTypeOf<TextFieldProps>().toHaveProperty('appearance').toEqualTypeOf<'default' | 'clear' | undefined>();
21
+ expectTypeOf<TextFieldProps>().toHaveProperty('disabled').toEqualTypeOf<boolean | undefined>();
22
+ expectTypeOf<TextFieldProps>().toHaveProperty('readOnly').toEqualTypeOf<boolean | undefined>();
23
+ expectTypeOf<TextFieldProps>().toHaveProperty('keepPlaceholder').toEqualTypeOf<boolean | undefined>();
24
+ expectTypeOf<TextFieldProps>().toHaveProperty('required').toEqualTypeOf<boolean | undefined>();
25
+ expectTypeOf<TextFieldProps>()
26
+ .toHaveProperty('requiredPlacement')
27
+ .toEqualTypeOf<'left' | 'right' | undefined>();
28
+ expectTypeOf<TextFieldProps>().toHaveProperty('optional').toEqualTypeOf<boolean | undefined>();
29
+ expectTypeOf<TextFieldProps>().toHaveProperty('optionalText').toEqualTypeOf<string | undefined>();
30
+ expectTypeOf<TextFieldProps>().toHaveProperty('hasRequiredIndicator').toEqualTypeOf<boolean | undefined>();
31
+ expectTypeOf<TextFieldProps>()
32
+ .toHaveProperty('onSearch')
33
+ .toEqualTypeOf<((value: string, event?: KeyboardEvent<HTMLInputElement>) => void) | undefined>();
34
+ expectTypeOf<TextFieldProps>().toHaveProperty('hintText').toEqualTypeOf<string | undefined>();
35
+ expectTypeOf<TextFieldProps>().toHaveProperty('hintTrigger').toEqualTypeOf<'hover' | 'click' | undefined>();
36
+ expectTypeOf<TextFieldProps>().toHaveProperty('hintTargetIcon').toEqualTypeOf<ReactNode>();
37
+ expectTypeOf<TextFieldProps>()
38
+ .toHaveProperty('hintTargetPlacement')
39
+ .toEqualTypeOf<'inner' | 'outer' | undefined>();
40
+ expectTypeOf<TextFieldProps>().toHaveProperty('hintHasArrow').toEqualTypeOf<boolean | undefined>();
41
+ expectTypeOf<TextFieldProps>().toHaveProperty('hintOffset').toEqualTypeOf<[number, number] | undefined>();
42
+ expectTypeOf<TextFieldProps>().toHaveProperty('hintWidth').toEqualTypeOf<string | undefined>();
43
+ expectTypeOf<TextFieldProps>().toHaveProperty('hintContentLeft').toEqualTypeOf<ReactNode>();
44
+ });
45
+
46
+ it('Variations', () => {
47
+ type View = NonNullable<TextFieldProps['view']>;
48
+ expectTypeOf<View>().toExtend<string>();
49
+ expectTypeOf<string>().not.toExtend<View>();
50
+
51
+ type Size = NonNullable<TextFieldProps['size']>;
52
+ expectTypeOf<Size>().toExtend<string>();
53
+ expectTypeOf<string>().not.toExtend<Size>();
54
+
55
+ type LabelPlacement = NonNullable<TextFieldProps['labelPlacement']>;
56
+ expectTypeOf<LabelPlacement>().toExtend<string>();
57
+ expectTypeOf<string>().not.toExtend<LabelPlacement>();
58
+
59
+ type HintView = NonNullable<TextFieldProps['hintView']>;
60
+ expectTypeOf<HintView>().toExtend<string>();
61
+ expectTypeOf<string>().not.toExtend<HintView>();
62
+
63
+ type HintSize = NonNullable<TextFieldProps['hintSize']>;
64
+ expectTypeOf<HintSize>().toExtend<string>();
65
+ expectTypeOf<string>().not.toExtend<HintSize>();
66
+ });
67
+
68
+ it('HTMLInputElement', () => {
69
+ expectTypeOf<TextFieldProps>().toHaveProperty('id').toEqualTypeOf<string | undefined>();
70
+ expectTypeOf<TextFieldProps>().toHaveProperty('className').toEqualTypeOf<string | undefined>();
71
+ expectTypeOf<TextFieldProps>().toHaveProperty('style').toEqualTypeOf<CSSProperties | undefined>();
72
+ expectTypeOf<TextFieldProps>()
73
+ .toHaveProperty('value')
74
+ .toEqualTypeOf<string | number | readonly string[] | undefined>();
75
+ expectTypeOf<TextFieldProps>()
76
+ .toHaveProperty('defaultValue')
77
+ .toEqualTypeOf<string | number | readonly string[] | undefined>();
78
+ expectTypeOf<TextFieldProps>()
79
+ .toHaveProperty('onChange')
80
+ .toEqualTypeOf<React.ChangeEventHandler<HTMLInputElement> | undefined>();
81
+ expectTypeOf<TextFieldProps>()
82
+ .toHaveProperty('onFocus')
83
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
84
+ expectTypeOf<TextFieldProps>()
85
+ .toHaveProperty('onBlur')
86
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
87
+ expectTypeOf<TextFieldProps>()
88
+ .toHaveProperty('onKeyDown')
89
+ .toEqualTypeOf<React.KeyboardEventHandler<HTMLInputElement> | undefined>();
90
+ expectTypeOf<TextFieldProps>().toHaveProperty('aria-label').toEqualTypeOf<string | undefined>();
91
+ expectTypeOf<TextFieldProps>().toHaveProperty('role').toEqualTypeOf<AriaRole | undefined>();
92
+ });
93
+ });
94
+
95
+ describe('Unions', () => {
96
+ it('ClearProps', () => {
97
+ expectTypeOf<TextFieldProps>({ clear: true, hasDivider: true });
98
+ expectTypeOf<TextFieldProps>({ clear: true, hasDivider: false });
99
+ expectTypeOf<TextFieldProps>({ clear: true });
100
+ // TODO: Неправильная работа юниона ClearProps. Должна быть ошибка.
101
+ expectTypeOf<TextFieldProps>({ hasDivider: true });
102
+ });
103
+
104
+ it('HintProps', () => {
105
+ expectTypeOf<TextFieldProps>({ hintText: 'hint' });
106
+ expectTypeOf<TextFieldProps>({ hintText: 'hint', hintTrigger: 'hover' });
107
+ expectTypeOf<TextFieldProps>({ hintText: 'hint', hintHasArrow: true });
108
+ expectTypeOf<TextFieldProps>({ hintText: 'hint', hintOffset: [0, 8] });
109
+ expectTypeOf<TextFieldProps>({ hintText: 'hint', hintWidth: '10rem' });
110
+ expectTypeOf<TextFieldProps>({});
111
+ // @ts-expect-error
112
+ expectTypeOf<TextFieldProps>({ hintTrigger: 'hover' });
113
+ // @ts-expect-error
114
+ expectTypeOf<TextFieldProps>({ hintHasArrow: true });
115
+ // @ts-expect-error
116
+ expectTypeOf<TextFieldProps>({ hintOffset: [0, 8] });
117
+ // @ts-expect-error
118
+ expectTypeOf<TextFieldProps>({ hintWidth: '10rem' });
119
+ });
120
+
121
+ it('EnumerationType', () => {
122
+ expectTypeOf<TextFieldProps>({ enumerationType: 'plain' });
123
+ expectTypeOf<TextFieldProps>({ enumerationType: 'chip', chips: ['tag1', 'tag2'] });
124
+ expectTypeOf<TextFieldProps>({
125
+ enumerationType: 'chip',
126
+ chips: ['tag1'],
127
+ onChangeChips: (value) => {},
128
+ chipType: 'default',
129
+ });
130
+ expectTypeOf<TextFieldProps>({
131
+ enumerationType: 'chip',
132
+ chips: ['tag1'],
133
+ chipView: 'secondary',
134
+ });
135
+ expectTypeOf<TextFieldProps>({
136
+ enumerationType: 'chip',
137
+ chips: ['tag1'],
138
+ chipValidator: (value) => ({ view: 'default' }),
139
+ });
140
+ expectTypeOf<TextFieldProps>({
141
+ enumerationType: 'chip',
142
+ chipType: 'text',
143
+ });
144
+ // @ts-expect-error
145
+ expectTypeOf<TextFieldProps>({ enumerationType: 'plain', chips: ['tag1'] });
146
+ // @ts-expect-error
147
+ expectTypeOf<TextFieldProps>({ enumerationType: 'plain', onChangeChips: (value: any[]) => {} });
148
+ // @ts-expect-error
149
+ expectTypeOf<TextFieldProps>({ enumerationType: 'plain', chipType: 'default' });
150
+ // @ts-expect-error
151
+ expectTypeOf<TextFieldProps>({ enumerationType: 'plain', chipView: 'secondary' });
152
+ // @ts-expect-error
153
+ expectTypeOf<TextFieldProps>({ enumerationType: 'plain', chipValidator: (value: string) => ({}) });
154
+ });
155
+ });
156
+
157
+ describe('Complex', () => {
158
+ it('Examples', () => {
159
+ expectTypeOf<TextFieldProps>({
160
+ label: 'Имя',
161
+ placeholder: 'Введите имя',
162
+ leftHelper: 'Обязательное поле',
163
+ });
164
+ expectTypeOf<TextFieldProps>({
165
+ label: 'Email',
166
+ placeholder: 'Введите email',
167
+ leftHelper: 'Некорректный email',
168
+ required: true,
169
+ requiredPlacement: 'right',
170
+ });
171
+ expectTypeOf<TextFieldProps>({
172
+ label: 'Tags',
173
+ enumerationType: 'chip',
174
+ chips: ['tag1', 'tag2'],
175
+ onChangeChips: (values) => {},
176
+ });
177
+ expectTypeOf<TextFieldProps>({
178
+ label: 'С подсказкой',
179
+ hintText: 'Это подсказка',
180
+ hintTrigger: 'hover',
181
+ hintView: 'default',
182
+ hintSize: 's',
183
+ });
184
+ expectTypeOf<TextFieldProps>({
185
+ label: 'Clear',
186
+ appearance: 'clear',
187
+ clear: true,
188
+ hasDivider: true,
189
+ });
190
+ expectTypeOf<TextFieldProps>({
191
+ textBefore: '$',
192
+ textAfter: 'USD',
193
+ placeholder: '0.00',
194
+ });
195
+ });
196
+ });
197
+
198
+ describe('Examples', () => {
199
+ it('Basic', () => {
200
+ () => {
201
+ const [value, setValue] = useState('');
202
+
203
+ return (
204
+ <TextField
205
+ value={value}
206
+ onChange={(e) => setValue(e.target.value)}
207
+ label="Имя"
208
+ placeholder="Введите имя"
209
+ leftHelper="Обязательное поле"
210
+ />
211
+ );
212
+ };
213
+ });
214
+
215
+ it('With chips', () => {
216
+ () => {
217
+ const [chips, setChips] = useState<Array<string | number | boolean>>(['tag1', 'tag2']);
218
+
219
+ return (
220
+ <TextField
221
+ label="Tags"
222
+ enumerationType="chip"
223
+ chips={chips}
224
+ onChangeChips={setChips}
225
+ chipType="default"
226
+ />
227
+ );
228
+ };
229
+ });
230
+
231
+ it('With hint', () => {
232
+ () => {
233
+ return (
234
+ <TextField
235
+ label="С подсказкой"
236
+ hintText="Это подсказка"
237
+ hintTrigger="hover"
238
+ hintView="default"
239
+ hintSize="s"
240
+ hintHasArrow
241
+ placeholder="Введите значение"
242
+ />
243
+ );
244
+ };
245
+ });
246
+
247
+ it('Clear appearance', () => {
248
+ () => {
249
+ return <TextField label="Clear" appearance="clear" clear hasDivider placeholder="Введите значение" />;
250
+ };
251
+ });
252
+
253
+ it('With text decorations', () => {
254
+ () => {
255
+ return <TextField textBefore="$" textAfter="USD" placeholder="0.00" label="Сумма" />;
256
+ };
257
+ });
258
+
259
+ it('Disabled and readOnly', () => {
260
+ () => {
261
+ return (
262
+ <>
263
+ <TextField label="Disabled" disabled value="Нельзя редактировать" />
264
+ <TextField label="ReadOnly" readOnly value="Только чтение" />
265
+ </>
266
+ );
267
+ };
268
+ });
269
+
270
+ it('With onSearch', () => {
271
+ () => {
272
+ return (
273
+ <TextField
274
+ label="Поиск"
275
+ placeholder="Введите запрос"
276
+ onSearch={(value) => {
277
+ console.log(value);
278
+ }}
279
+ />
280
+ );
281
+ };
282
+ });
283
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES5",
4
+ "module": "ESNext",
5
+ "lib": ["dom", "dom.iterable", "esnext"],
6
+ "jsx": "react",
7
+ "declaration": true,
8
+ "sourceMap": true,
9
+ "importHelpers": false,
10
+ "typeRoots": ["node_modules/@types"],
11
+
12
+ /* Strict Type-Checking Options */
13
+ "strict": true,
14
+ "noImplicitAny": false,
15
+ "strictNullChecks": true,
16
+ "strictFunctionTypes": true,
17
+ "strictBindCallApply": true,
18
+ "strictPropertyInitialization": true,
19
+ "noImplicitThis": true,
20
+ "alwaysStrict": true,
21
+ "isolatedModules": true,
22
+
23
+ /* Additional Checks */
24
+ "noUnusedLocals": false,
25
+ "noUnusedParameters": false,
26
+ "noImplicitReturns": true,
27
+ "noFallthroughCasesInSwitch": true,
28
+
29
+ /* Module Resolution Options */
30
+ "moduleResolution": "bundler",
31
+ "esModuleInterop": true,
32
+
33
+ /* Advanced Options */
34
+ "skipLibCheck": true,
35
+ "skipDefaultLibCheck": true,
36
+ "forceConsistentCasingInFileNames": true
37
+ },
38
+ "include": ["./src", "./tests"],
39
+ "exclude": ["node_modules/*"]
40
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ typecheck: {
6
+ enabled: true,
7
+ only: true,
8
+ include: ['./tests/**/*'],
9
+ ignoreSourceErrors: true,
10
+ },
11
+ },
12
+ });