1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

Adding Box component in TS to component-library (#19363)

* Adding TS version of Box to component-library

* Updates to types and comments
This commit is contained in:
George Marshall 2023-06-08 11:12:16 -07:00 committed by GitHub
parent fa70aec286
commit 35ae06d824
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 4448 additions and 1 deletions

View File

@ -0,0 +1,368 @@
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { Box } from './box';
# Box
The `Box` is a utility component that accepts many helper props to make styling easier.
<Canvas>
<Story id="components-componentlibrary-box--box-default-story" />
</Canvas>
## Props
<ArgsTable of={Box} />
## Usage
The following describes the props and example usage for this component.
### Margin
The margin props `margin`, `marginTop`, `marginRight`, `marginBottom`, `marginLeft`, `marginInline`, `marginInlineStart`, and `marginInlineEnd` can be used to set the margins of the `Box` component. All of the margin props accept [responsive props](#responsive-props) in the form of array props
Accepted props are: `0, 1, 2, 4, 6, 8, 9, 10, 12, 'auto` or array of these values
<Canvas>
<Story id="components-componentlibrary-box--margin" />
</Canvas>
```jsx
<Box margin={4} />
<Box marginTop={4} />
<Box marginRight={4} />
<Box marginBottom={4} />
<Box marginLeft={'auto'} />
<Box marginInline={4} />
<Box marginInlineStart={4} />
<Box marginInlineEnd={4} />
// Responsive props
<Box margin={[4, 8]} />
<Box marginTop={[4, 8]} />
<Box marginRight={[4, 8]} />
<Box marginBottom={[4, 8]} />
<Box marginLeft={['auto', 8]} />
<Box marginInline={['auto', 8]} />
<Box marginInlineStart={['auto', 8]} />
<Box marginInlineEnd={['auto', 8]} />
```
### Padding
The padding props work very similarly to margin. The padding props `padding`, `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft`, `paddingInline`, `paddingInlineStart`, and `paddingInlineEnd` can be used to set the paddings of the `Box` component. All of the padding props accept [responsive props](#responsive-props) in the form of array props
Accepted props are: `0, 1, 2, 4, 6, 8, 9, 10, 12` or array of these values
<Canvas>
<Story id="components-componentlibrary-box--padding" />
</Canvas>
```jsx
<Box padding={4} />
<Box paddingTop={4} />
<Box paddingRight={4} />
<Box paddingBottom={4} />
<Box paddingLeft={4} />
// Responsive props
<Box padding={[4, 8]} />
<Box paddingTop={[4, 8]} />
<Box paddingRight={[4, 8]} />
<Box paddingBottom={[4, 8]} />
<Box paddingLeft={[4, 8]} />
```
### Display
The `display` prop can be used to set the display of the `Box` component. All of the display props accept [responsive props](#responsive-props) in the form of array props.
Accepted props imported from the design system `Display` const are:
- `Display.Block`
- `Display.Flex`
- `Display.Grid`
- `Display.InlineBlock`
- `Display.InlineFlex`
- `Display.InlineGrid`
- `Display.Inline`
- `Display.ListItem`
- `Display.None`
or array of these values.
```jsx
import { Display } from '../../../helpers/constants/design-system';
import Box from '../ui/box';
<Box display={Display.Block} />
<Box display={Display.Flex} />
<Box display={Display.Grid} />
<Box display={Display.InlineBlock} />
<Box display={Display.InlineFlex} />
<Box display={Display.Inline} />
<Box display={Display.None} />
```
### Color
The `color` prop can be used to set the color of the content in Box component. The `color` prop accepts [responsive props](#responsive-props) in the form of array props. Defaults to `Color.textDefault`.
<Canvas>
<Story id="components-componentlibrary-box--color-story" />
</Canvas>
Example of importing `TextColor` object with `Box` component
```jsx
import { TextColor } from '../../../helpers/constants/design-system';
import Box from '../ui/box';
<Box color={TextColor.textDefault}>Text goes here</Box>;
```
### Background Color
Use the `backgroundColor` prop along with the `Color` or `BackgroundColor` object from `ui/helpers/constants/design-system.js` to change background color. The `backgroundColor` prop accepts [responsive props](#responsive-props) in the form of array props.
<Canvas>
<Story id="components-componentlibrary-box--background-color-story" />
</Canvas>
Example of importing `BackgroundColor` object with `Box` component
```jsx
import {
BackgroundColor,
TextColor,
} from '../../../helpers/constants/design-system';
import Box from '../ui/box';
<Box backgroundColor={BackgroundColor.backgroundDefault}>
<Typography color={TextColor.textDefault}>
BackgroundColor.backgroundDefault
</Typography>
</Box>;
```
### Border Color
Use the `borderColor` prop along with the `Color` or `BorderColor` object from `ui/helpers/constants/design-system.js` to change border color. The `borderColor` prop accepts [responsive props](#responsive-props) in the form of array props.
<Canvas>
<Story id="components-componentlibrary-box--border-color-story" />
</Canvas>
Example of importing `BorderColor` object with `Box` component
```jsx
import {
BackgroundColor,
BorderColor,
TextColor,
} from '../../../helpers/constants/design-system';
import Box from '../ui/box';
<Box
backgroundColor={BackgroundColor.backgroundDefault}
borderColor={BorderColor.borderDefault}
>
<Typography color={TextColor.textDefault}>
BorderColor.borderDefault
</Typography>
</Box>;
```
### Border Radius
Use the `borderRadius` prop along with the `BorderRadius` object from `ui/helpers/constants/design-system.js` to change border radius. The `borderRadius` prop accepts [responsive props](#responsive-props) in the form of array props.
<Canvas>
<Story id="components-componentlibrary-box--border-radius-story" />
</Canvas>
Example of importing `BorderRadius` object with `Box` component
```jsx
import { BorderRadius } from '../../../helpers/constants/design-system';
import Box from '../ui/box';
<Box borderRadius={BorderRadius.none}>BorderRadius.none 0px</Box>
<Box borderRadius={BorderRadius.XS}>BorderRadius.XS 2px</Box>
<Box borderRadius={BorderRadius.SM}>BorderRadius.SM 4px</Box>
<Box borderRadius={BorderRadius.MD}>BorderRadius.MD 6px</Box>
<Box borderRadius={BorderRadius.LG}>BorderRadius.LG 8px</Box>
<Box borderRadius={BorderRadius.XL}>BorderRadius.XL 12px</Box>
<Box borderRadius={BorderRadius.pill}>BorderRadius.pill 9999px</Box>
<Box borderRadius={BorderRadius.full}>BorderRadius.full 50%</Box>
```
### Responsive Props
The box component provides a responsive props api in the form of array props. Array props are inspired by [styled-systems array props](https://styled-system.com/guides/array-props). The responsive props follow a mobile first methodology with the first item in the array applying the style to the base level size e.g. `0px` and up. The second item overwrites the first items styles at the next breakpoint.
- All Box props accept the responsive props format
- To skip a breakpoint use `null` as the skipped item's value e.g. `<Box display={['display', null, ;flex']} />`
```
// the responsive props
<Box display={['block', 'flex']} />
// is equivalent to the css
.box {
display: block;
@media screen and (max-width: $breakpoint-sm) {
display: flex;
}
}
```
<Canvas>
<Story id="components-componentlibrary-box--responsive-props" />
</Canvas>
```jsx
import {
BorderColor,
BackgroundColor,
} from '../../../helpers/constants/design-system';
import Box from '../ui/box';
<Box
padding={[2, 4]}
gap={[2, 4]}
display={['flex']}
flexDirection={['column', 'row']}
borderColor={BorderColor.borderDefault}
>
<Box
padding={[4, 8]}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderMuted}
>
responsive
</Box>
<Box
padding={[4, 8]}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderMuted}
>
props
</Box>
<Box
padding={[4, 8]}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderMuted}
>
example
</Box>
</Box>;
```
### As
The polymorphic `as` prop allows you to change the root HTML element of the Box component. Defaults to `'div'`
<Canvas>
<Story id="components-componentlibrary-box--as" />
</Canvas>
```jsx
import Box from '../../ui/box';
<Box>div(default)</Box>
<Box as="ul">ul</Box>
<Box as="li">li</Box>
<Box as="button">button</Box>
<Box as="header">header</Box>
```
### Width
Use the `width` prop to pass a singular `BlockSize` or for a responsive width pass an array up to 4 `BlockSize`
Responsive widths go from smallest to largest screen sizes
Example: [BlockSize.Half, BlockSize.ThreeFourths, BlockSize.OneFifth, BlockSize.ThreeSixths]
- BlockSize.Half is the default width for `base screen size (0px)` and up
- BlockSize.ThreeFourths is the width for `small screen size (576px) ` and up
- BlockSize.OneFifth is the width for `medium screen size (768px)` and up
- BlockSize.ThreeSixths is the width for `large screen size (1280px)` and up
<Canvas>
<Story id="components-componentlibrary-box--width" />
</Canvas>
```jsx
import {
BlockSize,
Display,
FlexWrap,
} from '../../../helpers/constants/design-system';
import Box from '../../ui/box';
<>
<p>Static widths</p>
<Box display={Display.Flex}>
<Box width={BlockSize.Full}>BlockSize.Full</Box>
<Box width={BlockSize.Half}>BlockSize.Half</Box>
<Box width={BlockSize.Half}>BlockSize.Half</Box>
<Box width={BlockSize.OneThird}>BlockSize.OneThird</Box>
<Box width={BlockSize.OneThird}>BlockSize.OneThird</Box>
<Box width={BlockSize.OneThird}>BlockSize.OneThird</Box>
<Box width={BlockSize.OneFourth}>BlockSize.OneFourth</Box>
<Box width={BlockSize.OneFourth}>BlockSize.OneFourth</Box>
<Box width={BlockSize.OneFourth}>BlockSize.OneFourth</Box>
<Box width={BlockSize.OneFourth}>BlockSize.OneFourth</Box>
</Box>
<p>Responsive widths</p>
<Box display={Display.Flex} flexWrap={FlexWrap.Wrap}>
<Box
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird, BlockSize.OneFourth,
</Box>
<Box
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird, BlockSize.OneFourth,
</Box>
<Box
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird, BlockSize.OneFourth,
</Box>
<Box
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird, BlockSize.OneFourth,
</Box>
</Box>
</>;
```

View File

@ -0,0 +1,402 @@
@use "sass:list";
@use "sass:map";
@use "design-system";
@use "utilities";
$attributes: padding, margin, gap;
$extraProperties: auto;
$attributesToApplyExtraProperties: margin;
.mm-box {
// Padding, Margin, and Gap
@each $attribute in $attributes {
@each $size in design-system.$sizes-numeric {
&--#{$attribute}-#{$size} {
#{$attribute}: utilities.get-spacing($size);
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $size in design-system.$sizes-numeric {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:#{$attribute}-#{$size} {
#{$attribute}: utilities.get-spacing($size);
}
}
}
}
@each $size in design-system.$sizes-numeric {
@each $direction in design-system.$directions {
&--#{$attribute}-#{$direction}-#{$size} {
#{$attribute}-#{$direction}: utilities.get-spacing($size);
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $direction in design-system.$directions {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:#{$attribute}-#{$direction}-#{$size} {
#{$attribute}-#{$direction}: utilities.get-spacing($size);
}
}
}
}
}
@if list.index($attributesToApplyExtraProperties, $attribute) {
@each $property in $extraProperties {
&--#{$attribute}-#{$property} {
#{$attribute}: $property;
}
}
@each $property in $extraProperties {
@each $direction in design-system.$directions {
&--#{$attribute}-#{$direction}-#{$property} {
#{$attribute}-#{$direction}: $property;
}
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $property in $extraProperties {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:#{$attribute}-#{$property} {
#{$attribute}: $property;
}
}
@each $direction in design-system.$directions {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:#{$attribute}-#{$direction}-#{$property} {
#{$attribute}-#{$direction}: $property;
}
}
}
}
}
}
}
// Borders
@each $size in design-system.$sizes-numeric {
&--border-width-#{$size} {
border-width: #{$size}px;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $size in design-system.$sizes-numeric {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:border-width-#{$size} {
border-width: #{$size}px;
}
}
}
}
// border-color
@each $variant, $color in design-system.$color-map {
&--border-color-#{$variant} {
border-color: var($color);
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $variant, $color in design-system.$color-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:border-color-#{$variant} {
border-color: var($color);
}
}
}
}
// border-style
@each $border-style in design-system.$border-style {
&--border-style-#{$border-style} {
border-style: $border-style;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $border-style in design-system.$border-style {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:border-style-#{$border-style} {
border-style: $border-style;
}
}
}
}
// border-radius
@each $radius, $value in design-system.$border-radius {
&--rounded-#{$radius} {
border-radius: $value;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $radius, $value in design-system.$border-radius {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:rounded-#{$radius} {
border-radius: $value;
}
}
}
}
// Display and Flex/Grid alignment
@each $display in design-system.$display {
&--display-#{$display} {
display: $display;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $display in design-system.$display {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:display-#{$display} {
display: $display;
}
}
}
}
@each $alignment in design-system.$align-items {
&--align-items-#{$alignment} {
align-items: $alignment;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $alignment in design-system.$align-items {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:align-items-#{$alignment} {
align-items: $alignment;
}
}
}
}
@each $justification in design-system.$justify-content {
&--justify-content-#{$justification} {
justify-content: $justification;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $justification in design-system.$justify-content {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:justify-content-#{$justification} {
justify-content: $justification;
}
}
}
}
@each $direction in design-system.$flex-direction {
&--flex-direction-#{$direction} {
flex-direction: $direction;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $direction in design-system.$flex-direction {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:flex-direction-#{$direction} {
flex-direction: $direction;
}
}
}
}
@each $wrap in design-system.$flex-wrap {
&--flex-wrap-#{$wrap} {
flex-wrap: $wrap;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $wrap in design-system.$flex-wrap {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:flex-wrap-#{$wrap} {
flex-wrap: $wrap;
}
}
}
}
// Width and Height
&--width-full {
width: 100%;
}
&--height-full {
height: 100%;
}
@each $fraction, $value in design-system.$fractions {
&--width-#{$fraction} {
width: $value;
}
&--height-#{$fraction} {
height: $value;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $fraction, $value in design-system.$fractions {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:width-#{$fraction} {
width: $value;
}
&--#{$breakpoint}\:height-#{$fraction} {
height: $value;
}
}
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:width-full {
width: 100%;
}
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:height-full {
height: 100%;
}
}
}
&--height-screen {
height: 100vh;
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:height-screen {
height: 100vh;
}
}
}
&--width-screen {
width: 100vw;
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:width-screen {
width: 100vw;
}
}
}
&--height-max {
height: max-content;
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:height-max {
height: max-content;
}
}
}
&--width-max {
width: max-content;
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:height-max {
height: max-content;
}
}
}
&--height-min {
height: min-content;
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:height-min {
height: min-content;
}
}
}
&--width-min {
width: min-content;
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:width-min {
width: min-content;
}
}
}
// text
@each $alignment in design-system.$text-align {
&--text-align-#{$alignment} {
text-align: $alignment;
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $alignment in design-system.$text-align {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:text-align-#{$alignment} {
text-align: $alignment;
}
}
}
}
// background
@each $variant, $color in design-system.$color-map {
&--background-color-#{$variant} {
background-color: var($color);
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $variant, $color in design-system.$color-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\:background-color-#{$variant} {
background-color: var($color);
}
}
}
}
// color
@each $variant, $color in design-system.$color-map {
&--color-#{$variant} {
color: var($color);
}
}
// breakpoint classes
@each $breakpoint, $min-width in $screen-sizes-map {
@each $variant, $color in design-system.$color-map {
@media screen and (min-width: $min-width) {
&--#{$breakpoint}\color-#{$variant} {
color: var($color);
}
}
}
}
}

View File

@ -0,0 +1,956 @@
import React from 'react';
import { StoryFn, Meta } from '@storybook/react';
import {
BlockSize,
BorderStyle,
BorderRadius,
TextColor,
BorderColor,
BackgroundColor,
Display,
AlignItems,
JustifyContent,
TextAlign,
FlexDirection,
FlexWrap,
} from '../../../helpers/constants/design-system';
import { Text } from '..';
import { Box } from './box';
import README from './README.mdx';
const sizeControlOptions = [
undefined,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
];
const marginSizeControlOptions = [...sizeControlOptions, 'auto'];
export default {
title: 'Components/ComponentLibrary/Box',
component: Box,
parameters: {
docs: {
page: README,
},
},
argTypes: {
children: {
table: { category: 'children' },
},
display: {
options: Object.values(Display),
control: 'select',
table: { category: 'display' },
},
width: {
options: Object.values(BlockSize),
control: 'multi-select',
table: { category: 'display' },
},
height: {
options: Object.values(BlockSize),
control: 'select',
table: { category: 'display' },
},
gap: {
control: 'select',
options: sizeControlOptions,
table: { category: 'display' },
},
backgroundColor: {
options: Object.values(BackgroundColor),
control: 'select',
table: {
category: 'background',
},
},
color: {
options: Object.values(TextColor),
control: 'select',
table: { category: 'color' },
},
borderStyle: {
options: Object.values(BorderStyle),
control: 'select',
table: { category: 'border' },
},
borderWidth: {
options: sizeControlOptions,
control: 'select',
table: { category: 'border' },
},
borderColor: {
options: Object.values(BorderColor),
control: 'select',
table: { category: 'border' },
},
borderRadius: {
options: Object.values(BorderRadius),
control: 'select',
table: { category: 'border' },
},
flexWrap: {
options: Object.values(FlexWrap),
control: 'select',
table: { category: 'display' },
},
flexDirection: {
options: Object.values(FlexDirection),
control: 'select',
table: { category: 'display' },
},
justifyContent: {
options: Object.values(JustifyContent),
control: 'select',
table: { category: 'display' },
},
alignItems: {
options: Object.values(AlignItems),
control: 'select',
table: { category: 'display' },
},
textAlign: {
options: Object.values(TextAlign),
control: 'select',
table: { category: 'text' },
},
margin: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
marginTop: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
marginRight: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
marginBottom: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
marginLeft: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
marginInline: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
marginInlineStart: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
marginInlineEnd: {
options: marginSizeControlOptions,
control: 'select',
table: { category: 'margin' },
},
padding: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
paddingTop: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
paddingRight: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
paddingBottom: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
paddingLeft: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
paddingInline: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
paddingInlineStart: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
paddingInlineEnd: {
options: sizeControlOptions,
control: 'select',
table: { category: 'padding' },
},
as: {
control: 'select',
options: ['div', 'ul', 'li', 'span', 'a', 'button'],
table: { category: 'as (root html element)' },
},
},
} as Meta<typeof Box>;
export const BoxDefaultStory: StoryFn<typeof Box> = (args) => <Box {...args} />;
BoxDefaultStory.args = {
children: 'Box component',
display: Display.Flex,
justifyContent: JustifyContent.center,
alignItems: AlignItems.center,
width: BlockSize.Half,
height: BlockSize.Half,
borderColor: BorderColor.borderDefault,
padding: 4,
};
BoxDefaultStory.storyName = 'Default';
export const Margin: StoryFn<typeof Box> = (args) => {
return (
<Box borderColor={BorderColor.borderMuted}>
<Box
{...args}
margin={2}
padding={4}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderMuted}
>
Static margin
</Box>
<Box
{...args}
margin={[2, 4, 8, 12]}
padding={[4]}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderMuted}
>
Responsive margin changes based on breakpoint
</Box>
</Box>
);
};
export const Padding: StoryFn<typeof Box> = (args) => {
return (
<Box borderColor={BorderColor.borderMuted}>
<Box
{...args}
padding={4}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderMuted}
>
Static padding
</Box>
<Box
{...args}
padding={[4, 8, 12]}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderMuted}
>
Responsive padding changes based on breakpoint
</Box>
</Box>
);
};
export const ColorStory: StoryFn<typeof Box> = (args) => {
return (
<>
<Box {...args} padding={3} color={TextColor.textDefault}>
TextColor.textDefault
</Box>
<Box {...args} padding={3} color={TextColor.textAlternative}>
TextColor.textAlternative
</Box>
<Box {...args} padding={3} color={TextColor.textMuted}>
TextColor.textMuted
</Box>
<Box {...args} padding={3} color={TextColor.primaryDefault}>
TextColor.primaryDefault
</Box>
<Box
{...args}
padding={3}
color={TextColor.primaryInverse}
backgroundColor={BackgroundColor.primaryDefault}
>
TextColor.primaryInverse
</Box>
<Box {...args} padding={3} color={TextColor.errorDefault}>
TextColor.errorDefault
</Box>
<Box
{...args}
padding={3}
color={TextColor.errorInverse}
backgroundColor={BackgroundColor.errorDefault}
>
TextColor.errorInverse
</Box>
<Box {...args} padding={3} color={TextColor.successDefault}>
TextColor.successDefault
</Box>
<Box
{...args}
padding={3}
color={TextColor.successInverse}
backgroundColor={BackgroundColor.successDefault}
>
TextColor.successInverse
</Box>
<Box {...args} padding={3} color={TextColor.warningDefault}>
TextColor.warningDefault
</Box>
<Box
{...args}
padding={3}
color={TextColor.warningInverse}
backgroundColor={BackgroundColor.warningDefault}
>
TextColor.warningInverse
</Box>
<Box {...args} padding={3} color={TextColor.infoDefault}>
TextColor.infoDefault
</Box>
<Box
{...args}
padding={3}
color={TextColor.infoInverse}
backgroundColor={BackgroundColor.infoDefault}
>
TextColor.infoInverse
</Box>
<Box {...args} padding={3} color={TextColor.inherit}>
TextColor.inherit
</Box>
<Box
{...args}
padding={3}
backgroundColor={BackgroundColor.sepolia}
color={TextColor.sepoliaInverse}
>
TextColor.sepoliaInverse
</Box>
<Box
{...args}
padding={3}
backgroundColor={BackgroundColor.goerli}
color={TextColor.goerliInverse}
>
TextColor.goerliInverse
</Box>
</>
);
};
ColorStory.storyName = 'Color';
export const BackgroundColorStory = () => {
return (
<>
<Box padding={3} backgroundColor={BackgroundColor.backgroundDefault}>
<Text color={TextColor.textDefault}>
BackgroundColor.backgroundDefault
</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.backgroundAlternative}>
<Text color={TextColor.textDefault}>
BackgroundColor.backgroundAlternative
</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.overlayDefault}>
<Text color={TextColor.overlayInverse}>
BackgroundColor.overlayDefault
</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.primaryDefault}>
<Text color={TextColor.primaryInverse}>
BackgroundColor.primaryDefault
</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.primaryMuted}>
<Text color={TextColor.textDefault}>BackgroundColor.primaryMuted</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.errorDefault}>
<Text color={TextColor.errorInverse}>BackgroundColor.errorDefault</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.errorMuted}>
<Text color={TextColor.textDefault}>BackgroundColor.errorMuted</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.successDefault}>
<Text color={TextColor.successInverse}>
BackgroundColor.successDefault
</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.successMuted}>
<Text color={TextColor.textDefault}>BackgroundColor.successMuted</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.warningDefault}>
<Text color={TextColor.warningInverse}>
BackgroundColor.warningDefault
</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.warningMuted}>
<Text color={TextColor.textDefault}>BackgroundColor.warningMuted</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.sepolia}>
<Text color={TextColor.sepoliaInverse}>BackgroundColor.sepolia</Text>
</Box>
<Box padding={3} backgroundColor={BackgroundColor.goerli}>
<Text color={TextColor.goerliInverse}>BackgroundColor.goerli</Text>
</Box>
</>
);
};
BackgroundColorStory.storyName = 'BackgroundColor';
export const BorderColorStory = () => {
return (
<>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundDefault}
borderColor={BorderColor.borderDefault}
borderWidth={2}
marginBottom={1}
>
<Text color={TextColor.textDefault}>BorderColor.borderDefault</Text>
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundDefault}
borderColor={BorderColor.borderMuted}
borderWidth={2}
marginBottom={1}
>
<Text color={TextColor.textDefault}>BorderColor.borderMuted</Text>
</Box>
<Box
padding={3}
borderColor={BorderColor.primaryDefault}
borderWidth={2}
marginBottom={1}
backgroundColor={BackgroundColor.primaryMuted}
>
<Text color={TextColor.textDefault}>BorderColor.primaryDefault</Text>
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.errorMuted}
borderColor={BorderColor.errorDefault}
borderWidth={2}
marginBottom={1}
>
<Text color={TextColor.textDefault}>BorderColor.errorDefault</Text>
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.successMuted}
borderColor={BorderColor.successDefault}
borderWidth={2}
marginBottom={1}
>
<Text color={TextColor.textDefault}>BorderColor.successDefault</Text>
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.warningMuted}
borderColor={BorderColor.warningDefault}
borderWidth={2}
>
<Text color={TextColor.textDefault}>BorderColor.warningDefault</Text>
</Box>
</>
);
};
BorderColorStory.storyName = 'BorderColor';
export const BorderRadiusStory = () => {
return (
<>
<Box
display={Display.Grid}
style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))' }}
gap={4}
>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.none}
>
BorderRadius.NONE 0px
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.XS}
>
BorderRadius.XS 2px
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.SM}
>
BorderRadius.SM 4px
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.MD}
>
BorderRadius.MD 6px
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.LG}
>
BorderRadius.LG 8px
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.XL}
>
BorderRadius.XL 12px
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.pill}
>
BorderRadius.pill 9999px
</Box>
</Box>
<Box
padding={3}
backgroundColor={BackgroundColor.backgroundAlternative}
borderColor={BorderColor.borderDefault}
borderWidth={2}
borderRadius={BorderRadius.full}
margin={4}
display={Display.Flex}
alignItems={AlignItems.center}
style={{ height: '250px', width: '250px' }}
>
BorderRadius.full 50%
</Box>
</>
);
};
BorderRadiusStory.storyName = 'BorderRadius';
export const ResponsiveProps = () => {
return (
<>
<Text marginBottom={4}>
Responsive props example. Stacks vertically on small screens and aligns
horizontally on large screens. Padding is also adjusted between small
and large screens
</Text>
<Box
marginTop="auto"
marginBottom={[0]}
padding={[2, 4]}
gap={[2, 4]}
display={[Display.Flex, null, null, Display.None]}
flexDirection={[
FlexDirection.Column,
FlexDirection.Column,
FlexDirection.Row,
]}
borderColor={BorderColor.borderDefault}
>
<Box
padding={[4, 8]}
backgroundColor={[
BackgroundColor.backgroundAlternative,
BackgroundColor.primaryMuted,
]}
borderColor={BorderColor.borderMuted}
>
responsive
</Box>
<Box
padding={[4, 8]}
backgroundColor={[
BackgroundColor.backgroundAlternative,
BackgroundColor.primaryMuted,
]}
borderColor={BorderColor.borderMuted}
>
props
</Box>
<Box
padding={[4, 8]}
backgroundColor={[
BackgroundColor.backgroundAlternative,
BackgroundColor.primaryMuted,
]}
borderColor={BorderColor.borderMuted}
>
example
</Box>
<Box
padding={[4, 8]}
borderRadius={[
BorderRadius.XS,
BorderRadius.SM,
BorderRadius.MD,
BorderRadius.LG,
]}
backgroundColor={[
BackgroundColor.backgroundAlternative,
BackgroundColor.primaryMuted,
]}
borderColor={BorderColor.borderMuted}
>
Responsive Border Radius 1
</Box>
<Box
padding={[4, 8]}
borderRadius={[
BorderRadius.XL,
BorderRadius.pill,
BorderRadius.none,
BorderRadius.full,
]}
backgroundColor={[
BackgroundColor.backgroundAlternative,
BackgroundColor.primaryMuted,
]}
borderColor={BorderColor.borderMuted}
>
Responsive Border Radius 2
</Box>
</Box>
</>
);
};
export const As: StoryFn<typeof Box> = (args) => {
return (
<>
<Text marginBottom={4}>
You can change the root element of the Box component using the as prop.
Inspect the below elements to see the underlying HTML elements
</Text>
<Box {...args}>div(default)</Box>
<Box as="button" disabled>
Box as 'button' tag
</Box>
<br />
<Box as="button" disabled>
Box as 'button' tag and disabled
</Box>
<br />
<Box as="a" href="https://metamask.io">
Box as 'a' tag with href
</Box>
<br />
<Box as="p" href="https://metamask.io" data-testid="hello">
Box as 'p' tag with href and data-testid
</Box>
<br />
<Box as="p" disabled>
Box as 'p' tag and disabled
</Box>
<br />
<Box>Box as 'span' tag (default)</Box>
<br />
<Box as="p">Box as 'p' tag</Box>
<br />
<Box as="li">Box as 'li' tag</Box>
<br />
<Box as="h1">Box as 'h1' tag</Box>
</>
);
};
export const Width: StoryFn<typeof Box> = () => {
const getColumns = (): JSX.Element[] => {
const content: JSX.Element[] = [];
for (let i = 0; i < 12; i++) {
content.push(
<Box
key={i}
backgroundColor={
i % 2 === 0
? BackgroundColor.errorMuted
: BackgroundColor.warningMuted
}
width={BlockSize.OneTwelfth}
/>,
);
}
return content;
};
return (
<>
<p>
<b>Static widths</b>
</p>
<Box
display={Display.Flex}
borderColor={BorderColor.borderMuted}
style={{
height: '100vh',
position: 'relative',
}}
marginBottom={6}
>
{getColumns()}
<Box
width={BlockSize.Full}
display={Display.Flex}
flexWrap={FlexWrap.Wrap}
style={{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
}}
>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
marginBottom={6}
width={BlockSize.Full}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.Full
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
marginBottom={6}
width={BlockSize.Half}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.Half
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
marginBottom={6}
width={BlockSize.Half}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.Half
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
marginBottom={6}
width={BlockSize.OneThird}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.OneThird
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
marginBottom={6}
width={BlockSize.OneThird}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.OneThird
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
marginBottom={6}
width={BlockSize.OneThird}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.OneThird
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={BlockSize.OneFourth}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.OneFourth
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={BlockSize.OneFourth}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.OneFourth
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={BlockSize.OneFourth}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.OneFourth
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={BlockSize.OneFourth}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.OneFourth
</Box>
</Box>
</Box>
<p>
<b>Responsive widths</b>
</p>
<Box
display={Display.Flex}
borderColor={BorderColor.borderMuted}
style={{ height: '100vh', position: 'relative', textAlign: 'center' }}
>
{getColumns()}
<Box
width={BlockSize.Full}
display={Display.Flex}
flexWrap={FlexWrap.Wrap}
style={{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
}}
>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird,
BlockSize.OneFourth,
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird,
BlockSize.OneFourth,
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird,
BlockSize.OneFourth,
</Box>
<Box
borderColor={BorderColor.borderMuted}
borderWidth={6}
width={[
BlockSize.Full,
BlockSize.Half,
BlockSize.OneThird,
BlockSize.OneFourth,
]}
display={Display.Flex}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
BlockSize.Full, BlockSize.Half, BlockSize.OneThird,
BlockSize.OneFourth,
</Box>
</Box>
</Box>
</>
);
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,234 @@
import React from 'react';
import classnames from 'classnames';
import { memoize } from 'lodash';
import { BREAKPOINTS } from '../../../helpers/constants/design-system';
import type {
BoxComponent,
BoxProps,
PolymorphicRef,
StyleDeclarationType,
StylePropValueType,
ClassNamesObject,
} from './box.types';
const BASE_CLASS_NAME = 'mm-box';
function isValidSize(
styleProp: StyleDeclarationType,
value: StylePropValueType,
) {
// Only margin types allow 'auto'
return (
typeof value === 'number' ||
((styleProp === 'margin' ||
styleProp === 'margin-top' ||
styleProp === 'margin-right' ||
styleProp === 'margin-bottom' ||
styleProp === 'margin-left' ||
styleProp === 'margin-inline' ||
styleProp === 'margin-inline-start' ||
styleProp === 'margin-inline-end') &&
value === 'auto')
);
}
function isValidString(type: StyleDeclarationType, value: StylePropValueType) {
return typeof type === 'string' && typeof value === 'string';
}
/**
* Generate classnames
* Generates classnames for different utility styles
* Also accepts responsive props in the form of an array
* Maps responsive props to mobile first breakpoints
*
* @param {string} styleDeclaration - The style declaration type "margin", "margin-top", "padding", "display" etc
* @param {array || number || string} value - prop value being passed in array props are responsive props
* @param {*} validatorFn - The validation function for each type of value
* @returns
*/
const generateClassNames = memoize(
(
styleDeclaration: StyleDeclarationType,
value: StylePropValueType,
validatorFn: typeof isValidString | typeof isValidSize,
) => {
// if value does not exist return empty object for classnames library
// Accepts 0 as a valid value
if (!value && typeof value !== 'number') {
return {};
}
const classNamesObject: ClassNamesObject = {};
// if value is an array with single item e.g. marginTop={[1]}
const singleArrayItemProp =
Array.isArray(value) && value.length === 1 ? value[0] : undefined;
// if value single value e.g. marginTop={1}
const singleValueProp =
(!Array.isArray(value) && typeof value === 'string') ||
typeof value === 'number'
? value
: undefined;
// single digit equals single value or single array item
let singleValue;
if (singleValueProp || singleValueProp === 0) {
singleValue = singleValueProp;
}
if (singleArrayItemProp || singleArrayItemProp === 0) {
singleValue = singleArrayItemProp;
}
// 0 is an acceptable value but is falsy in js
if (singleValue || singleValue === 0) {
// add base style without any breakpoint prefixes to classObject
classNamesObject[
`${BASE_CLASS_NAME}--${styleDeclaration}-${singleValue}`
] = validatorFn(styleDeclaration, singleValue);
} else if (Array.isArray(value)) {
// If array with more than one item
switch (value.length) {
case 4:
// add base/sm/md/lg
classNamesObject[
`${BASE_CLASS_NAME}--${styleDeclaration}-${value[0]}`
] = validatorFn(styleDeclaration, value[0]);
classNamesObject[
`${BASE_CLASS_NAME}--${BREAKPOINTS[1]}:${styleDeclaration}-${value[1]}`
] = validatorFn(styleDeclaration, value[1]);
classNamesObject[
`${BASE_CLASS_NAME}--${BREAKPOINTS[2]}:${styleDeclaration}-${value[2]}`
] = validatorFn(styleDeclaration, value[2]);
classNamesObject[
`${BASE_CLASS_NAME}--${BREAKPOINTS[3]}:${styleDeclaration}-${value[3]}`
] = validatorFn(styleDeclaration, value[3]);
break;
case 3:
// add base/sm/md
classNamesObject[
`${BASE_CLASS_NAME}--${styleDeclaration}-${value[0]}`
] = validatorFn(styleDeclaration, value[0]);
classNamesObject[
`${BASE_CLASS_NAME}--${BREAKPOINTS[1]}:${styleDeclaration}-${value[1]}`
] = validatorFn(styleDeclaration, value[1]);
classNamesObject[
`${BASE_CLASS_NAME}--${BREAKPOINTS[2]}:${styleDeclaration}-${value[2]}`
] = validatorFn(styleDeclaration, value[2]);
break;
case 2:
// add base/sm
classNamesObject[
`${BASE_CLASS_NAME}--${styleDeclaration}-${value[0]}`
] = validatorFn(styleDeclaration, value[0]);
classNamesObject[
`${BASE_CLASS_NAME}--${BREAKPOINTS[1]}:${styleDeclaration}-${value[1]}`
] = validatorFn(styleDeclaration, value[1]);
break;
default:
console.log(`Invalid array prop length: ${value.length}`);
}
}
return classNamesObject;
},
(styleDeclaration, value) => [styleDeclaration, value],
);
export const Box: BoxComponent = React.forwardRef(
<C extends React.ElementType = 'div'>(
{
as,
padding,
paddingTop,
paddingRight,
paddingBottom,
paddingLeft,
paddingInline,
paddingInlineStart,
paddingInlineEnd,
margin,
marginTop,
marginRight,
marginBottom,
marginLeft,
marginInline,
marginInlineStart,
marginInlineEnd,
borderColor,
borderWidth,
borderRadius,
borderStyle,
alignItems,
justifyContent,
textAlign,
flexDirection,
flexWrap,
gap,
display,
width,
height,
children,
className = '',
backgroundColor,
color,
...props
}: BoxProps<C>,
ref?: PolymorphicRef<C>,
) => {
const Component = as || 'div';
const boxClassName = classnames(
BASE_CLASS_NAME,
className,
// Margin
generateClassNames('margin', margin, isValidSize),
generateClassNames('margin-top', marginTop, isValidSize),
generateClassNames('margin-right', marginRight, isValidSize),
generateClassNames('margin-bottom', marginBottom, isValidSize),
generateClassNames('margin-left', marginLeft, isValidSize),
generateClassNames('margin-inline', marginInline, isValidSize),
generateClassNames('margin-inline-start', marginInlineStart, isValidSize),
generateClassNames('margin-inline-end', marginInlineEnd, isValidSize),
// Padding
generateClassNames('padding', padding, isValidSize),
generateClassNames('padding-top', paddingTop, isValidSize),
generateClassNames('padding-right', paddingRight, isValidSize),
generateClassNames('padding-bottom', paddingBottom, isValidSize),
generateClassNames('padding-left', paddingLeft, isValidSize),
generateClassNames('padding-inline', paddingInline, isValidSize),
generateClassNames(
'padding-inline-start',
paddingInlineStart,
isValidSize,
),
generateClassNames('padding-inline-end', paddingInlineEnd, isValidSize),
generateClassNames('display', display, isValidString),
generateClassNames('gap', gap, isValidSize),
generateClassNames('flex-direction', flexDirection, isValidString),
generateClassNames('flex-wrap', flexWrap, isValidString),
generateClassNames('justify-content', justifyContent, isValidString),
generateClassNames('align-items', alignItems, isValidString),
generateClassNames('text-align', textAlign, isValidString),
generateClassNames('width', width, isValidString),
generateClassNames('height', height, isValidString),
generateClassNames('color', color, isValidString),
generateClassNames('background-color', backgroundColor, isValidString),
generateClassNames('rounded', borderRadius, isValidString),
generateClassNames('border-style', borderStyle, isValidString),
generateClassNames('border-color', borderColor, isValidString),
generateClassNames('border-width', borderWidth, isValidSize),
{
// Auto applied classes
// ---Borders---
// if borderWidth or borderColor is supplied w/o style, default to solid
'box--border-style-solid':
!borderStyle && (Boolean(borderWidth) || Boolean(borderColor)),
// if borderColor supplied w/o width, default to 1
'box--border-width-1': !borderWidth && Boolean(borderColor),
},
);
return (
<Component className={boxClassName} ref={ref} {...props}>
{children}
</Component>
);
},
);

View File

@ -0,0 +1,433 @@
import React from 'react';
import {
AlignItems,
BackgroundColor,
BlockSize,
BorderColor,
BorderRadius,
BorderStyle,
Color,
Display,
FlexDirection,
FlexWrap,
IconColor,
JustifyContent,
TextAlign,
TextColor,
} from '../../../helpers/constants/design-system';
export type StyleDeclarationType =
| 'margin'
| 'margin-top'
| 'margin-right'
| 'margin-bottom'
| 'margin-left'
| 'margin-inline'
| 'margin-inline-start'
| 'margin-inline-end'
| 'padding'
| 'padding-top'
| 'padding-right'
| 'padding-bottom'
| 'padding-left'
| 'padding-inline'
| 'padding-inline-start'
| 'padding-inline-end'
| 'display'
| 'gap'
| 'flex-direction'
| 'flex-wrap'
| 'justify-content'
| 'align-items'
| 'text-align'
| 'width'
| 'height'
| 'color'
| 'background-color'
| 'rounded'
| 'border-style'
| 'border-color'
| 'border-width';
export type StylePropValueType =
| AlignItems
| AlignItemsArray
| BackgroundColor
| BackgroundColorArray
| BlockSize
| BlockSizeArray
| BorderColor
| BorderColorArray
| BorderRadius
| BorderRadiusArray
| BorderStyle
| BorderStyleArray
| Color
| Display
| DisplayArray
| FlexDirection
| FlexDirectionArray
| FlexWrap
| FlexWrapArray
| IconColor
| JustifyContent
| JustifyContentArray
| SizeNumberAndAuto
| SizeNumberAndAutoArray
| TextAlign
| TextAlignArray
| TextColor
| TextColorArray
| IconColor
| IconColorArray
| undefined;
export interface ClassNamesObject {
[key: string]: any;
}
export type FlexDirectionArray = [
FlexDirection,
FlexDirection?,
FlexDirection?,
FlexDirection?,
];
export type FlexWrapArray = [FlexWrap, FlexWrap?, FlexWrap?, FlexWrap?];
export type TextAlignArray = [TextAlign, TextAlign?, TextAlign?, TextAlign?];
export type DisplayArray = [Display, Display?, Display?, Display?];
export type BlockSizeArray = [BlockSize, BlockSize?, BlockSize?, BlockSize?];
export type SizeNumber =
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| null;
export type SizeNumberArray = [
SizeNumber,
SizeNumber?,
SizeNumber?,
SizeNumber?,
];
export type SizeNumberAndAuto = SizeNumber | 'auto';
export type SizeNumberAndAutoArray = [
SizeNumberAndAuto,
SizeNumberAndAuto?,
SizeNumberAndAuto?,
SizeNumberAndAuto?,
];
export type BorderColorArray = [
BorderColor,
BorderColor?,
BorderColor?,
BorderColor?,
];
export type BorderRadiusArray = [
BorderRadius,
BorderRadius?,
BorderRadius?,
BorderRadius?,
];
export type BorderStyleArray = [
BorderStyle,
BorderStyle?,
BorderStyle?,
BorderStyle?,
];
export type AlignItemsArray = [
AlignItems,
AlignItems?,
AlignItems?,
AlignItems?,
];
export type JustifyContentArray = [
JustifyContent,
JustifyContent?,
JustifyContent?,
JustifyContent?,
];
export type BackgroundColorArray = [
BackgroundColor,
BackgroundColor?,
BackgroundColor?,
BackgroundColor?,
];
export type TextColorArray = [TextColor, TextColor?, TextColor?, TextColor?];
export type IconColorArray = [IconColor, IconColor?, IconColor?, IconColor?];
/**
* Polymorphic props based on Ohans Emmanuel's article below
* https://blog.logrocket.com/build-strongly-typed-polymorphic-components-react-typescript/#ensuring-as-prop-only-receives-valid-html-element-strings
*/
/**
* Uses generic type C to create polymorphic ref type
*/
export type PolymorphicRef<C extends React.ElementType> =
React.ComponentPropsWithRef<C>['ref'];
/**
* Uses generic type C to define the type for the polymorphic "as" prop
* "as" can be used to override the default HTML element
*/
type AsProp<C extends React.ElementType> = {
/**
* An override of the default HTML tag.
* Can also be a React component.
*/
as?: C;
};
/**
* Omits the as prop and props from component definition
*/
type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P);
/**
* Accepts 2 generic types: C which represents the as prop and the component props - Props
*/
type PolymorphicComponentProp<
C extends React.ElementType,
// eslint-disable-next-line @typescript-eslint/ban-types
Props = {},
> = React.PropsWithChildren<Props & AsProp<C>> &
Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>;
export type PolymorphicComponentPropWithRef<
C extends React.ElementType,
// eslint-disable-next-line @typescript-eslint/ban-types
Props = {},
> = PolymorphicComponentProp<C, Props> & { ref?: PolymorphicRef<C> };
/**
* Includes all style utility props. This should be used to extend the props of a component.
*/
export interface StyleUtilityProps {
/**
* The flex direction of the Box component.
* Use the FlexDirection enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
flexDirection?: FlexDirection | FlexDirectionArray;
/**
* The flex wrap of the Box component.
* Use the FlexWrap enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
flexWrap?: FlexWrap | FlexWrapArray;
/**
* The gap between the Box component's children.
* Use 1-12 for a gap of 4px-48px.
* Accepts responsive props in the form of an array.
*/
gap?: SizeNumber | SizeNumberArray | undefined;
/**
* The margin of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
margin?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The margin-top of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
marginTop?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The margin-bottom of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
marginBottom?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The margin-right of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
marginRight?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The margin-left of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
marginLeft?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The margin-inline of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
marginInline?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The margin-inline-start of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
marginInlineStart?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The margin-inline-end of the Box component.
* Use 1-12 for 4px-48px or 'auto'.
* Accepts responsive props in the form of an array.
*/
marginInlineEnd?: SizeNumberAndAuto | SizeNumberAndAutoArray;
/**
* The padding of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
padding?: SizeNumber | SizeNumberArray;
/**
* The padding-top of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
paddingTop?: SizeNumber | SizeNumberArray;
/**
* The padding-bottom of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
paddingBottom?: SizeNumber | SizeNumberArray;
/**
* The padding-right of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
paddingRight?: SizeNumber | SizeNumberArray;
/**
* The padding-left of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
paddingLeft?: SizeNumber | SizeNumberArray;
/**
* The padding-inline of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
paddingInline?: SizeNumber | SizeNumberArray;
/**
* The padding-inline-start of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
paddingInlineStart?: SizeNumber | SizeNumberArray;
/**
* The padding-inline-end of the Box component.
* Use 1-12 for 4px-48px.
* Accepts responsive props in the form of an array.
*/
paddingInlineEnd?: SizeNumber | SizeNumberArray;
/**
* The border-color of the Box component.
* Use BorderColor enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
borderColor?: BorderColor | BorderColorArray;
/**
* The border-width of the Box component.
* Use 1-12 for 1px-12px.
* Accepts responsive props in the form of an array.
*/
borderWidth?: SizeNumber | SizeNumberArray;
/**
* The border-radius of the Box component.
* Use BorderRadius enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
borderRadius?: BorderRadius | BorderRadiusArray;
/**
* The border-style of the Box component.
* Use BorderStyle enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
borderStyle?: BorderStyle | BorderStyleArray;
/**
* The align-items of the Box component.
* Use AlignItems enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
alignItems?: AlignItems | AlignItemsArray;
/**
* The justify-content of the Box component.
* Use JustifyContent enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
justifyContent?: JustifyContent | JustifyContentArray;
/**
* The text-align of the Box component.
* Use TextAlign enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
textAlign?: TextAlign | TextAlignArray;
/**
* The display of the Box component.
* Use Display enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
display?: Display | DisplayArray;
/**
* The width of the Box component.
* Use BlockSize enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
width?: BlockSize | BlockSizeArray;
/**
* The height of the Box component.
* Use BlockSize enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
height?: BlockSize | BlockSizeArray;
/**
* The background-color of the Box component.
* Use BackgroundColor enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
backgroundColor?: BackgroundColor | BackgroundColorArray;
/**
* The text-color of the Box component.
* Use TextColor enum from '../../../helpers/constants/design-system';
* Accepts responsive props in the form of an array.
*/
color?: TextColor | TextColorArray | IconColor | IconColorArray;
}
/**
* Box component props.
*/
interface Props extends StyleUtilityProps {
/**
* The content of the Box component.
*/
children?: React.ReactNode;
/**
* Additional className to apply to the Box component.
*/
className?: string;
}
export type BoxProps<C extends React.ElementType> =
PolymorphicComponentPropWithRef<C, Props>;
export type BoxComponent = <C extends React.ElementType = 'span'>(
props: BoxProps<C>,
) => React.ReactElement | null;

View File

@ -0,0 +1,8 @@
export { Box } from './box';
export type {
PolymorphicRef,
PolymorphicComponentPropWithRef,
StyleUtilityProps,
BoxProps,
BoxComponent,
} from './box.types';

View File

@ -5,6 +5,7 @@
* unintended overrides. * unintended overrides.
**/ **/
// Atoms // Atoms
@import 'box/box';
@import 'text/text'; @import 'text/text';
@import 'icon/icon'; @import 'icon/icon';
@import 'label/label'; @import 'label/label';

View File

@ -8,12 +8,13 @@ export { AvatarFavicon, AVATAR_FAVICON_SIZES } from './avatar-favicon';
export { AvatarIcon, AVATAR_ICON_SIZES } from './avatar-icon'; export { AvatarIcon, AVATAR_ICON_SIZES } from './avatar-icon';
export { AvatarNetwork, AVATAR_NETWORK_SIZES } from './avatar-network'; export { AvatarNetwork, AVATAR_NETWORK_SIZES } from './avatar-network';
export { AvatarToken } from './avatar-token'; export { AvatarToken } from './avatar-token';
export { AvatarBase } from './avatar-base';
export { export {
BadgeWrapper, BadgeWrapper,
BadgeWrapperPosition, BadgeWrapperPosition,
BadgeWrapperAnchorElementShape, BadgeWrapperAnchorElementShape,
} from './badge-wrapper'; } from './badge-wrapper';
export { AvatarBase } from './avatar-base'; export { Box } from './box';
export { Button, BUTTON_VARIANT, BUTTON_SIZES } from './button'; export { Button, BUTTON_VARIANT, BUTTON_SIZES } from './button';
export { ButtonBase, BUTTON_BASE_SIZES } from './button-base'; export { ButtonBase, BUTTON_BASE_SIZES } from './button-base';
export { ButtonIcon, ButtonIconSize } from './button-icon'; export { ButtonIcon, ButtonIconSize } from './button-icon';