@salutejs/sdds-api-tests 0.34.0-dev.1 → 0.35.0-dev.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salutejs/sdds-api-tests",
3
- "version": "0.34.0-dev.1",
3
+ "version": "0.35.0-dev.0",
4
4
  "description": "API tests for components",
5
5
  "author": "Salute Frontend Team <salute.developers@gmail.com>",
6
6
  "license": "MIT",
@@ -13,19 +13,19 @@
13
13
  "test": "rm -rf tests && node script.mjs && NODE_OPTIONS=--max-old-space-size=8192 tsc --noEmit -p ./tsconfig.typecheck.json"
14
14
  },
15
15
  "devDependencies": {
16
- "@salutejs/plasma-b2c": "1.645.0-dev.1",
17
- "@salutejs/plasma-giga": "0.372.0-dev.1",
16
+ "@salutejs/plasma-b2c": "1.646.0-dev.0",
17
+ "@salutejs/plasma-giga": "0.373.0-dev.0",
18
18
  "@salutejs/plasma-icons": "1.257.1-dev.0",
19
- "@salutejs/plasma-web": "1.647.0-dev.1",
20
- "@salutejs/sdds-bizcom": "0.377.0-dev.1",
21
- "@salutejs/sdds-cs": "0.381.0-dev.1",
22
- "@salutejs/sdds-dfa": "0.375.0-dev.1",
23
- "@salutejs/sdds-finai": "0.368.0-dev.1",
24
- "@salutejs/sdds-insol": "0.372.0-dev.1",
25
- "@salutejs/sdds-netology": "0.376.0-dev.1",
26
- "@salutejs/sdds-platform-ai": "0.376.0-dev.1",
27
- "@salutejs/sdds-scan": "0.375.0-dev.1",
28
- "@salutejs/sdds-serv": "0.376.0-dev.1",
19
+ "@salutejs/plasma-web": "1.648.0-dev.0",
20
+ "@salutejs/sdds-bizcom": "0.378.0-dev.0",
21
+ "@salutejs/sdds-cs": "0.382.0-dev.0",
22
+ "@salutejs/sdds-dfa": "0.376.0-dev.0",
23
+ "@salutejs/sdds-finai": "0.369.0-dev.0",
24
+ "@salutejs/sdds-insol": "0.373.0-dev.0",
25
+ "@salutejs/sdds-netology": "0.377.0-dev.0",
26
+ "@salutejs/sdds-platform-ai": "0.377.0-dev.0",
27
+ "@salutejs/sdds-scan": "0.376.0-dev.0",
28
+ "@salutejs/sdds-serv": "0.377.0-dev.0",
29
29
  "@types/node": "^25.9.1",
30
30
  "@types/react": "18.0.28",
31
31
  "@types/react-dom": "18.0.11",
@@ -35,5 +35,5 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  },
38
- "gitHead": "77c6420021dc3e4b07d35f80a29f28d008f393c5"
38
+ "gitHead": "a14f9c39f6edf2ba938e1c640ee5357a3629e928"
39
39
  }
package/script.mjs CHANGED
@@ -21,18 +21,19 @@ const config = {
21
21
  },
22
22
  {
23
23
  name: '@salutejs/plasma-giga',
24
+ excludeComponents: ['Editable'],
24
25
  },
25
26
  {
26
27
  name: '@salutejs/sdds-bizcom',
27
- excludeComponents: ['CodeArea', 'BottomSheet'],
28
+ excludeComponents: ['CodeArea', 'BottomSheet', 'Editable'],
28
29
  },
29
30
  {
30
31
  name: '@salutejs/sdds-cs',
31
- excludeComponents: ['RatingInput', 'Select', 'InformationWrapper', 'CodeArea', 'BottomSheet'],
32
+ excludeComponents: ['RatingInput', 'Select', 'InformationWrapper', 'CodeArea', 'BottomSheet', 'Editable'],
32
33
  },
33
34
  {
34
35
  name: '@salutejs/sdds-dfa',
35
- excludeComponents: ['InformationWrapper', 'CodeArea', 'BottomSheet'],
36
+ excludeComponents: ['InformationWrapper', 'CodeArea', 'BottomSheet', 'Editable'],
36
37
  },
37
38
  {
38
39
  name: '@salutejs/sdds-finai',
@@ -40,23 +41,23 @@ const config = {
40
41
  },
41
42
  {
42
43
  name: '@salutejs/sdds-insol',
43
- excludeComponents: ['RatingInput', 'CodeArea', 'BottomSheet'],
44
+ excludeComponents: ['RatingInput', 'CodeArea', 'BottomSheet', 'Editable'],
44
45
  },
45
46
  {
46
47
  name: '@salutejs/sdds-netology',
47
- excludeComponents: ['RatingInput', 'CodeArea', 'BottomSheet'],
48
+ excludeComponents: ['RatingInput', 'CodeArea', 'BottomSheet', 'Editable'],
48
49
  },
49
50
  {
50
51
  name: '@salutejs/sdds-platform-ai',
51
- excludeComponents: ['RatingInput', 'CodeArea', 'BottomSheet'],
52
+ excludeComponents: ['RatingInput', 'CodeArea', 'BottomSheet', 'Editable'],
52
53
  },
53
54
  {
54
55
  name: '@salutejs/sdds-scan',
55
- excludeComponents: ['CodeArea', 'Sheet', 'BottomSheet'],
56
+ excludeComponents: ['CodeArea', 'Sheet', 'BottomSheet', 'Editable'],
56
57
  },
57
58
  {
58
59
  name: '@salutejs/sdds-serv',
59
- excludeComponents: ['CodeArea', 'BottomSheet'],
60
+ excludeComponents: ['CodeArea', 'BottomSheet', 'Editable'],
60
61
  },
61
62
  ],
62
63
  includeComponents: [],
@@ -0,0 +1,107 @@
1
+ import * as React from 'react';
2
+ import type { AriaRole, ComponentProps, ComponentPropsWithRef, CSSProperties, FC, ReactNode } from 'react';
3
+ import { useState } from 'react';
4
+ import { describe, it } from 'node:test';
5
+ import { expectTypeOf } from 'expect-type';
6
+ import { IconDownload } from '@salutejs/plasma-icons';
7
+ import { Editable, H5, editableTokens } from '@salutejs/plasma-b2c';
8
+
9
+ type EditableProps = ComponentProps<typeof Editable>;
10
+
11
+ describe('Basics', () => {
12
+ it('Common', () => {
13
+ expectTypeOf<EditableProps>().toHaveProperty('textComponent').toEqualTypeOf<FC<ComponentPropsWithRef<'div'>>>();
14
+ expectTypeOf<EditableProps>().toHaveProperty('value').toEqualTypeOf<string | undefined>();
15
+ expectTypeOf<EditableProps>().toHaveProperty('placeholder').toEqualTypeOf<string | undefined>();
16
+ expectTypeOf<EditableProps>().toHaveProperty('maxLength').toEqualTypeOf<number | undefined>();
17
+ expectTypeOf<EditableProps>().toHaveProperty('spellCheck').toEqualTypeOf<'true' | 'false' | undefined>();
18
+ expectTypeOf<EditableProps>().toHaveProperty('icon').toEqualTypeOf<ReactNode>();
19
+ expectTypeOf<EditableProps>().toHaveProperty('multiline').toEqualTypeOf<boolean | undefined>();
20
+ });
21
+
22
+ it('Variations', () => {
23
+ type View = NonNullable<EditableProps['view']>;
24
+ expectTypeOf<View>().toExtend<string>();
25
+ expectTypeOf<string>().not.toExtend<View>();
26
+
27
+ type Size = NonNullable<EditableProps['size']>;
28
+ expectTypeOf<Size>().toExtend<string>();
29
+ expectTypeOf<string>().not.toExtend<Size>();
30
+ });
31
+
32
+ it('HTMLInputElement', () => {
33
+ expectTypeOf<EditableProps>().toHaveProperty('id').toEqualTypeOf<string | undefined>();
34
+ expectTypeOf<EditableProps>().toHaveProperty('className').toEqualTypeOf<string | undefined>();
35
+ expectTypeOf<EditableProps>().toHaveProperty('style').toEqualTypeOf<CSSProperties | undefined>();
36
+ expectTypeOf<EditableProps>().toHaveProperty('aria-label').toEqualTypeOf<string | undefined>();
37
+ expectTypeOf<EditableProps>().toHaveProperty('role').toEqualTypeOf<AriaRole | undefined>();
38
+ expectTypeOf<EditableProps>()
39
+ .toHaveProperty('defaultValue')
40
+ .toEqualTypeOf<string | number | readonly string[] | undefined>();
41
+ expectTypeOf<EditableProps>()
42
+ .toHaveProperty('onChange')
43
+ .toEqualTypeOf<React.ChangeEventHandler<HTMLInputElement> | undefined>();
44
+ expectTypeOf<EditableProps>()
45
+ .toHaveProperty('onFocus')
46
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
47
+ expectTypeOf<EditableProps>()
48
+ .toHaveProperty('onBlur')
49
+ .toEqualTypeOf<React.FocusEventHandler<HTMLInputElement> | undefined>();
50
+ expectTypeOf<EditableProps>()
51
+ .toHaveProperty('onKeyDown')
52
+ .toEqualTypeOf<React.KeyboardEventHandler<HTMLInputElement> | undefined>();
53
+ expectTypeOf<EditableProps>()
54
+ .toHaveProperty('onPaste')
55
+ .toEqualTypeOf<React.ClipboardEventHandler<HTMLInputElement> | undefined>();
56
+ });
57
+ });
58
+
59
+ describe('Tokens', () => {
60
+ it('iconGap', () => {
61
+ expectTypeOf(editableTokens).toHaveProperty('iconGap').toEqualTypeOf<string>();
62
+ expectTypeOf(editableTokens).toHaveProperty('editButtonBackground').toEqualTypeOf<string>();
63
+ expectTypeOf(editableTokens).toHaveProperty('editButtonColor').toEqualTypeOf<string>();
64
+ expectTypeOf(editableTokens).toHaveProperty('editButtonColorHover').toEqualTypeOf<string>();
65
+ expectTypeOf(editableTokens).toHaveProperty('editButtonPaddingLeft').toEqualTypeOf<string>();
66
+ expectTypeOf(editableTokens).toHaveProperty('placeholderColor').toEqualTypeOf<string>();
67
+ expectTypeOf(editableTokens).toHaveProperty('caretColor').toEqualTypeOf<string>();
68
+ expectTypeOf(editableTokens).toHaveProperty('textBoxMarginRight').toEqualTypeOf<string>();
69
+ expectTypeOf(editableTokens).toHaveProperty('textBoxPaddingRight').toEqualTypeOf<string>();
70
+ });
71
+ });
72
+
73
+ describe('Examples', () => {
74
+ it('With icon and gap', () => {
75
+ () => {
76
+ const [value, setValue] = useState('Header 5');
77
+
78
+ return (
79
+ <Editable
80
+ textComponent={H5}
81
+ value={value}
82
+ multiline={false}
83
+ placeholder="Введите текст"
84
+ spellCheck="false"
85
+ maxLength={120}
86
+ icon={<IconDownload color="inherit" />}
87
+ style={{ maxWidth: '20rem', [editableTokens.iconGap]: '6px' }}
88
+ onChange={(event) => setValue(event.target.value)}
89
+ />
90
+ );
91
+ };
92
+ });
93
+
94
+ it('Multiline', () => {
95
+ () => {
96
+ return (
97
+ <Editable
98
+ textComponent={H5}
99
+ value="Очень длинное название документа, которое не помещается в одну строку"
100
+ multiline
101
+ style={{ maxWidth: '20rem', [editableTokens.iconGap]: '6px' }}
102
+ icon={<IconDownload color="inherit" />}
103
+ />
104
+ );
105
+ };
106
+ });
107
+ });
@@ -0,0 +1,334 @@
1
+ import * as React from 'react';
2
+ import type { CSSProperties, ReactNode, Ref } from 'react';
3
+ import { describe, it } from 'node:test';
4
+ import { expectTypeOf } from 'expect-type';
5
+ // Wheel подключён только к DFA и HomeDS: проверяем публичные экспорты этих пакетов.
6
+ import { Wheel } from '@salutejs/sdds-dfa';
7
+ import { Wheel as HomeDSWheel } from '@salutejs/plasma-homeds';
8
+ import type { WheelProps as HomeDSWheelProps } from '@salutejs/plasma-homeds';
9
+ import type {
10
+ WheelProps,
11
+ WheelAlignment,
12
+ WheelSeparator,
13
+ WheelSeparatorPreset,
14
+ WheelColumn,
15
+ WheelColumns,
16
+ WheelItem,
17
+ WheelItemState,
18
+ } from '@salutejs/sdds-dfa';
19
+
20
+ type Value = { hours: number; period: 'am' | 'pm' };
21
+ type Props = WheelProps<Value>;
22
+
23
+ describe('Basics', () => {
24
+ it('Common', () => {
25
+ expectTypeOf<Props>().toHaveProperty('columns').toEqualTypeOf<WheelColumns<Value>>();
26
+ expectTypeOf<Props>().toHaveProperty('width').toEqualTypeOf<CSSProperties['width']>();
27
+ expectTypeOf<Props>().toHaveProperty('value').toEqualTypeOf<Value>();
28
+ expectTypeOf<Props>().toHaveProperty('disabled').toEqualTypeOf<boolean | undefined>();
29
+ expectTypeOf<Props>().toHaveProperty('hasControls').toEqualTypeOf<boolean | undefined>();
30
+ expectTypeOf<Props>().toHaveProperty('visibleItemsCount').toEqualTypeOf<3 | 5 | undefined>();
31
+ expectTypeOf<Props>().toHaveProperty('animated').toEqualTypeOf<boolean | undefined>();
32
+ expectTypeOf<Props>().toHaveProperty('textAfterMode').toEqualTypeOf<'dynamic' | 'static' | undefined>();
33
+ expectTypeOf<HomeDSWheelProps<Value>['textAfterMode']>().toEqualTypeOf<'dynamic' | 'static' | undefined>();
34
+ expectTypeOf<Props>().toHaveProperty('align').toEqualTypeOf<WheelAlignment | undefined>();
35
+ expectTypeOf<WheelAlignment>().toEqualTypeOf<'left' | 'center' | 'right' | 'mixed'>();
36
+ expectTypeOf<Props>().toHaveProperty('separator').toEqualTypeOf<WheelSeparator>();
37
+ expectTypeOf<WheelSeparatorPreset>().toEqualTypeOf<'none' | 'dots' | 'divider'>();
38
+ expectTypeOf<WheelSeparator>().toExtend<ReactNode>();
39
+ expectTypeOf<ReactNode>().toExtend<WheelSeparator>();
40
+ expectTypeOf<Props>()
41
+ .toHaveProperty('onChange')
42
+ .toEqualTypeOf<((value: Value, column: keyof Value) => void) | undefined>();
43
+ expectTypeOf<WheelColumn>().toHaveProperty('label').toEqualTypeOf<string>();
44
+ expectTypeOf<WheelColumn>().toHaveProperty('width').toEqualTypeOf<CSSProperties['width']>();
45
+ expectTypeOf<WheelColumn>().toHaveProperty('description').toEqualTypeOf<ReactNode>();
46
+ expectTypeOf<WheelColumn>().toHaveProperty('textAfter').toEqualTypeOf<ReactNode>();
47
+ expectTypeOf<WheelColumn>().toHaveProperty('disabled').toEqualTypeOf<boolean | undefined>();
48
+ expectTypeOf<WheelColumn>().toHaveProperty('loop').toEqualTypeOf<boolean | undefined>();
49
+ expectTypeOf<WheelItemState>().toHaveProperty('selected').toEqualTypeOf<boolean>();
50
+ expectTypeOf<WheelItemState>().toHaveProperty('offset').toEqualTypeOf<-3 | -2 | -1 | 0 | 1 | 2 | 3>();
51
+ expectTypeOf<WheelColumn>()
52
+ .toHaveProperty('renderItem')
53
+ .toEqualTypeOf<((item: WheelItem, state: WheelItemState) => ReactNode) | undefined>();
54
+ });
55
+
56
+ it('Variations', () => {
57
+ type Size = NonNullable<Props['size']>;
58
+ type View = NonNullable<Props['view']>;
59
+ expectTypeOf<Size>().toExtend<string>();
60
+ expectTypeOf<string>().not.toExtend<Size>();
61
+ expectTypeOf<View>().toEqualTypeOf<'default'>();
62
+ expectTypeOf<Size>().toEqualTypeOf<
63
+ 'displayL' | 'displayM' | 'displayS' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'l' | 'm' | 's'
64
+ >();
65
+ expectTypeOf<HomeDSWheelProps<Value>['size']>().toEqualTypeOf<'h1' | 'h4' | undefined>();
66
+ expectTypeOf<HomeDSWheelProps<Value>['view']>().toEqualTypeOf<'default' | 'highlighted' | undefined>();
67
+ expectTypeOf<React.ComponentProps<typeof Wheel>['size']>().toEqualTypeOf<Props['size']>();
68
+ expectTypeOf<React.ComponentProps<typeof HomeDSWheel>['size']>().toEqualTypeOf<
69
+ HomeDSWheelProps<Value>['size']
70
+ >();
71
+ });
72
+
73
+ it('HTMLDivElement', () => {
74
+ expectTypeOf<Props>().toHaveProperty('id').toEqualTypeOf<string | undefined>();
75
+ expectTypeOf<Props>().toHaveProperty('className').toEqualTypeOf<string | undefined>();
76
+ expectTypeOf<Props>().toHaveProperty('style').toEqualTypeOf<CSSProperties | undefined>();
77
+ expectTypeOf<Props>().toHaveProperty('aria-label').toEqualTypeOf<string | undefined>();
78
+ expectTypeOf<Props>()
79
+ .toHaveProperty('onFocus')
80
+ .toEqualTypeOf<React.FocusEventHandler<HTMLDivElement> | undefined>();
81
+ expectTypeOf<React.ComponentProps<typeof Wheel>>()
82
+ .toHaveProperty('ref')
83
+ .toEqualTypeOf<Ref<HTMLDivElement> | undefined>();
84
+ });
85
+ });
86
+
87
+ describe('Unions', () => {
88
+ it('TextAfter mode', () => {
89
+ void (<Wheel value={{ hours: 12 }} columns={[]} textAfterMode="dynamic" />);
90
+ void (<Wheel value={{ hours: 12 }} columns={[]} textAfterMode="static" />);
91
+ void (<HomeDSWheel value={{ hours: 12 }} columns={[]} size="h4" textAfterMode="static" />);
92
+ // @ts-expect-error Допустимы только dynamic и static.
93
+ void (<Wheel value={{ hours: 12 }} columns={[]} textAfterMode="fixed" />);
94
+ });
95
+
96
+ it('Vertical configurations', () => {
97
+ void (<Wheel value={{ hours: 12 }} columns={[]} size="displayL" view="default" />);
98
+ void (<Wheel value={{ hours: 12 }} columns={[]} size="s" />);
99
+ void (<HomeDSWheel value={{ hours: 12 }} columns={[]} size="h1" view="default" />);
100
+ void (<HomeDSWheel value={{ hours: 12 }} columns={[]} size="h4" view="highlighted" />);
101
+ // @ts-expect-error В DFA доступен только вид default.
102
+ void (<Wheel value={{ hours: 12 }} columns={[]} view="highlighted" />);
103
+ // @ts-expect-error В HomeDS доступны только размеры h1 и h4.
104
+ void (<HomeDSWheel value={{ hours: 12 }} columns={[]} size="m" />);
105
+ // @ts-expect-error В HomeDS нет размера displayL.
106
+ void (<HomeDSWheel value={{ hours: 12 }} columns={[]} size="displayL" />);
107
+ // @ts-expect-error Неизвестный размер DFA.
108
+ void (<Wheel value={{ hours: 12 }} columns={[]} size="xs" />);
109
+ });
110
+ it('Container and column widths', () => {
111
+ expectTypeOf<Props['width']>(640);
112
+ expectTypeOf<Props['width']>('100%');
113
+ expectTypeOf<Props['width']>('calc(100% - 2rem)');
114
+ expectTypeOf<WheelColumn['width']>(0);
115
+ expectTypeOf<WheelColumn['width']>('12rem');
116
+ expectTypeOf<WheelColumn['width']>(undefined);
117
+ void (
118
+ <Wheel
119
+ value={{ hours: 12 }}
120
+ width="100%"
121
+ columns={[{ name: 'hours', label: 'Часы', items: [], width: 160 }]}
122
+ />
123
+ );
124
+ // @ts-expect-error Ширина принимает только число или CSS-строку.
125
+ expectTypeOf<Props['width']>(true);
126
+ // @ts-expect-error Ширина колонки не является объектом стилей.
127
+ expectTypeOf<WheelColumn['width']>({ width: 100 });
128
+ });
129
+ it('Visible item count', () => {
130
+ void (<Wheel value={{ hours: 12 }} columns={[]} visibleItemsCount={3} />);
131
+ void (<Wheel value={{ hours: 12 }} columns={[]} visibleItemsCount={5} />);
132
+ // @ts-expect-error Поддерживаются только 3 и 5 видимых значений.
133
+ void (<Wheel value={{ hours: 12 }} columns={[]} visibleItemsCount={4} />);
134
+ // @ts-expect-error Количество задаётся числом.
135
+ void (<Wheel value={{ hours: 12 }} columns={[]} visibleItemsCount="3" />);
136
+ });
137
+ it('Loop belongs to a column', () => {
138
+ void (<Wheel value={{ hours: 12 }} columns={[{ name: 'hours', label: 'Часы', items: [], loop: true }]} />);
139
+ // @ts-expect-error Зацикливание задаётся отдельно в columns.
140
+ void (<Wheel value={{ hours: 12 }} columns={[]} loop />);
141
+ });
142
+ it('Storybook-only column count', () => {
143
+ // @ts-expect-error columnsCount — только контроль Storybook, количество определяется массивом columns.
144
+ void (<Wheel value={{ hours: 12 }} columns={[]} columnsCount={1} />);
145
+ });
146
+ it('Preset and custom separators', () => {
147
+ expectTypeOf<WheelSeparator>('none');
148
+ expectTypeOf<WheelSeparator>('dots');
149
+ expectTypeOf<WheelSeparator>('divider');
150
+ expectTypeOf<WheelSeparator>('/');
151
+ expectTypeOf<WheelSeparator>(0);
152
+ expectTypeOf<WheelSeparator>(<span>/</span>);
153
+ expectTypeOf<WheelSeparator>(
154
+ <>
155
+ от <b>до</b>
156
+ </>,
157
+ );
158
+ expectTypeOf<WheelSeparator>([<span key="slash">/</span>, '·']);
159
+ expectTypeOf<WheelSeparator>(null);
160
+ expectTypeOf<WheelSeparator>(false);
161
+ expectTypeOf<WheelSeparator>(undefined);
162
+ // @ts-expect-error render-функция не является ReactNode.
163
+ expectTypeOf<WheelSeparator>(() => <span>/</span>);
164
+ // @ts-expect-error Произвольный объект не является ReactNode.
165
+ expectTypeOf<WheelSeparator>({ custom: '/' });
166
+ void (<Wheel value={{ hours: 12 }} columns={[]} separator={<span>/</span>} />);
167
+ });
168
+ it('Alignment', () => {
169
+ expectTypeOf<Props['align']>('left');
170
+ expectTypeOf<Props['align']>('center');
171
+ expectTypeOf<Props['align']>('right');
172
+ expectTypeOf<Props['align']>('mixed');
173
+ // @ts-expect-error Произвольное CSS-значение не входит в API выравнивания.
174
+ expectTypeOf<Props['align']>('justify');
175
+ });
176
+ it('Accessible item content', () => {
177
+ expectTypeOf<WheelItem<number>>({ value: 1, label: 'Один' });
178
+ expectTypeOf<WheelItem<number>>({ value: 1, label: <b>Один</b>, textValue: 'Один' });
179
+ // @ts-expect-error JSX требует текст для скринридера.
180
+ expectTypeOf<WheelItem<number>>({ value: 1, label: <b>Один</b> });
181
+ // @ts-expect-error Тип значения опции сохраняется.
182
+ expectTypeOf<WheelItem<number>>({ value: '1', label: 'Один' });
183
+ });
184
+ });
185
+
186
+ describe('Generics', () => {
187
+ it('Infers inline column callbacks from value', () => {
188
+ const value: Value = { hours: 12, period: 'am' };
189
+ void (
190
+ <Wheel
191
+ value={value}
192
+ columns={[
193
+ {
194
+ name: 'hours',
195
+ label: 'Часы',
196
+ items: [{ value: 12, label: 12 }],
197
+ renderItem: (item) => {
198
+ expectTypeOf(item.value).toEqualTypeOf<number>();
199
+ return item.label;
200
+ },
201
+ },
202
+ {
203
+ name: 'period',
204
+ label: 'Период',
205
+ items: [{ value: 'pm', label: 'PM' }],
206
+ renderItem: (item) => {
207
+ expectTypeOf(item.value).toEqualTypeOf<'am' | 'pm'>();
208
+ return item.label;
209
+ },
210
+ },
211
+ ]}
212
+ onChange={(nextValue, column) => {
213
+ expectTypeOf(nextValue).toEqualTypeOf<Value>();
214
+ expectTypeOf(column).toEqualTypeOf<keyof Value>();
215
+ }}
216
+ />
217
+ );
218
+ const invalidPeriod = [
219
+ { name: 'period' as const, label: 'Период', items: [{ value: 'night', label: 'Ночь' }] },
220
+ ];
221
+ // @ts-expect-error Опции не должны расширять допустимые значения period.
222
+ void (<Wheel value={value} columns={invalidPeriod} />);
223
+ // @ts-expect-error HomeDS также выводит допустимые значения только из value.
224
+ void (<HomeDSWheel value={value} columns={invalidPeriod} />);
225
+ // @ts-expect-error Опции не должны добавлять ключи в value HomeDS.
226
+ void (<HomeDSWheel value={{ hours: 12 }} columns={[{ name: 'minutes', label: 'Минуты', items: [] }]} />);
227
+ });
228
+
229
+ it('HomeDS preserves value and column inference', () => {
230
+ void (
231
+ <HomeDSWheel
232
+ value={{ hours: 12, period: 'am' } as Value}
233
+ columns={[
234
+ { name: 'hours', label: 'Часы', items: [{ value: 12, label: 12 }] },
235
+ {
236
+ name: 'period',
237
+ label: 'Период',
238
+ items: [{ value: 'am', label: 'AM' }],
239
+ renderItem: (item) => {
240
+ expectTypeOf(item.value).toEqualTypeOf<'am' | 'pm'>();
241
+ return item.label;
242
+ },
243
+ },
244
+ ]}
245
+ onChange={(value, column) => {
246
+ expectTypeOf(value).toEqualTypeOf<Value>();
247
+ expectTypeOf(column).toEqualTypeOf<keyof Value>();
248
+ }}
249
+ ref={React.createRef<HTMLDivElement>()}
250
+ />
251
+ );
252
+ const invalidHours = [{ name: 'hours' as const, label: 'Часы', items: [{ value: '12', label: '12' }] }];
253
+ // @ts-expect-error Опции HomeDS также должны соответствовать типу hours из value.
254
+ void (<HomeDSWheel value={{ hours: 12 }} columns={invalidHours} />);
255
+ });
256
+ it('Accepts interfaces without an index signature', () => {
257
+ interface Time {
258
+ hours: number;
259
+ minutes: number;
260
+ }
261
+ const time: Time = { hours: 12, minutes: 30 };
262
+ void (
263
+ <Wheel
264
+ value={time}
265
+ columns={[]}
266
+ onChange={(value) => {
267
+ expectTypeOf(value).toEqualTypeOf<Time>();
268
+ }}
269
+ />
270
+ );
271
+ });
272
+
273
+ it('Column names and values', () => {
274
+ const columns: WheelColumns<Value> = [
275
+ {
276
+ name: 'hours',
277
+ label: 'Часы',
278
+ items: [{ value: 12, label: 12 }],
279
+ renderItem: (item) => {
280
+ expectTypeOf(item.value).toEqualTypeOf<number>();
281
+ return item.label;
282
+ },
283
+ },
284
+ { name: 'period', label: 'Период', items: [{ value: 'am', label: 'AM' }] },
285
+ ];
286
+ void (
287
+ <Wheel
288
+ value={{ hours: 12, period: 'am' } as Value}
289
+ columns={columns}
290
+ onChange={(value, column) => {
291
+ expectTypeOf(value).toEqualTypeOf<Value>();
292
+ expectTypeOf(column).toEqualTypeOf<keyof Value>();
293
+ }}
294
+ />
295
+ );
296
+ // @ts-expect-error Неизвестное имя столбца.
297
+ void (<Wheel value={{ hours: 12 }} columns={[{ name: 'minutes', label: 'Минуты', items: [] }]} />);
298
+ const invalidHours = [{ name: 'hours' as const, label: 'Часы', items: [{ value: '12', label: '12' }] }];
299
+ // @ts-expect-error Опции не должны расширять тип hours, выведенный из value.
300
+ void (<Wheel value={{ hours: 12 }} columns={invalidHours} />);
301
+ const invalidPeriod = [
302
+ { name: 'period' as const, label: 'Период', items: [{ value: 'night', label: 'Ночь' }] },
303
+ ];
304
+ // @ts-expect-error Период ограничен литералами am и pm.
305
+ expectTypeOf<WheelColumns<Value>>(invalidPeriod);
306
+ });
307
+ });
308
+
309
+ describe('Examples', () => {
310
+ it('Controlled', () => {
311
+ () => {
312
+ const [value, setValue] = React.useState<Value>({ hours: 12, period: 'am' });
313
+ return (
314
+ <Wheel
315
+ textAfterMode="static"
316
+ value={value}
317
+ onChange={setValue}
318
+ columns={[
319
+ { name: 'hours', label: 'Часы', textAfter: 'ч', items: [{ value: 12, label: 12 }] },
320
+ {
321
+ name: 'period',
322
+ label: 'Период',
323
+ items: [
324
+ { value: 'am', label: 'AM' },
325
+ { value: 'pm', label: 'PM' },
326
+ ],
327
+ },
328
+ ]}
329
+ ref={React.createRef<HTMLDivElement>()}
330
+ />
331
+ );
332
+ };
333
+ });
334
+ });