Skip to content

Commit

Permalink
feat: add stepper
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsimao committed Mar 15, 2024
1 parent d8243c9 commit eea6df0
Show file tree
Hide file tree
Showing 16 changed files with 260 additions and 5 deletions.
6 changes: 5 additions & 1 deletion packages/components/src/Card/Card.style.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ const StyledCard = styled(Flex)<StyledCardProps>`
const { border, boxShadow, backgroundColor, ...baseCss } = theme.card.base;
return css`
border: ${typeof $bordered === 'boolean' ? border : `1px solid ${theme.color($bordered)}`};
border: ${typeof $bordered === 'boolean'
? $bordered
? border
: undefined
: `1px solid ${theme.color($bordered)}`};
box-shadow: ${$shadowed && boxShadow};
background-color: ${$background ? theme.color($background) : backgroundColor};
${baseCss}
Expand Down
5 changes: 4 additions & 1 deletion packages/components/src/Drawer/Drawer.style.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const StyledModal = styled.div<StyledModalProps>`
right: 100%;
height: 100%;
max-width: 300px;
width: 100%;
transition: transform ${({ $isOpen }) => ($isOpen ? '250ms' : '100ms')} ease-in-out;
Expand All @@ -41,7 +43,8 @@ const StyledDialog = styled(Dialog)<StyledDialogProps>`
background: none;
border: none;
border-radius: 0px;
width: 300px;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
position: relative;
Expand Down
6 changes: 5 additions & 1 deletion packages/components/src/List/List.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ export default {
render: Render
} as Meta<typeof List>;

export const Default: StoryObj<ListProps> = {};
export const Default: StoryObj<ListProps> = {
args: {
selectedKeys: ['1']
}
};

export const Horizontal: StoryObj<ListProps> = {
args: {
Expand Down
25 changes: 25 additions & 0 deletions packages/components/src/Stepper/Stepper.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Meta, StoryObj } from '@storybook/react';

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

import { StepperItem } from '.';

const Render = (args: any) => (
<Stepper aria-label='Example Stepper' {...args}>
<StepperItem textValue='1' />
<StepperItem textValue='2' />
<StepperItem textValue='3' />
</Stepper>
);

export default {
title: 'Status/Stepper',
component: Stepper,
render: Render
} as Meta<typeof Stepper>;

export const Default: StoryObj<StepperProps> = {
args: {
index: 1
}
};
26 changes: 26 additions & 0 deletions packages/components/src/Stepper/Stepper.style.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled, { css } from 'styled-components';

import { Divider } from '../Divider';
import { Flex } from '../Flex';

type StyledStepProps = {
$status: 'incomplete' | 'complete' | 'active';
};

const StyledStep = styled(Flex)<StyledStepProps>`
${({ theme, $status }) => css`
${theme.stepper.step.base}
${theme.stepper.step[$status]}
`}
`;

const StyledDivider = styled(Divider)<StyledStepProps>`
width: 100%;
align-self: auto;
flex: 1 1 0%;
${({ theme, $status }) => css`
${theme.stepper.divider[$status]}
`}
`;

export { StyledStep, StyledDivider };
41 changes: 41 additions & 0 deletions packages/components/src/Stepper/Stepper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { forwardRef } from 'react';
import { Selection } from '@react-types/shared';
import { useCollection } from '@react-stately/collections';
import { ListCollection } from '@react-stately/list';

import { Flex, FlexProps } from '../Flex';

import { StepperItem } from './StepperItem';

type Props = {
index?: number;
fullWidth?: boolean;
};

type NativeAttrs = Omit<FlexProps, keyof Props | 'direction'>;

type StepperProps = Props & NativeAttrs;

const Stepper = forwardRef<HTMLDivElement, StepperProps>(({ index = 0, ...props }, ref): JSX.Element => {
const collection = useCollection(props as any, (nodes) => new ListCollection(nodes) as any);

const steps = [...collection];

return (
<Flex ref={ref} direction='row' gap='xl' {...props}>
{steps.map((item, idx) => (
<StepperItem
key={item.key}
isLast={steps.length - 1 === idx}
item={item}
status={idx > index ? 'incomplete' : idx === index ? 'active' : 'complete'}
/>
))}
</Flex>
);
});

Stepper.displayName = 'Stepper';

export { Stepper };
export type { StepperProps, Selection };
34 changes: 34 additions & 0 deletions packages/components/src/Stepper/StepperItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Node } from '@react-types/shared';

import { Span } from '../Text';
import { Flex, FlexProps } from '../Flex';

import { StyledDivider, StyledStep } from './Stepper.style';

type Props = {};

type InheritAttrs = Omit<FlexProps, keyof Props>;

type StepperItemProps = Props & InheritAttrs;

type InternalProps<T extends object> = StepperItemProps & {
item: Node<T>;
status: 'active' | 'complete' | 'incomplete';
isLast: boolean;
fullWidth?: boolean;
};

const StepperItem = <T extends object>({ status, isLast, item }: InternalProps<T>): JSX.Element => (
<Flex alignItems='center' flex={isLast ? 'initial' : '1 1 0%'} gap='xl'>
<Flex alignItems='center' gap='md' justifyContent='center'>
<StyledStep $status={status} alignItems='center' justifyContent='center'>
<Span weight='bold'>{(item.index || 0) + 1}</Span>
</StyledStep>
{item.rendered}
</Flex>
{!isLast && <StyledDivider $status={status} size='md' />}
</Flex>
);

export { StepperItem };
export type { StepperItemProps };
36 changes: 36 additions & 0 deletions packages/components/src/Stepper/__tests__/Stepper.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createRef } from 'react';
import { testA11y, render } from '@interlay/test-utils';

import { Stepper, StepperItem } from '..';

describe('Stepper', () => {
it('should render correctly', () => {
const wrapper = render(
<Stepper aria-label='progress'>
<StepperItem>item</StepperItem>
</Stepper>
);

expect(() => wrapper.unmount()).not.toThrow();
});

it('ref should be forwarded', () => {
const ref = createRef<HTMLDivElement>();

render(
<Stepper ref={ref} aria-label='progress'>
<StepperItem>item</StepperItem>
</Stepper>
);

expect(ref.current).not.toBeNull();
});

it('should pass a11y', async () => {
await testA11y(
<Stepper aria-label='progress'>
<StepperItem>item</StepperItem>
</Stepper>
);
});
});
15 changes: 15 additions & 0 deletions packages/components/src/Stepper/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Item } from '@react-stately/collections';
import { ItemProps } from '@react-types/shared';
import { ReactNode } from 'react';

import { StepperItemProps } from './StepperItem';

const StepperItem = Item as <T>(
props: Omit<ItemProps<T>, 'children'> & StepperItemProps & { children?: ReactNode }
) => JSX.Element;

export type { StepperProps, Selection } from './Stepper';
export { Stepper } from './Stepper';

export type { StepperItemProps };
export { StepperItem };
2 changes: 2 additions & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,5 @@ export { Button } from './Button';
export type { ButtonProps } from './Button';
export { UnstyledButton } from './UnstyledButton';
export type { UnstyledButtonProps } from './UnstyledButton';
export { Stepper, StepperItem } from './Stepper';
export type { StepperProps, StepperItemProps } from './Stepper';
1 change: 1 addition & 0 deletions packages/core/theme/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * from './tooltip';
export * from './token-input';
export * from './tabs';
export * from './table';
export * from './stepper';
17 changes: 17 additions & 0 deletions packages/core/theme/src/components/stepper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { StyledObject } from 'styled-components';

type StepperTheme = {
step: {
base: StyledObject<object>;
active: StyledObject<object>;
incomplete: StyledObject<object>;
complete: StyledObject<object>;
};
divider: {
active: StyledObject<object>;
incomplete: StyledObject<object>;
complete: StyledObject<object>;
};
};

export type { StepperTheme };
2 changes: 2 additions & 0 deletions packages/core/theme/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export type ProgressBarColors = 'default' | 'red';

export type LabelPosition = 'top' | 'side';

// export type DimensionValue =

export * from './font-size';
export * from './font-weight';
export * from './line-height';
Expand Down
4 changes: 3 additions & 1 deletion packages/core/theme/src/define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import {
TooltipTheme,
TokenInputTheme,
TableTheme,
TabsTheme
TabsTheme,
StepperTheme
} from './components';

const baseTheme = {
Expand Down Expand Up @@ -64,6 +65,7 @@ type ThemeParams = {
tokenInput: TokenInputTheme;
tabs: TabsTheme;
table: TableTheme;
stepper: StepperTheme;
};

const defineTheme = ({ colors, ...theme }: ThemeParams) => ({
Expand Down
4 changes: 3 additions & 1 deletion packages/core/theme/src/themes/bob/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { tooltip } from './tooltip';
import { tokenInput } from './token-input';
import { tabs } from './tabs';
import { table } from './table';
import { stepper } from './stepper';

const bobTheme = defineTheme({
colors,
Expand All @@ -37,7 +38,8 @@ const bobTheme = defineTheme({
tooltip,
tokenInput,
table,
tabs
tabs,
stepper
});

export { bobTheme };
41 changes: 41 additions & 0 deletions packages/core/theme/src/themes/bob/stepper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { rounded, spacing } from '../../core';
import { StepperTheme } from '../../components';

import { color } from './colors';

const stepper: StepperTheme = {
divider: {
incomplete: {
backgroundColor: color('grey-400')
},
active: {
backgroundColor: color('grey-400')
},
complete: {
backgroundColor: color('primary-500')
}
},
step: {
base: {
width: spacing('5xl'),
height: spacing('5xl'),
borderRadius: rounded('full'),
color: color('light'),
borderWidth: '2px',
borderStyle: 'solid',
backgroundColor: color('grey-700')
},
active: {
borderColor: color('primary-500')
},
incomplete: {
borderColor: color('grey-400')
},
complete: {
borderColor: color('primary-500'),
backgroundColor: color('primary-500')
}
}
};

export { stepper };

0 comments on commit eea6df0

Please sign in to comment.