Skip to content

Commit

Permalink
fix: fix issues
Browse files Browse the repository at this point in the history
  • Loading branch information
bityutskiyAO committed Mar 4, 2025
1 parent 5367c7b commit bc8e38c
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 80 deletions.
21 changes: 21 additions & 0 deletions src/components/Stepper/README-ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,27 @@ const Separator = () => {

<!--/GITHUB_BLOCK-->

### Step with floating element

<!--GITHUB_BLOCK-->

```jsx
<Stepper {...args} separator={<Separator />}>
<Tooltip content="fancy step with tooltip">
<Stepper.Item>Step 1</Stepper.Item>
</Tooltip>
<Stepper.Item view="error">Step 2</Stepper.Item>
<Stepper.Item view="success">Step 3</Stepper.Item>
<Stepper.Item>Step 4 with very long title</Stepper.Item>
</Stepper>
```

<!-- Storybook example -->

<StepperWithFloatingElements/>

<!--/GITHUB_BLOCK-->

## Properties

| Name | Description | Type | Default |
Expand Down
23 changes: 23 additions & 0 deletions src/components/Stepper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,29 @@ const Separator = () => {

<!--/GITHUB_BLOCK-->

<!--/GITHUB_BLOCK-->

### Step with floating element

<!--GITHUB_BLOCK-->

```jsx
<Stepper {...args} separator={<Separator />}>
<Tooltip content="fancy step with tooltip">
<Stepper.Item>Step 1</Stepper.Item>
</Tooltip>
<Stepper.Item view="error">Step 2</Stepper.Item>
<Stepper.Item view="success">Step 3</Stepper.Item>
<Stepper.Item>Step 4 with very long title</Stepper.Item>
</Stepper>
```

<!-- Storybook example -->

<StepperWithFloatingElements/>

<!--/GITHUB_BLOCK-->

## Properties

| Name | Description | Type | Default |
Expand Down
7 changes: 0 additions & 7 deletions src/components/Stepper/Stepper.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ $block: '.#{variables.$ns}stepper';
display: flex;
gap: var(--_--step-gap);

flex-wrap: nowrap;
overflow-x: auto;

&__list-item {
display: flex;
flex-wrap: nowrap;
Expand All @@ -22,10 +19,6 @@ $block: '.#{variables.$ns}stepper';
}

&__item {
&:hover:not(&_disabled) {
background-color: var(--g-color-base-generic);
}

&_selected:not(&_disabled) {
border-color: var(--g-color-line-info);
}
Expand Down
47 changes: 19 additions & 28 deletions src/components/Stepper/Stepper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import type {AriaLabelingProps, DOMProps, QAProps} from '../types';
import {filterDOMProps} from '../utils/filterDOMProps';

import {StepperItem} from './StepperItem';
import type {StepperItemProps} from './StepperItem';
import {StepperSeparator} from './StepperSeparator';
import {StepperContext} from './context';
import type {StepperSize} from './types';
import {b} from './utils';

import './Stepper.scss';

export interface StepperProps extends DOMProps, AriaLabelingProps, QAProps {
children: React.ReactElement<StepperItemProps> | React.ReactElement<StepperItemProps>[];
children: React.ReactElement | React.ReactElement[];
value?: number | string;
onUpdate: (id?: number | string) => void;
onUpdate?: (id?: number | string) => void;
size?: StepperSize;
separator?: React.ReactNode;
}
Expand All @@ -26,42 +26,33 @@ export const Stepper = (props: StepperProps) => {

const stepItems = React.useMemo(() => {
return React.Children.map(children, (child, index) => {
const id = child.props.id ?? index;
const itemId = child.props?.id || index;
const clonedChild = React.cloneElement(child, {id: itemId});

return (
<li key={id} className={b('list-item')}>
<StepperItem
{...child.props}
id={id}
size={size}
selected={value}
onUpdate={onUpdate}
/>
<li key={itemId} className={b('list-item')}>
{clonedChild}
{Boolean(index !== React.Children.count(children) - 1) && (
<StepperSeparator separator={separator} />
)}
</li>
);
});
}, [children, value, size, onUpdate, separator]);
}, [children, separator]);

return (
<ol
{...filterDOMProps(props, {labelable: true})}
className={b(null, className)}
style={props.style}
data-qa={props.qa}
>
{stepItems}
</ol>
<StepperContext.Provider value={{size, onUpdate, value}}>
<ol
{...filterDOMProps(props, {labelable: true})}
className={b(null, className)}
style={props.style}
data-qa={props.qa}
>
{stepItems}
</ol>
</StepperContext.Provider>
);
};

function Item(_props: StepperItemProps): React.ReactElement | null {
return null;
}

Stepper.Item = Item;
Stepper.Item = StepperItem;
Stepper.displayName = 'Stepper';

export default Stepper;
28 changes: 14 additions & 14 deletions src/components/Stepper/StepperItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,38 @@ import * as React from 'react';
import {CircleCheck, CircleDashed, CircleExclamation} from '@gravity-ui/icons';

import {Button} from '../Button';
import type {ButtonButtonProps} from '../Button';
import {Icon} from '../Icon';
import type {SVGIconData} from '../Icon/types';
import {Text} from '../Text';

import type {StepperProps} from './Stepper';
import {useStepperContext} from './context';
import type {StepperItemView} from './types';
import {b} from './utils';

import './Stepper.scss';

export interface StepperItemProps {
export type StepperItemProps = Omit<ButtonButtonProps, 'view'> & {
id?: string | number;
children: React.ReactNode;
view?: StepperItemView;
disabled?: boolean;
icon?: SVGIconData;
onClick?: (event: React.MouseEvent) => void;
className?: string;
}

type ComponentProps = StepperItemProps &
Pick<StepperProps, 'size' | 'onUpdate'> & {selected?: string | number};
};

export const StepperItem = (props: ComponentProps) => {
export const StepperItem = React.forwardRef<HTMLButtonElement, StepperItemProps>((props, ref) => {
const {
id,
size,
children,
view = 'idle',
disabled = false,
selected = false,
className,
onUpdate,
icon: customIcon,
...restButtonProps
} = props;

const {onUpdate, value, size} = useStepperContext();

const onClick = (e: React.MouseEvent) => {
props.onClick?.(e);

Expand Down Expand Up @@ -66,10 +62,12 @@ export const StepperItem = (props: ComponentProps) => {
}
}, [view, customIcon]);

const selectedItem = id === selected;
const selectedItem = id === undefined ? false : id === value;

return (
<Button
ref={ref}
{...restButtonProps}
width="auto"
title={typeof children === 'string' ? children : undefined}
className={b('item', {view, disabled, selected: selectedItem, size}, className)}
Expand All @@ -82,4 +80,6 @@ export const StepperItem = (props: ComponentProps) => {
<Text className={b('item-text')}>{children}</Text>
</Button>
);
};
});

StepperItem.displayName = 'StepperItem';
8 changes: 4 additions & 4 deletions src/components/Stepper/StepperSeparator.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {ChevronRight} from '@gravity-ui/icons';
import {ChevronLeft, ChevronRight} from '@gravity-ui/icons';

import {Icon} from '../Icon';
import {useDirection} from '../theme';

import {b} from './utils';

import './Stepper.scss';

type StepperSeparatorProps = {
separator?: React.ReactNode;
};

export const StepperSeparator = ({separator}: StepperSeparatorProps) => {
const direction = useDirection();
return (
<div className={b('separator')} aria-hidden={true}>
{separator ?? <Icon data={ChevronRight} />}
{separator ?? <Icon data={direction === 'rtl' ? ChevronLeft : ChevronRight} />}
</div>
);
};
4 changes: 4 additions & 0 deletions src/components/Stepper/__stories__/Docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export const StepperCustomSeparator = () => (
<Canvas of={Stories.CustomSeparator} sourceState="none" />
);
export const StepperDisabled = () => <Canvas of={Stories.Disabled} sourceState="none" />;
export const StepperWithFloatingElements = () => (
<Canvas of={Stories.WithFloatingElements} sourceState="none" />
);
export const StepperInteractiveShowcase = () => (
<Canvas of={Stories.InteractiveShowcase} sourceState="none" />
);
Expand All @@ -36,6 +39,7 @@ export const StepperInteractiveShowcase = () => (
StepperCustomSeparator,
StepperDisabled,
StepperInteractiveShowcase,
StepperWithFloatingElements,
},
}}
>
Expand Down
45 changes: 20 additions & 25 deletions src/components/Stepper/__stories__/Stepper.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import {Cloud, CreditCard, Rocket} from '@gravity-ui/icons';
import type {Meta, StoryObj} from '@storybook/react';

import {Text} from '../../Text';
import {Flex} from '../../layout';
import {Tooltip} from '../../Tooltip';
import {Stepper} from '../Stepper';

import {StepperShowcase} from './StepperShowcase';
import {StepperInteractiveShowcase, StepperSizeShowcase} from './StepperShowcase';

export default {
title: 'Components/Navigation/Stepper',
Expand Down Expand Up @@ -59,28 +59,8 @@ export const View = {
} satisfies Story;

export const Size = {
render: (args) => {
return (
<Flex direction="column" gap={4}>
<Stepper {...args} size="s">
<Stepper.Item>Step 1</Stepper.Item>
<Stepper.Item>Step 2</Stepper.Item>
<Stepper.Item>Step 3</Stepper.Item>
</Stepper>

<Stepper {...args} size="m">
<Stepper.Item>Step 1</Stepper.Item>
<Stepper.Item>Step 2</Stepper.Item>
<Stepper.Item>Step 3</Stepper.Item>
</Stepper>

<Stepper {...args} size="l">
<Stepper.Item>Step 1</Stepper.Item>
<Stepper.Item>Step 2</Stepper.Item>
<Stepper.Item>Step 3</Stepper.Item>
</Stepper>
</Flex>
);
render: () => {
return <StepperSizeShowcase />;
},
} satisfies Story;

Expand Down Expand Up @@ -133,6 +113,21 @@ export const CustomSeparator = {

export const InteractiveShowcase = {
render: (args) => {
return <StepperShowcase {...args} />;
return <StepperInteractiveShowcase {...args} />;
},
} satisfies Story;

export const WithFloatingElements = {
render: (args) => {
return (
<Stepper {...args} separator={<Separator />}>
<Tooltip content="fancy step with tooltip">
<Stepper.Item>Step 1</Stepper.Item>
</Tooltip>
<Stepper.Item view="error">Step 2</Stepper.Item>
<Stepper.Item view="success">Step 3</Stepper.Item>
<Stepper.Item>Step 4 with very long title</Stepper.Item>
</Stepper>
);
},
} satisfies Story;
29 changes: 27 additions & 2 deletions src/components/Stepper/__stories__/StepperShowcase.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as React from 'react';

import Stepper from '../Stepper';
import {Flex} from '../../layout/Flex/Flex';
import {Stepper} from '../Stepper';
import type {StepperProps} from '../Stepper';

export const StepperShowcase = (props: StepperProps) => {
export const StepperInteractiveShowcase = (props: StepperProps) => {
const [value, setValue] = React.useState<number | string | undefined>(0);

return (
Expand All @@ -15,3 +16,27 @@ export const StepperShowcase = (props: StepperProps) => {
</Stepper>
);
};

export const StepperSizeShowcase = () => {
return (
<Flex direction="column" gap={4}>
<Stepper size="s">
<Stepper.Item>Step 1</Stepper.Item>
<Stepper.Item>Step 2</Stepper.Item>
<Stepper.Item>Step 3</Stepper.Item>
</Stepper>

<Stepper size="m">
<Stepper.Item>Step 1</Stepper.Item>
<Stepper.Item>Step 2</Stepper.Item>
<Stepper.Item>Step 3</Stepper.Item>
</Stepper>

<Stepper size="l">
<Stepper.Item>Step 1</Stepper.Item>
<Stepper.Item>Step 2</Stepper.Item>
<Stepper.Item>Step 3</Stepper.Item>
</Stepper>
</Flex>
);
};
17 changes: 17 additions & 0 deletions src/components/Stepper/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';

import type {StepperProps} from './Stepper';

export type StepperContextProps = Pick<StepperProps, 'size' | 'onUpdate' | 'value'>;

export const StepperContext = React.createContext<StepperContextProps>({
size: 'm',
onUpdate: undefined,
value: undefined,
});

export const useStepperContext = () => {
const data = React.useContext(StepperContext);

return data;
};

0 comments on commit bc8e38c

Please sign in to comment.