-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: TET-848 add button group (#160)
* feat: TET-848 add ButtonGroup component * feat: TET-848 add tests and fix a bug * feat: TET-848 add export * feat: TET-848 resolve build problems
- Loading branch information
1 parent
9236719
commit d48dd7f
Showing
9 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { HTMLAttributes } from 'react'; | ||
|
||
import { ButtonGroupConfig } from './ButtonGroup.styles'; | ||
|
||
export type ButtonGroupSize = 'medium' | 'small'; | ||
export type ButtonGroupProps = { | ||
size?: ButtonGroupSize; | ||
custom?: ButtonGroupConfig; | ||
} & Omit<HTMLAttributes<HTMLSpanElement>, 'color'>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import { ButtonGroup } from './ButtonGroup'; | ||
import { Button } from '../Button/Button'; | ||
|
||
import { ButtonGroupDocs } from '@/docs-components/ButtonGroupDocs'; | ||
import { TetDocs } from '@/docs-components/TetDocs'; | ||
|
||
const meta = { | ||
title: 'ButtonGroup', | ||
component: ButtonGroup, | ||
tags: ['autodocs'], | ||
argTypes: { | ||
size: { | ||
options: ['small', 'medium'], | ||
defaultValue: 'medium', | ||
control: { type: 'radio' }, | ||
}, | ||
}, | ||
parameters: { | ||
docs: { | ||
description: { | ||
component: | ||
'A set of related buttons that are visually and functionally grouped. Button Group acts as a cohesive unit, providing users with clear options for actions or navigation.', | ||
}, | ||
page: () => ( | ||
<TetDocs docs="https://docs.tetrisly.com/components/list/buttongroup"> | ||
<ButtonGroupDocs /> | ||
</TetDocs> | ||
), | ||
}, | ||
}, | ||
} satisfies Meta<typeof ButtonGroup>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
children: [ | ||
<Button label="button" />, | ||
<Button label="button" />, | ||
<Button label="button" />, | ||
<Button label="button" />, | ||
], | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { BaseProps } from '@/types'; | ||
|
||
export type ButtonGroupConfig = { | ||
button: { | ||
size: { | ||
medium: BaseProps; | ||
small: BaseProps; | ||
}; | ||
} & BaseProps; | ||
} & BaseProps; | ||
|
||
export const defaultConfig = { | ||
display: 'inline-flex', | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
button: { | ||
color: { | ||
_: '$color-action-neutral-normal', | ||
hover: '$color-action-neutral-hover', | ||
}, | ||
backgroundColor: { | ||
_: '$color-action-inverted-normal', | ||
active: '$color-action-ghost-active', | ||
hover: '$color-action-ghost-hover', | ||
selected: '$color-action-ghost-selected', | ||
}, | ||
ringColor: '$color-action-outline-normal', | ||
size: { | ||
// TODO think if it can be done by passing size prop to a button component | ||
medium: { | ||
h: '$size-medium', | ||
px: '$space-component-padding-large', | ||
}, | ||
small: { | ||
h: '$size-small', | ||
px: '$space-component-padding-medium', | ||
}, | ||
}, | ||
borderRadius: { | ||
first: `$border-radius-large 0px 0px $border-radius-large`, | ||
last: `0px $border-radius-large $border-radius-large 0px`, | ||
}, | ||
transition: true, | ||
transitionDuration: 200, | ||
}, | ||
} as const satisfies ButtonGroupConfig; | ||
|
||
export const buttonGroupStyles = { | ||
defaultConfig, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { render } from '../../tests/render'; | ||
|
||
import { ButtonGroup } from '.'; | ||
|
||
import { customPropTester } from '@/tests/customPropTester'; | ||
|
||
// TODO | ||
|
||
const getButtonGroup = (jsx: JSX.Element) => { | ||
const { getByTestId } = render(jsx); | ||
return { | ||
buttonGroup: getByTestId('button-group'), | ||
}; | ||
}; | ||
describe('ButtonGroup', () => { | ||
it('should render the ButtonGroup ', () => { | ||
const { buttonGroup } = getButtonGroup(<ButtonGroup />); | ||
expect(buttonGroup).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render correct number of children', () => { | ||
const { buttonGroup } = getButtonGroup( | ||
<ButtonGroup> | ||
<ButtonGroup.Item label="label" /> | ||
<ButtonGroup.Item label="label" /> | ||
<ButtonGroup.Item label="label" /> | ||
<ButtonGroup.Item label="label" /> | ||
<ButtonGroup.Item label="label" /> | ||
</ButtonGroup>, | ||
); | ||
|
||
expect(buttonGroup?.children.length).toEqual(5); | ||
}); | ||
|
||
customPropTester(<ButtonGroup />, { | ||
containerId: 'button-group', | ||
props: { | ||
options: ['small', 'medium'], | ||
}, | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { MarginProps } from '@xstyled/styled-components'; | ||
import { | ||
Children, | ||
cloneElement, | ||
isValidElement, | ||
PropsWithChildren, | ||
useMemo, | ||
type FC, | ||
} from 'react'; | ||
|
||
import { ButtonGroupProps } from './ButtonGroup.props'; | ||
import { stylesBuilder } from './stylesBuilder'; | ||
import { Button, ButtonProps } from '../Button'; | ||
|
||
import { tet } from '@/tetrisly'; | ||
|
||
type Props = FC<PropsWithChildren<ButtonGroupProps & MarginProps>> & { | ||
Item: FC<ButtonProps & MarginProps>; | ||
}; | ||
|
||
export const ButtonGroup: Props = ({ | ||
size = 'medium', | ||
children, | ||
custom, | ||
...rest | ||
}) => { | ||
const styles = useMemo( | ||
() => | ||
stylesBuilder({ | ||
size, | ||
custom, | ||
}), | ||
[custom, size], | ||
); | ||
|
||
Children.forEach(children, (child) => { | ||
if (isValidElement(child) && child?.type !== ButtonGroup.Item) { | ||
console.error( | ||
'You should use only ButtonGroup.Item as a child of a CheckboxGroup component.', | ||
); | ||
} | ||
}); | ||
|
||
const childrenWithProps = Children.map(children, (child) => { | ||
if (isValidElement(child)) { | ||
return cloneElement(child, { ...styles.button }); | ||
} | ||
return child; | ||
}); | ||
|
||
return ( | ||
<tet.span data-testid="button-group" {...styles.container} {...rest}> | ||
{childrenWithProps} | ||
</tet.span> | ||
); | ||
}; | ||
|
||
ButtonGroup.Item = Button; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { ButtonGroup } from './ButtonGroup'; | ||
export type { ButtonGroupProps } from './ButtonGroup.props'; | ||
export { buttonGroupStyles } from './ButtonGroup.styles'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import type { ButtonGroupSize } from './ButtonGroup.props'; | ||
import { ButtonGroupConfig, defaultConfig } from './ButtonGroup.styles'; | ||
|
||
import { mergeConfigWithCustom } from '@/services'; | ||
import { BaseProps } from '@/types/BaseProps'; | ||
|
||
type ButtonGroupStyleBuilder = { | ||
container: BaseProps; | ||
button: BaseProps; | ||
}; | ||
|
||
type ButtonGroupStyleBuilderInput = { | ||
size: ButtonGroupSize; | ||
custom?: ButtonGroupConfig; | ||
}; | ||
|
||
export const stylesBuilder = ({ | ||
size, | ||
custom, | ||
}: ButtonGroupStyleBuilderInput): ButtonGroupStyleBuilder => { | ||
const { button, ...container } = mergeConfigWithCustom({ | ||
defaultConfig, | ||
custom, | ||
}); | ||
const buttonStyles = { ...button, ...button.size[size] }; | ||
|
||
return { | ||
container, | ||
button: buttonStyles, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { FC } from 'react'; | ||
import { v4 as uuidv4 } from 'uuid'; | ||
|
||
import { SectionHeader } from './common/SectionHeader'; | ||
|
||
import { Button } from '@/components/Button'; | ||
import { ButtonGroup } from '@/components/ButtonGroup'; | ||
import { tet } from '@/tetrisly'; | ||
|
||
const items = [4, 3, 2] as const; | ||
const sizes = ['medium', 'small'] as const; | ||
|
||
export const ButtonGroupDocs: FC = () => ( | ||
<> | ||
{items.map((item) => ( | ||
<tet.section | ||
key={item} | ||
display="flex" | ||
pt="$dimension-500" | ||
pb="$dimension-500" | ||
flexDirection="column" | ||
> | ||
<SectionHeader variant="H1" as="h1" px="$dimension-1000"> | ||
{item} items | ||
</SectionHeader> | ||
{sizes.map((size) => ( | ||
<tet.div key={size} display="flex" flexDirection="column"> | ||
<SectionHeader | ||
variant="H2" | ||
as="h3" | ||
px="$dimension-1000" | ||
py="$dimension-300" | ||
borderBottom="$color-neutral-strong" | ||
> | ||
Size: {size === 'medium' ? 'Medium (Default)' : 'Small'} | ||
</SectionHeader> | ||
<tet.div | ||
px="$dimension-1000" | ||
pb="$dimension-500" | ||
mt="$dimension-400" | ||
mb="$dimension-400" | ||
key={size} | ||
> | ||
<ButtonGroup> | ||
{[...Array(item)].map(() => { | ||
const id = uuidv4(); | ||
return ( | ||
<Button key={`button-item-${id}`} label="Button label" /> | ||
); | ||
})} | ||
</ButtonGroup> | ||
</tet.div> | ||
</tet.div> | ||
))} | ||
</tet.section> | ||
))} | ||
</> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters