From cd77a187b458e39680410ebb660a8635acefed9d Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Thu, 8 Oct 2026 20:27:09 +0800 Subject: [PATCH 01/10] fix outline button focus state --- src/lib/components/button/button.component.ts | 71 +++-- .../confirm-dialog.component.ts | 2 +- .../prime-preset/tokens/components/button.ts | 18 +- .../providers/prime-preset/tokens/tokens.json | 46 +-- .../components/button/button.stories.ts | 264 +++++++++++------- .../button/examples/button-badge.component.ts | 64 +++++ .../button/examples/button-base.component.ts | 164 +++++++++++ .../examples/button-disabled.component.ts | 53 ++++ .../button/examples/button-extra.component.ts | 107 +++++++ .../button-figma-mapping.component.ts | 144 ++++++++++ .../button/examples/button-icon.component.ts | 55 ++++ .../button/examples/button-icons.component.ts | 108 ------- .../examples/button-loading.component.ts | 53 ++++ .../examples/button-outlined.component.ts | 51 ++++ .../examples/button-rounded.component.ts | 50 +--- .../button-severity-figma.component.ts | 143 ++++++++++ .../examples/button-severity.component.ts | 123 ++++++++ .../button/examples/button-sizes.component.ts | 59 ++-- .../examples/button-states.component.ts | 97 ------- .../button/examples/button-text.component.ts | 51 ++++ .../examples/button-variants.component.ts | 77 ----- 21 files changed, 1290 insertions(+), 510 deletions(-) create mode 100644 src/stories/components/button/examples/button-badge.component.ts create mode 100644 src/stories/components/button/examples/button-base.component.ts create mode 100644 src/stories/components/button/examples/button-disabled.component.ts create mode 100644 src/stories/components/button/examples/button-extra.component.ts create mode 100644 src/stories/components/button/examples/button-figma-mapping.component.ts create mode 100644 src/stories/components/button/examples/button-icon.component.ts delete mode 100644 src/stories/components/button/examples/button-icons.component.ts create mode 100644 src/stories/components/button/examples/button-loading.component.ts create mode 100644 src/stories/components/button/examples/button-outlined.component.ts create mode 100644 src/stories/components/button/examples/button-severity-figma.component.ts create mode 100644 src/stories/components/button/examples/button-severity.component.ts delete mode 100644 src/stories/components/button/examples/button-states.component.ts create mode 100644 src/stories/components/button/examples/button-text.component.ts delete mode 100644 src/stories/components/button/examples/button-variants.component.ts diff --git a/src/lib/components/button/button.component.ts b/src/lib/components/button/button.component.ts index ecf14393..836362fd 100644 --- a/src/lib/components/button/button.component.ts +++ b/src/lib/components/button/button.component.ts @@ -2,9 +2,13 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from import { Button, ButtonSeverity as PrimeButtonSeverity } from 'primeng/button'; export type ExtraButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'text' | 'link'; -export type ExtraButtonSeverity = 'base' | 'danger' | 'warning' | 'success' | 'info'; +export type ExtraButtonSeverity = 'success' | 'warning' | 'danger' | 'info' | null; export type ExtraButtonSize = 'small' | 'base' | 'large' | 'xlarge'; -export type ExtraButtonIconPosition = 'left' | 'right'; +export type ExtraButtonIconPosition = 'prefix' | 'postfix' | null; +export type ExtraBadgeSeverity = 'success' | 'info' | 'warning' | 'danger' | 'secondary' | 'contrast' | null; +type PrimeBadgeSeverity = Extract; +type ExtraButtonSeverityValue = PrimeButtonSeverity; +type ExtraBadgeSeverityValue = PrimeBadgeSeverity; @Component({ selector: 'extra-button', @@ -13,34 +17,48 @@ export type ExtraButtonIconPosition = 'left' | 'right'; imports: [Button], template: ` ` }) export class ExtraButtonComponent { - @Input() label = ''; - @Input() icon = ''; - @Input() iconPosition: ExtraButtonIconPosition = 'left'; + @Input() label = 'Button'; @Input() variant: ExtraButtonVariant = 'primary'; - @Input() severity: ExtraButtonSeverity = 'base'; + @Input() severity: ExtraButtonSeverity = null; @Input() size: ExtraButtonSize = 'base'; @Input() rounded = false; - @Input() fluid = false; + @Input() iconPosition: ExtraButtonIconPosition = null; + @Input() iconOnly = false; + @Input() icon = ''; @Input() disabled = false; @Input() loading = false; + @Input() badge = ''; + @Input() badgeSeverity: ExtraBadgeSeverity = null; + @Input() showBadge = false; + @Input() fluid = false; + @Input() ariaLabel: string | undefined = undefined; + @Input() autofocus = false; + @Input() tabindex: number | undefined = undefined; + @Input() text = false; @Output() focus = new EventEmitter(); @Output() blur = new EventEmitter(); @@ -51,16 +69,19 @@ export class ExtraButtonComponent { return undefined; } - get primeStyleClass(): string { - return this.size === 'xlarge' ? 'p-button-xlg' : ''; + get primeIconPosition(): 'left' | 'right' { + return this.iconPosition === 'postfix' ? 'right' : 'left'; + } + + get primeSeverity(): ExtraButtonSeverityValue | null { + if (this.variant === 'secondary') return 'secondary'; + if (this.variant === 'tertiary' && this.severity === null) return 'contrast'; + if (this.severity === 'warning') return 'warn'; + return this.severity; } - get primeSeverity(): PrimeButtonSeverity { - if (this.severity === 'base') { - if (this.variant === 'secondary') return 'secondary'; - if (this.variant === 'tertiary') return 'contrast'; - return null; - } - return this.severity === 'warning' ? 'warn' : this.severity; + get primeBadgeSeverity(): ExtraBadgeSeverityValue { + if (this.badgeSeverity === 'warning') return 'warn'; + return this.badgeSeverity; } } diff --git a/src/lib/components/confirm-dialog/confirm-dialog.component.ts b/src/lib/components/confirm-dialog/confirm-dialog.component.ts index 27b850aa..e19f006e 100644 --- a/src/lib/components/confirm-dialog/confirm-dialog.component.ts +++ b/src/lib/components/confirm-dialog/confirm-dialog.component.ts @@ -76,7 +76,7 @@ export class ExtraConfirmDialogComponent { mapSeverity(severity: string | null | undefined): ExtraButtonSeverity { if (severity === 'danger' || severity === 'warn' || severity === 'warning') return severity === 'warn' ? 'warning' : severity; if (severity === 'success' || severity === 'info') return severity; - return 'base'; + return null; } get computedClass(): string { diff --git a/src/lib/providers/prime-preset/tokens/components/button.ts b/src/lib/providers/prime-preset/tokens/components/button.ts index 8fa91484..f8c32a44 100644 --- a/src/lib/providers/prime-preset/tokens/components/button.ts +++ b/src/lib/providers/prime-preset/tokens/components/button.ts @@ -57,13 +57,13 @@ export const buttonCss = ({ dt }: { dt: (token: string) => string }): string => } .p-button.p-button-outlined:is(.p-disabled, :disabled, .p-button-loading) { color: ${dt('button.extend.disabledColor')}; - background: transparent; - border-color: transparent; + background: ${dt('button.extend.disabledBackground')}; + border-color: ${dt('button.extend.disabledBackground')}; } .p-button.p-button-text:is(.p-disabled, :disabled, .p-button-loading) { color: ${dt('button.extend.disabledColor')}; - background: transparent; - border-color: transparent; + background: ${dt('button.extend.disabledBackground')}; + border-color: ${dt('button.extend.disabledBackground')}; } .p-button.p-button-link:is(.p-disabled, :disabled, .p-button-loading) { color: ${dt('button.extend.disabledColor')}; @@ -71,6 +71,16 @@ export const buttonCss = ({ dt }: { dt: (token: string) => string }): string => border-color: transparent; } + /* ─── Нейтральное кольцо фокуса для text/link без severity ─── + У text/link без severity-класса нет собственного focusRing, поэтому + они наследуют primary (зелёное кольцо). В Figma (Button.Base, + variant=text/link) кольцо должно быть нейтральным, как у secondary/ + tertiary (contrast): zinc.300 1px + black 10% 2px. */ + .p-button-text:not(.p-button-danger):not(.p-button-warn):not(.p-button-info):not(.p-button-help):not(.p-button-success):focus-visible, + .p-button-link:not(.p-button-danger):not(.p-button-warn):not(.p-button-info):not(.p-button-help):not(.p-button-success):focus-visible { + box-shadow: 0 0 0 ${dt('button.extend.extFocusRing.width')} ${dt('button.extend.extFocusRing.borderColor')}, 0 0 0 ${dt('button.extend.extFocusRing.ringWidth')} ${dt('button.extend.extFocusRing.neutralColor')}; + } + /* ─── Link кнопки ─── */ .p-button-link.p-button:is(.p-button, .p-button-xlg) { padding: ${dt('button.extend.extLink.paddingY')} ${dt('button.extend.extLink.paddingX')}; diff --git a/src/lib/providers/prime-preset/tokens/tokens.json b/src/lib/providers/prime-preset/tokens/tokens.json index 4b6d069e..cb9a6225 100644 --- a/src/lib/providers/prime-preset/tokens/tokens.json +++ b/src/lib/providers/prime-preset/tokens/tokens.json @@ -1658,6 +1658,12 @@ "sm": "{dimension.size.450}", "md": "{dimension.size.600}", "lg": "{dimension.size.700}" + }, + "extFocusRing": { + "borderColor": "{colors.solid.zinc.300}", + "neutralColor": "{colors.alpha.black.100}", + "width": "{dimension.borderWidth.100}", + "ringWidth": "{dimension.borderWidth.200}" } }, "colorScheme": { @@ -1674,8 +1680,8 @@ "hoverColor": "{color.fg.default}", "activeColor": "{color.fg.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.200}" + "color": "{colors.alpha.green.200}", + "shadow": "0 0 0 {dimension.borderWidth.200} {colors.alpha.green.200}" } }, "secondary": { @@ -1689,8 +1695,8 @@ "hoverColor": "{color.fg.on.fill.hover}", "activeColor": "{color.fg.on.fill.active}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.200}" + "color": "{colors.alpha.black.100}", + "shadow": "0 0 0 {dimension.borderWidth.100} {colors.solid.zinc.300}, 0 0 0 {dimension.borderWidth.200} {colors.alpha.black.100}" } }, "contrast": { @@ -1704,8 +1710,8 @@ "hoverColor": "{color.fg.default}", "activeColor": "{color.fg.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.200}" + "color": "{colors.alpha.black.100}", + "shadow": "0 0 0 {dimension.borderWidth.100} {colors.solid.zinc.300}, 0 0 0 {dimension.borderWidth.200} {colors.alpha.black.100}" } }, "info": { @@ -1719,8 +1725,8 @@ "hoverColor": "{color.fg.on.status.info.hover}", "activeColor": "{color.fg.on.status.info.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.none}" + "color": "{colors.alpha.blue.200}", + "shadow": "0 0 0 {dimension.borderWidth.200} {colors.alpha.blue.200}" } }, "success": { @@ -1734,8 +1740,8 @@ "hoverColor": "{color.fg.on.status.success.hover}", "activeColor": "{color.fg.on.status.success.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.none}" + "color": "{colors.alpha.green.200}", + "shadow": "0 0 0 {dimension.borderWidth.200} {colors.alpha.green.200}" } }, "warn": { @@ -1749,8 +1755,8 @@ "hoverColor": "{color.fg.on.status.warning.hover}", "activeColor": "{color.fg.on.status.warning.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.none}" + "color": "{colors.alpha.yellow.200}", + "shadow": "0 0 0 {dimension.borderWidth.200} {colors.alpha.yellow.200}" } }, "help": { @@ -1779,8 +1785,8 @@ "hoverColor": "{color.fg.on.status.danger.hover}", "activeColor": "{color.fg.on.status.danger.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.none}" + "color": "{colors.alpha.red.200}", + "shadow": "0 0 0 {dimension.borderWidth.200} {colors.alpha.red.200}" } } }, @@ -1964,8 +1970,8 @@ "borderColor": "transparent", "color": "{color.fg.inverse.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.200}" + "color": "{colors.alpha.black.100}", + "shadow": "0 0 0 {dimension.borderWidth.100} {colors.solid.zinc.300}, 0 0 0 {dimension.borderWidth.200} {colors.alpha.black.100}" }, "hoverBackground": "{color.bg.neutral.strong.active}", "hoverBorderColor": "transparent", @@ -2024,8 +2030,8 @@ "borderColor": "transparent", "color": "{color.fg.inverse.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.200}" + "color": "{colors.alpha.green.200}", + "shadow": "0 0 0 {dimension.borderWidth.200} {colors.alpha.green.200}" }, "hoverBackground": "{color.bg.status.success.strong.hover}", "hoverBorderColor": "transparent", @@ -2039,8 +2045,8 @@ "borderColor": "transparent", "color": "{color.fg.default}", "focusRing": { - "color": "{color.border.focus}", - "shadow": "{effects.elevation.200}" + "color": "{colors.alpha.black.100}", + "shadow": "0 0 0 {dimension.borderWidth.100} {colors.solid.zinc.300}, 0 0 0 {dimension.borderWidth.200} {colors.alpha.black.100}" }, "hoverBackground": "{color.bg.surface.canvas.hover}", "hoverBorderColor": "transparent", diff --git a/src/stories/components/button/button.stories.ts b/src/stories/components/button/button.stories.ts index 3ec246e4..79875841 100644 --- a/src/stories/components/button/button.stories.ts +++ b/src/stories/components/button/button.stories.ts @@ -1,10 +1,18 @@ import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../lib/components/button/button.component'; import { ButtonSizesComponent, Sizes } from './examples/button-sizes.component'; -import { ButtonVariantsComponent, Variants } from './examples/button-variants.component'; -import { ButtonIconsComponent, Icons } from './examples/button-icons.component'; -import { ButtonStatesComponent, States } from './examples/button-states.component'; +import { ButtonTextComponent, Text } from './examples/button-text.component'; +import { ButtonSeverityComponent, Severity } from './examples/button-severity.component'; +import { ButtonSeverityFigmaComponent, SeverityFigma } from './examples/button-severity-figma.component'; +import { ButtonFigmaMappingComponent, FigmaMapping } from './examples/button-figma-mapping.component'; import { ButtonRoundedComponent, Rounded } from './examples/button-rounded.component'; +import { ButtonOutlinedComponent, Outlined } from './examples/button-outlined.component'; +import { ButtonLoadingComponent, Loading } from './examples/button-loading.component'; +import { ButtonIconComponent, Icon } from './examples/button-icon.component'; +import { Extra } from './examples/button-extra.component'; +import { ButtonDisabledComponent, Disabled } from './examples/button-disabled.component'; +import { Base, ButtonBaseComponent } from './examples/button-base.component'; +import { Badge, ButtonBadgeComponent } from './examples/button-badge.component'; import { ButtonFluidComponent, Fluid } from './examples/button-fluid.component'; import { ButtonEventsComponent, Events } from './examples/button-events.component'; @@ -15,18 +23,26 @@ type ButtonArgs = ExtraButtonComponent & { }; const meta: Meta = { - title: 'Components/Button', + title: 'Components/Button/Button', component: ExtraButtonComponent, tags: ['autodocs'], decorators: [ moduleMetadata({ imports: [ ExtraButtonComponent, - ButtonVariantsComponent, ButtonSizesComponent, - ButtonIconsComponent, - ButtonStatesComponent, + ButtonBadgeComponent, + ButtonBaseComponent, + ButtonDisabledComponent, + ButtonIconComponent, + ButtonLoadingComponent, + ButtonOutlinedComponent, ButtonRoundedComponent, + ButtonSeverityComponent, + ButtonSeverityFigmaComponent, + ButtonFigmaMappingComponent, + ButtonSizesComponent, + ButtonTextComponent, ButtonFluidComponent, ButtonEventsComponent ] @@ -35,46 +51,35 @@ const meta: Meta = { parameters: { docs: { description: { - component: `Кнопка — интерактивный элемент интерфейса для инициализации действий, отправки форм и навигации. + component: `Интерактивный элемент интерфейса. Используется для инициации действий, отправки форм и навигации. -Реализована по спецификации [button.md](https://github.com/cdek-it/angular-ui-kit/blob/main/docs/components-api/button.md). +Помимо перечисленных свойств, на кнопку можно вешать нативные DOM-события (\`click\`, \`focus\`, \`blur\`, \`keydown\` и т.п.) напрямую; \`focus\` и \`blur\` также объявлены как типизированные события компонента. \`\`\`typescript import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; -\`\`\` - -Помимо перечисленных свойств, на кнопку можно вешать нативные DOM-события (\`click\`, \`focus\`, \`blur\`, \`keydown\` и т.п.) напрямую; \`focus\` и \`blur\` также объявлены как типизированные события компонента.` +\`\`\`` } } }, argTypes: { - // ── Свойства (docs/components-api/button.md) ─────────────── + // ── Props ──────────────────────────────────────────────── label: { control: 'text', - description: 'Текст на кнопке', + description: 'Текст кнопки', table: { - category: 'Свойства', - defaultValue: { summary: "''" }, + category: 'Props', + defaultValue: { summary: 'Button' }, type: { summary: 'string' } } }, - icon: { - control: 'text', - description: 'Класс иконки tabler icon на кнопке', - table: { - category: 'Свойства', - defaultValue: { summary: "''" }, - type: { summary: 'string' } - } - }, - iconPosition: { + severity: { control: 'select', - options: ['left', 'right'], - description: 'Позиция иконки относительно текста', + options: [null, 'success', 'info', 'warning', 'danger'], + description: 'Семантический вариант кнопки', table: { - category: 'Свойства', - defaultValue: { summary: 'left' }, - type: { summary: "'left' | 'right'" } + category: 'Props', + defaultValue: { summary: 'null' }, + type: { summary: "'success' | 'info' | 'warning' | 'danger' | null" } } }, variant: { @@ -82,74 +87,157 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; options: ['primary', 'secondary', 'tertiary', 'text', 'link'], description: 'Вариант отображения кнопки', table: { - category: 'Свойства', + category: 'Props', defaultValue: { summary: 'primary' }, type: { summary: "'primary' | 'secondary' | 'tertiary' | 'text' | 'link'" } } }, - severity: { - control: 'select', - options: ['base', 'danger', 'warning', 'success', 'info'], - description: 'Семантическое состояние кнопки', - table: { - category: 'Свойства', - defaultValue: { summary: 'base' }, - type: { summary: "'base' | 'danger' | 'warning' | 'success' | 'info'" } - } - }, size: { control: 'select', options: ['small', 'base', 'large', 'xlarge'], description: 'Размер кнопки', table: { - category: 'Свойства', + category: 'Props', defaultValue: { summary: 'base' }, type: { summary: "'small' | 'base' | 'large' | 'xlarge'" } } }, + icon: { + control: 'text', + description: 'CSS-класс иконки (например: ti ti-check)', + table: { + category: 'Props', + defaultValue: { summary: '' }, + type: { summary: 'string' } + } + }, + iconPosition: { + control: 'select', + options: [null, 'prefix', 'postfix'], + description: 'Позиция иконки относительно текста', + table: { + category: 'Props', + defaultValue: { summary: 'null' }, + type: { summary: "'prefix' | 'postfix' | null" } + } + }, + iconOnly: { + control: 'boolean', + description: 'Только иконка, без текста', + table: { + category: 'Props', + defaultValue: { summary: 'false' }, + type: { summary: 'boolean' } + } + }, rounded: { control: 'boolean', description: 'Скруглённая форма кнопки', table: { - category: 'Свойства', + category: 'Props', + defaultValue: { summary: 'false' }, + type: { summary: 'boolean' } + } + }, + disabled: { + control: 'boolean', + description: 'Отключённое состояние', + table: { + category: 'Props', + defaultValue: { summary: 'false' }, + type: { summary: 'boolean' } + } + }, + loading: { + control: 'boolean', + description: 'Состояние загрузки с индикатором', + table: { + category: 'Props', defaultValue: { summary: 'false' }, type: { summary: 'boolean' } } }, fluid: { control: 'boolean', - description: 'Растягивает кнопку на всю ширину контейнера', + description: 'Растягивать ли кнопку на всю ширину контейнера', table: { - category: 'Свойства', + category: 'Props', defaultValue: { summary: 'false' }, type: { summary: 'boolean' } } }, - disabled: { + ariaLabel: { + control: 'text', + description: 'Метка для экранных дикторов', + table: { + category: 'Props', + defaultValue: { summary: 'undefined' }, + type: { summary: 'string' } + } + }, + autofocus: { control: 'boolean', - description: 'Отключённое состояние кнопки', + description: 'Автофокус при загрузке', table: { - category: 'Свойства', + category: 'Props', defaultValue: { summary: 'false' }, type: { summary: 'boolean' } } }, - loading: { + tabindex: { + control: 'number', + description: 'Порядок фокуса', + table: { + category: 'Props', + defaultValue: { summary: 'undefined' }, + type: { summary: 'number' } + } + }, + text: { + control: 'boolean', + description: 'Текстовый вариант кнопки', + table: { + category: 'Props', + defaultValue: { summary: 'false' }, + type: { summary: 'boolean' } + } + }, + // ── Badge ──────────────────────────────────────────────── + badge: { + control: 'text', + description: 'Значение бейджа', + table: { + category: 'Badge', + defaultValue: { summary: '' }, + type: { summary: 'string' } + } + }, + badgeSeverity: { + control: 'select', + options: [null, 'success', 'info', 'warning', 'danger', 'secondary', 'contrast'], + description: 'Цветовая схема бейджа', + table: { + category: 'Badge', + defaultValue: { summary: 'null' }, + type: { summary: "'success' | 'info' | 'warning' | 'danger' | 'secondary' | 'contrast' | null" } + } + }, + showBadge: { control: 'boolean', - description: 'Состояние загрузки: спиннер, кнопка неактивна', + description: 'Показывать ли бейдж', table: { - category: 'Свойства', + category: 'Badge', defaultValue: { summary: 'false' }, type: { summary: 'boolean' } } }, - // ── События ───────────────────────────────────────────────── + // ── Events ─────────────────────────────────────────────── focus: { control: false, description: 'Кнопка получила фокус', action: 'focus', table: { - category: 'События', + category: 'Events', type: { summary: 'EventEmitter' } } }, @@ -158,87 +246,65 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; description: 'Кнопка потеряла фокус', action: 'blur', table: { - category: 'События', + category: 'Events', type: { summary: 'EventEmitter' } } }, onClick: { control: false, - description: 'Нативный клик по кнопке', - action: 'click', + description: 'Событие клика по кнопке', table: { - category: 'События', - type: { summary: 'DOM-событие click (нативное)' } - } - }, - onFocus: { - control: false, - description: 'Нативный фокус (прослушивается напрямую на extra-button)', - table: { - category: 'События', - disable: true - } - }, - onBlur: { - control: false, - description: 'Нативная потеря фокуса (прослушивается напрямую на extra-button)', - table: { - category: 'События', - disable: true + category: 'Events', + type: { summary: 'EventEmitter' } } } }, args: { - label: 'Button', - icon: '', - iconPosition: 'left', - variant: 'primary', - severity: 'base', - size: 'base', - rounded: false, + showBadge: false, + badge: '', + badgeSeverity: null, fluid: false, - disabled: false, - loading: false + autofocus: false, + text: false } }; export default meta; type Story = StoryObj; -// ── Primary (интерактивная) ────────────────────────────────────────────────── +// ── Default ────────────────────────────────────────────────────────────────── export const Default: Story = { - name: 'Primary', + name: 'Default', render: (args) => { const parts: string[] = []; if (args.label != null && args.label !== '') parts.push(`label="${args.label}"`); - if (args.icon != null && args.icon !== '') parts.push(`icon="${args.icon}"`); - if (args.iconPosition != null && args.iconPosition !== 'left') parts.push(`iconPosition="${args.iconPosition}"`); - if (args.variant != null && args.variant !== 'primary') parts.push(`variant="${args.variant}"`); - if (args.severity != null && args.severity !== 'base') parts.push(`severity="${args.severity}"`); - if (args.size != null && args.size !== 'base') parts.push(`size="${args.size}"`); + if (args.severity != null) parts.push(`severity="${args.severity}"`); + if (args.variant != null) parts.push(`variant="${args.variant}"`); + if (args.size != null) parts.push(`size="${args.size}"`); + if (args.icon != null && (args.icon as string) !== '') parts.push(`icon="${args.icon}"`); + if (args.iconPosition != null) parts.push(`iconPosition="${args.iconPosition}"`); if (args.rounded) parts.push(`[rounded]="true"`); - if (args.fluid) parts.push(`[fluid]="true"`); if (args.disabled) parts.push(`[disabled]="true"`); if (args.loading) parts.push(`[loading]="true"`); const template = parts.length - ? `` - : ``; + ? `` + : ``; return { props: args, template }; }, + args: { + label: 'Button' + }, parameters: { docs: { description: { - story: - 'Интерактивная кнопка со всеми свойствами спецификации (по умолчанию — primary). Используйте Controls для изменения пропсов; click/focus/blur логируются в панель Actions.' + story: 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.' } } } }; -// ── Комбинаторные истории ──────────────────────────────────────────────────── - -export { Variants, Sizes, Icons, States, Rounded, Fluid, Events }; +export { Sizes, Text, Severity, SeverityFigma, FigmaMapping, Rounded, Outlined, Loading, Icon, Extra, Disabled, Base, Badge, Fluid, Events }; diff --git a/src/stories/components/button/examples/button-badge.component.ts b/src/stories/components/button/examples/button-badge.component.ts new file mode 100644 index 00000000..84cf460e --- /dev/null +++ b/src/stories/components/button/examples/button-badge.component.ts @@ -0,0 +1,64 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+
+ + + + + + + +
+
+`; +const styles = ''; + +@Component({ + selector: 'app-button-badge', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonBadgeComponent {} + +export const Badge: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: 'Badge кнопка' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-button-text', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonBadgeComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-base.component.ts b/src/stories/components/button/examples/button-base.component.ts new file mode 100644 index 00000000..6f30abab --- /dev/null +++ b/src/stories/components/button/examples/button-base.component.ts @@ -0,0 +1,164 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { Button, ButtonIconPosition } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+ + +
+`; +const styles = ``; + +@Component({ + selector: 'app-button-base', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonBaseComponent { + @Input() label = ''; + @Input() disabled = false; + @Input() loading = false; + @Input() size: 'small' | 'large' | undefined; + @Input() rounded = false; + @Input() outlined = false; + @Input() text = false; + @Input() icon: string = ''; + @Input() iconPos: ButtonIconPosition = 'left'; + @Input() badge: string = ''; + @Input() severity: Extract = null; +} + +export const Base: StoryObj = { + render: (args) => ({ + props: args, + template: ` + +` + }), + args: { + label: 'Base button', + disabled: false, + loading: false, + size: null, + rounded: false, + outlined: false, + text: false, + icon: '', + iconPos: null, + badge: '', + severity: null + }, + argTypes: { + label: { + description: 'Текст кнопки.' + }, + disabled: { + description: 'При наличии указывает, что компонент должен быть отключен.' + }, + loading: { + description: 'Находится ли кнопка в состоянии загрузки.' + }, + // TODO Добавить xlarge после фикса в либе. + size: { + description: 'Определяет размер кнопки.', + control: { type: 'select' }, + options: [null, 'small', 'large'], + table: { + type: { summary: `'small' | 'large' | null` } // <-- тип в документации + } + }, + rounded: { + description: 'При наличии делает кнопку с закругленными краями.' + }, + outlined: { + description: 'При наличии делает кнопку с контуром без фона.' + }, + text: { + description: 'При наличии делает кнопку текстовой без фона и границ.' + }, + icon: { + description: 'Имя иконки для отображения в кнопке.' + }, + iconPos: { + description: 'Позиция иконки относительно текста.', + control: { type: 'select' }, + options: [null, 'left', 'right'], + table: { + type: { summary: `'left' | 'right' | null` } + } + }, + badge: { + description: 'Текст для отображения в виде бейджа на кнопке.' + }, + severity: { + description: 'Определяет цветовую схему кнопки.', + control: { type: 'select' }, + options: [null, 'success', 'info', 'warn', 'primary', 'help', 'danger'], + table: { + type: { + summary: `'success' | 'info' | 'warn' | 'primary' | 'help' | 'danger' | null` + } + } + } + }, + parameters: { + docs: { + description: { + story: 'Стандартная кнопка' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { Button } from 'primeng/button'; + +@Component({ + selector: 'app-button-base', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonBaseComponent { + @Input() label = ''; + @Input() disabled = false; + @Input() loading = false; + @Input() size: 'small' | 'large' | null = null; + @Input() rounded = false; +} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-disabled.component.ts b/src/stories/components/button/examples/button-disabled.component.ts new file mode 100644 index 00000000..52f73c5e --- /dev/null +++ b/src/stories/components/button/examples/button-disabled.component.ts @@ -0,0 +1,53 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+
+ +
+
+`; +const styles = ''; + +@Component({ + selector: 'app-button-disabled', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonDisabledComponent {} + +export const Disabled: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: 'Disabled кнопка' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-button-disabled', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonDisabledComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-extra.component.ts b/src/stories/components/button/examples/button-extra.component.ts new file mode 100644 index 00000000..a6127a44 --- /dev/null +++ b/src/stories/components/button/examples/button-extra.component.ts @@ -0,0 +1,107 @@ +import { StoryObj } from '@storybook/angular'; + +export { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; + +export const Extra: StoryObj = { + render: (args) => ({ + props: args, + template: ` +` + }), + args: { + label: 'Button', + showBadge: false, + fluid: false, + autofocus: false, + text: false + }, + argTypes: { + label: { control: 'text' }, + variant: { + control: 'select', + options: ['primary', 'secondary', 'tertiary', 'text', 'link'] + }, + severity: { + control: 'select', + options: [null, 'success', 'warning', 'danger', 'info'] + }, + size: { + control: 'select', + options: ['small', 'base', 'large', 'xlarge'] + }, + rounded: { control: 'boolean' }, + iconPosition: { + control: 'select', + options: [null, 'prefix', 'postfix'] + }, + iconOnly: { control: 'boolean' }, + icon: { control: 'text' }, + disabled: { control: 'boolean' }, + loading: { control: 'boolean' }, + badge: { control: 'text' }, + badgeSeverity: { + control: 'select', + options: [null, 'success', 'warning', 'danger', 'info', 'secondary', 'contrast'] + }, + showBadge: { control: 'boolean' }, + fluid: { control: 'boolean' }, + ariaLabel: { control: 'text' }, + autofocus: { control: 'boolean' }, + tabindex: { control: 'number' }, + text: { control: 'boolean' } + }, + parameters: { + docs: { + description: { + story: 'Интерактивный пример с пропсами, соответствующими Figma-компоненту Button.' + } + } + } +}; + +export const Badge: StoryObj = { + render: (args) => ({ + props: args, + template: ` +` + }), + args: { + label: 'Emails', + badge: '8', + badgeSeverity: 'danger', + showBadge: true, + severity: 'success' + }, + parameters: { + docs: { + description: { + story: 'Пример кнопки с бейджем для отображения уведомлений или счётчиков.' + } + } + } +}; diff --git a/src/stories/components/button/examples/button-figma-mapping.component.ts b/src/stories/components/button/examples/button-figma-mapping.component.ts new file mode 100644 index 00000000..c6ea1204 --- /dev/null +++ b/src/stories/components/button/examples/button-figma-mapping.component.ts @@ -0,0 +1,144 @@ +import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; + +/** + * Схема соответствия вариантов кнопок Figma ↔ код (UI Kit (DS) v2.1, node-id=484:6197). + * Слева — вариант из Figma (страница Buttons: primary / secondary / tertiary / text / link; + * страница Severity: outlined × severity), справа — реальный инстанс extra-button, + * отрисованный тем же PrimeNG v20, что и в проде. По одному инстансу на вариант. + */ + +const template = ` +
+
+
Figma variant
+
extra-button instance
+
PrimeNG v20 props
+
+ @for (row of rows; track row.figma) { +
+
+ {{ row.page }} + {{ row.figma }} +
+
+ + +
+
{{ row.props }}
+
+ } +
+`; + +const stateStyles = ` +.fm-root { display: flex; flex-direction: column; gap: 0.5rem; max-width: 56rem; } +.fm-grid { + display: grid; + grid-template-columns: 14rem 16rem 1fr; + gap: 1rem; + align-items: center; + padding: 0.5rem 0.75rem; + border-radius: 0.5rem; +} +.fm-head { font-weight: 600; font-size: 0.875rem; opacity: 0.7; } +.fm-figma { display: flex; flex-direction: column; gap: 0.125rem; } +.fm-page { font-size: 0.75rem; opacity: 0.6; } +.fm-variant { font-weight: 600; font-size: 0.875rem; } +.fm-props code { font-size: 0.8125rem; } +.fm-broken { outline: 2px dashed #dc3009; outline-offset: 2px; } +`; + +interface FigmaMappingRow { + page: string; + figma: string; + label: string; + variant: 'primary' | 'secondary' | 'tertiary' | 'text' | 'link'; + severity: 'success' | 'warning' | 'danger' | 'info' | null; + props: string; + broken?: boolean; +} + +@Component({ + selector: 'app-button-figma-mapping', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ExtraButtonComponent], + template, + styles: stateStyles +}) +export class ButtonFigmaMappingComponent { + // Расхождение tertiary → outlined исправлено: tertiary без severity теперь + // уходит в severity=contrast (серая заливка), outlined остаётся только для + // tertiary + severity (Figma Severity/variant=outlined). + readonly rows: FigmaMappingRow[] = [ + { + page: 'Buttons', + figma: 'variant=primary', + label: 'Primary', + variant: 'primary', + severity: null, + props: 'p-button label' + }, + { + page: 'Buttons', + figma: 'variant=secondary', + label: 'Secondary', + variant: 'secondary', + severity: null, + props: 'p-button severity="secondary"' + }, + { + page: 'Buttons', + figma: 'variant=tertiary (gray)', + label: 'Tertiary', + variant: 'tertiary', + severity: null, + props: 'p-button severity="contrast"' + }, + { + page: 'Buttons', + figma: 'variant=text', + label: 'Text', + variant: 'text', + severity: null, + props: 'p-button variant="text"' + }, + { + page: 'Buttons', + figma: 'variant=link', + label: 'Link', + variant: 'link', + severity: null, + props: 'p-button link' + }, + { + page: 'Severity', + figma: 'variant=outlined severity=danger', + label: 'Danger', + variant: 'tertiary', + severity: 'danger', + props: 'p-button variant="outlined" severity="danger"' + } + ]; +} + +export const FigmaMapping: StoryObj = { + name: 'Figma mapping', + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: + 'Схема соответствия вариантов кнопок Figma (UI Kit (DS) v2.1, node-id=484:6197) и инстансов extra-button на PrimeNG v20. По одному инстансу на вариант. Красной пунктирной рамкой отмечено расхождение: Figma tertiary (серая заливка) сейчас рендерится как outlined.' + } + } + } +}; diff --git a/src/stories/components/button/examples/button-icon.component.ts b/src/stories/components/button/examples/button-icon.component.ts new file mode 100644 index 00000000..f7a1d0fe --- /dev/null +++ b/src/stories/components/button/examples/button-icon.component.ts @@ -0,0 +1,55 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+
+ + + +
+
+`; +const styles = ''; + +@Component({ + selector: 'app-button-icon', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonIconComponent {} + +export const Icon: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: 'Кнопки с иконками' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-button-icon', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonIconComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-icons.component.ts b/src/stories/components/button/examples/button-icons.component.ts deleted file mode 100644 index 04338de3..00000000 --- a/src/stories/components/button/examples/button-icons.component.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { StoryObj } from '@storybook/angular'; -import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; - -const template = ` -
-
-
- - - -
-
- - - - -
-
- - - - - -
-
- - - - -
-
- - - - - -
-
-
-`; - -@Component({ - selector: 'app-button-icons', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ExtraButtonComponent], - template -}) -export class ButtonIconsComponent {} - -export const Icons: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: - 'Кнопки с иконкой: позиции icon-position (left / right, по умолчанию left), кнопки только с иконкой (пустой label) во всех размерах, вариантах, severity и в скруглённой форме.' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; -import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; - -@Component({ - selector: 'app-button-icons', - standalone: true, - imports: [ExtraButtonComponent], - template: \` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \`, -}) -export class ButtonIconsComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-loading.component.ts b/src/stories/components/button/examples/button-loading.component.ts new file mode 100644 index 00000000..4707dbd4 --- /dev/null +++ b/src/stories/components/button/examples/button-loading.component.ts @@ -0,0 +1,53 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+
+ +
+
+`; +const styles = ''; + +@Component({ + selector: 'app-button-loading', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonLoadingComponent {} + +export const Loading: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: 'Кнопка в состоянии загрузки' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-button-loading', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonLoadingComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-outlined.component.ts b/src/stories/components/button/examples/button-outlined.component.ts new file mode 100644 index 00000000..45b324ef --- /dev/null +++ b/src/stories/components/button/examples/button-outlined.component.ts @@ -0,0 +1,51 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+ + +
`; +const styles = ''; + +@Component({ + selector: 'app-button-outlined', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonOutlinedComponent {} + +export const Outlined: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: 'Outlined кнопка' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-button-outlined', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonOutlinedComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-rounded.component.ts b/src/stories/components/button/examples/button-rounded.component.ts index b3691efb..a8d71d14 100644 --- a/src/stories/components/button/examples/button-rounded.component.ts +++ b/src/stories/components/button/examples/button-rounded.component.ts @@ -1,32 +1,23 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; import { StoryObj } from '@storybook/angular'; -import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = `
-
-
- - - - -
-
- - - - -
+
+
`; +const styles = ''; @Component({ selector: 'app-button-rounded', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ExtraButtonComponent], - template + imports: [Button], + template, + styles }) export class ButtonRoundedComponent {} @@ -37,35 +28,22 @@ export const Rounded: StoryObj = { parameters: { docs: { description: { - story: - 'Скруглённая форма (rounded): варианты отображения при severity="base", семантические состояния (severity) и иконочные кнопки. Пересечения severity с вариантами не показываются — severity, кроме "base", перекрывает их.' + story: 'Скругленная кнопка' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; -import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; @Component({ selector: 'app-button-rounded', standalone: true, - imports: [ExtraButtonComponent], - template: \` -
-
- - - - -
-
- - - - -
-
- \`, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} }) export class ButtonRoundedComponent {} ` diff --git a/src/stories/components/button/examples/button-severity-figma.component.ts b/src/stories/components/button/examples/button-severity-figma.component.ts new file mode 100644 index 00000000..0e21801f --- /dev/null +++ b/src/stories/components/button/examples/button-severity-figma.component.ts @@ -0,0 +1,143 @@ +import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; + +/** + * Зеркало страницы Figma «Severity 🔵» (UI Kit (DS) v2.1, node 24646:65002): + * секция 📱Severity → light-theme, компонент-сеты ///. + * Матрица как в макете: колонки Basic / Outlined / Text × ряды Default / Disabled / Loading; + * в ячейке — size (small/base/large/xlarge) × rounded (false/true) × icon (plain/prefix/postfix/icon-only). + */ + +const ICON = 'ti ti-file-alert'; + +const template = ` +
+ @for (sev of severities; track sev.value) { +
+

{{ sev.label }}

+
+
+ @for (variant of variants; track variant.value) { +
{{ variant.label }}
+ } + @for (state of states; track state.value) { +
{{ state.label }}
+ @for (variant of variants; track variant.value) { +
+ @for (size of sizes; track size) { +
+ @for (cfg of iconConfigs; track cfg.position ?? 'none') { + @for (isRounded of roundedVals; track $index) { + + + } + } +
+ } +
+ } + } +
+
+ } +
+`; + +// Раскладка матрицы severity. +const stateStyles = ` +.sf-matrix { + display: grid; + grid-template-columns: minmax(7rem, auto) repeat(3, minmax(0, 1fr)); + gap: 0.75rem; + align-items: start; +} +.sf-col-head, .sf-row-head { font-weight: 600; font-size: 0.875rem; } +.sf-row-head { padding-top: 0.375rem; } +.sf-cell { display: flex; flex-direction: column; gap: 0.5rem; } +`; + +@Component({ + selector: 'app-button-severity-figma', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ExtraButtonComponent], + template, + styles: stateStyles +}) +export class ButtonSeverityFigmaComponent { + readonly icon = ICON; + readonly severities = [ + { label: 'Danger', value: 'danger' }, + { label: 'Warning', value: 'warning' }, + { label: 'Success', value: 'success' }, + { label: 'Info', value: 'info' } + ] as const; + readonly variants = [ + { label: 'Basic', value: 'primary' }, + { label: 'Outlined', value: 'tertiary' }, + { label: 'Text', value: 'text' } + ] as const; + readonly states = [ + { label: 'Default', value: 'default' }, + { label: 'Disabled', value: 'disabled' }, + { label: 'Loading', value: 'loading' } + ] as const; + readonly sizes = ['small', 'base', 'large', 'xlarge'] as const; + readonly roundedVals = [false, true]; + readonly iconConfigs = [ + { position: null, iconOnly: false, hasIcon: false }, + { position: 'prefix', iconOnly: false, hasIcon: true }, + { position: 'postfix', iconOnly: false, hasIcon: true }, + { position: null, iconOnly: true, hasIcon: true } + ] as const; +} + +export const SeverityFigma: StoryObj = { + name: 'Severity (Figma)', + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: + 'Зеркало страницы Figma «Severity 🔵» (UI Kit (DS) v2.1, node-id=24646:65002): матрица Basic / Outlined / Text × Default / Disabled / Loading для Button.Danger / Warning / Success / Info. В каждой ячейке — size (small / base / large / xlarge) × rounded × icon (plain / prefix / postfix / icon-only). Отрисовано как есть из базы кода, без подложек.' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; + +@Component({ + selector: 'app-button-severity-figma', + standalone: true, + imports: [ExtraButtonComponent], + template: \` + + + + + \` +}) +export class ButtonSeverityFigmaComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-severity.component.ts b/src/stories/components/button/examples/button-severity.component.ts new file mode 100644 index 00000000..87261fd7 --- /dev/null +++ b/src/stories/components/button/examples/button-severity.component.ts @@ -0,0 +1,123 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+
+
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+
+
+`; +const styles = ''; + +@Component({ + selector: 'app-button-severity', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonSeverityComponent {} + +export const Severity: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: 'Кнопки с разным Severity' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-button-severity', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonSeverityComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-sizes.component.ts b/src/stories/components/button/examples/button-sizes.component.ts index 20c8677a..841276af 100644 --- a/src/stories/components/button/examples/button-sizes.component.ts +++ b/src/stories/components/button/examples/button-sizes.component.ts @@ -4,35 +4,24 @@ import { ExtraButtonComponent } from '../../../../lib/components/button/button.c const template = `
-
-
- - - - -
-
- - - - -
-
- - - - -
+
+ + + +
`; +const styles = ''; + @Component({ selector: 'app-button-sizes', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ExtraButtonComponent], - template + template, + styles }) export class ButtonSizesComponent {} @@ -43,38 +32,22 @@ export const Sizes: StoryObj = { parameters: { docs: { description: { - story: 'Размеры кнопки: small, base, large, xlarge — обычные, с иконкой и скруглённые.' + story: 'Кнопки разных размеров' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; -import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; @Component({ selector: 'app-button-sizes', standalone: true, - imports: [ExtraButtonComponent], - template: \` -
- - - - -
-
- - - - -
-
- - - - -
- \`, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ExtraButtonComponent + ], + template: ${template}, + styles: ${styles} }) export class ButtonSizesComponent {} ` diff --git a/src/stories/components/button/examples/button-states.component.ts b/src/stories/components/button/examples/button-states.component.ts deleted file mode 100644 index 3eb4fd36..00000000 --- a/src/stories/components/button/examples/button-states.component.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { StoryObj } from '@storybook/angular'; -import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; - -const template = ` -
-
-
- - - - -
- - @for (variant of variants; track variant) { -
- - - -
- } - - @for (severity of severities; track severity) { -
- - - -
- } -
-
-`; - -@Component({ - selector: 'app-button-states', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ExtraButtonComponent], - template -}) -export class ButtonStatesComponent { - variants = ['secondary', 'tertiary', 'text', 'link']; - severities = ['danger', 'warning', 'success', 'info']; - - capitalize(value: string): string { - return value.charAt(0).toUpperCase() + value.slice(1); - } -} - -export const States: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: - 'Состояния disabled и loading (спиннер, кнопка неактивна) для каждого variant и каждого severity. Первая кнопка в ряду названа по variant/severity.' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; -import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; - -@Component({ - selector: 'app-button-states', - standalone: true, - imports: [ExtraButtonComponent], - template: \` -
- - - - -
- - - - - - - - - - - - - - \`, -}) -export class ButtonStatesComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-text.component.ts b/src/stories/components/button/examples/button-text.component.ts new file mode 100644 index 00000000..20ebb093 --- /dev/null +++ b/src/stories/components/button/examples/button-text.component.ts @@ -0,0 +1,51 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Button } from 'primeng/button'; +import { StoryObj } from '@storybook/angular'; + +const template = ` +
+ +
+`; +const styles = ''; + +@Component({ + selector: 'app-button-text', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [Button], + template, + styles +}) +export class ButtonTextComponent {} + +export const Text: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: 'Text кнопка' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-button-text', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + Button + ], + template: ${template}, + styles: ${styles} +}) +export class ButtonTextComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-variants.component.ts b/src/stories/components/button/examples/button-variants.component.ts deleted file mode 100644 index ccf644e4..00000000 --- a/src/stories/components/button/examples/button-variants.component.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { StoryObj } from '@storybook/angular'; -import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; - -const template = ` -
-
-
- - - - - -
-
- - - - -
-
-
-`; - -@Component({ - selector: 'app-button-variants', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ExtraButtonComponent], - template -}) -export class ButtonVariantsComponent {} - -export const Variants: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: - 'Варианты отображения (variant) при severity="base" и семантические состояния (severity). Пересечения не показываются: severity, кроме "base", перекрывает варианты primary/secondary/tertiary, и кнопка выглядит как обычная severity-кнопка.' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; -import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; - -@Component({ - selector: 'app-button-variants', - standalone: true, - imports: [ExtraButtonComponent], - template: \` -
-
- - - - - -
-
- - - - -
-
- \`, -}) -export class ButtonVariantsComponent {} - ` - } - } - } -}; From a81d7ce4ef80eb09545a57e8206ab439d544f9f8 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Fri, 9 Oct 2026 15:55:42 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B3=D0=B5=D1=82=D1=82=D0=B5=D1=80=20primeStyleClass?= =?UTF-8?q?=20=D0=B2=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/button/button.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/components/button/button.component.ts b/src/lib/components/button/button.component.ts index 836362fd..395e0f86 100644 --- a/src/lib/components/button/button.component.ts +++ b/src/lib/components/button/button.component.ts @@ -21,7 +21,7 @@ type ExtraBadgeSeverityValue = PrimeBadgeSeverity; [disabled]="disabled" [loading]="loading" [size]="primeSize" - [styleClass]="size === 'xlarge' ? 'p-button-xlg' : ''" + [styleClass]="primeStyleClass" [rounded]="rounded" [outlined]="variant === 'tertiary' && severity !== null" [text]="variant === 'text' || text" @@ -69,6 +69,10 @@ export class ExtraButtonComponent { return undefined; } + get primeStyleClass(): string { + return this.size === 'xlarge' ? 'p-button-xlg' : ''; + } + get primeIconPosition(): 'left' | 'right' { return this.iconPosition === 'postfix' ? 'right' : 'left'; } From 5be340374699c92aa83389df9b7363dee5ef6ca8 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Fri, 9 Oct 2026 15:56:05 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20bas?= =?UTF-8?q?e=20=D0=B2=20severity=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=81=D0=BF=D0=B5=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/button/button.component.ts | 11 +++++------ .../confirm-dialog/confirm-dialog.component.ts | 2 +- src/stories/components/button/button.stories.ts | 8 ++++---- .../examples/button-figma-mapping.component.ts | 14 +++++++------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/lib/components/button/button.component.ts b/src/lib/components/button/button.component.ts index 395e0f86..9b14c512 100644 --- a/src/lib/components/button/button.component.ts +++ b/src/lib/components/button/button.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from import { Button, ButtonSeverity as PrimeButtonSeverity } from 'primeng/button'; export type ExtraButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'text' | 'link'; -export type ExtraButtonSeverity = 'success' | 'warning' | 'danger' | 'info' | null; +export type ExtraButtonSeverity = 'base' | 'danger' | 'warning' | 'success' | 'info'; export type ExtraButtonSize = 'small' | 'base' | 'large' | 'xlarge'; export type ExtraButtonIconPosition = 'prefix' | 'postfix' | null; export type ExtraBadgeSeverity = 'success' | 'info' | 'warning' | 'danger' | 'secondary' | 'contrast' | null; @@ -23,7 +23,7 @@ type ExtraBadgeSeverityValue = PrimeBadgeSeverity; [size]="primeSize" [styleClass]="primeStyleClass" [rounded]="rounded" - [outlined]="variant === 'tertiary' && severity !== null" + [outlined]="variant === 'tertiary' && severity !== 'base'" [text]="variant === 'text' || text" [link]="variant === 'link'" [icon]="icon" @@ -43,7 +43,7 @@ type ExtraBadgeSeverityValue = PrimeBadgeSeverity; export class ExtraButtonComponent { @Input() label = 'Button'; @Input() variant: ExtraButtonVariant = 'primary'; - @Input() severity: ExtraButtonSeverity = null; + @Input() severity: ExtraButtonSeverity = 'base'; @Input() size: ExtraButtonSize = 'base'; @Input() rounded = false; @Input() iconPosition: ExtraButtonIconPosition = null; @@ -79,9 +79,8 @@ export class ExtraButtonComponent { get primeSeverity(): ExtraButtonSeverityValue | null { if (this.variant === 'secondary') return 'secondary'; - if (this.variant === 'tertiary' && this.severity === null) return 'contrast'; - if (this.severity === 'warning') return 'warn'; - return this.severity; + if (this.severity === 'base') return this.variant === 'tertiary' ? 'contrast' : null; + return this.severity === 'warning' ? 'warn' : this.severity; } get primeBadgeSeverity(): ExtraBadgeSeverityValue { diff --git a/src/lib/components/confirm-dialog/confirm-dialog.component.ts b/src/lib/components/confirm-dialog/confirm-dialog.component.ts index e19f006e..27b850aa 100644 --- a/src/lib/components/confirm-dialog/confirm-dialog.component.ts +++ b/src/lib/components/confirm-dialog/confirm-dialog.component.ts @@ -76,7 +76,7 @@ export class ExtraConfirmDialogComponent { mapSeverity(severity: string | null | undefined): ExtraButtonSeverity { if (severity === 'danger' || severity === 'warn' || severity === 'warning') return severity === 'warn' ? 'warning' : severity; if (severity === 'success' || severity === 'info') return severity; - return null; + return 'base'; } get computedClass(): string { diff --git a/src/stories/components/button/button.stories.ts b/src/stories/components/button/button.stories.ts index 79875841..a22e8183 100644 --- a/src/stories/components/button/button.stories.ts +++ b/src/stories/components/button/button.stories.ts @@ -74,12 +74,12 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; }, severity: { control: 'select', - options: [null, 'success', 'info', 'warning', 'danger'], + options: ['base', 'danger', 'warning', 'success', 'info'], description: 'Семантический вариант кнопки', table: { category: 'Props', - defaultValue: { summary: 'null' }, - type: { summary: "'success' | 'info' | 'warning' | 'danger' | null" } + defaultValue: { summary: 'base' }, + type: { summary: "'base' | 'danger' | 'warning' | 'success' | 'info'" } } }, variant: { @@ -280,7 +280,7 @@ export const Default: Story = { const parts: string[] = []; if (args.label != null && args.label !== '') parts.push(`label="${args.label}"`); - if (args.severity != null) parts.push(`severity="${args.severity}"`); + if (args.severity != null && args.severity !== 'base') parts.push(`severity="${args.severity}"`); if (args.variant != null) parts.push(`variant="${args.variant}"`); if (args.size != null) parts.push(`size="${args.size}"`); if (args.icon != null && (args.icon as string) !== '') parts.push(`icon="${args.icon}"`); diff --git a/src/stories/components/button/examples/button-figma-mapping.component.ts b/src/stories/components/button/examples/button-figma-mapping.component.ts index c6ea1204..afb1e507 100644 --- a/src/stories/components/button/examples/button-figma-mapping.component.ts +++ b/src/stories/components/button/examples/button-figma-mapping.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; import { StoryObj } from '@storybook/angular'; -import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; +import { ExtraButtonComponent, ExtraButtonSeverity } from '../../../../lib/components/button/button.component'; /** * Схема соответствия вариантов кнопок Figma ↔ код (UI Kit (DS) v2.1, node-id=484:6197). @@ -58,7 +58,7 @@ interface FigmaMappingRow { figma: string; label: string; variant: 'primary' | 'secondary' | 'tertiary' | 'text' | 'link'; - severity: 'success' | 'warning' | 'danger' | 'info' | null; + severity: ExtraButtonSeverity; props: string; broken?: boolean; } @@ -82,7 +82,7 @@ export class ButtonFigmaMappingComponent { figma: 'variant=primary', label: 'Primary', variant: 'primary', - severity: null, + severity: 'base', props: 'p-button label' }, { @@ -90,7 +90,7 @@ export class ButtonFigmaMappingComponent { figma: 'variant=secondary', label: 'Secondary', variant: 'secondary', - severity: null, + severity: 'base', props: 'p-button severity="secondary"' }, { @@ -98,7 +98,7 @@ export class ButtonFigmaMappingComponent { figma: 'variant=tertiary (gray)', label: 'Tertiary', variant: 'tertiary', - severity: null, + severity: 'base', props: 'p-button severity="contrast"' }, { @@ -106,7 +106,7 @@ export class ButtonFigmaMappingComponent { figma: 'variant=text', label: 'Text', variant: 'text', - severity: null, + severity: 'base', props: 'p-button variant="text"' }, { @@ -114,7 +114,7 @@ export class ButtonFigmaMappingComponent { figma: 'variant=link', label: 'Link', variant: 'link', - severity: null, + severity: 'base', props: 'p-button link' }, { From 38a1507b63df22ec53bb98c1e6c0e1a3346c758c Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Fri, 9 Oct 2026 17:44:23 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D1=82?= =?UTF-8?q?=D1=8C=20severity=20base=20=D0=B2=20args=20stories=20=D0=B8=20?= =?UTF-8?q?=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BD?= =?UTF-8?q?=D0=BE=D0=BF=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/button/button.figma.md | 2 +- src/stories/components/button/button.stories.ts | 1 + .../components/button/examples/button-extra.component.ts | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/components/button/button.figma.md b/src/lib/components/button/button.figma.md index 7d81e513..3ebbae71 100644 --- a/src/lib/components/button/button.figma.md +++ b/src/lib/components/button/button.figma.md @@ -25,7 +25,7 @@ updated: '2026-06-22' |----------|-----|--------------|---------| | `label` | `string` | `'Button'` | Текст кнопки | | `variant` | `'primary' \| 'secondary' \| 'outlined' \| 'text' \| 'link'` | `'primary'` | Вариант отображения — соответствует Figma-свойству `variant` | -| `severity` | `'success' \| 'warning' \| 'danger' \| 'info' \| null` | `null` | Семантический акцент — соответствует Figma-компоненту `` и аналогам | +| `severity` | `'base' \| 'danger' \| 'warning' \| 'success' \| 'info'` | `'base'` | Семантический акцент — соответствует Figma-компоненту `` и аналогам | | `size` | `'small' \| 'base' \| 'large' \| 'xlarge'` | `'base'` | Размер кнопки — соответствует Figma-свойству `size` | | `icon` | `string` | `''` | CSS-класс иконки (например `'pi pi-check'`); доступные иконки — [icons.md](../../figma-code-connect/icons.md) | | `iconPos` | `'prefix' \| 'postfix' \| null` | `null` | Позиция иконки — соответствует Figma-свойству `icon-position` | diff --git a/src/stories/components/button/button.stories.ts b/src/stories/components/button/button.stories.ts index a22e8183..8c1f7e2f 100644 --- a/src/stories/components/button/button.stories.ts +++ b/src/stories/components/button/button.stories.ts @@ -260,6 +260,7 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; } }, args: { + severity: 'base', showBadge: false, badge: '', badgeSeverity: null, diff --git a/src/stories/components/button/examples/button-extra.component.ts b/src/stories/components/button/examples/button-extra.component.ts index a6127a44..4b10a44f 100644 --- a/src/stories/components/button/examples/button-extra.component.ts +++ b/src/stories/components/button/examples/button-extra.component.ts @@ -29,6 +29,7 @@ export const Extra: StoryObj = { }), args: { label: 'Button', + severity: 'base', showBadge: false, fluid: false, autofocus: false, @@ -42,7 +43,7 @@ export const Extra: StoryObj = { }, severity: { control: 'select', - options: [null, 'success', 'warning', 'danger', 'info'] + options: ['base', 'danger', 'warning', 'success', 'info'] }, size: { control: 'select', From 34ef9d92491ec32694c720e25fddc65e33d5e19d Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Fri, 9 Oct 2026 17:44:43 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D1=82?= =?UTF-8?q?=D1=8C=20iconPosition=20left/right=20=D0=B2=20=D0=BA=D0=BD?= =?UTF-8?q?=D0=BE=D0=BF=D0=BA=D0=B5=20=D0=BF=D0=BE=20=D1=81=D0=BF=D0=B5?= =?UTF-8?q?=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/button/button.component.ts | 10 +++------- src/lib/components/button/button.figma.md | 6 +++--- src/stories/components/button/button.stories.ts | 9 +++++---- .../button/examples/button-extra.component.ts | 3 ++- .../button/examples/button-severity-figma.component.ts | 8 ++++---- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/lib/components/button/button.component.ts b/src/lib/components/button/button.component.ts index 9b14c512..b3a82e81 100644 --- a/src/lib/components/button/button.component.ts +++ b/src/lib/components/button/button.component.ts @@ -4,7 +4,7 @@ import { Button, ButtonSeverity as PrimeButtonSeverity } from 'primeng/button'; export type ExtraButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'text' | 'link'; export type ExtraButtonSeverity = 'base' | 'danger' | 'warning' | 'success' | 'info'; export type ExtraButtonSize = 'small' | 'base' | 'large' | 'xlarge'; -export type ExtraButtonIconPosition = 'prefix' | 'postfix' | null; +export type ExtraButtonIconPosition = 'left' | 'right'; export type ExtraBadgeSeverity = 'success' | 'info' | 'warning' | 'danger' | 'secondary' | 'contrast' | null; type PrimeBadgeSeverity = Extract; type ExtraButtonSeverityValue = PrimeButtonSeverity; @@ -27,7 +27,7 @@ type ExtraBadgeSeverityValue = PrimeBadgeSeverity; [text]="variant === 'text' || text" [link]="variant === 'link'" [icon]="icon" - [iconPos]="primeIconPosition" + [iconPos]="iconPosition" [severity]="primeSeverity" [badge]="showBadge ? badge || ' ' : undefined" [badgeSeverity]="primeBadgeSeverity" @@ -46,7 +46,7 @@ export class ExtraButtonComponent { @Input() severity: ExtraButtonSeverity = 'base'; @Input() size: ExtraButtonSize = 'base'; @Input() rounded = false; - @Input() iconPosition: ExtraButtonIconPosition = null; + @Input() iconPosition: ExtraButtonIconPosition = 'left'; @Input() iconOnly = false; @Input() icon = ''; @Input() disabled = false; @@ -73,10 +73,6 @@ export class ExtraButtonComponent { return this.size === 'xlarge' ? 'p-button-xlg' : ''; } - get primeIconPosition(): 'left' | 'right' { - return this.iconPosition === 'postfix' ? 'right' : 'left'; - } - get primeSeverity(): ExtraButtonSeverityValue | null { if (this.variant === 'secondary') return 'secondary'; if (this.severity === 'base') return this.variant === 'tertiary' ? 'contrast' : null; diff --git a/src/lib/components/button/button.figma.md b/src/lib/components/button/button.figma.md index 3ebbae71..60a41d76 100644 --- a/src/lib/components/button/button.figma.md +++ b/src/lib/components/button/button.figma.md @@ -28,7 +28,7 @@ updated: '2026-06-22' | `severity` | `'base' \| 'danger' \| 'warning' \| 'success' \| 'info'` | `'base'` | Семантический акцент — соответствует Figma-компоненту `` и аналогам | | `size` | `'small' \| 'base' \| 'large' \| 'xlarge'` | `'base'` | Размер кнопки — соответствует Figma-свойству `size` | | `icon` | `string` | `''` | CSS-класс иконки (например `'pi pi-check'`); доступные иконки — [icons.md](../../figma-code-connect/icons.md) | -| `iconPos` | `'prefix' \| 'postfix' \| null` | `null` | Позиция иконки — соответствует Figma-свойству `icon-position` | +| `iconPosition` | `'left' \| 'right'` | `'left'` | Позиция иконки — соответствует Figma-свойству `icon-position` | | `iconOnly` | `boolean` | `false` | Режим «только иконка» без текста — соответствует Figma-свойству `icon-only` | | `disabled` | `boolean` | `false` | Отключённое состояние — соответствует Figma-состоянию `state=disabled` | | `loading` | `boolean` | `false` | Состояние загрузки со спиннером — соответствует Figma-состоянию `state=loading` | @@ -84,7 +84,7 @@ Figma: ``, variant=link, size=base, state=default ``` -### С иконкой слева (prefix) +### С иконкой слева (left) Figma: ``, icon-position=prefix, icon-only=false @@ -92,7 +92,7 @@ Figma: ``, icon-position=prefix, icon-only=false ``` diff --git a/src/stories/components/button/button.stories.ts b/src/stories/components/button/button.stories.ts index 8c1f7e2f..461e1aa7 100644 --- a/src/stories/components/button/button.stories.ts +++ b/src/stories/components/button/button.stories.ts @@ -113,12 +113,12 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; }, iconPosition: { control: 'select', - options: [null, 'prefix', 'postfix'], + options: ['left', 'right'], description: 'Позиция иконки относительно текста', table: { category: 'Props', - defaultValue: { summary: 'null' }, - type: { summary: "'prefix' | 'postfix' | null" } + defaultValue: { summary: 'left' }, + type: { summary: "'left' | 'right'" } } }, iconOnly: { @@ -260,6 +260,7 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; } }, args: { + iconPosition: 'left', severity: 'base', showBadge: false, badge: '', @@ -285,7 +286,7 @@ export const Default: Story = { if (args.variant != null) parts.push(`variant="${args.variant}"`); if (args.size != null) parts.push(`size="${args.size}"`); if (args.icon != null && (args.icon as string) !== '') parts.push(`icon="${args.icon}"`); - if (args.iconPosition != null) parts.push(`iconPosition="${args.iconPosition}"`); + if (args.iconPosition != null && args.iconPosition !== 'left') parts.push(`iconPosition="${args.iconPosition}"`); if (args.rounded) parts.push(`[rounded]="true"`); if (args.disabled) parts.push(`[disabled]="true"`); if (args.loading) parts.push(`[loading]="true"`); diff --git a/src/stories/components/button/examples/button-extra.component.ts b/src/stories/components/button/examples/button-extra.component.ts index 4b10a44f..4bc20bc8 100644 --- a/src/stories/components/button/examples/button-extra.component.ts +++ b/src/stories/components/button/examples/button-extra.component.ts @@ -29,6 +29,7 @@ export const Extra: StoryObj = { }), args: { label: 'Button', + iconPosition: 'left', severity: 'base', showBadge: false, fluid: false, @@ -52,7 +53,7 @@ export const Extra: StoryObj = { rounded: { control: 'boolean' }, iconPosition: { control: 'select', - options: [null, 'prefix', 'postfix'] + options: ['left', 'right'] }, iconOnly: { control: 'boolean' }, icon: { control: 'text' }, diff --git a/src/stories/components/button/examples/button-severity-figma.component.ts b/src/stories/components/button/examples/button-severity-figma.component.ts index 0e21801f..9c495b1d 100644 --- a/src/stories/components/button/examples/button-severity-figma.component.ts +++ b/src/stories/components/button/examples/button-severity-figma.component.ts @@ -98,10 +98,10 @@ export class ButtonSeverityFigmaComponent { readonly sizes = ['small', 'base', 'large', 'xlarge'] as const; readonly roundedVals = [false, true]; readonly iconConfigs = [ - { position: null, iconOnly: false, hasIcon: false }, - { position: 'prefix', iconOnly: false, hasIcon: true }, - { position: 'postfix', iconOnly: false, hasIcon: true }, - { position: null, iconOnly: true, hasIcon: true } + { position: 'left', iconOnly: false, hasIcon: false }, + { position: 'left', iconOnly: false, hasIcon: true }, + { position: 'right', iconOnly: false, hasIcon: true }, + { position: 'left', iconOnly: true, hasIcon: true } ] as const; } From b1f6a4e2ad280276847536a10bbf861cd2b80da7 Mon Sep 17 00:00:00 2001 From: "ak.dmitriev" Date: Sun, 11 Oct 2026 23:00:09 +0700 Subject: [PATCH 06/10] fix cr --- docs/components-api/button.md | 31 ++-- src/lib/components/button/button.component.ts | 24 +-- src/lib/components/button/button.figma.md | 59 ++++++- .../components/button/button.stories.ts | 50 ++---- .../button/examples/button-badge.component.ts | 54 +++--- .../button/examples/button-base.component.ts | 164 ------------------ .../examples/button-disabled.component.ts | 53 ------ .../button/examples/button-extra.component.ts | 109 ------------ .../button-figma-mapping.component.ts | 144 --------------- .../button/examples/button-icon.component.ts | 55 ------ .../button/examples/button-icons.component.ts | 112 ++++++++++++ .../examples/button-loading.component.ts | 53 ------ .../examples/button-outlined.component.ts | 51 ------ .../examples/button-rounded.component.ts | 65 +++++-- .../button-severity-figma.component.ts | 143 --------------- .../examples/button-severity.component.ts | 123 ------------- .../button/examples/button-sizes.component.ts | 2 +- .../examples/button-states.component.ts | 98 +++++++++++ .../button/examples/button-text.component.ts | 51 ------ .../examples/button-variants.component.ts | 92 ++++++++++ 20 files changed, 479 insertions(+), 1054 deletions(-) delete mode 100644 src/stories/components/button/examples/button-base.component.ts delete mode 100644 src/stories/components/button/examples/button-disabled.component.ts delete mode 100644 src/stories/components/button/examples/button-extra.component.ts delete mode 100644 src/stories/components/button/examples/button-figma-mapping.component.ts delete mode 100644 src/stories/components/button/examples/button-icon.component.ts create mode 100644 src/stories/components/button/examples/button-icons.component.ts delete mode 100644 src/stories/components/button/examples/button-loading.component.ts delete mode 100644 src/stories/components/button/examples/button-outlined.component.ts delete mode 100644 src/stories/components/button/examples/button-severity-figma.component.ts delete mode 100644 src/stories/components/button/examples/button-severity.component.ts create mode 100644 src/stories/components/button/examples/button-states.component.ts delete mode 100644 src/stories/components/button/examples/button-text.component.ts create mode 100644 src/stories/components/button/examples/button-variants.component.ts diff --git a/docs/components-api/button.md b/docs/components-api/button.md index 0ce680ff..a056d2a8 100644 --- a/docs/components-api/button.md +++ b/docs/components-api/button.md @@ -4,15 +4,22 @@ > ✅ **Реализован**: `ExtraButtonComponent` (`@cdek-it/angular-ui-kit`) соответствует спецификации. -| Свойство | Описание | Типизация | -| --------------- | --------------------------------------------- | -------------------------------------------------- | -| `label` | текст на кнопке | `string` | -| `icon` | класс иконки tabler icon на кнопке | `string` | -| `iconPosition` | позиция иконки относительно текста | `left \| right` | -| `variant` | вариант отображения кнопки | `primary \| secondary \| tertiary \| text \| link` | -| `severity` | семантическое состояние кнопки | `base \| danger \| warning \| success \| info` | -| `size` | размер кнопки | `small \| base \| large \| xlarge` | -| `rounded` | скруглённая форма кнопки | `boolean` | -| `fluid` | растягивает кнопку на всю ширину контейнера | `boolean` | -| `disabled` | отключённое состояние кнопки | `boolean` | -| `loading` | состояние загрузки: спиннер, кнопка неактивна | `boolean` | +| Свойство | Описание | Типизация | +| --------------- | ------------------------------------------------ | ----------------------------------------------------------------------- | +| `label` | текст на кнопке | `string` | +| `icon` | класс иконки tabler icon на кнопке | `string` | +| `iconPosition` | позиция иконки относительно текста | `left \| right` | +| `variant` | вариант отображения кнопки | `primary \| secondary \| tertiary \| outlined \| text \| link` | +| `severity` | семантическое состояние кнопки | `base \| danger \| warning \| success \| info` | +| `size` | размер кнопки | `small \| base \| large \| xlarge` | +| `rounded` | скруглённая форма кнопки | `boolean` | +| `fluid` | растягивает кнопку на всю ширину контейнера | `boolean` | +| `disabled` | отключённое состояние кнопки | `boolean` | +| `loading` | состояние загрузки: спиннер, кнопка неактивна | `boolean` | +| `iconOnly` | режим «только иконка» без текста | `boolean` | +| `showBadge` | показывать бейдж поверх кнопки | `boolean` | +| `badge` | текст бейджа (виден только при `showBadge=true`) | `string` | +| `badgeSeverity` | цветовая схема бейджа | `success \| info \| warning \| danger \| secondary \| contrast \| null` | +| `ariaLabel` | метка для программ экранного доступа | `string \| undefined` | +| `autofocus` | автофокус при монтировании компонента | `boolean` | +| `tabindex` | порядок перехода по Tab | `number \| undefined` | diff --git a/src/lib/components/button/button.component.ts b/src/lib/components/button/button.component.ts index b3a82e81..42940120 100644 --- a/src/lib/components/button/button.component.ts +++ b/src/lib/components/button/button.component.ts @@ -1,14 +1,14 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { Button, ButtonSeverity as PrimeButtonSeverity } from 'primeng/button'; -export type ExtraButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'text' | 'link'; +export type ExtraButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'outlined' | 'text' | 'link'; export type ExtraButtonSeverity = 'base' | 'danger' | 'warning' | 'success' | 'info'; export type ExtraButtonSize = 'small' | 'base' | 'large' | 'xlarge'; export type ExtraButtonIconPosition = 'left' | 'right'; -export type ExtraBadgeSeverity = 'success' | 'info' | 'warning' | 'danger' | 'secondary' | 'contrast' | null; +export type ExtraButtonBadgeSeverity = 'success' | 'info' | 'warning' | 'danger' | 'secondary' | 'contrast' | null; type PrimeBadgeSeverity = Extract; type ExtraButtonSeverityValue = PrimeButtonSeverity; -type ExtraBadgeSeverityValue = PrimeBadgeSeverity; +type ExtraButtonBadgeSeverityValue = PrimeBadgeSeverity; @Component({ selector: 'extra-button', @@ -23,8 +23,8 @@ type ExtraBadgeSeverityValue = PrimeBadgeSeverity; [size]="primeSize" [styleClass]="primeStyleClass" [rounded]="rounded" - [outlined]="variant === 'tertiary' && severity !== 'base'" - [text]="variant === 'text' || text" + [outlined]="variant === 'outlined'" + [text]="variant === 'text'" [link]="variant === 'link'" [icon]="icon" [iconPos]="iconPosition" @@ -41,7 +41,7 @@ type ExtraBadgeSeverityValue = PrimeBadgeSeverity; ` }) export class ExtraButtonComponent { - @Input() label = 'Button'; + @Input() label = ''; @Input() variant: ExtraButtonVariant = 'primary'; @Input() severity: ExtraButtonSeverity = 'base'; @Input() size: ExtraButtonSize = 'base'; @@ -52,13 +52,12 @@ export class ExtraButtonComponent { @Input() disabled = false; @Input() loading = false; @Input() badge = ''; - @Input() badgeSeverity: ExtraBadgeSeverity = null; + @Input() badgeSeverity: ExtraButtonBadgeSeverity = null; @Input() showBadge = false; @Input() fluid = false; @Input() ariaLabel: string | undefined = undefined; @Input() autofocus = false; @Input() tabindex: number | undefined = undefined; - @Input() text = false; @Output() focus = new EventEmitter(); @Output() blur = new EventEmitter(); @@ -74,12 +73,15 @@ export class ExtraButtonComponent { } get primeSeverity(): ExtraButtonSeverityValue | null { - if (this.variant === 'secondary') return 'secondary'; - if (this.severity === 'base') return this.variant === 'tertiary' ? 'contrast' : null; + if (this.severity === 'base') { + if (this.variant === 'secondary') return 'secondary'; + if (this.variant === 'tertiary' || this.variant === 'outlined') return 'contrast'; + return null; + } return this.severity === 'warning' ? 'warn' : this.severity; } - get primeBadgeSeverity(): ExtraBadgeSeverityValue { + get primeBadgeSeverity(): ExtraButtonBadgeSeverityValue { if (this.badgeSeverity === 'warning') return 'warn'; return this.badgeSeverity; } diff --git a/src/lib/components/button/button.figma.md b/src/lib/components/button/button.figma.md index 60a41d76..a8cb314b 100644 --- a/src/lib/components/button/button.figma.md +++ b/src/lib/components/button/button.figma.md @@ -23,8 +23,8 @@ updated: '2026-06-22' | Свойство | Тип | По умолчанию | Описание | |----------|-----|--------------|---------| -| `label` | `string` | `'Button'` | Текст кнопки | -| `variant` | `'primary' \| 'secondary' \| 'outlined' \| 'text' \| 'link'` | `'primary'` | Вариант отображения — соответствует Figma-свойству `variant` | +| `label` | `string` | `''` | Текст кнопки | +| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'outlined' \| 'text' \| 'link'` | `'primary'` | Вариант отображения — `primary`/`secondary`/`tertiary`/`text`/`link` соответствуют Figma-свойству `variant` на `` (nodeId `160:5223`), `outlined` — Figma-свойству `variant=outlined` на ``/``/``/`` (nodeId `162:5457` и аналоги) | | `severity` | `'base' \| 'danger' \| 'warning' \| 'success' \| 'info'` | `'base'` | Семантический акцент — соответствует Figma-компоненту `` и аналогам | | `size` | `'small' \| 'base' \| 'large' \| 'xlarge'` | `'base'` | Размер кнопки — соответствует Figma-свойству `size` | | `icon` | `string` | `''` | CSS-класс иконки (например `'pi pi-check'`); доступные иконки — [icons.md](../../figma-code-connect/icons.md) | @@ -37,11 +37,43 @@ updated: '2026-06-22' | `showBadge` | `boolean` | `false` | Показывать бейдж поверх кнопки | | `badge` | `string` | `''` | Текст бейджа (отображается только при `showBadge=true`) | | `badgeSeverity` | `'success' \| 'info' \| 'warning' \| 'danger' \| 'secondary' \| 'contrast' \| null` | `null` | Цветовая схема бейджа; цвета соответствуют токенам [tokens.md](../../figma-code-connect/tokens.md) | -| `text` | `boolean` | `false` | Текстовый вариант без фона и рамки (алиас для `variant='text'`) | | `ariaLabel` | `string \| undefined` | `undefined` | Метка для программ экранного доступа | | `autofocus` | `boolean` | `false` | Автофокус при монтировании компонента | | `tabindex` | `number \| undefined` | `undefined` | Порядок перехода по Tab | +Отдельного `text` нет — текстовый стиль задаётся только через `variant="text"`; дублирующий булев +проп был в черновике ветки, убран как нарушающий правило «одно имя — один смысл» +(`docs/naming-rules.md`). + +### `tertiary` vs `outlined` — два разных Figma-компонента, два разных кода-значения + +В Figma `` и ``/``/``/`` — +РАЗНЫЕ компонент-сеты с разными наборами значений `variant`: у Base — `primary | secondary | +tertiary | text | link`, у severity-семейства — `basic | outlined | text` (подтверждено +скриншотами `` nodeId `160:5223` и `` nodeId `162:5457`). Визуально это +два разных вида: + +- `` variant=tertiary — сплошная тёмная заливка без рамки (`severity=contrast` у + PrimeNG, `outlined=false`). +- ``/аналоги variant=outlined — кнопка с цветной рамкой и прозрачным фоном + (`outlined=true`). + +Раньше оба вида смешивались в один код-уровневый `variant="tertiary"`, который неявно превращался +в контурный при `severity !== 'base'`. Это было неочевидно и не давало собрать контурную кнопку +нейтрального цвета. Теперь `outlined` — самостоятельное значение `variant` +(`[outlined]="variant === 'outlined'"`, без зависимости от `severity`): `tertiary` всегда даёт +тёмную заливку, `outlined` всегда даёт рамку, цвет рамки/текста в обоих случаях задаётся отдельно +через `severity`. + +> ⚠️ **`variant="outlined"` + `severity="base"` не имеет референса в Figma и визуально не +> работает.** В Figma `outlined` существует только у ``/``/ +> ``/`` — у `` такого состояния нет. На уровне кода +> комбинация технически доступна (оба пропа независимы), но при `severity="base"` она маппится на +> PrimeNG `severity="contrast"`, чей цветовой токен — белый текст, рассчитанный на тёмную заливку +> (`#2b2e33`); при `outlined=true` заливка снимается, и текст остаётся белым на прозрачном фоне — +> нечитаемо на светлой странице. Использовать `outlined` только вместе с `severity !== 'base'` +> (`danger`/`warning`/`success`/`info`). + ## Variants ### Primary / Base (основная кнопка) @@ -54,18 +86,29 @@ Figma: ``, variant=primary, size=base, state=default — nodeId `16 ### Secondary / Base (вторичная кнопка) -Figma: ``, variant=tertiary (в коде: `variant="secondary"`), size=base, state=default +Figma: ``, variant=secondary, size=base, state=default ```html ``` -### Outlined / Large (контурная кнопка, большой размер) +### Tertiary / Large (тёмная заливка, большой размер) + +Figma: ``, variant=tertiary, size=large, state=default — сплошная тёмная заливка, +не зависит от `severity` (см. раздел «`tertiary` vs `outlined`» выше) + +```html + +``` + +### Outlined (контурная кнопка) -Figma: ``, variant=outlined, size=large, state=default +Figma: ``/``/``/``, variant=outlined — +nodeId `162:5457` и аналоги. Только с `severity` отличной от `base` — см. предупреждение выше. ```html - + + ``` ### Text / Base (текстовая кнопка) @@ -164,7 +207,7 @@ Figma: ``, rounded=true ## Do / Don't **Do:** -- Используйте `variant="text"` для текстовых кнопок; `[text]="true"` оставлен только как алиас для совместимости с PrimeNG. +- Используйте `variant="text"` для текстовых кнопок — отдельного булева `text` нет, это единственный способ - Используйте `variant="primary"` для единственного главного действия на экране - Для деструктивных действий задавайте `severity="danger"` и добавляйте иконку через [icons.md](../../figma-code-connect/icons.md) - При кнопке только с иконкой всегда задавайте `ariaLabel` для доступности diff --git a/src/stories/components/button/button.stories.ts b/src/stories/components/button/button.stories.ts index 461e1aa7..ee49ff50 100644 --- a/src/stories/components/button/button.stories.ts +++ b/src/stories/components/button/button.stories.ts @@ -1,17 +1,10 @@ import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../lib/components/button/button.component'; import { ButtonSizesComponent, Sizes } from './examples/button-sizes.component'; -import { ButtonTextComponent, Text } from './examples/button-text.component'; -import { ButtonSeverityComponent, Severity } from './examples/button-severity.component'; -import { ButtonSeverityFigmaComponent, SeverityFigma } from './examples/button-severity-figma.component'; -import { ButtonFigmaMappingComponent, FigmaMapping } from './examples/button-figma-mapping.component'; +import { ButtonVariantsComponent, Variants } from './examples/button-variants.component'; +import { ButtonIconsComponent, Icons } from './examples/button-icons.component'; +import { ButtonStatesComponent, States } from './examples/button-states.component'; import { ButtonRoundedComponent, Rounded } from './examples/button-rounded.component'; -import { ButtonOutlinedComponent, Outlined } from './examples/button-outlined.component'; -import { ButtonLoadingComponent, Loading } from './examples/button-loading.component'; -import { ButtonIconComponent, Icon } from './examples/button-icon.component'; -import { Extra } from './examples/button-extra.component'; -import { ButtonDisabledComponent, Disabled } from './examples/button-disabled.component'; -import { Base, ButtonBaseComponent } from './examples/button-base.component'; import { Badge, ButtonBadgeComponent } from './examples/button-badge.component'; import { ButtonFluidComponent, Fluid } from './examples/button-fluid.component'; import { ButtonEventsComponent, Events } from './examples/button-events.component'; @@ -23,7 +16,7 @@ type ButtonArgs = ExtraButtonComponent & { }; const meta: Meta = { - title: 'Components/Button/Button', + title: 'Components/Button', component: ExtraButtonComponent, tags: ['autodocs'], decorators: [ @@ -31,18 +24,11 @@ const meta: Meta = { imports: [ ExtraButtonComponent, ButtonSizesComponent, - ButtonBadgeComponent, - ButtonBaseComponent, - ButtonDisabledComponent, - ButtonIconComponent, - ButtonLoadingComponent, - ButtonOutlinedComponent, + ButtonVariantsComponent, + ButtonIconsComponent, + ButtonStatesComponent, ButtonRoundedComponent, - ButtonSeverityComponent, - ButtonSeverityFigmaComponent, - ButtonFigmaMappingComponent, - ButtonSizesComponent, - ButtonTextComponent, + ButtonBadgeComponent, ButtonFluidComponent, ButtonEventsComponent ] @@ -68,7 +54,7 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; description: 'Текст кнопки', table: { category: 'Props', - defaultValue: { summary: 'Button' }, + defaultValue: { summary: "''" }, type: { summary: 'string' } } }, @@ -84,12 +70,12 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; }, variant: { control: 'select', - options: ['primary', 'secondary', 'tertiary', 'text', 'link'], + options: ['primary', 'secondary', 'tertiary', 'outlined', 'text', 'link'], description: 'Вариант отображения кнопки', table: { category: 'Props', defaultValue: { summary: 'primary' }, - type: { summary: "'primary' | 'secondary' | 'tertiary' | 'text' | 'link'" } + type: { summary: "'primary' | 'secondary' | 'tertiary' | 'outlined' | 'text' | 'link'" } } }, size: { @@ -193,15 +179,6 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; type: { summary: 'number' } } }, - text: { - control: 'boolean', - description: 'Текстовый вариант кнопки', - table: { - category: 'Props', - defaultValue: { summary: 'false' }, - type: { summary: 'boolean' } - } - }, // ── Badge ──────────────────────────────────────────────── badge: { control: 'text', @@ -266,8 +243,7 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; badge: '', badgeSeverity: null, fluid: false, - autofocus: false, - text: false + autofocus: false } }; @@ -309,4 +285,4 @@ export const Default: Story = { } }; -export { Sizes, Text, Severity, SeverityFigma, FigmaMapping, Rounded, Outlined, Loading, Icon, Extra, Disabled, Base, Badge, Fluid, Events }; +export { Sizes, Variants, Icons, States, Rounded, Badge, Fluid, Events }; diff --git a/src/stories/components/button/examples/button-badge.component.ts b/src/stories/components/button/examples/button-badge.component.ts index 84cf460e..24d9e6e4 100644 --- a/src/stories/components/button/examples/button-badge.component.ts +++ b/src/stories/components/button/examples/button-badge.component.ts @@ -1,34 +1,31 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; import { StoryObj } from '@storybook/angular'; +import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = `
- - - - - - - + + + +
`; -const styles = ''; @Component({ selector: 'app-button-badge', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles + imports: [ExtraButtonComponent], + template }) export class ButtonBadgeComponent {} @@ -39,22 +36,29 @@ export const Badge: StoryObj = { parameters: { docs: { description: { - story: 'Badge кнопка' + story: 'Бейдж поверх кнопки (`showBadge` + `badge` + `badgeSeverity`) — например, для счётчиков уведомлений.' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; +import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; @Component({ - selector: 'app-button-text', + selector: 'app-button-badge', standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} + imports: [ExtraButtonComponent], + template: \` + + + \`, }) export class ButtonBadgeComponent {} ` diff --git a/src/stories/components/button/examples/button-base.component.ts b/src/stories/components/button/examples/button-base.component.ts deleted file mode 100644 index 6f30abab..00000000 --- a/src/stories/components/button/examples/button-base.component.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { Button, ButtonIconPosition } from 'primeng/button'; -import { StoryObj } from '@storybook/angular'; - -const template = ` -
- - -
-`; -const styles = ``; - -@Component({ - selector: 'app-button-base', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles -}) -export class ButtonBaseComponent { - @Input() label = ''; - @Input() disabled = false; - @Input() loading = false; - @Input() size: 'small' | 'large' | undefined; - @Input() rounded = false; - @Input() outlined = false; - @Input() text = false; - @Input() icon: string = ''; - @Input() iconPos: ButtonIconPosition = 'left'; - @Input() badge: string = ''; - @Input() severity: Extract = null; -} - -export const Base: StoryObj = { - render: (args) => ({ - props: args, - template: ` - -` - }), - args: { - label: 'Base button', - disabled: false, - loading: false, - size: null, - rounded: false, - outlined: false, - text: false, - icon: '', - iconPos: null, - badge: '', - severity: null - }, - argTypes: { - label: { - description: 'Текст кнопки.' - }, - disabled: { - description: 'При наличии указывает, что компонент должен быть отключен.' - }, - loading: { - description: 'Находится ли кнопка в состоянии загрузки.' - }, - // TODO Добавить xlarge после фикса в либе. - size: { - description: 'Определяет размер кнопки.', - control: { type: 'select' }, - options: [null, 'small', 'large'], - table: { - type: { summary: `'small' | 'large' | null` } // <-- тип в документации - } - }, - rounded: { - description: 'При наличии делает кнопку с закругленными краями.' - }, - outlined: { - description: 'При наличии делает кнопку с контуром без фона.' - }, - text: { - description: 'При наличии делает кнопку текстовой без фона и границ.' - }, - icon: { - description: 'Имя иконки для отображения в кнопке.' - }, - iconPos: { - description: 'Позиция иконки относительно текста.', - control: { type: 'select' }, - options: [null, 'left', 'right'], - table: { - type: { summary: `'left' | 'right' | null` } - } - }, - badge: { - description: 'Текст для отображения в виде бейджа на кнопке.' - }, - severity: { - description: 'Определяет цветовую схему кнопки.', - control: { type: 'select' }, - options: [null, 'success', 'info', 'warn', 'primary', 'help', 'danger'], - table: { - type: { - summary: `'success' | 'info' | 'warn' | 'primary' | 'help' | 'danger' | null` - } - } - } - }, - parameters: { - docs: { - description: { - story: 'Стандартная кнопка' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; -import { Button } from 'primeng/button'; - -@Component({ - selector: 'app-button-base', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} -}) -export class ButtonBaseComponent { - @Input() label = ''; - @Input() disabled = false; - @Input() loading = false; - @Input() size: 'small' | 'large' | null = null; - @Input() rounded = false; -} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-disabled.component.ts b/src/stories/components/button/examples/button-disabled.component.ts deleted file mode 100644 index 52f73c5e..00000000 --- a/src/stories/components/button/examples/button-disabled.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; -import { StoryObj } from '@storybook/angular'; - -const template = ` -
-
- -
-
-`; -const styles = ''; - -@Component({ - selector: 'app-button-disabled', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles -}) -export class ButtonDisabledComponent {} - -export const Disabled: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: 'Disabled кнопка' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-button-disabled', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} -}) -export class ButtonDisabledComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-extra.component.ts b/src/stories/components/button/examples/button-extra.component.ts deleted file mode 100644 index 4bc20bc8..00000000 --- a/src/stories/components/button/examples/button-extra.component.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { StoryObj } from '@storybook/angular'; - -export { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; - -export const Extra: StoryObj = { - render: (args) => ({ - props: args, - template: ` -` - }), - args: { - label: 'Button', - iconPosition: 'left', - severity: 'base', - showBadge: false, - fluid: false, - autofocus: false, - text: false - }, - argTypes: { - label: { control: 'text' }, - variant: { - control: 'select', - options: ['primary', 'secondary', 'tertiary', 'text', 'link'] - }, - severity: { - control: 'select', - options: ['base', 'danger', 'warning', 'success', 'info'] - }, - size: { - control: 'select', - options: ['small', 'base', 'large', 'xlarge'] - }, - rounded: { control: 'boolean' }, - iconPosition: { - control: 'select', - options: ['left', 'right'] - }, - iconOnly: { control: 'boolean' }, - icon: { control: 'text' }, - disabled: { control: 'boolean' }, - loading: { control: 'boolean' }, - badge: { control: 'text' }, - badgeSeverity: { - control: 'select', - options: [null, 'success', 'warning', 'danger', 'info', 'secondary', 'contrast'] - }, - showBadge: { control: 'boolean' }, - fluid: { control: 'boolean' }, - ariaLabel: { control: 'text' }, - autofocus: { control: 'boolean' }, - tabindex: { control: 'number' }, - text: { control: 'boolean' } - }, - parameters: { - docs: { - description: { - story: 'Интерактивный пример с пропсами, соответствующими Figma-компоненту Button.' - } - } - } -}; - -export const Badge: StoryObj = { - render: (args) => ({ - props: args, - template: ` -` - }), - args: { - label: 'Emails', - badge: '8', - badgeSeverity: 'danger', - showBadge: true, - severity: 'success' - }, - parameters: { - docs: { - description: { - story: 'Пример кнопки с бейджем для отображения уведомлений или счётчиков.' - } - } - } -}; diff --git a/src/stories/components/button/examples/button-figma-mapping.component.ts b/src/stories/components/button/examples/button-figma-mapping.component.ts deleted file mode 100644 index afb1e507..00000000 --- a/src/stories/components/button/examples/button-figma-mapping.component.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; -import { StoryObj } from '@storybook/angular'; -import { ExtraButtonComponent, ExtraButtonSeverity } from '../../../../lib/components/button/button.component'; - -/** - * Схема соответствия вариантов кнопок Figma ↔ код (UI Kit (DS) v2.1, node-id=484:6197). - * Слева — вариант из Figma (страница Buttons: primary / secondary / tertiary / text / link; - * страница Severity: outlined × severity), справа — реальный инстанс extra-button, - * отрисованный тем же PrimeNG v20, что и в проде. По одному инстансу на вариант. - */ - -const template = ` -
-
-
Figma variant
-
extra-button instance
-
PrimeNG v20 props
-
- @for (row of rows; track row.figma) { -
-
- {{ row.page }} - {{ row.figma }} -
-
- - -
-
{{ row.props }}
-
- } -
-`; - -const stateStyles = ` -.fm-root { display: flex; flex-direction: column; gap: 0.5rem; max-width: 56rem; } -.fm-grid { - display: grid; - grid-template-columns: 14rem 16rem 1fr; - gap: 1rem; - align-items: center; - padding: 0.5rem 0.75rem; - border-radius: 0.5rem; -} -.fm-head { font-weight: 600; font-size: 0.875rem; opacity: 0.7; } -.fm-figma { display: flex; flex-direction: column; gap: 0.125rem; } -.fm-page { font-size: 0.75rem; opacity: 0.6; } -.fm-variant { font-weight: 600; font-size: 0.875rem; } -.fm-props code { font-size: 0.8125rem; } -.fm-broken { outline: 2px dashed #dc3009; outline-offset: 2px; } -`; - -interface FigmaMappingRow { - page: string; - figma: string; - label: string; - variant: 'primary' | 'secondary' | 'tertiary' | 'text' | 'link'; - severity: ExtraButtonSeverity; - props: string; - broken?: boolean; -} - -@Component({ - selector: 'app-button-figma-mapping', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, - imports: [ExtraButtonComponent], - template, - styles: stateStyles -}) -export class ButtonFigmaMappingComponent { - // Расхождение tertiary → outlined исправлено: tertiary без severity теперь - // уходит в severity=contrast (серая заливка), outlined остаётся только для - // tertiary + severity (Figma Severity/variant=outlined). - readonly rows: FigmaMappingRow[] = [ - { - page: 'Buttons', - figma: 'variant=primary', - label: 'Primary', - variant: 'primary', - severity: 'base', - props: 'p-button label' - }, - { - page: 'Buttons', - figma: 'variant=secondary', - label: 'Secondary', - variant: 'secondary', - severity: 'base', - props: 'p-button severity="secondary"' - }, - { - page: 'Buttons', - figma: 'variant=tertiary (gray)', - label: 'Tertiary', - variant: 'tertiary', - severity: 'base', - props: 'p-button severity="contrast"' - }, - { - page: 'Buttons', - figma: 'variant=text', - label: 'Text', - variant: 'text', - severity: 'base', - props: 'p-button variant="text"' - }, - { - page: 'Buttons', - figma: 'variant=link', - label: 'Link', - variant: 'link', - severity: 'base', - props: 'p-button link' - }, - { - page: 'Severity', - figma: 'variant=outlined severity=danger', - label: 'Danger', - variant: 'tertiary', - severity: 'danger', - props: 'p-button variant="outlined" severity="danger"' - } - ]; -} - -export const FigmaMapping: StoryObj = { - name: 'Figma mapping', - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: - 'Схема соответствия вариантов кнопок Figma (UI Kit (DS) v2.1, node-id=484:6197) и инстансов extra-button на PrimeNG v20. По одному инстансу на вариант. Красной пунктирной рамкой отмечено расхождение: Figma tertiary (серая заливка) сейчас рендерится как outlined.' - } - } - } -}; diff --git a/src/stories/components/button/examples/button-icon.component.ts b/src/stories/components/button/examples/button-icon.component.ts deleted file mode 100644 index f7a1d0fe..00000000 --- a/src/stories/components/button/examples/button-icon.component.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; -import { StoryObj } from '@storybook/angular'; - -const template = ` -
-
- - - -
-
-`; -const styles = ''; - -@Component({ - selector: 'app-button-icon', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles -}) -export class ButtonIconComponent {} - -export const Icon: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: 'Кнопки с иконками' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-button-icon', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} -}) -export class ButtonIconComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-icons.component.ts b/src/stories/components/button/examples/button-icons.component.ts new file mode 100644 index 00000000..ceebeeae --- /dev/null +++ b/src/stories/components/button/examples/button-icons.component.ts @@ -0,0 +1,112 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; + +const template = ` +
+
+
+ + + +
+
+ + + + +
+
+ + + + + + +
+
+ + + + +
+
+ + + + + + +
+
+
+`; + +@Component({ + selector: 'app-button-icons', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ExtraButtonComponent], + template +}) +export class ButtonIconsComponent {} + +export const Icons: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: + 'Кнопки с иконкой: позиции icon-position (left / right, по умолчанию left), кнопки только с иконкой (пустой label) во всех размерах, вариантах, severity и в скруглённой форме.' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; + +@Component({ + selector: 'app-button-icons', + standalone: true, + imports: [ExtraButtonComponent], + template: \` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \`, +}) +export class ButtonIconsComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-loading.component.ts b/src/stories/components/button/examples/button-loading.component.ts deleted file mode 100644 index 4707dbd4..00000000 --- a/src/stories/components/button/examples/button-loading.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; -import { StoryObj } from '@storybook/angular'; - -const template = ` -
-
- -
-
-`; -const styles = ''; - -@Component({ - selector: 'app-button-loading', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles -}) -export class ButtonLoadingComponent {} - -export const Loading: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: 'Кнопка в состоянии загрузки' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-button-loading', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} -}) -export class ButtonLoadingComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-outlined.component.ts b/src/stories/components/button/examples/button-outlined.component.ts deleted file mode 100644 index 45b324ef..00000000 --- a/src/stories/components/button/examples/button-outlined.component.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; -import { StoryObj } from '@storybook/angular'; - -const template = ` -
- - -
`; -const styles = ''; - -@Component({ - selector: 'app-button-outlined', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles -}) -export class ButtonOutlinedComponent {} - -export const Outlined: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: 'Outlined кнопка' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-button-outlined', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} -}) -export class ButtonOutlinedComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-rounded.component.ts b/src/stories/components/button/examples/button-rounded.component.ts index a8d71d14..5a4b4343 100644 --- a/src/stories/components/button/examples/button-rounded.component.ts +++ b/src/stories/components/button/examples/button-rounded.component.ts @@ -1,23 +1,39 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; import { StoryObj } from '@storybook/angular'; +import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = `
-
- +
+
+ + + + + +
+
+ + + + +
+
+ + + + +
`; -const styles = ''; @Component({ selector: 'app-button-rounded', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles + imports: [ExtraButtonComponent], + template }) export class ButtonRoundedComponent {} @@ -28,22 +44,43 @@ export const Rounded: StoryObj = { parameters: { docs: { description: { - story: 'Скругленная кнопка' + story: + 'Скруглённая форма (rounded): варианты отображения при severity="base", семантические состояния (severity, variant="primary") и variant="outlined" × severity (контурная скруглённая кнопка).' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; +import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; @Component({ selector: 'app-button-rounded', standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} + imports: [ExtraButtonComponent], + template: \` +
+
+ + + + + +
+
+ + + + +
+ +
+ + + + +
+
+ \`, }) export class ButtonRoundedComponent {} ` diff --git a/src/stories/components/button/examples/button-severity-figma.component.ts b/src/stories/components/button/examples/button-severity-figma.component.ts deleted file mode 100644 index 9c495b1d..00000000 --- a/src/stories/components/button/examples/button-severity-figma.component.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; -import { StoryObj } from '@storybook/angular'; -import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; - -/** - * Зеркало страницы Figma «Severity 🔵» (UI Kit (DS) v2.1, node 24646:65002): - * секция 📱Severity → light-theme, компонент-сеты ///. - * Матрица как в макете: колонки Basic / Outlined / Text × ряды Default / Disabled / Loading; - * в ячейке — size (small/base/large/xlarge) × rounded (false/true) × icon (plain/prefix/postfix/icon-only). - */ - -const ICON = 'ti ti-file-alert'; - -const template = ` -
- @for (sev of severities; track sev.value) { -
-

{{ sev.label }}

-
-
- @for (variant of variants; track variant.value) { -
{{ variant.label }}
- } - @for (state of states; track state.value) { -
{{ state.label }}
- @for (variant of variants; track variant.value) { -
- @for (size of sizes; track size) { -
- @for (cfg of iconConfigs; track cfg.position ?? 'none') { - @for (isRounded of roundedVals; track $index) { - - - } - } -
- } -
- } - } -
-
- } -
-`; - -// Раскладка матрицы severity. -const stateStyles = ` -.sf-matrix { - display: grid; - grid-template-columns: minmax(7rem, auto) repeat(3, minmax(0, 1fr)); - gap: 0.75rem; - align-items: start; -} -.sf-col-head, .sf-row-head { font-weight: 600; font-size: 0.875rem; } -.sf-row-head { padding-top: 0.375rem; } -.sf-cell { display: flex; flex-direction: column; gap: 0.5rem; } -`; - -@Component({ - selector: 'app-button-severity-figma', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, - imports: [ExtraButtonComponent], - template, - styles: stateStyles -}) -export class ButtonSeverityFigmaComponent { - readonly icon = ICON; - readonly severities = [ - { label: 'Danger', value: 'danger' }, - { label: 'Warning', value: 'warning' }, - { label: 'Success', value: 'success' }, - { label: 'Info', value: 'info' } - ] as const; - readonly variants = [ - { label: 'Basic', value: 'primary' }, - { label: 'Outlined', value: 'tertiary' }, - { label: 'Text', value: 'text' } - ] as const; - readonly states = [ - { label: 'Default', value: 'default' }, - { label: 'Disabled', value: 'disabled' }, - { label: 'Loading', value: 'loading' } - ] as const; - readonly sizes = ['small', 'base', 'large', 'xlarge'] as const; - readonly roundedVals = [false, true]; - readonly iconConfigs = [ - { position: 'left', iconOnly: false, hasIcon: false }, - { position: 'left', iconOnly: false, hasIcon: true }, - { position: 'right', iconOnly: false, hasIcon: true }, - { position: 'left', iconOnly: true, hasIcon: true } - ] as const; -} - -export const SeverityFigma: StoryObj = { - name: 'Severity (Figma)', - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: - 'Зеркало страницы Figma «Severity 🔵» (UI Kit (DS) v2.1, node-id=24646:65002): матрица Basic / Outlined / Text × Default / Disabled / Loading для Button.Danger / Warning / Success / Info. В каждой ячейке — size (small / base / large / xlarge) × rounded × icon (plain / prefix / postfix / icon-only). Отрисовано как есть из базы кода, без подложек.' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; -import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; - -@Component({ - selector: 'app-button-severity-figma', - standalone: true, - imports: [ExtraButtonComponent], - template: \` - - - - - \` -}) -export class ButtonSeverityFigmaComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-severity.component.ts b/src/stories/components/button/examples/button-severity.component.ts deleted file mode 100644 index 87261fd7..00000000 --- a/src/stories/components/button/examples/button-severity.component.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; -import { StoryObj } from '@storybook/angular'; - -const template = ` -
-
-
- - - - - - -
- -
- - - - - - -
- -
- - - - - - -
- -
- - - - - - -
- -
- - - - - - -
- -
- - - - - - -
- -
- - - - - - -
- -
- - - - - - -
-
-
-`; -const styles = ''; - -@Component({ - selector: 'app-button-severity', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles -}) -export class ButtonSeverityComponent {} - -export const Severity: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: 'Кнопки с разным Severity' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-button-severity', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} -}) -export class ButtonSeverityComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-sizes.component.ts b/src/stories/components/button/examples/button-sizes.component.ts index 841276af..04fe973d 100644 --- a/src/stories/components/button/examples/button-sizes.component.ts +++ b/src/stories/components/button/examples/button-sizes.component.ts @@ -8,7 +8,7 @@ const template = ` - +
`; diff --git a/src/stories/components/button/examples/button-states.component.ts b/src/stories/components/button/examples/button-states.component.ts new file mode 100644 index 00000000..1230d470 --- /dev/null +++ b/src/stories/components/button/examples/button-states.component.ts @@ -0,0 +1,98 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; + +const template = ` +
+
+
+ + + + +
+ + @for (variant of variants; track variant) { +
+ + + +
+ } + + @for (severity of severities; track severity) { +
+ + + +
+ } +
+
+`; + +@Component({ + selector: 'app-button-states', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ExtraButtonComponent], + template +}) +export class ButtonStatesComponent { + variants = ['secondary', 'tertiary', 'outlined', 'text', 'link']; + severities = ['danger', 'warning', 'success', 'info']; + + capitalize(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1); + } +} + +export const States: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: + 'Состояния disabled и loading (спиннер, кнопка неактивна) для каждого variant и каждого severity. Первая кнопка в ряду названа по variant/severity.' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; + +@Component({ + selector: 'app-button-states', + standalone: true, + imports: [ExtraButtonComponent], + template: \` +
+ + + + +
+ + + + + + + + + + + + + + + \`, +}) +export class ButtonStatesComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/button/examples/button-text.component.ts b/src/stories/components/button/examples/button-text.component.ts deleted file mode 100644 index 20ebb093..00000000 --- a/src/stories/components/button/examples/button-text.component.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Button } from 'primeng/button'; -import { StoryObj } from '@storybook/angular'; - -const template = ` -
- -
-`; -const styles = ''; - -@Component({ - selector: 'app-button-text', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [Button], - template, - styles -}) -export class ButtonTextComponent {} - -export const Text: StoryObj = { - render: () => ({ - template: `` - }), - parameters: { - docs: { - description: { - story: 'Text кнопка' - }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-button-text', - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - Button - ], - template: ${template}, - styles: ${styles} -}) -export class ButtonTextComponent {} - ` - } - } - } -}; diff --git a/src/stories/components/button/examples/button-variants.component.ts b/src/stories/components/button/examples/button-variants.component.ts new file mode 100644 index 00000000..00f8b7c4 --- /dev/null +++ b/src/stories/components/button/examples/button-variants.component.ts @@ -0,0 +1,92 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; + +const template = ` +
+
+
+ + + + + + +
+
+ + + + +
+
+ + + + +
+
+
+`; + +@Component({ + selector: 'app-button-variants', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ExtraButtonComponent], + template +}) +export class ButtonVariantsComponent {} + +export const Variants: StoryObj = { + render: () => ({ + template: `` + }), + parameters: { + docs: { + description: { + story: + 'Варианты отображения (variant) при severity="base", семантические состояния (severity) при variant="primary" (сплошная заливка) и variant="outlined" × severity (контурная кнопка с цветной рамкой). "outlined" — самостоятельное значение variant, не зависит от severity: при severity="base" даёт нейтральную контурную кнопку (см. первый ряд), в остальных случаях рамка окрашивается по severity.' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; + +@Component({ + selector: 'app-button-variants', + standalone: true, + imports: [ExtraButtonComponent], + template: \` +
+
+ + + + + + +
+
+ + + + +
+ +
+ + + + +
+
+ \`, +}) +export class ButtonVariantsComponent {} + ` + } + } + } +}; From 426a9fa4d610fd60c910012acb1e6b826bb71daa Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Mon, 12 Oct 2026 15:35:03 +0800 Subject: [PATCH 07/10] =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D1=8C=20=D0=B2=D1=81=D0=B5=20controls=20?= =?UTF-8?q?=D0=B2=20Default=20story=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stories/components/button/button.stories.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/stories/components/button/button.stories.ts b/src/stories/components/button/button.stories.ts index ee49ff50..9ad90a3e 100644 --- a/src/stories/components/button/button.stories.ts +++ b/src/stories/components/button/button.stories.ts @@ -263,9 +263,17 @@ export const Default: Story = { if (args.size != null) parts.push(`size="${args.size}"`); if (args.icon != null && (args.icon as string) !== '') parts.push(`icon="${args.icon}"`); if (args.iconPosition != null && args.iconPosition !== 'left') parts.push(`iconPosition="${args.iconPosition}"`); + if (args.iconOnly) parts.push(`[iconOnly]="true"`); if (args.rounded) parts.push(`[rounded]="true"`); + if (args.fluid) parts.push(`[fluid]="true"`); if (args.disabled) parts.push(`[disabled]="true"`); if (args.loading) parts.push(`[loading]="true"`); + if (args.showBadge) parts.push(`[showBadge]="true"`); + if (args.badge) parts.push(`badge="${args.badge}"`); + if (args.badgeSeverity != null) parts.push(`badgeSeverity="${args.badgeSeverity}"`); + if (args.ariaLabel) parts.push(`ariaLabel="${args.ariaLabel}"`); + if (args.autofocus) parts.push(`[autofocus]="true"`); + if (args.tabindex != null) parts.push(`[tabindex]="${args.tabindex}"`); const template = parts.length ? `` From 829b1cfa3d533ed00240d3c2bd9a8dc2f803c9d8 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Mon, 12 Oct 2026 15:35:18 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=BB=D0=BE=D0=B6=D0=BA=D0=B8=20=D0=B8=D0=B7?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D0=BE=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/button/examples/button-badge.component.ts | 2 +- .../components/button/examples/button-events.component.ts | 4 ++-- .../components/button/examples/button-fluid.component.ts | 2 +- .../components/button/examples/button-icons.component.ts | 2 +- .../components/button/examples/button-rounded.component.ts | 2 +- .../components/button/examples/button-sizes.component.ts | 2 +- .../components/button/examples/button-states.component.ts | 2 +- .../components/button/examples/button-variants.component.ts | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/stories/components/button/examples/button-badge.component.ts b/src/stories/components/button/examples/button-badge.component.ts index 24d9e6e4..ca846414 100644 --- a/src/stories/components/button/examples/button-badge.component.ts +++ b/src/stories/components/button/examples/button-badge.component.ts @@ -3,7 +3,7 @@ import { StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = ` -
+
diff --git a/src/stories/components/button/examples/button-events.component.ts b/src/stories/components/button/examples/button-events.component.ts index aa481587..1a646b37 100644 --- a/src/stories/components/button/examples/button-events.component.ts +++ b/src/stories/components/button/examples/button-events.component.ts @@ -3,7 +3,7 @@ import { StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = ` -
+
+
diff --git a/src/stories/components/button/examples/button-icons.component.ts b/src/stories/components/button/examples/button-icons.component.ts index ceebeeae..ae462c76 100644 --- a/src/stories/components/button/examples/button-icons.component.ts +++ b/src/stories/components/button/examples/button-icons.component.ts @@ -3,7 +3,7 @@ import { StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = ` -
+
diff --git a/src/stories/components/button/examples/button-rounded.component.ts b/src/stories/components/button/examples/button-rounded.component.ts index 5a4b4343..df15857f 100644 --- a/src/stories/components/button/examples/button-rounded.component.ts +++ b/src/stories/components/button/examples/button-rounded.component.ts @@ -3,7 +3,7 @@ import { StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = ` -
+
diff --git a/src/stories/components/button/examples/button-sizes.component.ts b/src/stories/components/button/examples/button-sizes.component.ts index 04fe973d..5f8db78b 100644 --- a/src/stories/components/button/examples/button-sizes.component.ts +++ b/src/stories/components/button/examples/button-sizes.component.ts @@ -3,7 +3,7 @@ import { StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = ` -
+
diff --git a/src/stories/components/button/examples/button-states.component.ts b/src/stories/components/button/examples/button-states.component.ts index 1230d470..efe45839 100644 --- a/src/stories/components/button/examples/button-states.component.ts +++ b/src/stories/components/button/examples/button-states.component.ts @@ -3,7 +3,7 @@ import { StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = ` -
+
diff --git a/src/stories/components/button/examples/button-variants.component.ts b/src/stories/components/button/examples/button-variants.component.ts index 00f8b7c4..12e6cc6a 100644 --- a/src/stories/components/button/examples/button-variants.component.ts +++ b/src/stories/components/button/examples/button-variants.component.ts @@ -3,7 +3,7 @@ import { StoryObj } from '@storybook/angular'; import { ExtraButtonComponent } from '../../../../lib/components/button/button.component'; const template = ` -
+
From f91d8bf6df34f4ac6d5a20c137d1b5c034a9c676 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Mon, 12 Oct 2026 15:35:32 +0800 Subject: [PATCH 09/10] =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20ou?= =?UTF-8?q?tlined=20=D0=B1=D0=B5=D0=B7=20severity=20=D0=B8=D0=B7=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D0=BE=D0=B2=20=D0=BA=D0=BD=D0=BE?= =?UTF-8?q?=D0=BF=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/button/examples/button-rounded.component.ts | 2 -- .../components/button/examples/button-variants.component.ts | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/stories/components/button/examples/button-rounded.component.ts b/src/stories/components/button/examples/button-rounded.component.ts index df15857f..10fb4131 100644 --- a/src/stories/components/button/examples/button-rounded.component.ts +++ b/src/stories/components/button/examples/button-rounded.component.ts @@ -9,7 +9,6 @@ const template = ` -
@@ -63,7 +62,6 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; -
diff --git a/src/stories/components/button/examples/button-variants.component.ts b/src/stories/components/button/examples/button-variants.component.ts index 12e6cc6a..93022451 100644 --- a/src/stories/components/button/examples/button-variants.component.ts +++ b/src/stories/components/button/examples/button-variants.component.ts @@ -9,7 +9,6 @@ const template = ` -
@@ -46,7 +45,7 @@ export const Variants: StoryObj = { docs: { description: { story: - 'Варианты отображения (variant) при severity="base", семантические состояния (severity) при variant="primary" (сплошная заливка) и variant="outlined" × severity (контурная кнопка с цветной рамкой). "outlined" — самостоятельное значение variant, не зависит от severity: при severity="base" даёт нейтральную контурную кнопку (см. первый ряд), в остальных случаях рамка окрашивается по severity.' + 'Варианты отображения (variant) при severity="base", семантические состояния (severity) при variant="primary" (сплошная заливка) и variant="outlined" × severity (контурная кнопка с цветной рамкой). "outlined" используется только вместе с severity (danger, warning, success, info): рамка и текст окрашиваются по severity.' }, source: { language: 'ts', @@ -64,7 +63,6 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; -
From 4d0d93067be0ffede1878a05a35d8b98b13fc572 Mon Sep 17 00:00:00 2001 From: "ak.dmitriev" Date: Mon, 12 Oct 2026 23:43:53 +0700 Subject: [PATCH 10/10] fix stories --- .../components/button/examples/button-badge.component.ts | 2 ++ .../components/button/examples/button-states.component.ts | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stories/components/button/examples/button-badge.component.ts b/src/stories/components/button/examples/button-badge.component.ts index ca846414..8c8bdb40 100644 --- a/src/stories/components/button/examples/button-badge.component.ts +++ b/src/stories/components/button/examples/button-badge.component.ts @@ -15,6 +15,7 @@ const template = ` badge="2" badgeSeverity="danger" variant="outlined" + severity="danger" >
@@ -57,6 +58,7 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; badge="2" badgeSeverity="danger" variant="outlined" + severity="danger" > \`, }) diff --git a/src/stories/components/button/examples/button-states.component.ts b/src/stories/components/button/examples/button-states.component.ts index efe45839..bcefc06f 100644 --- a/src/stories/components/button/examples/button-states.component.ts +++ b/src/stories/components/button/examples/button-states.component.ts @@ -39,7 +39,7 @@ const template = ` template }) export class ButtonStatesComponent { - variants = ['secondary', 'tertiary', 'outlined', 'text', 'link']; + variants = ['secondary', 'tertiary', 'text', 'link']; severities = ['danger', 'warning', 'success', 'info']; capitalize(value: string): string { @@ -78,7 +78,6 @@ import { ExtraButtonComponent } from '@cdek-it/angular-ui-kit'; -