diff --git a/packages/components/src/Card/Card.style.tsx b/packages/components/src/Card/Card.style.tsx index f0b4617a0..7cc111db0 100644 --- a/packages/components/src/Card/Card.style.tsx +++ b/packages/components/src/Card/Card.style.tsx @@ -22,7 +22,11 @@ const StyledCard = styled(Flex)` 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} diff --git a/packages/components/src/Drawer/Drawer.style.tsx b/packages/components/src/Drawer/Drawer.style.tsx index c698b0a62..eaf1f2c40 100644 --- a/packages/components/src/Drawer/Drawer.style.tsx +++ b/packages/components/src/Drawer/Drawer.style.tsx @@ -30,6 +30,8 @@ const StyledModal = styled.div` right: 100%; height: 100%; + max-width: 300px; + width: 100%; transition: transform ${({ $isOpen }) => ($isOpen ? '250ms' : '100ms')} ease-in-out; @@ -41,7 +43,8 @@ const StyledDialog = styled(Dialog)` background: none; border: none; border-radius: 0px; - width: 300px; + width: 100%; + height: 100%; display: flex; flex-direction: column; position: relative; diff --git a/packages/components/src/List/List.stories.tsx b/packages/components/src/List/List.stories.tsx index f71effb3f..105466b50 100644 --- a/packages/components/src/List/List.stories.tsx +++ b/packages/components/src/List/List.stories.tsx @@ -26,7 +26,11 @@ export default { render: Render } as Meta; -export const Default: StoryObj = {}; +export const Default: StoryObj = { + args: { + selectedKeys: ['1'] + } +}; export const Horizontal: StoryObj = { args: { diff --git a/packages/components/src/Stepper/Stepper.stories.tsx b/packages/components/src/Stepper/Stepper.stories.tsx new file mode 100644 index 000000000..205666a9a --- /dev/null +++ b/packages/components/src/Stepper/Stepper.stories.tsx @@ -0,0 +1,25 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { Stepper, StepperProps } from './Stepper'; + +import { StepperItem } from '.'; + +const Render = (args: any) => ( + + + + + +); + +export default { + title: 'Status/Stepper', + component: Stepper, + render: Render +} as Meta; + +export const Default: StoryObj = { + args: { + index: 1 + } +}; diff --git a/packages/components/src/Stepper/Stepper.style.tsx b/packages/components/src/Stepper/Stepper.style.tsx new file mode 100644 index 000000000..b6ab7d449 --- /dev/null +++ b/packages/components/src/Stepper/Stepper.style.tsx @@ -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)` + ${({ theme, $status }) => css` + ${theme.stepper.step.base} + ${theme.stepper.step[$status]} + `} +`; + +const StyledDivider = styled(Divider)` + width: 100%; + align-self: auto; + flex: 1 1 0%; + ${({ theme, $status }) => css` + ${theme.stepper.divider[$status]} + `} +`; + +export { StyledStep, StyledDivider }; diff --git a/packages/components/src/Stepper/Stepper.tsx b/packages/components/src/Stepper/Stepper.tsx new file mode 100644 index 000000000..4728d19ff --- /dev/null +++ b/packages/components/src/Stepper/Stepper.tsx @@ -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; + +type StepperProps = Props & NativeAttrs; + +const Stepper = forwardRef(({ index = 0, ...props }, ref): JSX.Element => { + const collection = useCollection(props as any, (nodes) => new ListCollection(nodes) as any); + + const steps = [...collection]; + + return ( + + {steps.map((item, idx) => ( + index ? 'incomplete' : idx === index ? 'active' : 'complete'} + /> + ))} + + ); +}); + +Stepper.displayName = 'Stepper'; + +export { Stepper }; +export type { StepperProps, Selection }; diff --git a/packages/components/src/Stepper/StepperItem.tsx b/packages/components/src/Stepper/StepperItem.tsx new file mode 100644 index 000000000..1c25bf67d --- /dev/null +++ b/packages/components/src/Stepper/StepperItem.tsx @@ -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; + +type StepperItemProps = Props & InheritAttrs; + +type InternalProps = StepperItemProps & { + item: Node; + status: 'active' | 'complete' | 'incomplete'; + isLast: boolean; + fullWidth?: boolean; +}; + +const StepperItem = ({ status, isLast, item }: InternalProps): JSX.Element => ( + + + + {(item.index || 0) + 1} + + {item.rendered} + + {!isLast && } + +); + +export { StepperItem }; +export type { StepperItemProps }; diff --git a/packages/components/src/Stepper/__tests__/Stepper.test.tsx b/packages/components/src/Stepper/__tests__/Stepper.test.tsx new file mode 100644 index 000000000..2a315a440 --- /dev/null +++ b/packages/components/src/Stepper/__tests__/Stepper.test.tsx @@ -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( + + item + + ); + + expect(() => wrapper.unmount()).not.toThrow(); + }); + + it('ref should be forwarded', () => { + const ref = createRef(); + + render( + + item + + ); + + expect(ref.current).not.toBeNull(); + }); + + it('should pass a11y', async () => { + await testA11y( + + item + + ); + }); +}); diff --git a/packages/components/src/Stepper/index.tsx b/packages/components/src/Stepper/index.tsx new file mode 100644 index 000000000..05d99b7c0 --- /dev/null +++ b/packages/components/src/Stepper/index.tsx @@ -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 ( + props: Omit, 'children'> & StepperItemProps & { children?: ReactNode } +) => JSX.Element; + +export type { StepperProps, Selection } from './Stepper'; +export { Stepper } from './Stepper'; + +export type { StepperItemProps }; +export { StepperItem }; diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index cd2126615..51c8942fe 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -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'; diff --git a/packages/core/theme/src/components/index.ts b/packages/core/theme/src/components/index.ts index cd5c088c8..1c2fb1cc1 100644 --- a/packages/core/theme/src/components/index.ts +++ b/packages/core/theme/src/components/index.ts @@ -15,3 +15,4 @@ export * from './tooltip'; export * from './token-input'; export * from './tabs'; export * from './table'; +export * from './stepper'; diff --git a/packages/core/theme/src/components/stepper.ts b/packages/core/theme/src/components/stepper.ts new file mode 100644 index 000000000..566655a96 --- /dev/null +++ b/packages/core/theme/src/components/stepper.ts @@ -0,0 +1,17 @@ +import { StyledObject } from 'styled-components'; + +type StepperTheme = { + step: { + base: StyledObject; + active: StyledObject; + incomplete: StyledObject; + complete: StyledObject; + }; + divider: { + active: StyledObject; + incomplete: StyledObject; + complete: StyledObject; + }; +}; + +export type { StepperTheme }; diff --git a/packages/core/theme/src/core/index.ts b/packages/core/theme/src/core/index.ts index 134577479..df52e70f7 100644 --- a/packages/core/theme/src/core/index.ts +++ b/packages/core/theme/src/core/index.ts @@ -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'; diff --git a/packages/core/theme/src/define.ts b/packages/core/theme/src/define.ts index 71ab3802b..b94145071 100644 --- a/packages/core/theme/src/define.ts +++ b/packages/core/theme/src/define.ts @@ -30,7 +30,8 @@ import { TooltipTheme, TokenInputTheme, TableTheme, - TabsTheme + TabsTheme, + StepperTheme } from './components'; const baseTheme = { @@ -64,6 +65,7 @@ type ThemeParams = { tokenInput: TokenInputTheme; tabs: TabsTheme; table: TableTheme; + stepper: StepperTheme; }; const defineTheme = ({ colors, ...theme }: ThemeParams) => ({ diff --git a/packages/core/theme/src/themes/bob/index.ts b/packages/core/theme/src/themes/bob/index.ts index 53ca5b688..af733762d 100644 --- a/packages/core/theme/src/themes/bob/index.ts +++ b/packages/core/theme/src/themes/bob/index.ts @@ -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, @@ -37,7 +38,8 @@ const bobTheme = defineTheme({ tooltip, tokenInput, table, - tabs + tabs, + stepper }); export { bobTheme }; diff --git a/packages/core/theme/src/themes/bob/stepper.ts b/packages/core/theme/src/themes/bob/stepper.ts new file mode 100644 index 000000000..02f1010a1 --- /dev/null +++ b/packages/core/theme/src/themes/bob/stepper.ts @@ -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 };