-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat(web-react): Introduce Box component #DS-1595
- Loading branch information
Showing
17 changed files
with
662 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,34 @@ | ||
'use client'; | ||
|
||
import classNames from 'classnames'; | ||
import React, { ElementType } from 'react'; | ||
import { useStyleProps } from '../../hooks'; | ||
import { SpiritBoxProps } from '../../types'; | ||
import { useBoxStyleProps } from './useBoxStyleProps'; | ||
|
||
const defaultProps: Partial<SpiritBoxProps> = { | ||
elementType: 'div', | ||
}; | ||
|
||
const Box = <T extends ElementType = 'div'>(props: SpiritBoxProps<T>) => { | ||
const propsWithDefaults = { ...defaultProps, ...props }; | ||
const { elementType: ElementTag = 'div', children, ...restProps } = propsWithDefaults; | ||
|
||
const { classProps, props: modifiedProps, styleProps: boxStyle } = useBoxStyleProps(restProps); | ||
const { styleProps, props: otherProps } = useStyleProps(modifiedProps); | ||
|
||
const boxStyleProps = { | ||
style: { | ||
...styleProps.style, | ||
...boxStyle, | ||
}, | ||
}; | ||
|
||
return ( | ||
<ElementTag {...otherProps} {...boxStyleProps} className={classNames(classProps, styleProps.className)}> | ||
{children} | ||
</ElementTag> | ||
); | ||
}; | ||
|
||
export default Box; |
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,82 @@ | ||
# Box | ||
|
||
The Box component is a simple container around content or other components. | ||
|
||
```jsx | ||
<Box>{/* Content go here */}</Box> | ||
``` | ||
|
||
## Border | ||
|
||
You can define border width, color, and radius using the `borderColor`, `borderRadius`, and `borderWidth` props. | ||
|
||
```jsx | ||
<Box borderColor="basic" borderRadius="200" borderWidth="100"> | ||
{/* Content go here */} | ||
</Box> | ||
``` | ||
|
||
## Padding | ||
|
||
You can define padding using the `padding` prop. | ||
|
||
```jsx | ||
<Box padding="space-200">{/* Content go here */}</Box> | ||
``` | ||
|
||
It is also possible to define padding for horizontal and vertical sides using the `paddingX` and `paddingY` props. | ||
|
||
```jsx | ||
<Box paddingX="space-200" paddingY="space-300"> | ||
{/* Content go here */} | ||
</Box> | ||
``` | ||
|
||
You can also define padding for each side using the `paddingTop`, `paddingRight`, `paddingBottom`, and `paddingLeft` props. | ||
|
||
```jsx | ||
<Box paddingTop="space-200" paddingRight="space-300" paddingBottom="space-400" paddingLeft="space-500"> | ||
{/* Content go here */} | ||
</Box> | ||
``` | ||
|
||
Responsive values can be set for each prop using an object: | ||
|
||
```jsx | ||
<Box padding={{ mobile: 'space-200', tablet: 'space-300', desktop: 'space-400' }}>{/* Content go here */}</Box> | ||
``` | ||
|
||
## Background Color | ||
|
||
You can define background color using the `backgroundColor` prop. | ||
|
||
```jsx | ||
<Box backgroundColor="basic">{/* Content go here */}</Box> | ||
``` | ||
|
||
## API | ||
|
||
| Name | Type | Default | Required | Description | | ||
| ----------------- | ----------------------------------------------------------------- | ------- | -------- | ----------------------------- | | ||
| `backgroundColor` | [Background Color dictionary][dictionary-background-color] | - | ✕ | Background color of the Box | | ||
| `borderColor` | [Border Color dictionary][dictionary-border-color] | - | ✕ | Border color of the Box | | ||
| `borderRadius` | `string` | - | ✕ | Border radius of the Box | | ||
| `borderWidth` | `string` | - | ✕ | Border width of the Box | | ||
| `elementType` | `ElementType` | `div` | ✕ | Type of element | | ||
| `padding` | \[`SpaceToken` \| `Partial<Record<BreakpointToken, SpaceToken>>`] | - | ✕ | Padding of the Box | | ||
| `paddingX` | \[`SpaceToken` \| `Partial<Record<BreakpointToken, SpaceToken>>`] | - | ✕ | Horizontal padding of the Box | | ||
| `paddingY` | \[`SpaceToken` \| `Partial<Record<BreakpointToken, SpaceToken>>`] | - | ✕ | Vertical padding of the Box | | ||
| `paddingTop` | \[`SpaceToken` \| `Partial<Record<BreakpointToken, SpaceToken>>`] | - | ✕ | Padding top of the Box | | ||
| `paddingRight` | \[`SpaceToken` \| `Partial<Record<BreakpointToken, SpaceToken>>`] | - | ✕ | Padding right of the Box | | ||
| `paddingBottom` | \[`SpaceToken` \| `Partial<Record<BreakpointToken, SpaceToken>>`] | - | ✕ | Padding bottom of the Box | | ||
| `paddingLeft` | \[`SpaceToken` \| `Partial<Record<BreakpointToken, SpaceToken>>`] | - | ✕ | Padding left of the Box | | ||
|
||
On top of the API options, the components accept [additional attributes][readme-additional-attributes]. | ||
If you need more control over the styling of a component, you can use [style props][readme-style-props] | ||
and [escape hatches][readme-escape-hatches]. | ||
|
||
[dictionary-background-color]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#color | ||
[dictionary-border-color]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#color | ||
[readme-additional-attributes]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#additional-attributes | ||
[readme-escape-hatches]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#escape-hatches | ||
[readme-style-props]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#style-props |
123 changes: 123 additions & 0 deletions
123
packages/web-react/src/components/Box/__tests__/Box.test.tsx
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,123 @@ | ||
import '@testing-library/jest-dom'; | ||
import { render, screen } from '@testing-library/react'; | ||
import React from 'react'; | ||
import { restPropsTest } from '../../../../tests/providerTests/restPropsTest'; | ||
import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest'; | ||
import Box from '../Box'; | ||
|
||
const dataProvider = [ | ||
{ | ||
prop: 'backgroundColor', | ||
value: 'primary', | ||
className: 'bg-primary', | ||
description: 'background color', | ||
}, | ||
{ | ||
prop: 'borderColor', | ||
value: 'basic', | ||
className: 'border-basic', | ||
description: 'border color', | ||
}, | ||
{ | ||
prop: 'borderWidth', | ||
value: '100', | ||
className: 'border-100', | ||
description: 'border width', | ||
}, | ||
{ | ||
prop: 'padding', | ||
value: 'space-800', | ||
className: 'p-800', | ||
description: 'padding', | ||
}, | ||
{ | ||
prop: 'paddingX', | ||
value: 'space-800', | ||
className: 'px-800', | ||
description: 'horizontal padding', | ||
}, | ||
{ | ||
prop: 'paddingY', | ||
value: 'space-800', | ||
className: 'py-800', | ||
description: 'vertical padding', | ||
}, | ||
{ | ||
prop: 'paddingTop', | ||
value: 'space-800', | ||
className: 'pt-800', | ||
description: 'padding top', | ||
}, | ||
{ | ||
prop: 'paddingBottom', | ||
value: 'space-800', | ||
className: 'pb-800', | ||
description: 'padding bottom', | ||
}, | ||
{ | ||
prop: 'paddingLeft', | ||
value: 'space-800', | ||
className: 'pl-800', | ||
description: 'padding left', | ||
}, | ||
{ | ||
prop: 'paddingRight', | ||
value: 'space-800', | ||
className: 'pr-800', | ||
description: 'padding right', | ||
}, | ||
{ | ||
prop: 'padding', | ||
value: { mobile: 'space-600', tablet: 'space-800', desktop: 'space-1000' }, | ||
className: 'p-600 p-tablet-800 p-desktop-1000', | ||
description: 'responsive padding', | ||
}, | ||
]; | ||
|
||
describe('Box', () => { | ||
stylePropsTest(Box); | ||
|
||
restPropsTest(Box, 'div'); | ||
|
||
it('should render children', () => { | ||
render(<Box>Content</Box>); | ||
|
||
expect(screen.getByText('Content')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render with background color', () => { | ||
render(<Box backgroundColor="primary">Content</Box>); | ||
|
||
expect(screen.getByText('Content')).toHaveStyle('background-color: var(--color-primary)'); | ||
}); | ||
|
||
it('should render with border radius and width', () => { | ||
render( | ||
<Box borderRadius="200" borderWidth="100" data-testid="Box"> | ||
Content | ||
</Box>, | ||
); | ||
|
||
expect(screen.getByTestId('Box')).toHaveClass('rounded-200'); | ||
}); | ||
|
||
it('should not render correct border radius class when border width is not set', () => { | ||
render( | ||
<Box borderRadius="200" data-testid="Box"> | ||
Content | ||
</Box>, | ||
); | ||
|
||
expect(screen.getByTestId('Box')).not.toHaveClass('rounded-200'); | ||
}); | ||
|
||
it.each(dataProvider)('should render with $description', ({ prop, value, className }) => { | ||
render( | ||
<Box {...{ [prop]: value }} data-testid="Box"> | ||
Content | ||
</Box>, | ||
); | ||
|
||
expect(screen.getByTestId('Box')).toHaveClass(className); | ||
}); | ||
}); |
122 changes: 122 additions & 0 deletions
122
packages/web-react/src/components/Box/__tests__/useBoxStyleProps.test.ts
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,122 @@ | ||
import { renderHook } from '@testing-library/react'; | ||
import { SpiritBoxProps } from '../../../types'; | ||
import { useBoxStyleProps } from '../useBoxStyleProps'; | ||
|
||
describe('useBoxStyleProps', () => { | ||
it('should return defaults', () => { | ||
const props: SpiritBoxProps = {}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe(''); | ||
}); | ||
|
||
it('should return background classProps', () => { | ||
const props: SpiritBoxProps = { | ||
backgroundColor: 'secondary', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('bg-secondary'); | ||
}); | ||
|
||
it('should return padding classProps', () => { | ||
const props: SpiritBoxProps = { | ||
padding: 'space-400', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('p-400'); | ||
}); | ||
|
||
it('should return paddingX classProps', () => { | ||
const props: SpiritBoxProps = { | ||
paddingX: 'space-400', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('px-400'); | ||
}); | ||
|
||
it('should return paddingY classProps', () => { | ||
const props: SpiritBoxProps = { | ||
paddingY: 'space-400', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('py-400'); | ||
}); | ||
|
||
it('should return paddingTop classProps', () => { | ||
const props: SpiritBoxProps = { | ||
paddingTop: 'space-400', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('pt-400'); | ||
}); | ||
|
||
it('should return paddingBottom classProps', () => { | ||
const props: SpiritBoxProps = { | ||
paddingBottom: 'space-400', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('pb-400'); | ||
}); | ||
|
||
it('should return paddingLeft classProps', () => { | ||
const props: SpiritBoxProps = { | ||
paddingLeft: 'space-400', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('pl-400'); | ||
}); | ||
|
||
it('should return paddingRight classProps', () => { | ||
const props: SpiritBoxProps = { | ||
paddingRight: 'space-400', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('pr-400'); | ||
}); | ||
|
||
it('should return responsive padding classProps', () => { | ||
const props: SpiritBoxProps = { | ||
padding: { mobile: 'space-400', tablet: 'space-500', desktop: 'space-600' }, | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('p-400 p-tablet-500 p-desktop-600'); | ||
}); | ||
|
||
it('should return border radius classProps', () => { | ||
const props: SpiritBoxProps = { | ||
borderRadius: '200', | ||
borderWidth: '100', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('border-basic rounded-200 border-100'); | ||
}); | ||
|
||
it('should not return border radius classProps if border with is not set', () => { | ||
const props: SpiritBoxProps = { | ||
borderRadius: '200', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).not.toBe('rounded-200'); | ||
}); | ||
|
||
it('should return border color classProps', () => { | ||
const props: SpiritBoxProps = { | ||
borderColor: 'basic', | ||
borderWidth: '100', | ||
}; | ||
const { result } = renderHook(() => useBoxStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('border-basic border-100'); | ||
}); | ||
}); |
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,6 @@ | ||
import React from 'react'; | ||
import Box from '../Box'; | ||
|
||
const BoxDefault = () => <Box>Content</Box>; | ||
|
||
export default BoxDefault; |
21 changes: 21 additions & 0 deletions
21
packages/web-react/src/components/Box/demo/BoxWithBackgroundColor.tsx
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,21 @@ | ||
import React from 'react'; | ||
import Box from '../Box'; | ||
|
||
const BoxWithBackgroundColor = () => ( | ||
<> | ||
<fieldset className="mb-0" style={{ border: 0 }}> | ||
<legend className="mb-500">For demo purposes the box is bordered</legend> | ||
</fieldset> | ||
<Box padding="space-800" borderWidth="100"> | ||
Primary Background | ||
</Box> | ||
<Box padding="space-800" borderWidth="100" backgroundColor="secondary"> | ||
Secondary Background | ||
</Box> | ||
<Box padding="space-800" borderWidth="100" backgroundColor="tertiary"> | ||
Tertiary Background | ||
</Box> | ||
</> | ||
); | ||
|
||
export default BoxWithBackgroundColor; |
Oops, something went wrong.