@sberbusiness/triplex-next-mcp-server 1.40.0-beta → 1.42.0-beta
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/bundle/components/AlertProcess.md +460 -0
- package/bundle/components/Amount.md +145 -169
- package/bundle/components/Badge.md +155 -219
- package/bundle/components/Body.md +108 -116
- package/bundle/components/Calendar.md +183 -368
- package/bundle/components/CarouselExtended.md +355 -194
- package/bundle/components/Chip.md +310 -314
- package/bundle/components/ComposedKeyDownListener.md +323 -0
- package/bundle/components/FooterPage.md +2 -1
- package/bundle/components/FormField.md +468 -232
- package/bundle/components/HeaderPage.md +2 -1
- package/bundle/components/IconWrapper.md +142 -89
- package/bundle/components/Island.md +303 -123
- package/bundle/components/KeyDownListener.md +152 -100
- package/bundle/components/LoaderMiddle.md +265 -0
- package/bundle/components/LoaderScreen.md +178 -81
- package/bundle/components/LoaderSmall.md +230 -0
- package/bundle/components/MaskedField.md +481 -0
- package/bundle/components/MediaWidth.md +246 -108
- package/bundle/components/MobileView.md +192 -68
- package/bundle/components/ModalWindow.md +3 -1
- package/bundle/components/Page.md +3 -1
- package/bundle/components/Pagination.md +20 -0
- package/bundle/components/SelectExtendedField.md +691 -236
- package/bundle/components/ThemeProvider.md +305 -117
- package/bundle/components/Tooltip.md +421 -181
- package/bundle/guides/commits.md +54 -9
- package/bundle/guides/context.md +26 -0
- package/bundle/guides/stories.md +2 -0
- package/bundle/guides/template.md +1 -1
- package/bundle/manifest.json +63 -18
- package/bundle/release-notes/1.41.0.mdx +76 -0
- package/bundle/release-notes/1.41.1.mdx +18 -0
- package/bundle/release-notes/1.42.0.mdx +88 -0
- package/bundle/release-notes/1.43.0.mdx +7 -0
- package/package.json +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Chip
|
|
3
3
|
category: Chips
|
|
4
|
-
|
|
5
|
-
figma-file: ""
|
|
6
|
-
related: [ChipGroup, Badge, IconWrapper, SelectField, MultiselectField]
|
|
4
|
+
related: [ChipGroup, ChipIcon, ChipOptions, ChipSelect, ChipMultiselect, ChipSort, ChipSuggest, ChipDatePicker, ChipClearButton, ChipDropdownArrow, Badge, IconWrapper]
|
|
7
5
|
tokens:
|
|
8
|
-
- --triplex-next-Chip-
|
|
9
|
-
- --triplex-next-Chip-
|
|
6
|
+
- --triplex-next-Chip-Background_Type1_Default
|
|
7
|
+
- --triplex-next-Chip-Background_Type1_Hover
|
|
8
|
+
- --triplex-next-Chip-Background_Type2_Default
|
|
9
|
+
- --triplex-next-Chip-Background_Type2_Hover
|
|
10
10
|
- --triplex-next-Chip-Background_Active
|
|
11
11
|
- --triplex-next-Chip-Background_Disabled
|
|
12
12
|
- --triplex-next-Chip-Background_Selected_Default
|
|
@@ -22,7 +22,7 @@ tokens:
|
|
|
22
22
|
- --triplex-next-Chip-Color_Selected_Active
|
|
23
23
|
- --triplex-next-Chip-Color_Selected_Disabled
|
|
24
24
|
- --triplex-next-Chip-Shadow_Focus
|
|
25
|
-
stories: stories/Chips/Chip.stories.tsx
|
|
25
|
+
stories: stories/Chips/Chip.stories.tsx
|
|
26
26
|
version: "1.0"
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -30,375 +30,371 @@ version: "1.0"
|
|
|
30
30
|
|
|
31
31
|
## Назначение
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Базовый `Chip` — `<span role="button">`, реагирует на клавиатуру (Space
|
|
39
|
-
блокирует скролл), поддерживает selected/disabled, prefix/postfix иконки
|
|
40
|
-
и "точку уведомления" через `Badge.Dot`.
|
|
41
|
-
|
|
42
|
-
Варианты:
|
|
43
|
-
- **`ChipIcon`** — чип только с иконкой (иконка в prefix, пустой span в postfix для симметрии).
|
|
44
|
-
- **`ChipMultiselect`** — target-чип для `MultiselectField` с ClearButton / DropdownArrow.
|
|
45
|
-
- **`ChipSelect`** (в subfolder) — target-чип для `SelectExtendedField`.
|
|
46
|
-
- **`ChipOptions`** — чип с иконкой опций + clear button.
|
|
47
|
-
- **`ChipSort`** — чип-иконка для сортировки + selectExtendedField dropdown.
|
|
48
|
-
- **`ChipSuggest`** (в subfolder) — чип-input с саджестами.
|
|
49
|
-
- **`ChipDatePicker`** (в subfolder) — чип с календарным попапом.
|
|
50
|
-
- **`ChipClearButton`** — кнопка "×" для очистки выбранного.
|
|
51
|
-
- **`ChipDropdownArrow`** — стрелка dropdown (ротируется при opened).
|
|
52
|
-
|
|
53
|
-
Используй когда: нужен фильтр/сортировка/мультивыбор в линейном наборе.
|
|
54
|
-
Рекомендуется всегда внутри `ChipGroup`.
|
|
55
|
-
Не используй когда: нужна "метка-значок" без интерактива (бери `Badge`/`Tag`);
|
|
56
|
-
нужен input поля формы (бери `TextField`/`SelectField`).
|
|
33
|
+
Компактный интерактивный элемент-«чипс»: выполняет действие по нажатию и отображает
|
|
34
|
+
выбранное (`selected`) состояние. Одновременно служит базой для всего семейства Chip*
|
|
35
|
+
— поверх него построены `ChipIcon`, `ChipOptions`, а также target-элементы
|
|
36
|
+
`ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`.
|
|
57
37
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
38
|
+
Используй когда: нужен переключаемый фильтр, тег или компактная кнопка-действие
|
|
39
|
+
в ряду однотипных элементов.
|
|
40
|
+
Не используй когда: нужна обычная кнопка формы (возьми `Button` — у него нативная
|
|
41
|
+
клавиатурная активация и семантика `<button>`) или нужен выбор значения из списка
|
|
42
|
+
(возьми `ChipSelect` / `ChipMultiselect`).
|
|
61
43
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
├── Chip.tsx # Базовый, React.forwardRef
|
|
65
|
-
├── ChipClearButton.tsx # ButtonIcon-обёртка с крестиком по size
|
|
66
|
-
├── ChipDropdownArrow.tsx # Стрелка, rotate при rotated
|
|
67
|
-
├── ChipIcon.tsx # Chip с prefix=children и postfix=<span />
|
|
68
|
-
├── ChipMultiselect.tsx # MultiselectField target
|
|
69
|
-
├── ChipOptions.tsx # Chip + OptionsIcon + ChipClearButton
|
|
70
|
-
├── ChipSort.tsx # SelectExtendedField target с SortIcon
|
|
71
|
-
├── ChipDatePicker/
|
|
72
|
-
│ ├── ChipDatePicker.tsx
|
|
73
|
-
│ ├── ChipDatePickerTarget.tsx
|
|
74
|
-
│ └── index.ts
|
|
75
|
-
├── ChipSelect/
|
|
76
|
-
│ ├── ChipSelect.tsx
|
|
77
|
-
│ ├── ChipSelectTarget.tsx
|
|
78
|
-
│ └── index.ts
|
|
79
|
-
├── ChipSuggest/
|
|
80
|
-
│ ├── ChipSuggest.tsx
|
|
81
|
-
│ ├── ChipSuggestTarget.tsx
|
|
82
|
-
│ ├── ChipSuggestDropdown.tsx
|
|
83
|
-
│ ├── desktop/
|
|
84
|
-
│ ├── types.ts
|
|
85
|
-
│ └── index.ts
|
|
86
|
-
├── index.ts # Barrel всех вариантов
|
|
87
|
-
├── styles/
|
|
88
|
-
│ ├── Chip.module.less # Root чипа
|
|
89
|
-
│ ├── ChipClearButton.module.less
|
|
90
|
-
│ ├── ChipDropdownArrow.module.less
|
|
91
|
-
│ ├── ChipOptions.module.less
|
|
92
|
-
│ └── ChipSuggest.module.less
|
|
93
|
-
└── __tests__/
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Особенности:
|
|
97
|
-
- **Нет общего enum/типов файла** — каждый чип декларирует свой интерфейс
|
|
98
|
-
через `extends IChipProps` / `Omit<IChipProps, ...>` / подобное.
|
|
99
|
-
- **`ChipGroup` — отдельный компонент** (в `src/components/ChipGroup`). Большинство
|
|
100
|
-
чипов получают `styles.chipGroupItem` класс, чтобы быть валидным flex-item
|
|
101
|
-
внутри ChipGroup.
|
|
102
|
-
- **`ChipIcon` делает `postfix={<span />}`** — трюк для симметричных
|
|
103
|
-
паддингов, так как Chip меняет padding при наличии postfix.
|
|
44
|
+
Рекомендуется всегда располагать `Chip` внутри `ChipGroup` — он задаёт отступы,
|
|
45
|
+
перенос строк и однострочный режим со скроллом.
|
|
104
46
|
|
|
105
47
|
---
|
|
106
48
|
|
|
107
49
|
## Варианты и props
|
|
108
50
|
|
|
109
|
-
###
|
|
110
|
-
|
|
111
|
-
| Prop | Тип | Default | Описание |
|
|
112
|
-
|---|---|---|---|
|
|
113
|
-
| `size` | `EComponentSize` | `MD` | SM=28px, MD=40px, LG=56px. |
|
|
114
|
-
| `disabled` | `boolean` | — | Визуальный disabled + `tabIndex=-1`. |
|
|
115
|
-
| `selected` | `boolean` | — | "Активен" (галочка-выбор). |
|
|
116
|
-
| `prefix` | `ReactNode` | — | Контент слева (обычно иконка). Оборачивается в `IconWrapper`. |
|
|
117
|
-
| `postfix` | `ReactNode` | — | Контент справа. |
|
|
118
|
-
| `showNotificationIcon` | `boolean` | — | Отрисовать `Badge.Dot` в правом-верхнем углу. |
|
|
119
|
-
| `children` | `ReactNode` | — | Основной контент. |
|
|
120
|
-
| `...HTMLSpanAttributes` | — | — | Все кроме `prefix` (HTML-атрибут переопределён). |
|
|
51
|
+
### Типы (`EChipType`)
|
|
121
52
|
|
|
122
|
-
|
|
53
|
+
| Значение | Описание |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `TYPE_1` | Основной фон (по умолчанию) |
|
|
56
|
+
| `TYPE_2` | Альтернативный фон — для случаев, когда `TYPE_1` сливается с подложкой |
|
|
123
57
|
|
|
124
|
-
|
|
125
|
-
|
|
58
|
+
Тип влияет **только** на фон в обычном состоянии и на hover. В состояниях `selected`,
|
|
59
|
+
`active` и `disabled` фон задаётся общими токенами и от типа не зависит.
|
|
126
60
|
|
|
127
|
-
### `
|
|
61
|
+
### Размеры (`EComponentSize`)
|
|
128
62
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
63
|
+
| Значение | Высота | Padding | Border-radius | Font-size |
|
|
64
|
+
|---|---|---|---|---|
|
|
65
|
+
| `SM` | 28px | 0 12px | 6px | 12px |
|
|
66
|
+
| `MD` | 40px | 0 12px | 8px | 14px |
|
|
67
|
+
| `LG` | 56px | 0 16px | 10px | 16px |
|
|
133
68
|
|
|
134
|
-
|
|
69
|
+
`size` пробрасывается в `Badge.Dot` при `showNotificationIcon` — размер значка
|
|
70
|
+
уведомлений всегда согласован с размером чипса.
|
|
135
71
|
|
|
136
|
-
|
|
137
|
-
|---|---|---|
|
|
138
|
-
| `rotated` | `boolean` | `true` → rotate(180deg). |
|
|
139
|
-
| `size` | `EComponentSize` | Выбирает иконку Caret 16/20/24. |
|
|
72
|
+
### Опциональные props
|
|
140
73
|
|
|
141
|
-
|
|
74
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| `type` | `EChipType` | `TYPE_1` | Визуальный тип |
|
|
77
|
+
| `size` | `EComponentSize` | `MD` | Размер |
|
|
78
|
+
| `selected` | `boolean` | `false` | Выбранное состояние (визуальное; компонент неуправляемый — состояние держит потребитель) |
|
|
79
|
+
| `disabled` | `boolean` | `false` | Визуально блокирует и убирает из tab-порядка (см. ограничения ниже) |
|
|
80
|
+
| `prefix` | `React.ReactNode` | — | Контент перед основным, обычно иконка слева; оборачивается в `IconWrapper` |
|
|
81
|
+
| `postfix` | `React.ReactNode` | — | Контент после основного, обычно иконка справа; оборачивается в `IconWrapper` |
|
|
82
|
+
| `showNotificationIcon` | `boolean` | `false` | Значок новых уведомлений (`Badge.Dot`) в правом верхнем углу |
|
|
83
|
+
| `children` | `React.ReactNode` | — | Основной контент; обрезается по `max-width` с многоточием |
|
|
84
|
+
| `...HTMLSpanAttributes` | — | — | Все атрибуты `<span>` кроме `prefix`, включая `onClick`, `aria-expanded`, `data-*` |
|
|
85
|
+
|
|
86
|
+
### Ограничения
|
|
87
|
+
|
|
88
|
+
- **`prefix` / `postfix`: отсутствие значения передавай как `undefined`, не как `null` или `false`.**
|
|
89
|
+
Классы `.withPrefix` / `.withPostfix` (обнуляют боковой `padding`) выставляются по
|
|
90
|
+
`!== undefined`, а сама иконка рендерится по truthiness. Поэтому
|
|
91
|
+
`postfix={condition && <Icon/>}` при `condition === false` даст чипс с нулевым
|
|
92
|
+
правым паддингом и без иконки. Пиши `postfix={condition ? <Icon/> : undefined}`.
|
|
93
|
+
Внутри библиотеки все обёртки передают либо JSX, либо `<span />`-заглушку —
|
|
94
|
+
проблема проявляется только у внешнего потребителя.
|
|
95
|
+
- **`disabled` — не нативный атрибут.** Корневой элемент — `<span>`, поэтому блокировка
|
|
96
|
+
чисто визуальная: `pointer-events: none` в LESS плюс `tabIndex={-1}`. Реальные
|
|
97
|
+
пользовательские клики и клавиатура до обработчиков не доходят, но программно
|
|
98
|
+
диспатченные события (`fireEvent.click` в тестах, синтетический `dispatchEvent`)
|
|
99
|
+
вызовут `onClick` / `onKeyDown`. Не полагайся на `disabled` как на защиту в логике —
|
|
100
|
+
проверяй флаг в самом обработчике, если это критично.
|
|
101
|
+
- **`selected` и `disabled` совместимы** — есть отдельные токены
|
|
102
|
+
`--triplex-next-Chip-*_Selected_Disabled`.
|
|
103
|
+
- Ширина ограничена: `min-width: 40px`, `max-width: 264px`. Длинный `children`
|
|
104
|
+
обрезается многоточием, а не переносится.
|
|
142
105
|
|
|
143
|
-
|
|
144
|
-
`ClearButton` (если selected) или `DropdownArrow` (если нет).
|
|
106
|
+
---
|
|
145
107
|
|
|
146
|
-
|
|
147
|
-
|---|---|---|
|
|
148
|
-
| `clearSelected` | `() => void` | Коллбек на крестик. |
|
|
149
|
-
| `disabled` | `boolean` | Форвардится в Chip и MultiselectField. |
|
|
150
|
-
| `selected` | `boolean` | Включает "выбранное" состояние + ClearButton. |
|
|
151
|
-
| `label` | `ReactNode` | Текст при пустом выборе. |
|
|
152
|
-
| `displayedValue` | `ReactNode` | Текст при выборе (fallback на label). |
|
|
108
|
+
## Дизайн-токены
|
|
153
109
|
|
|
154
|
-
|
|
110
|
+
Паттерн: `--triplex-next-Chip-{Property}_{Modifier}_{State}`
|
|
155
111
|
|
|
156
|
-
|
|
157
|
-
|
|
112
|
+
```text
|
|
113
|
+
--triplex-next-Chip-Background_Type1_Default
|
|
114
|
+
--triplex-next-Chip-Background_Type1_Hover
|
|
115
|
+
--triplex-next-Chip-Background_Type2_Default
|
|
116
|
+
--triplex-next-Chip-Background_Type2_Hover
|
|
117
|
+
--triplex-next-Chip-Background_Active
|
|
118
|
+
--triplex-next-Chip-Background_Disabled
|
|
119
|
+
--triplex-next-Chip-Background_Selected_Default
|
|
120
|
+
--triplex-next-Chip-Background_Selected_Hover
|
|
121
|
+
--triplex-next-Chip-Background_Selected_Active
|
|
122
|
+
--triplex-next-Chip-Background_Selected_Disabled
|
|
123
|
+
--triplex-next-Chip-Color_Default
|
|
124
|
+
--triplex-next-Chip-Color_Hover
|
|
125
|
+
--triplex-next-Chip-Color_Active
|
|
126
|
+
--triplex-next-Chip-Color_Disabled
|
|
127
|
+
--triplex-next-Chip-Color_Selected_Default
|
|
128
|
+
--triplex-next-Chip-Color_Selected_Hover
|
|
129
|
+
--triplex-next-Chip-Color_Selected_Active
|
|
130
|
+
--triplex-next-Chip-Color_Selected_Disabled
|
|
131
|
+
--triplex-next-Chip-Shadow_Focus
|
|
132
|
+
```
|
|
158
133
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
| `clearSelected` | `() => void` | Коллбек крестика. |
|
|
134
|
+
Токены `_Type1_` / `_Type2_` существуют только для `Background` в состояниях
|
|
135
|
+
Default и Hover. Цвет текста (`Color_*`) от типа не зависит.
|
|
162
136
|
|
|
163
|
-
|
|
137
|
+
---
|
|
164
138
|
|
|
165
|
-
|
|
166
|
-
простого `value`: если текущее значение равно дефолту, чип не подсвечивается
|
|
167
|
-
как изменённый. **Это ключевое поведение Sort-чипа** — дефолтная сортировка
|
|
168
|
-
не должна выглядеть как "активный фильтр".
|
|
139
|
+
## Инварианты
|
|
169
140
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
141
|
+
- **`forwardRef`** — обязателен, `ref` указывает на корневой `<span>`. Не убирать.
|
|
142
|
+
- **Корневой элемент — `<span role="button">`, а не нативный `<button>`.** Это осознанное
|
|
143
|
+
решение: в `postfix` живут вложенные интерактивные элементы (`ChipClearButton`,
|
|
144
|
+
`ChipDropdownArrow`), а `<button>` внутри `<button>` — невалидный HTML. Смена корневого
|
|
145
|
+
тега сломает всё семейство Chip*.
|
|
146
|
+
- **`role` и `tabIndex` выставляются до `{...restProps}`**, то есть потребитель может
|
|
147
|
+
их переопределить через props. Это часть текущего контракта — менять порядок осознанно.
|
|
148
|
+
(`ref` при `forwardRef` в `restProps` не попадает, на него порядок не влияет.)
|
|
149
|
+
- **Класс `.chipGroupItem`** выставляется всегда, наравне с `.chip`. Объявлен
|
|
150
|
+
в `styles/Chip.module.less` и служит общим маркером «элемент верхнего уровня
|
|
151
|
+
в ряду чипсов»: его проставляют себе все члены семейства — `Chip`, `ChipSort`,
|
|
152
|
+
`ChipMultiselect`, `ChipSelect`, `ChipDatePicker`, `ChipSuggest`. Причём
|
|
153
|
+
`.chipGroupItem` не всегда совпадает с `.chip`: в `ChipSelect` / `ChipMultiselect`
|
|
154
|
+
`.chip` лежит внутри элемента с `.chipGroupItem`. Раскладка `ChipGroup` на этот
|
|
155
|
+
класс не опирается — в её стилях он не встречается. Не удалять и не переименовывать.
|
|
156
|
+
- **`EChipType` значения** (`type_1`, `type_2`) — публичное API, не переименовывать.
|
|
157
|
+
- **`EComponentSize`** — общий enum из `src/enums/EComponentSize`, локального аналога не заводить.
|
|
158
|
+
- **Селектор `[aria-expanded="true"]`** в LESS даёт active-стиль. На него опираются
|
|
159
|
+
chip-триггеры дропдаунов — не убирать вместе с рефакторингом стилей.
|
|
160
|
+
- **Barrel-экспорты** `src/components/Chip/index.ts` — `Chip`, `IChipProps`, `EChipType`
|
|
161
|
+
и всё семейство. Сохранять.
|
|
162
|
+
- **Story ids** `chips-chip--*` завязаны на baseline-скриншоты в `__screenshots__/` —
|
|
163
|
+
переименование story требует синхронного обновления baseline.
|
|
173
164
|
|
|
174
165
|
---
|
|
175
166
|
|
|
176
|
-
##
|
|
167
|
+
## Accessibility
|
|
177
168
|
|
|
178
|
-
|
|
169
|
+
- Роль `role="button"` на `<span>`; в tab-порядке через `tabIndex={0}`, при `disabled` —
|
|
170
|
+
`tabIndex={-1}`.
|
|
171
|
+
- **Клавиатурная активация не реализована внутри компонента.** `Chip` перехватывает
|
|
172
|
+
`keydown` только чтобы вызвать `preventDefault()` на пробеле (код `Space`) — это гасит
|
|
173
|
+
прокрутку страницы, поскольку чипс фокусируем. Синтетический `click` по Enter/Space
|
|
174
|
+
компонент не генерирует (у `<span role="button">` нет нативного поведения кнопки),
|
|
175
|
+
внешний `onKeyDown` вызывается всегда и после `preventDefault`. Если чипс должен
|
|
176
|
+
срабатывать с клавиатуры, потребитель обрабатывает Enter/Space сам через `onKeyDown`.
|
|
177
|
+
Обёртки семейства (`ChipSelect`, `ChipSuggest`, `ChipDatePicker`) делают это на своём уровне.
|
|
178
|
+
- Фокус-стиль через `:focus-visible` (`--triplex-next-Chip-Shadow_Focus`) — виден только
|
|
179
|
+
при клавиатурной навигации. Нативная обводка на корневом элементе намеренно отключена
|
|
180
|
+
(`outline: none` в LESS) в пользу `box-shadow` — убирать правило нельзя, иначе фокус
|
|
181
|
+
отрисуется дважды.
|
|
182
|
+
- **Выбранное состояние не объявляется ассистивным технологиям автоматически.** `selected`
|
|
183
|
+
меняет только визуал. Для роли `button` передавай `aria-pressed` через `...rest`;
|
|
184
|
+
в списке-подобных сценариях используй `ChipSelect` / `ChipMultiselect`.
|
|
185
|
+
- **`aria-expanded`** потребитель передаёт сам, когда чипс работает триггером дропдауна —
|
|
186
|
+
атрибут дополнительно активирует active-стиль через CSS.
|
|
187
|
+
- Чипс без текстового `children` (только `prefix`/`postfix`, как в `ChipIcon`) требует
|
|
188
|
+
`aria-label` от потребителя — библиотека мультиязычная, текст не хардкодится.
|
|
179
189
|
|
|
180
|
-
|
|
181
|
-
if (isKey(event.code, "SPACE")) {
|
|
182
|
-
event.preventDefault();
|
|
183
|
-
}
|
|
184
|
-
onKeyDown?.(event);
|
|
185
|
-
```
|
|
190
|
+
---
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
потребителя. Потребители обычно через свой `onKeyDown` обрабатывают ENTER/SPACE
|
|
189
|
-
для активации.
|
|
192
|
+
## Связанные компоненты
|
|
190
193
|
|
|
191
|
-
|
|
194
|
+
- `ChipGroup` (`src/components/ChipGroup`) — контейнер-раскладка для чипсов;
|
|
195
|
+
рекомендуемая обёртка, задаёт `size` и режим `oneLine`
|
|
196
|
+
- `ChipIcon` — тонкая обёртка: `children` уходит в `prefix`, `postfix` заполняется
|
|
197
|
+
пустым `<span />` ради симметричных отступов
|
|
198
|
+
- `ChipOptions` — чипс с иконкой опций и кнопкой сброса выбора (`clearSelected`)
|
|
199
|
+
- `ChipClearButton` — кнопка сброса для `postfix`; `forwardRef` на `HTMLButtonElement`,
|
|
200
|
+
props расширяют `Omit<IButtonIconProps, "children">` (то есть доступен весь API
|
|
201
|
+
`ButtonIcon`, включая `onClick` и `aria-label`) плюс собственный `size?: EComponentSize`,
|
|
202
|
+
выбирающий размер иконки-крестика
|
|
203
|
+
- `ChipDropdownArrow` — стрелка дропдауна для `postfix`; объявлена как `React.FC`
|
|
204
|
+
(не `forwardRef`), собственных обработчиков не имеет. Props: **обязательный**
|
|
205
|
+
`rotated: boolean` (даёт класс `.rotated`) и `size?: EComponentSize`
|
|
206
|
+
- `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`, `ChipSort` —
|
|
207
|
+
составные компоненты, использующие `Chip` как target-элемент дропдауна
|
|
208
|
+
- `Badge` (`Badge.Dot`) — значок уведомлений при `showNotificationIcon`
|
|
209
|
+
- `IconWrapper` — обёртка `prefix` / `postfix`, добавляет классы `hoverable` / `disabled`,
|
|
210
|
+
управляющие цветом иконки при взаимодействии
|
|
192
211
|
|
|
193
|
-
|
|
194
|
-
`role` через `{...rest}` — возможно (например, ChipMultiselect передаёт
|
|
195
|
-
`role="listbox"`).
|
|
212
|
+
---
|
|
196
213
|
|
|
197
|
-
|
|
214
|
+
## Stories
|
|
198
215
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
```
|
|
216
|
+
Основные истории: `stories/Chips/Chip.stories.tsx`
|
|
217
|
+
Файлы примеров: `stories/Chips/examples/Chip/`
|
|
202
218
|
|
|
203
|
-
|
|
204
|
-
|
|
219
|
+
| Story | Example file | Что демонстрирует |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль `type`, `size`, `disabled`, `showNotificationIcon` |
|
|
222
|
+
| `Default` | `DefaultExample.tsx` | Базовое использование: переключение `selected` по клику |
|
|
223
|
+
| `Types` | `TypesExample.tsx` | `TYPE_1` и `TYPE_2` |
|
|
224
|
+
| `Sizes` | `SizesExample.tsx` | Размеры SM / MD / LG |
|
|
225
|
+
| `States` | `StatesExample.tsx` | Состояния `selected` и `disabled` |
|
|
226
|
+
| `WithPrefixAndPostfix` | `WithPrefixAndPostfixExample.tsx` | Иконки слева и справа от контента |
|
|
227
|
+
| `WithNotificationIcon` | `WithNotificationIconExample.tsx` | Значок уведомлений во всех трёх размерах |
|
|
228
|
+
| `VisualTests` | `DefaultExample.tsx` | Скриншот-регрессия, `play` доводит чипс до состояния фокуса |
|
|
205
229
|
|
|
206
|
-
|
|
230
|
+
---
|
|
207
231
|
|
|
208
|
-
|
|
209
|
-
<IconWrapper className={styles.prefix} disabled={Boolean(disabled)}>
|
|
210
|
-
{prefix}
|
|
211
|
-
</IconWrapper>
|
|
212
|
-
```
|
|
232
|
+
## История изменений
|
|
213
233
|
|
|
214
|
-
|
|
215
|
-
|
|
234
|
+
| Дата | Изменение |
|
|
235
|
+
|---|---|
|
|
236
|
+
| 2026-07-29 | Создан документ (TRI-26, AI-Ready Phase 1) |
|
|
237
|
+
| 2026-07-29 | AI-рефакторинг (TRI-26): codestyle-чистка `Chip.tsx` (константы-маппинги в UPPER_SNAKE_CASE, эквивалентные упрощения в `clsx`, JSDoc на `children` и `handleKeyDown`), unit-тесты расширены с 5 до 22 кейсов; публичный API, DOM и визуал не изменены |
|
|
238
|
+
| 2026-07-29 | Правки по ревью PR #487: уточнена атрибуция класса `.chipGroupItem` (его проставляет семейство Chip*, а не раскладка `ChipGroup`) и описание API `ChipClearButton` / `ChipDropdownArrow` |
|
|
216
239
|
|
|
217
|
-
|
|
240
|
+
---
|
|
218
241
|
|
|
219
|
-
|
|
220
|
-
<Chip {...rest} prefix={children} postfix={<span />} ref={ref} />
|
|
221
|
-
```
|
|
242
|
+
## Примеры использования
|
|
222
243
|
|
|
223
|
-
|
|
224
|
-
был бы по обычному правилу, и чип выглядел бы асимметрично. **Hack, но
|
|
225
|
-
намеренный.**
|
|
244
|
+
### Default
|
|
226
245
|
|
|
227
|
-
|
|
246
|
+
`stories/Chips/examples/Chip/DefaultExample.tsx`
|
|
228
247
|
|
|
229
248
|
```tsx
|
|
230
|
-
|
|
249
|
+
import React, { useState } from "react";
|
|
250
|
+
import { Chip } from "@sberbusiness/triplex-next";
|
|
251
|
+
|
|
252
|
+
export const DefaultExample = () => {
|
|
253
|
+
const [selected, setSelected] = useState(false);
|
|
254
|
+
return (
|
|
255
|
+
<Chip selected={selected} onClick={() => setSelected((s) => !s)}>
|
|
256
|
+
Value
|
|
257
|
+
</Chip>
|
|
258
|
+
);
|
|
259
|
+
};
|
|
231
260
|
```
|
|
232
261
|
|
|
233
|
-
|
|
234
|
-
chip-таргет действует как listbox button. Screen reader-ы разного качества
|
|
235
|
-
по-разному интерпретируют. Если нужна более строгая семантика — потребителю
|
|
236
|
-
надо делать свой renderTarget.
|
|
262
|
+
### Types
|
|
237
263
|
|
|
238
|
-
|
|
264
|
+
`stories/Chips/examples/Chip/TypesExample.tsx`
|
|
239
265
|
|
|
240
|
-
```
|
|
241
|
-
|
|
266
|
+
```tsx
|
|
267
|
+
import React, { useState } from "react";
|
|
268
|
+
import { Chip, EChipType } from "@sberbusiness/triplex-next";
|
|
269
|
+
|
|
270
|
+
type TypeItemProps = {
|
|
271
|
+
type: EChipType;
|
|
272
|
+
label: string;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const TypeItem = ({ type, label }: TypeItemProps) => {
|
|
276
|
+
const [selected, setSelected] = useState(false);
|
|
277
|
+
|
|
278
|
+
const handleClick = () => setSelected((prevSelected) => !prevSelected);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div>
|
|
282
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{label}</div>
|
|
283
|
+
<Chip type={type} selected={selected} onClick={handleClick}>
|
|
284
|
+
Value
|
|
285
|
+
</Chip>
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const typeOptions = Object.values(EChipType).map((type) => ({
|
|
291
|
+
value: type,
|
|
292
|
+
label: type.toUpperCase(),
|
|
293
|
+
}));
|
|
294
|
+
|
|
295
|
+
export const TypesExample = () => (
|
|
296
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
297
|
+
{typeOptions.map(({ value, label }) => (
|
|
298
|
+
<TypeItem key={value} type={value} label={label} />
|
|
299
|
+
))}
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
242
302
|
```
|
|
243
303
|
|
|
244
|
-
|
|
245
|
-
Deep equal нужен, потому что options — объекты (value, label).
|
|
304
|
+
### Sizes
|
|
246
305
|
|
|
247
|
-
|
|
306
|
+
`stories/Chips/examples/Chip/SizesExample.tsx`
|
|
248
307
|
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
308
|
+
```tsx
|
|
309
|
+
import React, { useState } from "react";
|
|
310
|
+
import { Chip, EComponentSize } from "@sberbusiness/triplex-next";
|
|
311
|
+
|
|
312
|
+
type SizeItemProps = {
|
|
313
|
+
size: EComponentSize;
|
|
314
|
+
label: string;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const SizeItem = ({ size, label }: SizeItemProps) => {
|
|
318
|
+
const [selected, setSelected] = useState(false);
|
|
319
|
+
|
|
320
|
+
const handleClick = () => setSelected((prevSelected) => !prevSelected);
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<div>
|
|
324
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{label}</div>
|
|
325
|
+
<Chip size={size} selected={selected} onClick={handleClick}>
|
|
326
|
+
Value
|
|
327
|
+
</Chip>
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const sizeOptions = Object.values(EComponentSize).map((size) => ({
|
|
333
|
+
value: size,
|
|
334
|
+
label: size.toUpperCase(),
|
|
335
|
+
}));
|
|
336
|
+
|
|
337
|
+
export const SizesExample = () => (
|
|
338
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
339
|
+
{sizeOptions.map(({ value, label }) => (
|
|
340
|
+
<SizeItem key={value} size={value} label={label} />
|
|
341
|
+
))}
|
|
342
|
+
</div>
|
|
343
|
+
);
|
|
254
344
|
```
|
|
255
345
|
|
|
256
|
-
|
|
257
|
-
дополнительного className. Умный приём.
|
|
346
|
+
### States
|
|
258
347
|
|
|
259
|
-
|
|
348
|
+
`stories/Chips/examples/Chip/StatesExample.tsx`
|
|
260
349
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
350
|
+
```tsx
|
|
351
|
+
import React from "react";
|
|
352
|
+
import { Chip } from "@sberbusiness/triplex-next";
|
|
353
|
+
|
|
354
|
+
export const StatesExample = () => (
|
|
355
|
+
<div style={{ display: "flex", gap: 12 }}>
|
|
356
|
+
<Chip selected>Selected</Chip>
|
|
357
|
+
<Chip disabled>Disabled</Chip>
|
|
358
|
+
</div>
|
|
359
|
+
);
|
|
264
360
|
```
|
|
265
361
|
|
|
266
|
-
|
|
267
|
-
удалять класс** — иначе чипы сломают layout в ChipGroup.
|
|
362
|
+
### WithPrefixAndPostfix
|
|
268
363
|
|
|
269
|
-
|
|
364
|
+
`stories/Chips/examples/Chip/WithPrefixAndPostfixExample.tsx`
|
|
270
365
|
|
|
271
|
-
```
|
|
272
|
-
|
|
366
|
+
```tsx
|
|
367
|
+
import React from "react";
|
|
368
|
+
import { SortStrokeSrvIcon20 } from "@sberbusiness/icons-next";
|
|
369
|
+
import { Chip } from "@sberbusiness/triplex-next";
|
|
370
|
+
|
|
371
|
+
export const WithPrefixAndPostfixExample = () => (
|
|
372
|
+
<Chip prefix={<SortStrokeSrvIcon20 paletteIndex={5} />} postfix={<SortStrokeSrvIcon20 paletteIndex={5} />}>
|
|
373
|
+
Value
|
|
374
|
+
</Chip>
|
|
375
|
+
);
|
|
273
376
|
```
|
|
274
377
|
|
|
275
|
-
|
|
276
|
-
— значение стабильно между рендерами, но каждый инстанс получит свой id.
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
## Инварианты
|
|
281
|
-
|
|
282
|
-
- **`Chip` — forwardRef**, не удалять.
|
|
283
|
-
- **`role="button"` и `tabIndex` на Chip** — не менять семантику без
|
|
284
|
-
согласования.
|
|
285
|
-
- **`Space preventDefault`** в `handleKeyDown` — не удалять (скролл
|
|
286
|
-
страницы).
|
|
287
|
-
- **`Omit<HTMLAttributes, "prefix">`** — нужно, т.к. `prefix` — HTML
|
|
288
|
-
устаревший атрибут (RDFa).
|
|
289
|
-
- **`IconWrapper` для prefix/postfix** — не замена на div. `IconWrapper`
|
|
290
|
-
управляет цветом иконок по disabled.
|
|
291
|
-
- **`styles.chipGroupItem`** — на каждом root-элементе вариантов чипов.
|
|
292
|
-
Ожидается ChipGroup'ом.
|
|
293
|
-
- **`ChipIcon` → `postfix={<span />}`** — не удалять span. Нужен для
|
|
294
|
-
симметричных паддингов.
|
|
295
|
-
- **`ChipSort` — `!isEqual(defaultValue, value)` для selected** — не
|
|
296
|
-
упрощать до `Boolean(value)`. Это UX-правило (дефолт не "активный фильтр").
|
|
297
|
-
- **`Badge.Dot` для notification icon** — не заменять на произвольный
|
|
298
|
-
элемент, размер синхронизирован с size чипа.
|
|
299
|
-
- **Базовые токены `--triplex-next-Chip-*`** — не дублировать для вариантов.
|
|
300
|
-
Варианты используют одни и те же токены (цвета).
|
|
301
|
-
- **Варианты (ChipSelect/ChipSuggest/ChipDatePicker) обёртки над
|
|
302
|
-
SelectExtendedField/SuggestField/подобными** — не инвертировать зависимость
|
|
303
|
-
(не Chip оборачивает Field, а наоборот).
|
|
304
|
-
- **`lodash` (не `-es`) в ChipSort** — исторически, не менять точечно.
|
|
305
|
-
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
## Accessibility
|
|
309
|
-
|
|
310
|
-
- **`role="button"`** на Chip — работает с screen reader'ом как кнопка.
|
|
311
|
-
- **`tabIndex` управляется через disabled** — disabled чип вне tab-order.
|
|
312
|
-
- **`aria-expanded`** на Chip в ChipMultiselect / ChipSort — для dropdown
|
|
313
|
-
состояния. Screen reader озвучит "раскрыто"/"свёрнуто".
|
|
314
|
-
- **`aria-controls`** в ChipSort связывает чип с dropdown через
|
|
315
|
-
`instanceId`.
|
|
316
|
-
- **Клавиатура**:
|
|
317
|
-
- Space — preventDefault (против скролла), потребитель обрабатывает
|
|
318
|
-
активацию.
|
|
319
|
-
- Enter — bubble up, потребитель вешает handler через `onKeyDown`.
|
|
320
|
-
- ChipMultiselect/ChipSort — свои `handleKeyDown` обрабатывают Enter/Space
|
|
321
|
-
для toggle opened.
|
|
322
|
-
- **Notification icon `Badge.Dot`** — чисто визуальный, без ARIA. Если
|
|
323
|
-
означает "есть новые" — потребитель должен добавить `aria-label` на
|
|
324
|
-
Chip (например, "Фильтр, есть новые").
|
|
325
|
-
- **Nested interactive**: ClearButton внутри Chip — кнопка в "кнопке".
|
|
326
|
-
Решается `event.stopPropagation()` на ClearButton, чтобы клик на крестик
|
|
327
|
-
не триггерил родительский toggle.
|
|
328
|
-
|
|
329
|
-
---
|
|
330
|
-
|
|
331
|
-
## Дизайн-токены
|
|
378
|
+
### WithNotificationIcon
|
|
332
379
|
|
|
333
|
-
|
|
334
|
-
```
|
|
335
|
-
--triplex-next-Chip-Background_Default
|
|
336
|
-
--triplex-next-Chip-Background_Hover
|
|
337
|
-
--triplex-next-Chip-Background_Active
|
|
338
|
-
--triplex-next-Chip-Background_Disabled
|
|
339
|
-
--triplex-next-Chip-Color_Default
|
|
340
|
-
--triplex-next-Chip-Color_Hover
|
|
341
|
-
--triplex-next-Chip-Color_Active
|
|
342
|
-
--triplex-next-Chip-Color_Disabled
|
|
343
|
-
--triplex-next-Chip-Shadow_Focus # focus-visible
|
|
344
|
-
```
|
|
380
|
+
`stories/Chips/examples/Chip/WithNotificationIconExample.tsx`
|
|
345
381
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
382
|
+
```tsx
|
|
383
|
+
import React from "react";
|
|
384
|
+
import { Chip, EComponentSize } from "@sberbusiness/triplex-next";
|
|
385
|
+
|
|
386
|
+
export const WithNotificationIconExample = () => (
|
|
387
|
+
<div style={{ display: "flex", gap: 12 }}>
|
|
388
|
+
<Chip size={EComponentSize.SM} showNotificationIcon>
|
|
389
|
+
SM
|
|
390
|
+
</Chip>
|
|
391
|
+
<Chip size={EComponentSize.MD} showNotificationIcon>
|
|
392
|
+
MD
|
|
393
|
+
</Chip>
|
|
394
|
+
<Chip size={EComponentSize.LG} showNotificationIcon>
|
|
395
|
+
LG
|
|
396
|
+
</Chip>
|
|
397
|
+
</div>
|
|
398
|
+
);
|
|
356
399
|
```
|
|
357
400
|
|
|
358
|
-
Размеры (высоты, radius, padding, font-size) — hardcoded в LESS по size-
|
|
359
|
-
классам, не токенизированы.
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
## Связанные компоненты
|
|
364
|
-
|
|
365
|
-
- `ChipGroup` (`src/components/ChipGroup`) — контейнер для чипов с
|
|
366
|
-
горизонтальным скроллом/переносом.
|
|
367
|
-
- `Badge` (`src/components/Badge`) — notification dot (`Badge.Dot`) —
|
|
368
|
-
та же реализация.
|
|
369
|
-
- `IconWrapper` (`src/components/IconWrapper`) — обёртка для prefix/postfix
|
|
370
|
-
иконок.
|
|
371
|
-
- `ButtonIcon` (`src/components/Button`) — основа для `ChipClearButton`.
|
|
372
|
-
- `MultiselectField`, `SelectExtendedField`, `SuggestField` — варианты
|
|
373
|
-
чипов обёртки над этими field'ами.
|
|
374
|
-
- `EComponentSize`, `createSizeToClassNameMap` — общий паттерн size'ов.
|
|
375
|
-
|
|
376
|
-
---
|
|
377
|
-
|
|
378
|
-
## Stories
|
|
379
|
-
|
|
380
|
-
Отдельная story-файл для каждого варианта:
|
|
381
|
-
|
|
382
|
-
| File | Variant |
|
|
383
|
-
|---|---|
|
|
384
|
-
| `stories/Chips/Chip.stories.tsx` | Base Chip |
|
|
385
|
-
| `stories/Chips/ChipIcon.stories.tsx` | ChipIcon |
|
|
386
|
-
| `stories/Chips/ChipGroup.stories.tsx` | ChipGroup (отдельный компонент) |
|
|
387
|
-
| `stories/Chips/ChipMultiselect.stories.tsx` | ChipMultiselect |
|
|
388
|
-
| `stories/Chips/ChipOptions.stories.tsx` | ChipOptions |
|
|
389
|
-
| `stories/Chips/ChipSelect.stories.tsx` | ChipSelect |
|
|
390
|
-
| `stories/Chips/ChipSort.stories.tsx` | ChipSort |
|
|
391
|
-
| `stories/Chips/ChipSuggest.stories.tsx` | ChipSuggest |
|
|
392
|
-
| `stories/Chips/ChipDatePicker.stories.tsx` | ChipDatePicker |
|
|
393
|
-
|
|
394
|
-
Примеры в `stories/Chips/examples/<Name>/`. Typical: `Default`, `Playground`,
|
|
395
|
-
`Sizes`, специфичные для варианта (`States`, `WithNotificationIcon`,
|
|
396
|
-
`WithPrefixAndPostfix` для base Chip).
|
|
397
|
-
|
|
398
|
-
---
|
|
399
|
-
|
|
400
|
-
## История изменений
|
|
401
|
-
|
|
402
|
-
| Дата | Изменение |
|
|
403
|
-
|---|---|
|
|
404
|
-
| 2026-04-18 | Создан документ |
|