diff --git a/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _size.snap.png b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _size.snap.png new file mode 100644 index 0000000000..2ace0c8cbe Binary files /dev/null and b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _size.snap.png differ diff --git a/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _type.snap.png b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _type.snap.png new file mode 100644 index 0000000000..8b299c1c7d Binary files /dev/null and b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _type.snap.png differ diff --git a/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _viewclear.snap.png b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _viewclear.snap.png new file mode 100644 index 0000000000..e10225a93a Binary files /dev/null and b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _viewclear.snap.png differ diff --git a/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- animation.snap.png b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- animation.snap.png new file mode 100644 index 0000000000..3b4daa25f4 Binary files /dev/null and b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- animation.snap.png differ diff --git a/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- simple.snap.png b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- simple.snap.png new file mode 100644 index 0000000000..ad98162a99 Binary files /dev/null and b/cypress/snapshots/b2c/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- simple.snap.png differ diff --git a/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _size.snap.png b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _size.snap.png new file mode 100644 index 0000000000..15dd871e30 Binary files /dev/null and b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _size.snap.png differ diff --git a/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _type.snap.png b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _type.snap.png new file mode 100644 index 0000000000..0b84fdd887 Binary files /dev/null and b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _type.snap.png differ diff --git a/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _viewclear.snap.png b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _viewclear.snap.png new file mode 100644 index 0000000000..5c0175397e Binary files /dev/null and b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- _viewclear.snap.png differ diff --git a/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- animation.snap.png b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- animation.snap.png new file mode 100644 index 0000000000..d5f6662447 Binary files /dev/null and b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- animation.snap.png differ diff --git a/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- simple.snap.png b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- simple.snap.png new file mode 100644 index 0000000000..693d319db2 Binary files /dev/null and b/cypress/snapshots/web/components/Accordion/Accordion.component-test.tsx/plasma-web Accordion -- simple.snap.png differ diff --git a/packages/plasma-b2c/api/plasma-b2c.api.md b/packages/plasma-b2c/api/plasma-b2c.api.md index 76da048d43..732cf021a0 100644 --- a/packages/plasma-b2c/api/plasma-b2c.api.md +++ b/packages/plasma-b2c/api/plasma-b2c.api.md @@ -6,6 +6,8 @@ /// +import { AccordionItem } from '@salutejs/plasma-new-hope/styled-components'; +import { AccordionProps } from '@salutejs/plasma-new-hope/styled-components'; import { addFocus } from '@salutejs/plasma-core'; import { addNotification } from '@salutejs/plasma-new-hope/styled-components'; import { AlignProp } from '@salutejs/plasma-new-hope/types/components/Cell/Cell.types'; @@ -279,6 +281,38 @@ import { WithAutoFocusProps } from '@salutejs/plasma-core'; import { withSkeleton } from '@salutejs/plasma-new-hope/styled-components'; import { WithSkeletonProps } from '@salutejs/plasma-new-hope/styled-components'; +// @public +export const Accordion: FunctionComponent & { +view: string; +size?: string | undefined; +singleActive?: boolean | undefined; +defaultActiveEventKey?: number[] | undefined; +disabled?: boolean | undefined; +stretching?: "fixed" | "filled" | undefined; +onChange?: ((index?: number | undefined, value?: boolean | undefined) => void) | undefined; +children?: ReactNode; +className?: string | undefined; +} & RefAttributes>; + +export { AccordionItem } + +export { AccordionProps } + export { addFocus } export { addNotification } @@ -825,11 +859,11 @@ l: string; view: { default: string; }; -}> & ((Omit, "onChange" | "value" | "type" | "target" | "size" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { +}> & ((Omit, "onChange" | "size" | "value" | "type" | "target" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { valueType?: "single" | undefined; value?: ComboboxPrimitiveValue | undefined; onChangeValue?: ((value?: ComboboxPrimitiveValue | undefined) => void) | undefined; -} & RefAttributes) | (Omit, "onChange" | "value" | "type" | "target" | "size" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { +} & RefAttributes) | (Omit, "onChange" | "size" | "value" | "type" | "target" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { valueType: "multiple"; value?: ComboboxPrimitiveValue[] | undefined; onChangeValue?: ((value?: ComboboxPrimitiveValue[] | undefined) => void) | undefined; diff --git a/packages/plasma-b2c/src/components/Accordion/Accordion.component-test.tsx b/packages/plasma-b2c/src/components/Accordion/Accordion.component-test.tsx new file mode 120000 index 0000000000..12574f0698 --- /dev/null +++ b/packages/plasma-b2c/src/components/Accordion/Accordion.component-test.tsx @@ -0,0 +1 @@ +../../../../plasma-web/src/components/Accordion/Accordion.component-test.tsx \ No newline at end of file diff --git a/packages/plasma-b2c/src/components/Accordion/Accordion.config.ts b/packages/plasma-b2c/src/components/Accordion/Accordion.config.ts new file mode 100644 index 0000000000..4818c3cb31 --- /dev/null +++ b/packages/plasma-b2c/src/components/Accordion/Accordion.config.ts @@ -0,0 +1,121 @@ +import { css, accordionTokens } from '@salutejs/plasma-new-hope/styled-components'; + +export const config = { + defaults: { + view: 'default', + size: 'm', + }, + variations: { + view: { + default: css` + ${accordionTokens.accordionGap}: 0.125rem; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) var(${accordionTokens.accordionItemPaddingHorizontal}); + ${accordionTokens.accordionItemBackground}: var(--surface-solid-card); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0; + `, + clear: css` + ${accordionTokens.accordionGap}: 0.125rem; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) 0rem; + ${accordionTokens.accordionItemBackground}: var(--surface-clear); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionItemBorderRadius}: 0rem !important; + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0.125rem solid var(--surface-solid-tertiary); + `, + }, + size: { + l: css` + ${accordionTokens.accordionItemPaddingVertical}: 1.0625rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.25rem; + ${accordionTokens.accordionItemGap}: 0.5rem; + ${accordionTokens.accordionItemBorderRadius}: 0.875rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-l-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-l-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-l-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-l-line-height); + `, + m: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.125rem; + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.75rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-m-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-m-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-m-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-m-line-height); + `, + s: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.6875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.875rem; + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.625rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-s-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-s-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-s-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-s-line-height); + `, + xs: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.5rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.75rem; + ${accordionTokens.accordionItemGap}: 0.25rem; + ${accordionTokens.accordionItemBorderRadius}: 0.5rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-xs-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-xs-line-height); + `, + }, + stretching: { + filled: css``, + fixed: css``, + }, + }, +}; diff --git a/packages/plasma-b2c/src/components/Accordion/Accordion.stories.tsx b/packages/plasma-b2c/src/components/Accordion/Accordion.stories.tsx new file mode 100644 index 0000000000..7c9abfa2f3 --- /dev/null +++ b/packages/plasma-b2c/src/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import type { ComponentProps } from 'react'; +import type { StoryObj, Meta } from '@storybook/react'; +import { disableProps } from '@salutejs/plasma-sb-utils'; + +import { Accordion, AccordionItem } from '.'; + +const meta: Meta = { + title: 'Content/Accordion', + component: Accordion, + args: { + singleActive: false, + view: 'default', + size: 'm', + stretching: 'filled', + type: 'arrow', + title: 'Как оплатить заправку бонусами СберСпасибо?', + body: + 'После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива', + }, + argTypes: { + ...disableProps(['text']), + stretching: { + options: ['filled', 'fixed'], + control: { + type: 'select', + }, + }, + type: { + options: ['arrow', 'sign', 'clear'], + control: { + type: 'select', + }, + }, + }, +}; + +export default meta; + +export const Default: StoryObj> = { + render: (props: ComponentProps) => { + const args = { ...props, text: undefined }; + + return ( + + + {args.body} + + + {args.body} + + + {args.body} + + + ); + }, +}; diff --git a/packages/plasma-b2c/src/components/Accordion/Accordion.tsx b/packages/plasma-b2c/src/components/Accordion/Accordion.tsx new file mode 100644 index 0000000000..d56bbf45e0 --- /dev/null +++ b/packages/plasma-b2c/src/components/Accordion/Accordion.tsx @@ -0,0 +1,11 @@ +import { accordionConfig, component, mergeConfig } from '@salutejs/plasma-new-hope/styled-components'; + +import { config } from './Accordion.config'; + +const mergedConfig = mergeConfig(accordionConfig, config); +const AccordionComponent = component(mergedConfig); + +/** + * Accordion + */ +export const Accordion = AccordionComponent; diff --git a/packages/plasma-b2c/src/components/Accordion/index.ts b/packages/plasma-b2c/src/components/Accordion/index.ts new file mode 100644 index 0000000000..8a291f450d --- /dev/null +++ b/packages/plasma-b2c/src/components/Accordion/index.ts @@ -0,0 +1,4 @@ +export { AccordionItem } from '@salutejs/plasma-new-hope/styled-components'; +export { Accordion } from './Accordion'; + +export type { AccordionProps } from '@salutejs/plasma-new-hope/styled-components'; diff --git a/packages/plasma-b2c/src/index.ts b/packages/plasma-b2c/src/index.ts index f1d57b5ddb..745315f580 100644 --- a/packages/plasma-b2c/src/index.ts +++ b/packages/plasma-b2c/src/index.ts @@ -1,3 +1,4 @@ +export * from './components/Accordion'; export * from './components/AudioPlayer'; export * from './components/Badge'; export * from './components/Button'; diff --git a/packages/plasma-new-hope/src/components/Accordion/Accordion.styles.ts b/packages/plasma-new-hope/src/components/Accordion/Accordion.styles.ts new file mode 100644 index 0000000000..bd01dca854 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/Accordion.styles.ts @@ -0,0 +1,19 @@ +import { css } from '@linaria/core'; + +import { classes, tokens } from './Accordion.tokens'; + +export const base = css` + display: flex; + flex-direction: column; + gap: var(${tokens.accordionGap}); + align-items: stretch; + height: auto; + background: var(${tokens.accordionBackground}); + + &.${classes.fixedStretching} { + width: var(${tokens.accordionWidth}); + } + &.${classes.filledStretching} { + width: 100%; + } +`; diff --git a/packages/plasma-new-hope/src/components/Accordion/Accordion.tokens.ts b/packages/plasma-new-hope/src/components/Accordion/Accordion.tokens.ts new file mode 100644 index 0000000000..53510b3333 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/Accordion.tokens.ts @@ -0,0 +1,41 @@ +export const classes = { + filledStretching: 'accordion-stretching-filled', + fixedStretching: 'accordion-stretching-fixed', + accordionRoot: 'accordion-root', + accordionItem: 'accordion-item', + clearAccordionItem: 'clear-accordion-item', + accordionItemShowBody: 'accordion-item-show-body', + accordionPlusAnimationElement: 'accordion-plus-animation-element', + accordionDisabled: 'accordion-disabled', +}; + +export const tokens = { + accordionGap: '--plasma-accordion-gap', + accordionWidth: '--plasma-accordion-width', + accordionBackground: '--plasma-accordion-background', + + accordionItemBackground: '--plasma-accordion-item-background', + accordionItemBorderRadius: '--plasma-accordion-item-border-radius', + accordionItemPadding: '--plasma-accordion-item-padding', + accordionItemPaddingVertical: '--plasma-accordion-item-padding-vertical', + accordionItemPaddingHorizontal: '--plasma-accordion-item-padding-horizontal', + accordionItemGap: '--plasma-accordion-item-gap', + accordionItemFocus: '--plasma-accordion-item-focus', + accordionItemBorderBottom: '--plasma-accordion-item-border-bottom', + + accordionItemTitleColor: '--plasma-accordion-item-title-color', + accordionItemTitleFontFamily: '--plasma-accordion-item-title-font-family', + accordionItemTitleFontSize: '--plasma-accordion-item-title-font-size', + accordionItemTitleFontStyle: '--plasma-accordion-item-title-font-style', + accordionItemTitleFontWeight: '--plasma-accordion-item-title-font-weight', + accordionItemTitleLetterSpacing: '--plasma-accordion-item-title-letter-spacing', + accordionItemTitleLineHeight: '--plasma-accordion-item-title-line-height', + + accordionItemTextColor: '--plasma-accordion-item-text-color', + accordionItemTextFontFamily: '--plasma-accordion-item-text-font-family', + accordionItemTextFontSize: '--plasma-accordion-item-text-font-size', + accordionItemTextFontStyle: '--plasma-accordion-item-text-font-style', + accordionItemTextFontWeight: '--plasma-accordion-item-text-font-weight', + accordionItemTextLetterSpacing: '--plasma-accordion-item-text-letter-spacing', + accordionItemTextLineHeight: '--plasma-accordion-item-text-line-height', +}; diff --git a/packages/plasma-new-hope/src/components/Accordion/Accordion.tsx b/packages/plasma-new-hope/src/components/Accordion/Accordion.tsx new file mode 100644 index 0000000000..cf8cc923bf --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/Accordion.tsx @@ -0,0 +1,82 @@ +import React, { Children, forwardRef, useState } from 'react'; + +import { RootPropsOmitOnChange } from '../../engines'; +import { cx } from '../../utils'; + +import type { AccordionProps } from './Accordion.types'; +import { base } from './Accordion.styles'; +import { classes } from './Accordion.tokens'; +import { base as viewCSS } from './variations/_view/base'; +import { base as sizeCSS } from './variations/_size/base'; +import { getChildren } from './utils'; + +export const accordionRoot = (Root: RootPropsOmitOnChange) => + forwardRef( + ( + { + size, + view, + stretching = 'filled', + defaultActiveEventKey = [], + children, + disabled, + className, + singleActive, + onChange, + }, + outerRootRef, + ) => { + const stretchingClass = classes[`${stretching}Stretching` as keyof typeof classes]; + + const [activeIndex, setActiveIndex] = useState(defaultActiveEventKey); + + const updateValue = (index: number, value?: boolean) => { + if (onChange) { + onChange(index, value); + } + if (singleActive) { + if (value) { + setActiveIndex([index]); + } else { + setActiveIndex([]); + } + } else if (value) { + setActiveIndex([index, ...activeIndex]); + } else { + const updatedActiveIndex = activeIndex.filter((i) => i !== index); + setActiveIndex(updatedActiveIndex); + } + }; + + const childrenArray = Children.toArray(children) as React.ReactElement[]; + return ( + + {getChildren(childrenArray, activeIndex, disabled, updateValue)} + + ); + }, + ); + +export const accordionConfig = { + name: 'Accordion', + tag: 'div', + layout: accordionRoot, + base, + variations: { + view: { + css: viewCSS, + }, + size: { + css: sizeCSS, + }, + }, + defaults: { + view: 'default', + size: 'm', + }, +}; diff --git a/packages/plasma-new-hope/src/components/Accordion/Accordion.types.ts b/packages/plasma-new-hope/src/components/Accordion/Accordion.types.ts new file mode 100644 index 0000000000..02588fac74 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/Accordion.types.ts @@ -0,0 +1,43 @@ +import type { ReactNode } from 'react'; + +type CustomAccordionProps = { + /** + * Тип аккордеона + */ + view: string; + + /** + * Размер + */ + size?: string; + + /** + * Возможность раскрытия всех элементов или только одногo + */ + singleActive?: boolean; + + /** + * Индекс элемента, который должен быть изначально раскрыт + */ + defaultActiveEventKey?: number[]; + + /** + * Блокировка всех элементов + */ + disabled?: boolean; + + /** + * Ширина + */ + stretching?: 'fixed' | 'filled'; + + /** + * Коллбэк при открытии или закрытии элемента аккордеона + */ + onChange?: (index?: number, value?: boolean) => void; + + children?: ReactNode; + className?: string; +}; + +export type AccordionProps = CustomAccordionProps; diff --git a/packages/plasma-new-hope/src/components/Accordion/index.ts b/packages/plasma-new-hope/src/components/Accordion/index.ts new file mode 100644 index 0000000000..24e2eefab6 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/index.ts @@ -0,0 +1,5 @@ +export { accordionRoot, accordionConfig } from './Accordion'; +export type { AccordionProps } from './Accordion.types'; + +export { AccordionItem } from './ui/AccordionItem/AccordionItem'; +export { tokens as accordionTokens, classes as accordionClasses } from './Accordion.tokens'; diff --git a/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.styles.ts b/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.styles.ts new file mode 100644 index 0000000000..a1191e1985 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.styles.ts @@ -0,0 +1,147 @@ +import styled from 'styled-components'; + +import { IconChevronDownFill, IconMinus } from '../../../_Icon'; +import { classes, tokens } from '../../Accordion.tokens'; +import { addFocus } from '../../../../mixins'; + +export const StyledAccordionItem = styled.div` + background: var(${tokens.accordionItemBackground}); + border-bottom: var(${tokens.accordionItemBorderBottom}); + + &:last-child { + border-bottom: none; + } +`; + +export const StyledAccordionHeader = styled.button` + width: 100%; + border: none; + padding: var(${tokens.accordionItemPadding}); + display: flex; + gap: var(${tokens.accordionItemGap}); + justify-content: space-between; + align-items: center; + background: none; + box-sizing: border-box; + cursor: pointer; + + &.${classes.accordionDisabled} { + opacity: 0.4; + cursor: not-allowed; + } + + &:focus { + outline: none; + } + + ${addFocus({ + outlineOffset: '0.125rem', + outlineSize: '0.125rem', + outlineRadius: '0', + outlineColor: `var(${tokens.accordionItemFocus})`, + })} +`; + +export const StyledAccordionHeaderLeft = styled.div` + display: flex; + gap: var(${tokens.accordionItemGap}); + justify-content: space-between; + align-items: center; +`; + +export const StyledAccordionContentRight = styled.div` + transition: 0.2s; + transform: rotate(90deg); + + &.${classes.accordionItemShowBody} { + transition: 0.2s; + transform: rotate(0deg); + } +`; + +export const StyledAccordionContentLeft = styled.div` + transition: 0.2s; + display: flex; + align-items: center; + + &.${classes.accordionItemShowBody} { + transition: 0.2s; + transform: rotate(180deg); + } +`; + +export const StyledAccordionTitle = styled.div` + color: var(${tokens.accordionItemTitleColor}); + font-family: var(${tokens.accordionItemTitleFontFamily}); + font-size: var(${tokens.accordionItemTitleFontSize}); + font-weight: var(${tokens.accordionItemTitleFontWeight}); + font-style: var(${tokens.accordionItemTitleFontStyle}); + letter-spacing: var(${tokens.accordionItemTitleLetterSpacing}); + line-height: var(${tokens.accordionItemTitleLineHeight}); +`; + +export const StyledAccordionBodyAnimate = styled.div` + display: grid; + grid-template-rows: 0fr; + transition: grid-template-rows 0.2s ease-out; + overflow: hidden; + + &.${classes.accordionItemShowBody} { + grid-template-rows: 1fr; + padding-bottom: var(${tokens.accordionItemPaddingVertical}); + + &.${classes.accordionPlusAnimationElement} { + transition: 0.2s; + transform: rotate(0deg); + } + } +`; + +export const StyledAccordionBody = styled.div` + color: var(${tokens.accordionItemTextColor}); + font-family: var(${tokens.accordionItemTextFontFamily}); + font-size: var(${tokens.accordionItemTextFontSize}); + font-weight: var(${tokens.accordionItemTextFontWeight}); + font-style: var(${tokens.accordionItemTextFontStyle}); + letter-spacing: var(${tokens.accordionItemTextLetterSpacing}); + line-height: var(${tokens.accordionItemTextLineHeight}); + overflow: hidden; + padding-right: var(${tokens.accordionItemPaddingHorizontal}); + padding-left: var(${tokens.accordionItemPaddingHorizontal}); +`; + +export const StyledArrow = styled(IconChevronDownFill)` + pointer-events: none; + user-select: none; + color: var(${tokens.accordionItemTextColor}); +`; + +export const StyledMinus = styled(IconMinus)` + pointer-events: none; + user-select: none; + color: var(${tokens.accordionItemTextColor}); + display: flex; + align-items: center; + position: absolute; + top: 0; + left: 0; + + &.${classes.accordionPlusAnimationElement} { + transition: 0.2s; + transform: rotate(90deg); + } + + &.${classes.accordionItemShowBody} { + transition: 0.2s; + transform: rotate(0deg); + } +`; + +export const StyledPlus = styled.div` + position: relative; + height: 1rem; + display: flex; + align-items: center; + justify-content: center; + width: 1rem; +`; diff --git a/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.tsx b/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.tsx new file mode 100644 index 0000000000..38003473bf --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.tsx @@ -0,0 +1,116 @@ +import React, { HTMLAttributes, useState, useRef, useEffect } from 'react'; + +import { convertRoundnessMatrix } from '../../../../utils/roundness'; +import { classes, tokens } from '../../Accordion.tokens'; + +import { + StyledAccordionItem, + StyledAccordionHeader, + StyledAccordionBody, + StyledAccordionTitle, + StyledAccordionContentLeft, + StyledAccordionHeaderLeft, + StyledAccordionContentRight, + StyledArrow, + StyledMinus, + StyledPlus, + StyledAccordionBodyAnimate, +} from './AccordionItem.styles'; +import type { AccordionItemProps } from './AccordionItem.types'; + +export const AccordionItem: React.FC & AccordionItemProps> = ({ + value, + contentRight, + contentLeft, + title, + pin = 'square-square', + children, + type = 'sign', + index, + eventKey, + disabled, + onChange, +}) => { + const key = eventKey ?? index; + + const [leftPadding, setLeftPadding] = useState(); + + const handleOpen = () => { + if (disabled) { + return; + } + if (onChange) { + onChange(key, !value); + } + }; + + const leftContentRef = useRef(null); + + useEffect(() => { + const leftContentWidth = leftContentRef?.current?.offsetWidth ?? 0; + const leftPaddingBody = leftContentWidth ? `calc(${leftContentWidth}px + var(${tokens.accordionItemGap}))` : 0; + setLeftPadding(leftPaddingBody); + }, [value, type, leftContentRef, setLeftPadding]); + + const openedBodyClass = value ? classes.accordionItemShowBody : undefined; + + const StyledAnimationPLus = () => ( + + + + + ); + + const accordionBorderRadius = convertRoundnessMatrix(pin, `var(${tokens.accordionItemBorderRadius})`, '1.5rem'); + const disabledClass = disabled ? classes.accordionDisabled : ''; + + const leftContent = contentLeft ?? (type === 'arrow' ? : undefined); + const leftContentRotate = type === 'arrow' && value ? classes.accordionItemShowBody : undefined; + + const rightContent = contentRight ?? (type === 'sign' ? : undefined); + const rightContentRotate = type === 'sign' && value ? classes.accordionItemShowBody : undefined; + + return ( + + + + {leftContent && ( + + {leftContent} + + )} + {title} + + + + {rightContent && rightContent} + + + + {children} + + + ); +}; diff --git a/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.types.ts b/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.types.ts new file mode 100644 index 0000000000..7759730f7a --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/ui/AccordionItem/AccordionItem.types.ts @@ -0,0 +1,66 @@ +import type { ReactNode } from 'react'; + +import type { Pin } from '../../../../utils/roundness'; + +type CustomAccordionItemProps = { + /** + * Значение раскрытия аккордеона + */ + value?: boolean; + + /** + * Тип анимации раскрытия + */ + type?: 'clear' | 'arrow' | 'sign'; + + /** + * Контент слева + */ + contentLeft?: ReactNode; + + /** + * Контент справа + */ + contentRight?: ReactNode; + + /** + * Скругление аккордеона + */ + pin?: Pin; + + /** + * Заголовок аккордеона + */ + title: ReactNode; + + /** + * Контент аккордеона + */ + children: ReactNode; + + /** + * Функция при открытии аккардеона + */ + onChange: (index: number, value: boolean) => void; + + /** + * Блокировка элемента + */ + disabled?: boolean; + + // + // Свойства которые автоматически добавляется Accordion'ом + // + + /** + * Индекс элемента + */ + eventKey?: number; + + /** + * Индекс элемента + */ + index: number; +}; + +export type AccordionItemProps = CustomAccordionItemProps; diff --git a/packages/plasma-new-hope/src/components/Accordion/utils/index.ts b/packages/plasma-new-hope/src/components/Accordion/utils/index.ts new file mode 100644 index 0000000000..f96d91e0a5 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/utils/index.ts @@ -0,0 +1,33 @@ +import { Children, ReactElement, ReactNode, cloneElement, isValidElement } from 'react'; + +import { AccordionItemProps } from '../ui/AccordionItem/AccordionItem.types'; + +export const updatePropsRecursively = ( + children?: ReactElement[], + activeIndex?: number[], + disabled?: boolean, + updateValue?: (index: number, value?: boolean) => void, +): ReactNode[] => + Children.map(children || [], (child, index) => { + if (!isValidElement(child)) { + return child; + } + + const props = { + index, + value: !!(activeIndex?.findIndex((i: number) => i === (child.props.eventKey ?? index)) !== -1), + disabled, + onChange: updateValue, + }; + + return cloneElement(child, props); + }); + +export const getChildren = ( + children: ReactElement[], + activeIndex?: number[], + disabled?: boolean, + updateValue?: (index: number, value?: boolean) => void, +) => { + return updatePropsRecursively(children, activeIndex, disabled, updateValue); +}; diff --git a/packages/plasma-new-hope/src/components/Accordion/variations/_size/base.ts b/packages/plasma-new-hope/src/components/Accordion/variations/_size/base.ts new file mode 100644 index 0000000000..cd585b76c4 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/variations/_size/base.ts @@ -0,0 +1,3 @@ +import { css } from '@linaria/core'; + +export const base = css``; diff --git a/packages/plasma-new-hope/src/components/Accordion/variations/_size/tokens.json b/packages/plasma-new-hope/src/components/Accordion/variations/_size/tokens.json new file mode 100644 index 0000000000..802d8646a9 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/variations/_size/tokens.json @@ -0,0 +1,6 @@ +[ + "--plasma-accordion-item-padding-vertical", + "--plasma-accordion-item-padding-horizontal", + "--plasma-accordion-item-gap", + "--plasma-accordion-item-border-radius" +] diff --git a/packages/plasma-new-hope/src/components/Accordion/variations/_stretching/base.ts b/packages/plasma-new-hope/src/components/Accordion/variations/_stretching/base.ts new file mode 100644 index 0000000000..cd585b76c4 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/variations/_stretching/base.ts @@ -0,0 +1,3 @@ +import { css } from '@linaria/core'; + +export const base = css``; diff --git a/packages/plasma-new-hope/src/components/Accordion/variations/_stretching/tokens.json b/packages/plasma-new-hope/src/components/Accordion/variations/_stretching/tokens.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/variations/_stretching/tokens.json @@ -0,0 +1 @@ +[] diff --git a/packages/plasma-new-hope/src/components/Accordion/variations/_view/base.ts b/packages/plasma-new-hope/src/components/Accordion/variations/_view/base.ts new file mode 100644 index 0000000000..cd585b76c4 --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/variations/_view/base.ts @@ -0,0 +1,3 @@ +import { css } from '@linaria/core'; + +export const base = css``; diff --git a/packages/plasma-new-hope/src/components/Accordion/variations/_view/tokens.json b/packages/plasma-new-hope/src/components/Accordion/variations/_view/tokens.json new file mode 100644 index 0000000000..205cee632c --- /dev/null +++ b/packages/plasma-new-hope/src/components/Accordion/variations/_view/tokens.json @@ -0,0 +1,8 @@ +[ + "--plasma-accordion-width", + "--plasma-accordion-gap", + "--plasma-accordion-item-padding", + "--plasma-accordion-background", + "--plasma-accordion-item-background", + "--plasma-accordion-item-border-bottom" +] diff --git a/packages/plasma-new-hope/src/components/_Icon/Icon.assets/ChevronDownFill.tsx b/packages/plasma-new-hope/src/components/_Icon/Icon.assets/ChevronDownFill.tsx new file mode 100644 index 0000000000..8290d80d5a --- /dev/null +++ b/packages/plasma-new-hope/src/components/_Icon/Icon.assets/ChevronDownFill.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { IconProps } from '../IconRoot'; + +export const ChevronDownFill: React.FC = (props) => ( + + + +); diff --git a/packages/plasma-new-hope/src/components/_Icon/Icon.assets/Minus.tsx b/packages/plasma-new-hope/src/components/_Icon/Icon.assets/Minus.tsx new file mode 100644 index 0000000000..b0ab2a83ea --- /dev/null +++ b/packages/plasma-new-hope/src/components/_Icon/Icon.assets/Minus.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import { IconProps } from '../IconRoot'; + +export const Minus: React.FC = (props) => ( + + + +); diff --git a/packages/plasma-new-hope/src/components/_Icon/Icons/IconChevronDownFill.tsx b/packages/plasma-new-hope/src/components/_Icon/Icons/IconChevronDownFill.tsx new file mode 100644 index 0000000000..4fe3da595f --- /dev/null +++ b/packages/plasma-new-hope/src/components/_Icon/Icons/IconChevronDownFill.tsx @@ -0,0 +1,8 @@ +import React from 'react'; + +import { ChevronDownFill } from '../Icon.assets/ChevronDownFill'; +import { IconRoot, IconProps } from '../IconRoot'; + +export const IconChevronDownFill: React.FC = ({ size = 'xs', color, className }) => { + return ; +}; diff --git a/packages/plasma-new-hope/src/components/_Icon/Icons/IconMinus.tsx b/packages/plasma-new-hope/src/components/_Icon/Icons/IconMinus.tsx new file mode 100644 index 0000000000..e7911283eb --- /dev/null +++ b/packages/plasma-new-hope/src/components/_Icon/Icons/IconMinus.tsx @@ -0,0 +1,8 @@ +import React from 'react'; + +import { Minus } from '../Icon.assets/Minus'; +import { IconRoot, IconProps } from '../IconRoot'; + +export const IconMinus: React.FC = ({ size = 's', color, className }) => { + return ; +}; diff --git a/packages/plasma-new-hope/src/components/_Icon/index.tsx b/packages/plasma-new-hope/src/components/_Icon/index.tsx index a386e46458..cf8b1e70a0 100644 --- a/packages/plasma-new-hope/src/components/_Icon/index.tsx +++ b/packages/plasma-new-hope/src/components/_Icon/index.tsx @@ -4,6 +4,8 @@ export { IconMic } from './Icons/IconMic'; export { IconChevronLeft } from './Icons/IconChevronLeft'; export { IconChevronDoubleLeft } from './Icons/IconChevronDoubleLeft'; export { IconChevronDown } from './Icons/IconChevronDown'; +export { IconChevronDownFill } from './Icons/IconChevronDownFill'; export { IconClose } from './Icons/IconClose'; export { IconCross } from './Icons/IconCross'; export { IconDone } from './Icons/IconDone'; +export { IconMinus } from './Icons/IconMinus'; diff --git a/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.config.ts b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.config.ts new file mode 100644 index 0000000000..f2b295d92f --- /dev/null +++ b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.config.ts @@ -0,0 +1,122 @@ +import { css } from '@linaria/core'; + +import { accordionTokens } from '../../../../components/Accordion'; + +export const config = { + defaults: { + view: 'default', + size: 'm', + }, + variations: { + view: { + default: css` + ${accordionTokens.accordionGap}: 0.125rem; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) var(${accordionTokens.accordionItemPaddingHorizontal}); + ${accordionTokens.accordionItemBackground}: var(--surface-solid-card); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0; + `, + clear: css` + ${accordionTokens.accordionGap}: 0; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) 0rem; + ${accordionTokens.accordionItemBackground}: var(--surface-clear); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionItemBorderRadius}: 0rem !important; + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0.125rem solid var(--surface-solid-tertiary); + `, + }, + size: { + l: css` + ${accordionTokens.accordionItemPaddingVertical}: 1.0625rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.25rem; + ${accordionTokens.accordionItemGap}: 0.5rem; + ${accordionTokens.accordionItemBorderRadius}: 0.875rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-l-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-l-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-l-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-l-line-height); + `, + m: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.125rem; + + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.75rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-m-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-m-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-m-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-m-line-height); + `, + s: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.6875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.875rem; + + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.625rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-s-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-s-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-s-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-s-line-height); + `, + xs: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.5rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.75rem; + + ${accordionTokens.accordionItemGap}: 0.25rem; + ${accordionTokens.accordionItemBorderRadius}: 0.5rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-xs-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-xs-line-height); + `, + }, + }, +}; diff --git a/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.stories.tsx b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.stories.tsx new file mode 100644 index 0000000000..6f113abdf9 --- /dev/null +++ b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; +import type { ComponentProps } from 'react'; +import type { StoryObj, Meta } from '@storybook/react'; +import { disableProps } from '@salutejs/plasma-sb-utils'; + +import { mergeConfig } from '../../../../engines'; +import { WithTheme, argTypesFromConfig } from '../../../_helpers'; +import { accordionConfig } from '../../../../components/Accordion'; + +import { config } from './Accordion.config'; +import { Accordion, AccordionItem } from './Accordion'; + +const meta: Meta = { + title: 'plasma_b2c/Accordion', + decorators: [WithTheme], + component: Accordion, + args: { + singleActive: false, + view: 'default', + size: 'm', + stretching: 'filled', + type: 'arrow', + disabled: false, + title: 'Как оплатить заправку бонусами СберСпасибо?', + body: + 'После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива', + }, + argTypes: { + ...argTypesFromConfig(mergeConfig(accordionConfig, config)), + ...disableProps(['text']), + stretching: { + options: ['filled', 'fixed'], + control: { + type: 'select', + }, + }, + type: { + options: ['arrow', 'sign', 'clear'], + control: { + type: 'select', + }, + }, + }, +}; + +export default meta; + +export const Default: StoryObj> = { + render: (props: ComponentProps) => { + const args = { ...props, text: undefined }; + + return ( + + + {args.body} + + + {args.body} + + + {args.body} + + + ); + }, +}; diff --git a/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.ts b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.ts new file mode 100644 index 0000000000..fbae3a6c54 --- /dev/null +++ b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Accordion/Accordion.ts @@ -0,0 +1,9 @@ +import { accordionConfig, AccordionItem } from '../../../../components/Accordion'; +import { component, mergeConfig } from '../../../../engines'; + +import { config } from './Accordion.config'; + +const mergedConfig = mergeConfig(accordionConfig, config); + +export const Accordion = component(mergedConfig); +export { AccordionItem }; diff --git a/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.config.ts b/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.config.ts new file mode 100644 index 0000000000..8282213aa6 --- /dev/null +++ b/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.config.ts @@ -0,0 +1,123 @@ +import { css } from '@linaria/core'; + +import { accordionTokens } from '../../../../components/Accordion'; + +export const config = { + defaults: { + view: 'default', + size: 'm', + }, + variations: { + view: { + default: css` + ${accordionTokens.accordionGap}: 0.125rem; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) var(${accordionTokens.accordionItemPaddingHorizontal}); + ${accordionTokens.accordionItemBackground}: var(--surface-solid-card); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0; + `, + clear: css` + ${accordionTokens.accordionGap}: 0.125rem; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) 0rem; + ${accordionTokens.accordionItemBackground}: var(--surface-clear); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionItemBorderRadius}: 0rem !important; + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0.125rem solid var(--surface-solid-tertiary); + `, + }, + size: { + l: css` + ${accordionTokens.accordionItemPaddingVertical}: 1.0625rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.25rem; + ${accordionTokens.accordionItemGap}: 0.5rem; + ${accordionTokens.accordionItemBorderRadius}: 0.875rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-l-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-l-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-l-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-l-line-height); + `, + m: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.125rem; + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.75rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-m-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-m-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-m-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-m-line-height); + `, + s: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.6875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.875rem; + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.625rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-s-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-s-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-s-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-s-line-height); + `, + xs: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.5rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.75rem; + ${accordionTokens.accordionItemGap}: 0.25rem; + ${accordionTokens.accordionItemBorderRadius}: 0.5rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-xs-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-xs-line-height); + `, + }, + stretching: { + filled: css``, + fixed: css``, + }, + }, +}; diff --git a/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.stories.tsx b/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.stories.tsx new file mode 100644 index 0000000000..68bc73a979 --- /dev/null +++ b/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import type { ComponentProps } from 'react'; +import type { StoryObj, Meta } from '@storybook/react'; +import { disableProps } from '@salutejs/plasma-sb-utils'; + +import { mergeConfig } from '../../../../engines'; +import { WithTheme, argTypesFromConfig } from '../../../_helpers'; +import { accordionConfig } from '../../../../components/Accordion'; + +import { config } from './Accordion.config'; +import { Accordion, AccordionItem } from './Accordion'; + +const meta: Meta = { + title: 'plasma_web/Accordion', + decorators: [WithTheme], + component: Accordion, + args: { + singleActive: false, + view: 'default', + size: 'm', + stretching: 'filled', + type: 'arrow', + title: 'Как оплатить заправку бонусами СберСпасибо?', + body: + 'После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива', + }, + argTypes: { + ...argTypesFromConfig(mergeConfig(accordionConfig, config)), + ...disableProps(['text']), + stretching: { + options: ['filled', 'fixed'], + control: { + type: 'select', + }, + }, + type: { + options: ['arrow', 'sign', 'clear'], + control: { + type: 'select', + }, + }, + }, +}; + +export default meta; + +export const Default: StoryObj> = { + render: (props: ComponentProps) => { + const args = { ...props, text: undefined }; + + return ( + + + {args.body} + + + {args.body} + + + {args.body} + + + ); + }, +}; diff --git a/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.ts b/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.ts new file mode 100644 index 0000000000..fbae3a6c54 --- /dev/null +++ b/packages/plasma-new-hope/src/examples/plasma_web/components/Accordion/Accordion.ts @@ -0,0 +1,9 @@ +import { accordionConfig, AccordionItem } from '../../../../components/Accordion'; +import { component, mergeConfig } from '../../../../engines'; + +import { config } from './Accordion.config'; + +const mergedConfig = mergeConfig(accordionConfig, config); + +export const Accordion = component(mergedConfig); +export { AccordionItem }; diff --git a/packages/plasma-new-hope/src/index.ts b/packages/plasma-new-hope/src/index.ts index 79922ab55b..f66d520310 100644 --- a/packages/plasma-new-hope/src/index.ts +++ b/packages/plasma-new-hope/src/index.ts @@ -48,3 +48,4 @@ export * from './components/Divider'; export * from './components/Toolbar'; export * from './components/Slider'; export * from './components/Range'; +export * from './components/Accordion'; diff --git a/packages/plasma-web/api/plasma-web.api.md b/packages/plasma-web/api/plasma-web.api.md index af9e27202d..af0062b310 100644 --- a/packages/plasma-web/api/plasma-web.api.md +++ b/packages/plasma-web/api/plasma-web.api.md @@ -6,6 +6,8 @@ /// +import { AccordionItem } from '@salutejs/plasma-new-hope/styled-components'; +import { AccordionProps } from '@salutejs/plasma-new-hope/styled-components'; import { addFocus } from '@salutejs/plasma-core'; import { addNotification } from '@salutejs/plasma-new-hope/styled-components'; import { AlignProp } from '@salutejs/plasma-new-hope/types/components/Cell/Cell.types'; @@ -279,6 +281,38 @@ import { WithAutoFocusProps } from '@salutejs/plasma-core'; import { withSkeleton } from '@salutejs/plasma-new-hope/styled-components'; import { WithSkeletonProps } from '@salutejs/plasma-new-hope/styled-components'; +// @public +export const Accordion: FunctionComponent & { +view: string; +size?: string | undefined; +singleActive?: boolean | undefined; +defaultActiveEventKey?: number[] | undefined; +disabled?: boolean | undefined; +stretching?: "fixed" | "filled" | undefined; +onChange?: ((index?: number | undefined, value?: boolean | undefined) => void) | undefined; +children?: ReactNode; +className?: string | undefined; +} & RefAttributes>; + +export { AccordionItem } + +export { AccordionProps } + export { addFocus } export { addNotification } @@ -826,11 +860,11 @@ l: string; view: { default: string; }; -}> & ((Omit, "onChange" | "value" | "type" | "target" | "size" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { +}> & ((Omit, "onChange" | "size" | "value" | "type" | "target" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { valueType?: "single" | undefined; value?: ComboboxPrimitiveValue | undefined; onChangeValue?: ((value?: ComboboxPrimitiveValue | undefined) => void) | undefined; -} & RefAttributes) | (Omit, "onChange" | "value" | "type" | "target" | "size" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { +} & RefAttributes) | (Omit, "onChange" | "size" | "value" | "type" | "target" | "checked" | "minLength" | "maxLength"> & CustomComboboxProps & { valueType: "multiple"; value?: ComboboxPrimitiveValue[] | undefined; onChangeValue?: ((value?: ComboboxPrimitiveValue[] | undefined) => void) | undefined; diff --git a/packages/plasma-web/src/components/Accordion/Accordion.component-test.tsx b/packages/plasma-web/src/components/Accordion/Accordion.component-test.tsx new file mode 100644 index 0000000000..82777d9ea5 --- /dev/null +++ b/packages/plasma-web/src/components/Accordion/Accordion.component-test.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import type { FC, PropsWithChildren } from 'react'; +import { CypressTestDecorator, getComponent, mount, PadMe } from '@salutejs/plasma-cy-utils'; +import { standard as standardTypo } from '@salutejs/plasma-typo'; +import { createGlobalStyle } from 'styled-components'; + +const StandardTypoStyle = createGlobalStyle(standardTypo); + +describe('plasma-web: Accordion', () => { + const Accordion = getComponent('Accordion'); + const AccordionItem = getComponent('AccordionItem'); + + const title = 'Как оплатить заправку бонусами СберСпасибо?'; + const body = + 'После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топливa'; + + const CypressTestDecoratorWithTypo: FC = ({ children }) => ( + + + {children} + + ); + + it('simple', () => { + mount( + + + {body} + + , + ); + cy.matchImageSnapshot(); + }); + + it('_view:clear', () => { + mount( + + + {body} + {body} + + , + ); + cy.matchImageSnapshot(); + }); + + it('_size', () => { + mount( + + + {body} + + {body} + + + + + {body} + + {body} + + + + + {body} + + {body} + + + + + {body} + + {body} + + + + , + ); + cy.matchImageSnapshot(); + }); + + it('_type', () => { + mount( + + + + {body} + + + {body} + + + {body} + + + , + ); + cy.matchImageSnapshot(); + }); + + it('animation', () => { + mount( + + + {body} + + , + ); + + cy.get('.accordion-root').last().trigger('click', { waitForAnimations: true }); + cy.matchImageSnapshot(); + }); +}); diff --git a/packages/plasma-web/src/components/Accordion/Accordion.config.ts b/packages/plasma-web/src/components/Accordion/Accordion.config.ts new file mode 100644 index 0000000000..4818c3cb31 --- /dev/null +++ b/packages/plasma-web/src/components/Accordion/Accordion.config.ts @@ -0,0 +1,121 @@ +import { css, accordionTokens } from '@salutejs/plasma-new-hope/styled-components'; + +export const config = { + defaults: { + view: 'default', + size: 'm', + }, + variations: { + view: { + default: css` + ${accordionTokens.accordionGap}: 0.125rem; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) var(${accordionTokens.accordionItemPaddingHorizontal}); + ${accordionTokens.accordionItemBackground}: var(--surface-solid-card); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0; + `, + clear: css` + ${accordionTokens.accordionGap}: 0.125rem; + ${accordionTokens.accordionWidth}: 20rem; + ${accordionTokens.accordionItemPadding}: var(${accordionTokens.accordionItemPaddingVertical}) 0rem; + ${accordionTokens.accordionItemBackground}: var(--surface-clear); + ${accordionTokens.accordionItemTitleColor}: var(--text-primary); + ${accordionTokens.accordionItemTextColor}: var(--text-primary); + ${accordionTokens.accordionItemFocus}: var(--surface-accent); + ${accordionTokens.accordionItemBorderRadius}: 0rem !important; + ${accordionTokens.accordionBackground}: var(--surface-clear); + ${accordionTokens.accordionItemBorderBottom}: 0.125rem solid var(--surface-solid-tertiary); + `, + }, + size: { + l: css` + ${accordionTokens.accordionItemPaddingVertical}: 1.0625rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.25rem; + ${accordionTokens.accordionItemGap}: 0.5rem; + ${accordionTokens.accordionItemBorderRadius}: 0.875rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-l-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-l-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-l-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-l-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-l-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-l-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-l-line-height); + `, + m: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 1.125rem; + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.75rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-m-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-m-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-m-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-m-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-m-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-m-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-m-line-height); + `, + s: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.6875rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.875rem; + ${accordionTokens.accordionItemGap}: 0.375rem; + ${accordionTokens.accordionItemBorderRadius}: 0.625rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-s-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-s-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-s-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-s-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-s-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-s-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-s-line-height); + `, + xs: css` + ${accordionTokens.accordionItemPaddingVertical}: 0.5rem; + ${accordionTokens.accordionItemPaddingHorizontal}: 0.75rem; + ${accordionTokens.accordionItemGap}: 0.25rem; + ${accordionTokens.accordionItemBorderRadius}: 0.5rem; + + ${accordionTokens.accordionItemTitleFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTitleFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTitleFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTitleFontWeight}: var(--plasma-typo-body-xs-bold-font-weight); + ${accordionTokens.accordionItemTitleLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTitleLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${accordionTokens.accordionItemTextFontFamily}: var(--plasma-typo-body-xs-font-family); + ${accordionTokens.accordionItemTextFontSize}: var(--plasma-typo-body-xs-font-size); + ${accordionTokens.accordionItemTextFontStyle}: var(--plasma-typo-body-xs-font-style); + ${accordionTokens.accordionItemTextFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${accordionTokens.accordionItemTextLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${accordionTokens.accordionItemTextLineHeight}: var(--plasma-typo-body-xs-line-height); + `, + }, + stretching: { + filled: css``, + fixed: css``, + }, + }, +}; diff --git a/packages/plasma-web/src/components/Accordion/Accordion.stories.tsx b/packages/plasma-web/src/components/Accordion/Accordion.stories.tsx new file mode 100644 index 0000000000..7c9abfa2f3 --- /dev/null +++ b/packages/plasma-web/src/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import type { ComponentProps } from 'react'; +import type { StoryObj, Meta } from '@storybook/react'; +import { disableProps } from '@salutejs/plasma-sb-utils'; + +import { Accordion, AccordionItem } from '.'; + +const meta: Meta = { + title: 'Content/Accordion', + component: Accordion, + args: { + singleActive: false, + view: 'default', + size: 'm', + stretching: 'filled', + type: 'arrow', + title: 'Как оплатить заправку бонусами СберСпасибо?', + body: + 'После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива', + }, + argTypes: { + ...disableProps(['text']), + stretching: { + options: ['filled', 'fixed'], + control: { + type: 'select', + }, + }, + type: { + options: ['arrow', 'sign', 'clear'], + control: { + type: 'select', + }, + }, + }, +}; + +export default meta; + +export const Default: StoryObj> = { + render: (props: ComponentProps) => { + const args = { ...props, text: undefined }; + + return ( + + + {args.body} + + + {args.body} + + + {args.body} + + + ); + }, +}; diff --git a/packages/plasma-web/src/components/Accordion/Accordion.tsx b/packages/plasma-web/src/components/Accordion/Accordion.tsx new file mode 100644 index 0000000000..d56bbf45e0 --- /dev/null +++ b/packages/plasma-web/src/components/Accordion/Accordion.tsx @@ -0,0 +1,11 @@ +import { accordionConfig, component, mergeConfig } from '@salutejs/plasma-new-hope/styled-components'; + +import { config } from './Accordion.config'; + +const mergedConfig = mergeConfig(accordionConfig, config); +const AccordionComponent = component(mergedConfig); + +/** + * Accordion + */ +export const Accordion = AccordionComponent; diff --git a/packages/plasma-web/src/components/Accordion/index.ts b/packages/plasma-web/src/components/Accordion/index.ts new file mode 100644 index 0000000000..8a291f450d --- /dev/null +++ b/packages/plasma-web/src/components/Accordion/index.ts @@ -0,0 +1,4 @@ +export { AccordionItem } from '@salutejs/plasma-new-hope/styled-components'; +export { Accordion } from './Accordion'; + +export type { AccordionProps } from '@salutejs/plasma-new-hope/styled-components'; diff --git a/packages/plasma-web/src/index.ts b/packages/plasma-web/src/index.ts index 7b163a8a86..d31e26e420 100644 --- a/packages/plasma-web/src/index.ts +++ b/packages/plasma-web/src/index.ts @@ -1,3 +1,4 @@ +export * from './components/Accordion'; export * from './components/AudioPlayer'; export * from './components/Badge'; export * from './components/Button'; diff --git a/website/plasma-web-docs/docs/components/Accordion.mdx b/website/plasma-web-docs/docs/components/Accordion.mdx new file mode 100644 index 0000000000..794ace7554 --- /dev/null +++ b/website/plasma-web-docs/docs/components/Accordion.mdx @@ -0,0 +1,238 @@ +--- +id: accordion +title: Accordion +--- + +import { PropsTable, Description, StorybookLink } from '@site/src/components'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + +# Accordion +Компонент выпадающей информации + + + +# AccordionItem +Компонент выпадающей информации + + + +Компонент представляет собой заголовок и контент, который раскрывается при нажатии. + +## Примеры Accordion + + + + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ ); + } + ``` +
+
+ +### Использование Accorion SignleActive + +```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` + +### Использование Accorion с eventKey и defaultActiveEventKey + +```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` + + +### Использование AccorionItem атрибута Type + + + + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-web'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+
+