Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
34a8e45
fix(tokens): синхронизированы токены полей ввода с Figma
fabulous-unicorn Oct 13, 2026
7fd69ae
refactor(input-switch): компонент перенесён в отдельную папку
fabulous-unicorn Oct 8, 2026
9640b8f
refactor(input-otp): компонент перенесён в отдельную папку
fabulous-unicorn Oct 8, 2026
bbb211b
chore(storybook): упорядочены истории и настройка форматирования
fabulous-unicorn Oct 8, 2026
c5609d0
refactor(input): вынесены legacy-компоненты и сохранены старые экспорты
fabulous-unicorn Oct 8, 2026
112cc85
feat(input): добавлен InputCaption
fabulous-unicorn Oct 12, 2026
31e813d
feat(input): добавлен InputGroupAddon
fabulous-unicorn Oct 12, 2026
451ecee
feat(input): добавлен InputText с новым API
fabulous-unicorn Oct 12, 2026
33f6cca
feat(input): добавлен InputGroup с базовой доступностью
fabulous-unicorn Oct 12, 2026
c931dd0
feat(input): добавлен Password с базовой доступностью
fabulous-unicorn Oct 12, 2026
d01a6af
fix(input): улучшена доступность полей и подписей
fabulous-unicorn Oct 12, 2026
b849166
fix(input): упрощено озвучивание полей и подписей
fabulous-unicorn Oct 12, 2026
36727ba
docs(input): добавлена документация компонентов ввода
fabulous-unicorn Oct 13, 2026
1e93f61
fix(input): стабилизированы высота и масштабирование подписей
fabulous-unicorn Oct 19, 2026
dc71d80
docs(input): уточнена и упрощена документация полей ввода
fabulous-unicorn Oct 20, 2026
8070648
fix(input): исправлено отображение плавающего лейбла и привязки токенов
fabulous-unicorn Oct 23, 2026
a0a71db
refactor(input): разделены раскладка поля и оформление кнопок
fabulous-unicorn Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ android/app/.cxx
android/app/build
android/build
CHANGELOG.md
.storybook/storybook.requires.ts
8 changes: 8 additions & 0 deletions .storybook/inputDocumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const documentationRoot =
'https://github.com/cdek-it/react-native-ui-kit/blob/main/src/components/Input'

export const inputDocumentationNotes = {
InputGroup: `[Документация InputGroup](${documentationRoot}/InputGroup/InputGroup.md)`,
Password: `[Документация Password](${documentationRoot}/Password/Password.md)`,
InputCaption: `[Документация InputCaption](${documentationRoot}/InputCaption/InputCaption.md)`,
}
67 changes: 67 additions & 0 deletions .storybook/inputLabelControls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { Meta } from '@storybook/react'
import { Alert } from 'react-native'

import type {
InputLabelInfo,
InputLabelProps,
} from '../src/components/Input/core/types'

type InputLabelControls = {
labelMode: 'none' | 'label' | 'floatLabel'
labelText: string
labelInfo?: InputLabelInfo
}

export type InputLabelStoryArgs<Props> = Omit<
Props,
'label' | 'floatLabel' | 'labelInfo'
> &
InputLabelControls

export type InputStoryMeta<Props, Args> = Omit<Meta<Args>, 'component'> &
Pick<Meta<Props>, 'component'>

export const inputLabelControls = {
labelMode: {
control: 'radio',
options: ['none', 'label', 'floatLabel'],
description:
'Режим примера: передать label, floatLabel или не передавать подпись. Не свойство компонента.',
},
labelText: {
control: 'text',
description: 'Текст для выбранного label или floatLabel в этом примере.',
},
labelInfo: {
control: 'select',
options: ['none', 'info'],
mapping: {
none: undefined,
info: {
onPress: () =>
Alert.alert(
'Подробнее',
'Пример справки, которую открывает приложение.'
),
},
},
if: { arg: 'labelMode', eq: 'label' },
description:
'Кнопка ⓘ у обычного label. Обработчик примера открывает справку через Alert.',
},
} as const

export const getInputLabelProps = ({
labelMode,
labelText,
labelInfo,
}: InputLabelControls): InputLabelProps => {
switch (labelMode) {
case 'label':
return { label: labelText, labelInfo }
case 'floatLabel':
return { floatLabel: labelText }
case 'none':
return {}
}
}
1 change: 1 addition & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const preview: Preview = {
},
],
parameters: {
options: { storySort: { method: 'alphabetical', includeNames: true } },
actions: { argTypesRegex: '^on[A-Z].*' },
controls: { matchers: { color: /(background|color)$/i, date: /Date$/ } },
},
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

Документация компонентов:

- [InputText](src/components/Input/InputText/InputText.md)
- [InputGroup](src/components/Input/InputGroup/InputGroup.md)
- [Password](src/components/Input/Password/Password.md)
- [Skeleton](src/components/Skeleton/Skeleton.md)

## Installation
Expand Down
25 changes: 13 additions & 12 deletions design-tokens/input/tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,7 @@
"dimension": {
"space": {
"none": "{sizing.none}",
"50": "{sizing.1x}",
"100": "{sizing.2x}",
"150": "{sizing.3x}",
"200": "{sizing.4x}",
Expand Down Expand Up @@ -1067,7 +1068,7 @@
"200": "{sizing.1x}",
"300": "{sizing.2x}"
},
"focusRing": { "width": "{sizing.2x}", "offset": "{sizing.none}" },
"focusRing": { "width": "{sizing.1x}", "offset": "{sizing.none}" },
"depth": {
"100": "1000",
"200": "1100",
Expand Down Expand Up @@ -3016,7 +3017,7 @@
},
"addon": {
"borderRadius": "{dimension.borderRadius.400}",
"padding": "{dimension.space.400}",
"padding": "{dimension.space.300}",
"minWidth": "{dimension.size.1700}"
}
},
Expand Down Expand Up @@ -3086,9 +3087,9 @@
"iconSize": "{dimension.size.450}",
"borderWidth": "{dimension.borderWidth.100}",
"extXlg": {
"fontSize": "{fonts.fontSize.300}",
"paddingX": "{dimension.space.300}",
"paddingY": "{dimension.space.600}"
"fontSize": "{fonts.fontSize.500}",
"paddingX": "{dimension.space.600}",
"paddingY": "{dimension.space.400}"
},
"invalidFocusRing": { "color": "{color.border.status.danger.focus}" }
},
Expand All @@ -3107,18 +3108,18 @@
"placeholderColor": "{color.fg.muted}",
"invalidPlaceholderColor": "{color.fg.status.danger.hover}",
"shadow": "{effects.elevation.none}",
"paddingX": "{dimension.space.300}",
"paddingY": "{dimension.space.300}",
"paddingX": "{dimension.space.400}",
"paddingY": "{dimension.space.350}",
"borderRadius": "{dimension.borderRadius.300}",
"transitionDuration": "{effects.transition.duration.200}",
"sm": {
"fontSize": "{fonts.fontSize.300}",
"paddingX": "{dimension.space.300}",
"paddingY": "{dimension.space.200}"
"fontSize": "{fonts.fontSize.200}",
"paddingX": "{dimension.space.350}",
"paddingY": "{dimension.space.300}"
},
"lg": {
"fontSize": "{fonts.fontSize.300}",
"paddingX": "{dimension.space.300}",
"fontSize": "{fonts.fontSize.400}",
"paddingX": "{dimension.space.500}",
"paddingY": "{dimension.space.400}"
},
"focusRing": {
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./legacy": {
"types": "./dist/legacy.d.ts",
"default": "./dist/legacy.js"
}
},
"scripts": {
Expand Down
18 changes: 9 additions & 9 deletions scripts/token-generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@

## Использование токенов

Component-токены предназначены только для реализации компонентов UI Kit. Они не
входят в темы и публичные экспорты библиотеки.
Component-токены предназначены только для реализации компонентов UI Kit. Они
доступны через `components` темы, но не экспортируются отдельно из пакета.

В `semantic` темы находится только `colorScheme`: Unistyles подставляет
соответствующие light- или dark-токены при переключении темы. Не зависящие от
темы `dimension` и `effects` доступны в статическом `semanticTokens`.
В `semantic` темы находятся `colorScheme`, `dimension` и `effects`. Unistyles
переключает `colorScheme` и component-токены вместе с темой. Общие `dimension` и
`effects` также доступны в статическом `semanticTokens`.

## Результат генерации

Expand Down Expand Up @@ -54,12 +54,12 @@ components.button.colorScheme.light.outlined
```

`semantic.dimension` записывается в `semantic/dimensions.json`, а
`semantic.effects` — в `semantic/effects.json`. Эти секции генерируются один раз
и не входят в тему. Если их итоговые значения зависят от `colorScheme`,
генератор завершится с ошибкой.
`semantic.effects` — в `semantic/effects.json`. Эти секции генерируются один
раз. Если их итоговые значения зависят от `colorScheme`, генератор завершится с
ошибкой.

`fonts.json` собирается из `primitive.fonts`, нормализуется по общим правилам и
не разделяется по цветовым схемам. Пока файл не экспортируется из библиотеки.
не разделяется по цветовым схемам. В теме эти токены находятся в `fonts`.

Остальная часть `primitive` нужна только для подстановки ссылок и не
записывается в результат. Light- и dark-файлы должны иметь одинаковую структуру
Expand Down
17 changes: 0 additions & 17 deletions scripts/token-generator/__tests__/compiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,23 +572,6 @@ describe('production input tokens', () => {
expect(JSON.stringify(compiled)).not.toMatch(/cubic-bezier\s*\(/)
})

test('экспортирует focusRing без изменения его структуры', () => {
expect(compiled.semantic.effects).toHaveProperty(
'focusRing.shadow',
'inset 0 0 0 4px #d4fedc'
)
expect(compiled.components.light).toHaveProperty(
'accordion.header.focusRing',
{
width: 4,
style: 'none',
color: 'rgba(68, 232, 88, 0.2)',
offset: 0,
shadow: 'inset 0 0 0 4px #d4fedc',
}
)
})

test('совпадает с закоммиченными сгенерированными токенами', async () => {
await expect(
findGeneratedTokenIssues(compiled, TOKENS_DIRECTORY)
Expand Down
2 changes: 1 addition & 1 deletion src/components/Checkbox/Checkbox.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react'
import { Checkbox } from './Checkbox'

const meta: Meta<typeof Checkbox> = {
title: 'Form/Checkbox',
title: 'Other Form/Checkbox',
component: Checkbox,
args: {
checked: false,
Expand Down
4 changes: 1 addition & 3 deletions src/components/Dialog/Dialog.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,4 @@ export default meta

type Story = StoryObj<typeof Dialog>

const DialogStory: Story = { args: {} }

export { DialogStory as 'Dialog modal=true' }
export const Modal: Story = { name: 'Dialog modal=true', args: {} }
4 changes: 1 addition & 3 deletions src/components/Dialog/DialogComponent.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,4 @@ export default meta

type Story = StoryObj<typeof DialogComponent>

const DialogStory: Story = { args: {} }

export { DialogStory as 'Dialog modal=false' }
export const NonModal: Story = { name: 'Dialog modal=false', args: {} }
13 changes: 13 additions & 0 deletions src/components/Input/Input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Поля ввода

- [InputText](./InputText/InputText.md) — текстовый ввод, подписи и сообщения
- [InputGroup](./InputGroup/InputGroup.md) — поле с дополнениями слева и справа
- [Password](./Password/Password.md) — пароль с переключением видимости

## Вспомогательные компоненты

- [InputCaption](./InputCaption/InputCaption.md) — текстовая подпись с
визуальным статусом

Общие правила ввода и доступности описаны в InputText. Интерактивные примеры
доступны в Storybook, в разделе Input.
44 changes: 44 additions & 0 deletions src/components/Input/InputCaption/InputCaption.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# InputCaption

Дополнительное сообщение с визуальным статусом

## Использование

```tsx
import { InputCaption, InputText } from '@cdek-it/react-native-ui-kit'

export function AddressField() {
return (
<InputText
label='Адрес'
caption={
<InputCaption variant='info' showIcon>
Улица, дом и квартира
</InputCaption>
}
/>
)
}
```

В `caption` поля строка или число автоматически оформляются как InputCaption.
Для статуса или иконки можно передать компонент явно. Его `variant` задаётся
независимо от `state` поля.

## Props

- `children` — содержимое сообщения.
- `variant` — `base | info | success | warning | danger`; по умолчанию `base`.
Определяет оформление и стандартную иконку.
- `showIcon` — показывает иконку; по умолчанию `false`.
- `Icon` — SVG, переопределяющий стандартную иконку. Для показа нужен
`showIcon`.
- `style` — стиль контейнера сообщения.

Полный контракт — [InputCaptionProps](./InputCaption.tsx).

## Доступность

Сообщение озвучивается отдельно от поля; SVG декоративна. Подсказка поля
задаётся через `accessibilityHint` у [InputText](../InputText/InputText.md);
содержимое InputCaption автоматически в неё не переносится.
32 changes: 32 additions & 0 deletions src/components/Input/InputCaption/InputCaption.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Meta, StoryObj } from '@storybook/react'

import { inputDocumentationNotes } from '../../../../.storybook/inputDocumentation'

import { InputCaption } from './InputCaption'

const meta: Meta<typeof InputCaption> = {
title: 'Input/InputCaption',
component: InputCaption,
args: { children: 'Подсказка к полю', variant: 'base', showIcon: false },
argTypes: {
children: { control: 'text' },
variant: {
control: 'radio',
options: ['base', 'success', 'warning', 'danger', 'info'],
},
showIcon: { control: 'boolean' },
},
parameters: {
notes: inputDocumentationNotes.InputCaption,
controls: { include: ['children', 'variant', 'showIcon'] },
},
render: (args) => <InputCaption {...args} />,
}

export default meta

type Story = StoryObj<typeof meta>

const InputCaptionStory: Story = { name: 'InputCaption' }

export { InputCaptionStory as InputCaption }
Loading
Loading