1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Add box component (#10289)

This commit is contained in:
Brad Decker 2021-01-27 11:54:25 -06:00 committed by GitHub
parent 3806e0a2a6
commit 293b8a0f53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 611 additions and 40 deletions

View File

@ -0,0 +1,149 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import {
ALIGN_ITEMS,
BLOCK_SIZES,
BORDER_STYLE,
COLORS,
DISPLAY,
JUSTIFY_CONTENT,
SIZES,
} from '../../../helpers/constants/design-system'
const ValidSize = PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
const ArrayOfValidSizes = PropTypes.arrayOf(ValidSize)
const MultipleSizes = PropTypes.oneOf([ValidSize, ArrayOfValidSizes])
function generateSizeClasses(baseClass, type, main, top, right, bottom, left) {
const arr = Array.isArray(main) ? main : []
const singleDigit = Array.isArray(main) ? undefined : main
if (Array.isArray(main) && ![2, 3, 4].includes(main.length)) {
throw new Error(
`Expected prop ${type} to have length between 2 and 4, received ${main.length}`,
)
}
const isHorizontalAndVertical = arr.length === 2
const isTopHorizontalAndBottom = arr.length === 3
const isAllFour = arr.length === 4
const hasAtLeastTwo = arr.length >= 2
const hasAtLeastThree = arr.length >= 3
return {
[`${baseClass}--${type}-${singleDigit}`]: singleDigit !== undefined,
[`${baseClass}--${type}-top-${top}`]: typeof top === 'number',
[`${baseClass}--${type}-right-${right}`]: typeof right === 'number',
[`${baseClass}--${type}-bottom-${bottom}`]: typeof bottom === 'number',
[`${baseClass}--${type}-left-${left}`]: typeof left === 'number',
// As long as an array of length >= 2 has been provided, the first number
// will always be for the top value.
[`${baseClass}--${type}-top-${arr?.[0]}`]: hasAtLeastTwo,
// As long as an array of length >= 2 has been provided, the second number
// will always be for the right value.
[`${baseClass}--${type}-right-${arr?.[1]}`]: hasAtLeastTwo,
// If an array has 2 values, the first number is the bottom value. If
// instead if has 3 or more values, the third number will be the bottom.
[`${baseClass}--${type}-bottom-${arr?.[2]}`]: hasAtLeastThree,
[`${baseClass}--${type}-bottom-${arr?.[0]}`]: isHorizontalAndVertical,
// If an array has 2 or 3 values, the second number will be the left value
[`${baseClass}--${type}-left-${arr?.[1]}`]:
isHorizontalAndVertical || isTopHorizontalAndBottom,
// If an array has 4 values, the fourth number is the left value
[`${baseClass}--${type}-left-${arr?.[3]}`]: isAllFour,
}
}
export default function Box({
padding,
paddingTop,
paddingRight,
paddingBottom,
paddingLeft,
margin,
marginTop,
marginRight,
marginBottom,
marginLeft,
borderColor,
borderWidth,
borderRadius,
borderStyle,
alignItems,
justifyContent,
display,
width,
height,
children,
}) {
const boxClassName = classnames('box', {
// ---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-size-1': !borderWidth && Boolean(borderColor),
[`box--border-color-${borderColor}`]: Boolean(borderColor),
[`box--rounded-${borderRadius}`]: Boolean(borderRadius),
[`box--border-style-${borderStyle}`]: Boolean(borderStyle),
[`box--border-size-${borderWidth}`]: Boolean(borderWidth),
// Margin
...generateSizeClasses(
'box',
'margin',
margin,
marginTop,
marginRight,
marginBottom,
marginLeft,
),
// Padding
...generateSizeClasses(
'box',
'padding',
padding,
paddingTop,
paddingRight,
paddingBottom,
paddingLeft,
),
// ---Flex/Grid alignment---
// if justifyContent or alignItems supplied w/o display, default to flex
'box--display-flex':
!display && (Boolean(justifyContent) || Boolean(alignItems)),
[`box--justify-content-${justifyContent}`]: Boolean(justifyContent),
[`box--align-items-${alignItems}`]: Boolean(alignItems),
// display
[`box--display-${display}`]: Boolean(display),
// width & height
[`box--width-${width}`]: Boolean(width),
[`box--height-${height}`]: Boolean(height),
})
// Apply Box styles to any other component using function pattern
if (typeof children === 'function') {
return children(boxClassName)
}
return <div className={boxClassName}>{children}</div>
}
Box.propTypes = {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
margin: MultipleSizes,
marginTop: ValidSize,
marginBottom: ValidSize,
marginRight: ValidSize,
marginLeft: ValidSize,
padding: MultipleSizes,
paddingTop: ValidSize,
paddingBottom: ValidSize,
paddingRight: ValidSize,
paddingLeft: ValidSize,
borderColor: PropTypes.oneOf(Object.values(COLORS)),
borderWidth: PropTypes.number,
borderRadius: PropTypes.oneOf(Object.values(SIZES)),
borderStyle: PropTypes.oneOf(Object.values(BORDER_STYLE)),
alignItems: PropTypes.oneOf(Object.values(ALIGN_ITEMS)),
justifyContent: PropTypes.oneOf(Object.values(JUSTIFY_CONTENT)),
display: PropTypes.oneOf(Object.values(DISPLAY)),
width: PropTypes.oneOf(Object.values(BLOCK_SIZES)),
height: PropTypes.oneOf(Object.values(BLOCK_SIZES)),
}

View File

@ -0,0 +1,134 @@
@use "sass:map";
@use "design-system";
@use "utilities";
$attributes: padding, margin;
.box {
// Padding and Margin
@each $attribute in $attributes {
@each $size in design-system.$sizes-numeric {
&--#{$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);
}
}
}
}
// Borders
@each $size in design-system.$sizes-numeric {
&--border-size-#{$size} {
border-width: #{$size}px;
}
}
@each $variant, $color in design-system.$color-map {
&--border-color-#{$variant} {
border-color: $color;
}
}
@each $border-style in design-system.$border-style {
&--border-style-#{$border-style} {
border-style: $border-style;
}
}
&--rounded-none {
border-radius: 0;
}
&--rounded-xs {
border-radius: 0.125rem;
}
&--rounded-sm {
border-radius: 0.25rem;
}
&--rounded-md {
border-radius: 0.375rem;
}
&--rounded-lg {
border-radius: 0.5rem;
}
&--rounded-xl {
border-radius: 0.75rem;
}
// Display and Flex/Grid alignment
@each $display in design-system.$display {
&--display-#{$display} {
display: $display;
}
}
@each $alignment in design-system.$align-items {
&--align-items-#{$alignment} {
align-items: $alignment;
}
}
@each $justification in design-system.$justify-content {
&--justify-content-#{$justification} {
justify-content: $justification;
}
}
// 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;
}
}
&--height-screen {
height: 100vh;
}
&--width-screen {
width: 100vw;
}
&--height-max {
height: max-content;
}
&--width-max {
width: max-content;
}
&--height-min {
height: min-content;
}
&--width-min {
width: min-content;
}
// text
@each $alignment in design-system.$text-align {
text-align: $alignment;
}
}

View File

@ -0,0 +1,79 @@
import { number, select } from '@storybook/addon-knobs'
import React from 'react'
import {
ALIGN_ITEMS,
BLOCK_SIZES,
BORDER_STYLE,
COLORS,
DISPLAY,
JUSTIFY_CONTENT,
} from '../../../helpers/constants/design-system'
import Box from './box'
export default {
title: 'Box',
}
const sizeKnobOptions = [undefined, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
export const box = () => {
const items = []
const size = number(
'size',
100,
{ range: true, min: 50, max: 500, step: 10 },
'children',
)
for (let $i = 0; $i < number('items', 1, {}, 'children'); $i++) {
items.push(<img width={size} height={size} src="/images/eth_logo.svg" />)
}
return (
<Box
display={select('display', DISPLAY, DISPLAY.BLOCK, 'display')}
width={select('width', BLOCK_SIZES, BLOCK_SIZES.HALF, 'display')}
height={select('height', BLOCK_SIZES, BLOCK_SIZES.HALF, 'display')}
justifyContent={select(
'justifyContent',
JUSTIFY_CONTENT,
undefined,
'display',
)}
alignItems={select('alignItems', ALIGN_ITEMS, undefined, 'display')}
margin={select('margin', sizeKnobOptions, undefined, 'margin')}
marginTop={select('marginTop', sizeKnobOptions, undefined, 'margin')}
marginRight={select('marginRight', sizeKnobOptions, undefined, 'margin')}
marginBottom={select(
'marginBottom',
sizeKnobOptions,
undefined,
'margin',
)}
marginLeft={select('marginLeft', sizeKnobOptions, undefined, 'margin')}
padding={select('padding', sizeKnobOptions, undefined, 'padding')}
paddingTop={select('paddingTop', sizeKnobOptions, undefined, 'padding')}
paddingRight={select(
'paddingRight',
sizeKnobOptions,
undefined,
'padding',
)}
paddingBottom={select(
'paddingBottom',
sizeKnobOptions,
undefined,
'padding',
)}
paddingLeft={select('paddingLeft', sizeKnobOptions, undefined, 'padding')}
borderStyle={select(
'borderStyle',
BORDER_STYLE,
BORDER_STYLE.DASHED,
'border',
)}
borderWidth={number('borderWidth', 1, sizeKnobOptions, 'border')}
borderColor={select('borderColor', COLORS, COLORS.BLACK, 'border')}
>
{items}
</Box>
)
}

View File

@ -0,0 +1 @@
export { default } from './box'

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import { omit } from 'lodash'
import Typography from '../typography'
import { COLORS } from '../../../helpers/constants/design-system'
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
export default function Chip({
className,
@ -37,9 +37,9 @@ export default function Chip({
{children ?? (
<Typography
className="chip__label"
variant="h6"
variant={TYPOGRAPHY.H6}
tag="span"
color="UI4"
color={COLORS.UI4}
{...labelProps}
>
{label}
@ -54,7 +54,9 @@ Chip.propTypes = {
borderColor: PropTypes.oneOf(Object.values(COLORS)),
label: PropTypes.string,
children: PropTypes.node,
labelProps: PropTypes.shape(omit(Typography.propTypes, ['className'])),
labelProps: PropTypes.shape({
...omit(Typography.propTypes, ['className']),
}),
leftIcon: PropTypes.node,
rightIcon: PropTypes.node,
className: PropTypes.string,

View File

@ -1,7 +1,13 @@
import React from 'react'
import classnames from 'classnames'
import PropTypes from 'prop-types'
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
import {
COLORS,
FONT_WEIGHT,
TEXT_ALIGN,
TYPOGRAPHY,
} from '../../../helpers/constants/design-system'
import Box from '../box'
const { H6, H7, H8, H9 } = TYPOGRAPHY
@ -11,16 +17,15 @@ export default function Typography({
color = COLORS.BLACK,
tag,
children,
spacing = 1,
fontWeight = 'normal',
align,
boxProps = {},
}) {
const computedClassName = classnames(
'typography',
className,
`typography--${variant}`,
`typography--align-${align}`,
`typography--spacing-${spacing}`,
`typography--color-${color}`,
`typography--weight-${fontWeight}`,
)
@ -33,7 +38,15 @@ export default function Typography({
Tag = H6
}
return <Tag className={computedClassName}>{children}</Tag>
return (
<Box margin={[1, 0]} {...boxProps}>
{(boxClassName) => (
<Tag className={classnames(boxClassName, computedClassName)}>
{children}
</Tag>
)}
</Box>
)
}
Typography.propTypes = {
@ -41,9 +54,11 @@ Typography.propTypes = {
children: PropTypes.node.isRequired,
color: PropTypes.oneOf(Object.values(COLORS)),
className: PropTypes.string,
align: PropTypes.oneOf(['center', 'right']),
spacing: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8]),
fontWeight: PropTypes.oneOf(['bold', 'normal']),
align: PropTypes.oneOf(Object.values(TEXT_ALIGN)),
boxProps: PropTypes.shape({
...Box.propTypes,
}),
fontWeight: PropTypes.oneOf(Object.values(FONT_WEIGHT)),
tag: PropTypes.oneOf([
'p',
'h1',

View File

@ -4,6 +4,10 @@
.typography {
@include design-system.Paragraph;
& b {
font-weight: 700;
}
@each $variant in map.keys(design-system.$typography-variants) {
&--#{$variant} {
@include design-system.typography($variant);
@ -16,18 +20,16 @@
}
}
@each $variant, $weight in design-system.$typography-font-weights {
&--weight-#{$variant} {
@each $weight in design-system.$font-weight {
&--weight-#{$weight} {
font-weight: $weight;
}
}
&--align-center {
text-align: center;
}
&--align-right {
text-align: right;
@each $alignment in design-system.$text-align {
&--align-#{$alignment} {
text-align: $alignment;
}
}
@for $i from 1 through 8 {

View File

@ -1,23 +1,17 @@
import React from 'react'
import { number, select, text } from '@storybook/addon-knobs'
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
import {
COLORS,
FONT_WEIGHT,
TEXT_ALIGN,
TYPOGRAPHY,
} from '../../../helpers/constants/design-system'
import Typography from '.'
export default {
title: 'Typography',
}
const fontWeightOptions = {
bold: 'bold',
normal: 'normal',
}
const alignOptions = {
left: undefined,
center: 'center',
right: 'right',
}
export const list = () => (
<div style={{ width: '80%', flexDirection: 'column' }}>
{Object.values(TYPOGRAPHY).map((variant) => (
@ -26,8 +20,12 @@ export const list = () => (
variant={variant}
color={select('color', COLORS, COLORS.BLACK)}
spacing={number('spacing', 1, { range: true, min: 1, max: 8 })}
align={select('align', alignOptions, undefined)}
fontWeight={select('font weight', fontWeightOptions, 'normal')}
align={select('align', TEXT_ALIGN, undefined)}
fontWeight={select(
'font weight',
Object.values(FONT_WEIGHT),
FONT_WEIGHT.NORMAL,
)}
>
{variant}
</Typography>
@ -43,8 +41,8 @@ export const TheQuickOrangeFox = () => (
color={select('color', COLORS, COLORS.BLACK)}
variant={select('variant', TYPOGRAPHY, TYPOGRAPHY.Paragraph)}
spacing={number('spacing', 1, { range: true, min: 1, max: 8 })}
align={select('align', alignOptions, undefined)}
fontWeight={select('font weight', fontWeightOptions, 'normal')}
align={select('align', TEXT_ALIGN, undefined)}
fontWeight={select('font weight', FONT_WEIGHT, FONT_WEIGHT.NORMAL)}
>
{text('content', 'The quick orange fox jumped over the lazy dog.')}
</Typography>

View File

@ -2,6 +2,7 @@
@import 'account-mismatch-warning/index';
@import 'alert-circle-icon/index';
@import 'alert/index';
@import 'box/box';
@import 'breadcrumbs/index';
@import 'button-group/index';
@import 'button/buttons';

View File

@ -0,0 +1,72 @@
$align-items:
baseline,
center,
flex-end,
flex-start,
stretch;
$justify-content:
center,
flex-end,
flex-start,
space-around,
space-between,
space-evenly;
$fractions: (
1\/2: 50%,
1\/3: 33.333333%,
2\/3: 66.666667%,
1\/4: 25%,
2\/4: 50%,
3\/4: 75%,
1\/5: 20%,
2\/5: 40%,
3\/5: 60%,
4\/5: 80%,
1\/6: 16.666667%,
2\/6: 33.333333%,
3\/6: 50%,
4\/6: 66.666667%,
5\/6: 83.333333%,
1\/12: 8.333333%,
2\/12: 16.666667%,
3\/12: 25%,
4\/12: 33.333333%,
5\/12: 41.666667%,
6\/12: 50%,
7\/12: 58.333333%,
8\/12: 66.666667%,
9\/12: 75%,
10\/12: 83.333333%,
11\/12: 91.666667%,
);
$sizes-numeric:
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12;
$sizes-strings:
xs,
sm,
md,
lg,
xl,
none;
$border-style: solid, double, none, dashed, dotted;
$directions: top, right, bottom, left;
$display: block, grid, flex, inline-block, inline-grid, inline-flex, list-item;
$text-align: left, right, center, justify;
$font-weight: bold, normal, 100, 200, 300, 400, 500, 600, 700, 800, 900;

View File

@ -1,3 +1,4 @@
@forward 'attributes';
@forward 'breakpoints';
@forward 'colors';
@forward 'deprecated-colors';

View File

@ -82,11 +82,6 @@ $typography-variants: (
'h9': 0.5rem,
);
$typography-font-weights: (
'bold': 700,
'normal': 400,
);
$font-size-h1: map-get($typography-variants, 'h1');
$font-size-h2: map-get($typography-variants, 'h2');
$font-size-h3: map-get($typography-variants, 'h3');

View File

@ -0,0 +1,7 @@
$theme-spacing-value: 4px;
@function get-spacing($spacing) {
$spacingInPx: $spacing * 4px;
@return $spacingInPx;
}

View File

@ -1 +1,2 @@
@forward 'colors';
@forward 'spacing';

View File

@ -1,3 +1,9 @@
/**
* A note about the existence of both singular and plural variable names here:
* When dealing with a literal property name, e.g. ALIGN_ITEMS, the constant
* should match the property. When detailing a collection of things, it should
* match the plural form of the thing. e.g. COLORS, TYPOGRAPHY
*/
export const COLORS = {
UI1: 'ui-1',
UI2: 'ui-2',
@ -40,3 +46,111 @@ export const TYPOGRAPHY = {
H9: 'h9',
Paragraph: 'paragraph',
}
const NONE = 'none'
export const SIZES = {
XS: 'xs',
SM: 'sm',
MD: 'md',
LG: 'lg',
XL: 'xl',
NONE,
}
export const BORDER_STYLE = {
DASHED: 'dashed',
SOLID: 'solid',
DOTTED: 'dotted',
DOUBLE: 'double',
NONE,
}
const FLEX_END = 'flex-end'
const FLEX_START = 'flex-start'
const CENTER = 'center'
export const ALIGN_ITEMS = {
FLEX_START,
FLEX_END,
CENTER,
BASELINE: 'baseline',
STRETCH: 'stretch',
}
export const JUSTIFY_CONTENT = {
FLEX_START,
FLEX_END,
CENTER,
SPACE_AROUND: 'space-around',
SPACE_BETWEEN: 'space-between',
SPACE_EVENLY: 'space-evenly',
}
export const DISPLAY = {
BLOCK: 'block',
FLEX: 'flex',
GRID: 'grid',
INLINE_BLOCK: 'inline-block',
INLINE_FLEX: 'inline-flex',
INLINE_GRID: 'inline-grid',
LIST_ITEM: 'list-item',
}
const FRACTIONS = {
HALF: '1/2',
ONE_THIRD: '1/3',
TWO_THIRDS: '2/3',
ONE_FOURTH: '1/4',
TWO_FOURTHS: '2/4',
THREE_FOURTHS: '3/4',
ONE_FIFTH: '1/5',
TWO_FIFTHS: '2/5',
THREE_FIFTHS: '3/5',
FOUR_FIFTHS: '4/5',
ONE_SIXTH: '1/6',
TWO_SIXTHS: '2/6',
THREE_SIXTHS: '3/6',
FOUR_SIXTHS: '4/6',
FIVE_SIXTHS: '5/6',
ONE_TWELFTH: '1/12',
TWO_TWELFTHS: '2/12',
THREE_TWELFTHS: '3/12',
FOUR_TWELFTHS: '4/12',
FIVE_TWELFTHS: '5/12',
SIX_TWELFTHS: '6/12',
SEVEN_TWELFTHS: '7/12',
EIGHT_TWELFTHS: '8/12',
NINE_TWELFTHS: '9/12',
TEN_TWELFTHS: '10/12',
ELEVEN_TWELFTHS: '11/12',
}
export const BLOCK_SIZES = {
...FRACTIONS,
SCREEN: 'screen',
MAX: 'max',
MIN: 'min',
FULL: 'full',
}
export const TEXT_ALIGN = {
LEFT: 'left',
CENTER: 'center',
RIGHT: 'right',
JUSTIFY: 'justify',
}
export const FONT_WEIGHT = {
BOLD: 'bold',
NORMAL: 'normal',
100: 100,
200: 200,
300: 300,
400: 400,
500: 500,
600: 600,
700: 700,
800: 800,
900: 900,
}