diff --git a/.storybook/gnosisTheme.js b/.storybook/gnosisTheme.js index 9ffba40e..64b97a7c 100644 --- a/.storybook/gnosisTheme.js +++ b/.storybook/gnosisTheme.js @@ -10,11 +10,7 @@ export default create({ appBg: 'white', appContentBg: 'white', appBorderColor: 'grey', - appBorderRadius: 4, - - // Typography - fontBase: '"Roboto", sans-serif', - fontCode: 'monospace', + appBorderRadius: 4, // Text colors textColor: '#333333', diff --git a/.storybook/preview.js b/.storybook/preview.js index 221af33b..c4b605e4 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -6,15 +6,15 @@ import { ThemeProvider } from 'styled-components'; import theme from '../src/theme'; import GlobalStyles from '../src/global'; -addDecorator(storyFn => ( - +addDecorator((storyFn) => ( + <> - {storyFn()} - + {storyFn()} + )); addParameters({ options: { - showRoots: false - } + showRoots: false, + }, }); diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index cc490772..ebcefdd1 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -11,6 +11,18 @@ module.exports = ({ config }) => { ] }); + // There was a problem with the fonts not being loaded + // I took the fix from here: https://github.com/storybookjs/storybook/issues/5936#issuecomment-532902187 + config.module.rules = config.module.rules.map(rule => { + if (rule.test && rule.test.toString().includes('woff')) { + return { + ...rule, + test: /\.(svg|ico|jpg|jpeg|png|gif|webp|cur|ani|pdf)(\?.*)?$/ + } + } + return rule + }) + config.module.rules.push({ test: /\.(woff|woff2|eot|ttf)$/, use: [ diff --git a/src/dataDisplay/Card/card.stories.tsx b/src/dataDisplay/Card/card.stories.tsx index 7fcb6c88..a2a65a39 100644 --- a/src/dataDisplay/Card/card.stories.tsx +++ b/src/dataDisplay/Card/card.stories.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Card from './index'; -import { Title } from '../../index'; +import { Button, Dot, Title, Text } from '../../index'; export default { title: 'Data Display/Card', @@ -13,6 +13,42 @@ export default { export const SimpleCard = (): React.ReactElement => ( + + + 1 + + Some text + + +); + +export const CardDisabled = (): React.ReactElement => ( + + + + 1 + + + Some text + ); diff --git a/src/dataDisplay/Card/index.tsx b/src/dataDisplay/Card/index.tsx index 3c5cdf07..2286a5b1 100644 --- a/src/dataDisplay/Card/index.tsx +++ b/src/dataDisplay/Card/index.tsx @@ -4,22 +4,37 @@ import { rgba } from 'polished'; const StyledCard = styled.div` box-shadow: 1px 2px 10px 0 - ${({ theme }) => rgba(theme.colors.shadow.color, 0.08)}; + ${({ theme }) => rgba(theme.colors.shadow.color, 0.18)}; border-radius: 8px; padding: 24px; background-color: ${({ theme }) => theme.colors.white}; + position: relative; +`; + +const Disabled = styled.div` + opacity: 0.5; + position: absolute; + height: 100%; + width: 100%; + background-color: ${({ theme }) => theme.colors.white}; + z-index: 1; + top: 0; + left: 0; `; type Props = { className?: string; + disabled?: boolean; } & React.HTMLAttributes; const Card: React.FC = ({ className, children, + disabled, ...rest }): React.ReactElement => ( + {disabled && } {children} ); diff --git a/src/dataDisplay/Divider/divider.stories.tsx b/src/dataDisplay/Divider/divider.stories.tsx index f446ebf0..311e9e84 100644 --- a/src/dataDisplay/Divider/divider.stories.tsx +++ b/src/dataDisplay/Divider/divider.stories.tsx @@ -19,7 +19,13 @@ export const Horizontal = (): React.ReactElement => ( ); export const Vertical = (): React.ReactElement => ( -
+
Some content
Some content2
diff --git a/src/dataDisplay/Dot/dot.stories.tsx b/src/dataDisplay/Dot/dot.stories.tsx new file mode 100644 index 00000000..637dadd9 --- /dev/null +++ b/src/dataDisplay/Dot/dot.stories.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +import { Dot, Text, Icon } from '../../index'; + +export default { + title: 'Data Display/Dot', + component: Dot, + parameters: { + componentSubtitle: 'Generic Dot container for text or icons.', + }, +}; + +export const DotWithText = (): React.ReactElement => ( + + + 1 + + +); + +export const DotWithIcon = (): React.ReactElement => ( + + + +); diff --git a/src/dataDisplay/Dot/index.tsx b/src/dataDisplay/Dot/index.tsx new file mode 100644 index 00000000..637beb26 --- /dev/null +++ b/src/dataDisplay/Dot/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import styled from 'styled-components'; +import { ThemeColors } from '../../theme'; + +type Props = { + className?: string; + color: ThemeColors; +}; + +const StyledDot = styled.div` + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + height: 36px; + width: 36px; + background-color: ${({ theme, color }) => theme.colors[color]}; +`; + +const Dot: React.FC = ({ children, ...rest }): React.ReactElement => ( + {children} +); + +export default Dot; diff --git a/src/dataDisplay/Icon/icon.stories.tsx b/src/dataDisplay/Icon/icon.stories.tsx index 4a66c7aa..4955b360 100644 --- a/src/dataDisplay/Icon/icon.stories.tsx +++ b/src/dataDisplay/Icon/icon.stories.tsx @@ -28,7 +28,7 @@ export const Icons = (): React.ReactElement => { width: 140px; height: 140px; border: 1px solid ${({ theme }) => theme.colors.background}; - font-family: 'Averta', sans-serif; + font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: 14px; `; @@ -88,6 +88,7 @@ export const Icons = (): React.ReactElement => { 'resync', 'rocket', 'scan', + 'safe', 'search', 'sendAgain', 'sent', diff --git a/src/dataDisplay/Icon/images/safe.tsx b/src/dataDisplay/Icon/images/safe.tsx new file mode 100644 index 00000000..c4bea6eb --- /dev/null +++ b/src/dataDisplay/Icon/images/safe.tsx @@ -0,0 +1,47 @@ +import React from 'react'; + +export default { + sm: ( + + + + + + + + ), + md: ( + + + + + + + + + ), +}; diff --git a/src/dataDisplay/Icon/index.tsx b/src/dataDisplay/Icon/index.tsx index 923eebbf..9d51ed0f 100644 --- a/src/dataDisplay/Icon/index.tsx +++ b/src/dataDisplay/Icon/index.tsx @@ -57,6 +57,7 @@ import requiredConfirmations from './images/requiredConfirmations'; import restricted from './images/restricted'; import resync from './images/resync'; import rocket from './images/rocket'; +import safe from './images/safe'; import scan from './images/scan'; import search from './images/search'; import sendAgain from './images/sendAgain'; @@ -151,6 +152,7 @@ const icons = { restricted, resync, rocket, + safe, scan, search, sendAgain, diff --git a/src/dataDisplay/Title/index.tsx b/src/dataDisplay/Title/index.tsx index bffd8d4a..ba899bed 100644 --- a/src/dataDisplay/Title/index.tsx +++ b/src/dataDisplay/Title/index.tsx @@ -7,89 +7,65 @@ type Props = { children: string | React.ReactNode; size: ThemeTitleSize; withoutMargin?: boolean; + strong?: boolean; }; -const StyledH1 = styled.h1<{ withoutMargin?: boolean }>` - font-family: 'Averta'; +const StyledH1 = styled.h1<{ withoutMargin?: boolean; strong?: boolean }>` + font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: ${({ theme }) => theme.title.size.xl.fontSize}; line-height: ${({ theme }) => theme.title.size.xl.lineHeight}; - font-weight: normal; + font-weight: ${({ strong }) => (strong ? 'bold' : 'normal')}; margin: ${({ withoutMargin }) => (withoutMargin ? 0 : '30px')} 0; `; -const StyledH2 = styled.h2<{ withoutMargin?: boolean }>` - font-family: 'Averta'; +const StyledH2 = styled.h2<{ withoutMargin?: boolean; strong?: boolean }>` + font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: ${({ theme }) => theme.title.size.lg.fontSize}; line-height: ${({ theme }) => theme.title.size.lg.lineHeight}; - font-weight: normal; + font-weight: ${({ strong }) => (strong ? 'bold' : 'normal')}; margin: ${({ withoutMargin }) => (withoutMargin ? 0 : '28px')} 0; `; -const StyledH3 = styled.h3<{ withoutMargin?: boolean }>` - font-family: 'Averta'; +const StyledH3 = styled.h3<{ withoutMargin?: boolean; strong?: boolean }>` + font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: ${({ theme }) => theme.title.size.md.fontSize}; line-height: ${({ theme }) => theme.title.size.md.lineHeight}; - font-weight: normal; + font-weight: ${({ strong }) => (strong ? 'bold' : 'normal')}; margin: ${({ withoutMargin }) => (withoutMargin ? 0 : '26px')} 0; `; -const StyledH4 = styled.h4<{ withoutMargin?: boolean }>` - font-family: 'Averta'; +const StyledH4 = styled.h4<{ withoutMargin?: boolean; strong?: boolean }>` + font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: ${({ theme }) => theme.title.size.sm.fontSize}; line-height: ${({ theme }) => theme.title.size.sm.lineHeight}; - font-weight: normal; + font-weight: ${({ strong }) => (strong ? 'bold' : 'normal')}; margin: ${({ withoutMargin }) => (withoutMargin ? 0 : '22px')} 0; `; -const StyledH5 = styled.h5<{ withoutMargin?: boolean }>` - font-family: 'Averta'; +const StyledH5 = styled.h5<{ withoutMargin?: boolean; strong?: boolean }>` + font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: ${({ theme }) => theme.title.size.xs.fontSize}; line-height: ${({ theme }) => theme.title.size.xs.lineHeight}; - font-weight: normal; + font-weight: ${({ strong }) => (strong ? 'bold' : 'normal')}; margin: ${({ withoutMargin }) => (withoutMargin ? 0 : '18px')} 0; `; -const Title = ({ - children, - size, - withoutMargin, - ...rest -}: Props): React.ReactElement => { +const Title = ({ children, size, ...rest }: Props): React.ReactElement => { switch (size) { case 'xl': { - return ( - - {children} - - ); + return {children}; } case 'lg': { - return ( - - {children} - - ); + return {children}; } case 'md': { - return ( - - {children} - - ); + return {children}; } case 'sm': { - return ( - - {children} - - ); + return {children}; } case 'xs': { - return ( - - {children} - - ); + return {children}; } } }; diff --git a/src/dataDisplay/Title/title.stories.tsx b/src/dataDisplay/Title/title.stories.tsx index aabd4b3e..d8460d30 100644 --- a/src/dataDisplay/Title/title.stories.tsx +++ b/src/dataDisplay/Title/title.stories.tsx @@ -25,3 +25,14 @@ export const WithSizes = (): React.ReactElement => { ); }; + +export const Bold = (): React.ReactElement => ( + <> + + Title LG + + + Title MD + + +); diff --git a/src/dataDisplay/index.ts b/src/dataDisplay/index.ts index d5c82a96..0f29ac95 100644 --- a/src/dataDisplay/index.ts +++ b/src/dataDisplay/index.ts @@ -1,5 +1,6 @@ export { default as Card } from './Card'; export { default as Divider } from './Divider'; +export { default as Dot } from './Dot'; export { default as FixedIcon } from './FixedIcon'; export { default as FixedDialog } from './FixedDialog'; export * from './Icon'; diff --git a/src/inputs/Button/button.stories.tsx b/src/inputs/Button/button.stories.tsx index 1a157c83..3d2dd7a9 100644 --- a/src/inputs/Button/button.stories.tsx +++ b/src/inputs/Button/button.stories.tsx @@ -1,6 +1,8 @@ import React from 'react'; +import styled from 'styled-components'; import Button from './index'; +import { Text } from '../../index'; export default { title: 'Inputs/Button', @@ -10,42 +12,83 @@ export default { }, }; +const Wrapper = styled.div` + > * { + margin-right: 5px; + } +`; + export const SimpleButton = (): React.ReactElement => ( <> -
- - - -
-
- - - -
+ + + + + ); -export const Sizes = (): React.ReactElement => ( +export const DisabledButton = (): React.ReactElement => ( <> + + + + + + +); + +export const Sizes = (): React.ReactElement => ( + - + + ); export const withIcon = (): React.ReactElement => ( @@ -55,17 +98,87 @@ export const withIcon = (): React.ReactElement => ( iconType="addressBook" color="primary" onClick={() => alert('click')}> - text + + Text + + + +); + +export const withIconSize = (): React.ReactElement => ( + <> + + ); + +export const withCustomComponent = (): React.ReactElement => { + /* eslint-disable react/display-name */ + const Link = React.forwardRef< + HTMLAnchorElement, + { children: React.ReactNode } + >((props, ref) => { + return ( + + {props.children} + + ); + }); + + return ( + + ); +}; diff --git a/src/inputs/Button/index.tsx b/src/inputs/Button/index.tsx index 8a775963..f8ccbeb5 100644 --- a/src/inputs/Button/index.tsx +++ b/src/inputs/Button/index.tsx @@ -3,18 +3,27 @@ import ButtonMUI from '@material-ui/core/Button'; import { withStyles } from '@material-ui/core/styles'; import styled from 'styled-components'; -import theme, { ThemeButtonSize } from '../../theme'; +import theme, { ThemeButtonSize, ThemeIconSize } from '../../theme'; import { Icon, IconType } from '../../dataDisplay/Icon'; +enum Variant { + outlined = 'outlined', + bordered = 'bordered', + contained = 'contained', +} export interface Props extends React.ComponentPropsWithoutRef<'button'> { children: React.ReactNode; - iconType?: keyof IconType; size: ThemeButtonSize; color: 'primary' | 'secondary' | 'error'; - variant?: 'outlined' | 'contained'; + variant?: keyof typeof Variant; + iconType?: keyof IconType; + iconSize?: ThemeIconSize; + component?: React.ReactNode; + // for compatibility with react-router-dom Link + to?: string; } -type HoverColor = 'primaryHover' | 'secondaryHover' | 'errorHover'; +type HoverColor = 'secondary' | 'secondaryHover' | 'errorHover'; const StyledIcon = styled(Icon)` margin-right: 5px; @@ -24,33 +33,60 @@ const Button = ({ children, iconType, size, + iconSize = 'md', color, variant, ...rest }: Props): React.ReactElement => { + const getColor = (isDisabled?: boolean) => { + switch (variant) { + case Variant.contained: + return theme.colors.white; + case Variant.outlined: + case Variant.bordered: + return isDisabled + ? theme.colors[`${color}Hover` as HoverColor] + : theme.colors[color]; + } + }; + + const getBackgroundColor = (isHover?: boolean) => { + switch (variant) { + case Variant.contained: + return isHover ? theme.colors.secondary : theme.colors.primary; + case Variant.outlined: + case Variant.bordered: + return isHover ? theme.colors.background : theme.colors.white; + } + }; + + const getBorder = () => { + switch (variant) { + case Variant.contained: + case Variant.outlined: + return 'none'; + case Variant.bordered: + return `2px solid ${theme.colors[color]}`; + } + }; + const BootstrapButton = withStyles({ root: { height: theme.buttons.size[size].height, + minWidth: theme.buttons.size[size].minWidth, padding: theme.buttons.size[size].padding, fontFamily: theme.fonts.fontFamily, - color: variant === 'contained' ? theme.colors.white : theme.colors[color], + color: getColor(), 'text-transform': 'capitalize', - 'background-color': - variant === 'contained' ? theme.colors[color] : theme.colors.white, - 'border-color': theme.colors[color], - + 'background-color': getBackgroundColor(), + border: getBorder(), + 'border-radius': '8px', '&:hover': { - 'border-color': theme.colors[color], - 'background-color': - variant === 'contained' - ? theme.colors[`${color}Hover` as HoverColor] - : theme.colors.white, + 'background-color': getBackgroundColor(true), }, - '&:disabled': { opacity: theme.colors.disabled.opacity, - color: - variant === 'contained' ? theme.colors.white : theme.colors[color], + color: getColor(true), }, }, })(ButtonMUI); @@ -59,8 +95,8 @@ const Button = ({ {iconType && ( )} diff --git a/src/inputs/ButtonLink/buttonLink.stories.tsx b/src/inputs/ButtonLink/buttonLink.stories.tsx index bd65357a..0c75e6d7 100644 --- a/src/inputs/ButtonLink/buttonLink.stories.tsx +++ b/src/inputs/ButtonLink/buttonLink.stories.tsx @@ -15,3 +15,14 @@ export const SimpleButtonLink = (): React.ReactElement => ( Some text ); + +export const IconSize = (): React.ReactElement => ( + <> + + Small Icon + + + Big Icon + + +); diff --git a/src/inputs/ButtonLink/index.tsx b/src/inputs/ButtonLink/index.tsx index d6663570..aa973d1b 100644 --- a/src/inputs/ButtonLink/index.tsx +++ b/src/inputs/ButtonLink/index.tsx @@ -1,12 +1,13 @@ import React from 'react'; import styled from 'styled-components'; -import { ThemeColors, ThemeTextSize } from '../../theme'; +import { ThemeColors, ThemeTextSize, ThemeIconSize } from '../../theme'; import { Icon, IconType } from '../../dataDisplay/Icon'; import { Text } from '../../index'; export interface Props extends React.ComponentPropsWithoutRef<'button'> { iconType?: keyof IconType; + iconSize?: ThemeIconSize; textSize?: ThemeTextSize; color: ThemeColors; children: React.ReactNode; @@ -18,7 +19,7 @@ const StyledButtonLink = styled.button` text-decoration: underline; cursor: pointer; color: ${({ theme, color }) => theme['colors'][color]}; - font-family: inherit; + font-family: ${({ theme }) => theme.fonts.fontFamily}; display: flex; align-items: center; @@ -27,18 +28,23 @@ const StyledButtonLink = styled.button` } `; +const StyledText = styled(Text)` + margin: 0 4px; +`; + const ButtonLink = ({ iconType, + iconSize = 'md', children, textSize = 'lg', ...rest }: Props): React.ReactElement => { return ( - {iconType && } - + {iconType && } + {children} - + ); }; diff --git a/src/inputs/Link/index.tsx b/src/inputs/Link/index.tsx index bd3bc333..419a9aca 100644 --- a/src/inputs/Link/index.tsx +++ b/src/inputs/Link/index.tsx @@ -12,7 +12,7 @@ const StyledLink = styled.a` text-decoration: underline; cursor: pointer; color: ${({ theme, color = 'primary' }) => theme['colors'][color]}; - font-family: inherit; + font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: ${({ size = 'md', theme }) => theme.text.size[size].fontSize}; `; diff --git a/src/theme.ts b/src/theme.ts index ac1ecc5b..461c6b3d 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -1,8 +1,8 @@ const theme = { buttons: { size: { - md: { height: '36px', padding: '0 16px' }, - lg: { height: '52px', padding: '0 25px' }, + md: { height: '36px', minWidth: '130px', padding: '0 24px' }, + lg: { height: '52px', minWidth: '240px', padding: '0 48px' }, }, }, colors: { diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts index 1f17fc28..241f358d 100644 --- a/src/typings/index.d.ts +++ b/src/typings/index.d.ts @@ -8,11 +8,5 @@ declare module '*.svg' { export default src; } -declare module '*.woff2' { - import React = require('react'); - - export const ReactComponent: React.SFC>; - - const src: string; - export default src; -} +declare module '*.ttf'; +declare module '*.woff2';