├── .gitattributes
├── src
├── Drawer
│ ├── constants.ts
│ ├── types.ts
│ ├── DrawerContent.ts
│ ├── DrawerTitleRoot.ts
│ ├── DrawerTitleClose.ts
│ ├── DrawerTitleContent.ts
│ ├── DrawerProvider.tsx
│ ├── index.ts
│ ├── DrawerRoot.ts
│ ├── DrawerOverlay.ts
│ └── Drawer.stories.tsx
├── Image
│ ├── index.ts
│ ├── ImageRoot.tsx
│ ├── Image.tsx
│ └── Image.stories.tsx
├── Hidden
│ ├── index.ts
│ ├── Hidden.tsx
│ └── Hidden.stories.tsx
├── ReactUtils
│ ├── index.ts
│ ├── hooks
│ │ ├── useTheme.ts
│ │ ├── index.ts
│ │ ├── useDebounce.ts
│ │ └── useBreakpoint.ts
│ ├── utils
│ │ ├── index.ts
│ │ ├── getCurrentBreakpoint.ts
│ │ └── breakpointComparison.ts
│ └── ReactUtils.stories.mdx
├── TextLink
│ ├── index.ts
│ ├── TextLinkRoot.tsx
│ ├── TextLink.tsx
│ └── TextLink.stories.mdx
├── Price
│ ├── types.ts
│ ├── index.ts
│ ├── PriceRoot.ts
│ ├── Price.test.tsx
│ └── Price.tsx
├── Styleguide
│ ├── query
│ │ ├── index.ts
│ │ ├── types.ts
│ │ └── query.ts
│ ├── value
│ │ ├── index.ts
│ │ └── SizeValue.ts
│ ├── mixin
│ │ ├── index.ts
│ │ ├── margin.ts
│ │ ├── spacing.ts
│ │ ├── Mixin.stories.mdx
│ │ └── typography.ts
│ ├── theme
│ │ ├── zIndex.ts
│ │ ├── shape.ts
│ │ ├── elevation.ts
│ │ ├── Transition.stories.mdx
│ │ ├── breakpoint.ts
│ │ ├── spacing.ts
│ │ ├── Shape.stories.mdx
│ │ ├── Breakpoint.stories.mdx
│ │ ├── index.ts
│ │ ├── palette.ts
│ │ ├── transition.ts
│ │ ├── Theme.stories.mdx
│ │ ├── Spacing.stories.mdx
│ │ ├── Elevation.stories.mdx
│ │ ├── Palette.stories.mdx
│ │ ├── Color.stories.mdx
│ │ └── color.ts
│ ├── index.ts
│ ├── Types.stories.mdx
│ └── types.ts
├── Container
│ ├── types.ts
│ ├── index.ts
│ ├── Container.tsx
│ ├── Container.stories.tsx
│ └── ContainerRoot.ts
├── CssBaseline
│ ├── index.ts
│ ├── CssBaseline.stories.mdx
│ └── CssBaseline.tsx
├── Grid
│ ├── types.ts
│ ├── index.ts
│ ├── Grid.tsx
│ ├── GridRoot.ts
│ └── mixin
│ │ └── options.ts
├── Button
│ ├── types.ts
│ ├── mixin
│ │ ├── pill.ts
│ │ ├── disabled.ts
│ │ ├── iconOnly.ts
│ │ ├── fullWidth.ts
│ │ ├── noBackground.ts
│ │ ├── elevation.ts
│ │ ├── index.ts
│ │ ├── link.ts
│ │ ├── size.ts
│ │ ├── variant.ts
│ │ └── status.ts
│ ├── ButtonLoadingIndicator.ts
│ ├── ButtonLabel.ts
│ ├── index.ts
│ ├── ButtonAttachment.ts
│ ├── ButtonRoot.ts
│ └── Button.tsx
├── ThemeProvider
│ ├── index.ts
│ ├── styled.d.ts
│ ├── ThemeProvider.tsx
│ └── ThemeProvider.stories.mdx
├── ContentBox
│ ├── types.ts
│ ├── index.ts
│ ├── ContentBox.stories.tsx
│ ├── ContentBox.tsx
│ └── ContentBoxRoot.tsx
├── locales
│ └── index.ts
├── CircularProgress
│ ├── types.ts
│ ├── CircularProgressSVG.ts
│ ├── keyframe
│ │ ├── circularRotateKeyframe.ts
│ │ └── circularDashKeyframe.ts
│ ├── index.ts
│ ├── CircularProgressCircle.ts
│ ├── CircularProgressRoot.ts
│ ├── CircularProgress.stories.tsx
│ └── CircularProgress.tsx
├── Typography
│ ├── mixin
│ │ ├── index.ts
│ │ ├── status.ts
│ │ └── variant.ts
│ ├── index.ts
│ ├── types.ts
│ ├── TypographyRoot.tsx
│ └── Typography.tsx
├── Icon
│ ├── base
│ │ ├── index.ts
│ │ ├── IconHover.tsx
│ │ ├── IconHoverRoot.ts
│ │ ├── SvgIconRoot.ts
│ │ └── SvgIcon.tsx
│ ├── ChevronUpIcon.tsx
│ ├── ChevronDownIcon.tsx
│ ├── ChevronRightIcon.tsx
│ ├── SearchIcon.tsx
│ ├── CheckboxUncheckedIcon.tsx
│ ├── CheckboxCheckedIcon.tsx
│ ├── HeartIcon.tsx
│ ├── UserIcon.tsx
│ ├── BurgerIcon.tsx
│ ├── CloseIcon.tsx
│ ├── types.ts
│ ├── PinterestIcon.tsx
│ ├── TwitterIcon.tsx
│ ├── FacebookIcon.tsx
│ ├── Amex.tsx
│ ├── CartIcon.tsx
│ ├── Visa.tsx
│ ├── MasterCard.tsx
│ ├── GiftIcon.tsx
│ ├── Icon.stories.mdx
│ ├── index.ts
│ ├── SofortKlarna.tsx
│ ├── GiroPay.tsx
│ └── InstagramIcon.tsx
├── Navigation
│ ├── NavigationFooterLink.tsx
│ ├── index.ts
│ ├── NavigationItemRoot.ts
│ ├── NavigationHeaderLink.tsx
│ ├── NavigationItem.tsx
│ ├── Navigation.tsx
│ ├── NavigationRoot.ts
│ └── Navigation.stories.tsx
├── declarations.d.ts
├── Divider
│ ├── types.ts
│ ├── index.ts
│ ├── Divider.tsx
│ ├── Divider.stories.tsx
│ └── DividerRoot.ts
├── ButtonGroup
│ ├── index.ts
│ ├── ButtonGroup.tsx
│ ├── ButtonGroup.stories.tsx
│ └── ButtonGroupRoot.ts
├── CssGrid
│ ├── index.ts
│ ├── types.ts
│ ├── CssGridStyles.ts
│ ├── CssGrid.tsx
│ └── CssGrid.stories.tsx
├── index.ts
├── Layout
│ ├── index.stories.mdx
│ └── index.tsx
├── __tests__
│ └── utils
│ │ └── render.tsx
└── Stack
│ ├── index.stories.mdx
│ └── index.tsx
├── .prettierrc.json
├── tsconfig.test.json
├── .github
├── labeler.yml
├── CODEOWNERS
├── pull_request_template.md
├── workflows
│ ├── size_labels.yaml
│ ├── pull_request.yml
│ └── push.yml
└── dependabot.yaml
├── docs
├── preview
│ ├── index.ts
│ ├── SpacingPreview.tsx
│ ├── ColorPreview.tsx
│ ├── clearThemeTypes.ts
│ └── preview.css
├── Introduction.stories.mdx
└── utils
│ └── arg-types-control.ts
├── .releaserc.js
├── test-runner-jest.config.js
├── jsconfig.json
├── .gitignore
├── CHANGELOG.md
├── sonar-project.properties
├── tsconfig.json
├── README.md
└── .eslintrc.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | build/* linguist-vendored
2 | .releaserc.js linguist-vendored
--------------------------------------------------------------------------------
/src/Drawer/constants.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_IDENTIFIER = 'drawer-root'
2 |
--------------------------------------------------------------------------------
/src/Image/index.ts:
--------------------------------------------------------------------------------
1 | import Image from './Image'
2 |
3 | export { Image }
4 |
--------------------------------------------------------------------------------
/src/Hidden/index.ts:
--------------------------------------------------------------------------------
1 | import Hidden from './Hidden'
2 |
3 | export { Hidden }
4 |
--------------------------------------------------------------------------------
/src/ReactUtils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './utils'
2 | export * from './hooks'
3 |
--------------------------------------------------------------------------------
/src/TextLink/index.ts:
--------------------------------------------------------------------------------
1 | import TextLink from './TextLink'
2 |
3 | export { TextLink }
4 |
--------------------------------------------------------------------------------
/src/Price/types.ts:
--------------------------------------------------------------------------------
1 | export enum Currency {
2 | Euro = '€',
3 | None = 'None',
4 | }
5 |
--------------------------------------------------------------------------------
/src/Styleguide/query/index.ts:
--------------------------------------------------------------------------------
1 | import query from './query'
2 |
3 | export default query
4 |
--------------------------------------------------------------------------------
/src/Container/types.ts:
--------------------------------------------------------------------------------
1 | export enum Behavior {
2 | Fluid = 'fluid',
3 | Fixed = 'fixed',
4 | }
5 |
--------------------------------------------------------------------------------
/src/CssBaseline/index.ts:
--------------------------------------------------------------------------------
1 | import CssBaseline from './CssBaseline'
2 |
3 | export { CssBaseline }
4 |
--------------------------------------------------------------------------------
/src/Grid/types.ts:
--------------------------------------------------------------------------------
1 | export type GridSize = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 'auto'
2 |
--------------------------------------------------------------------------------
/src/Button/types.ts:
--------------------------------------------------------------------------------
1 | export enum AttachmentPos {
2 | Left = 'row-reverse',
3 | Right = 'row',
4 | }
5 |
--------------------------------------------------------------------------------
/src/Drawer/types.ts:
--------------------------------------------------------------------------------
1 | export enum DrawerOrientation {
2 | Left = 'left',
3 | Right = 'right',
4 | }
5 |
--------------------------------------------------------------------------------
/src/ThemeProvider/index.ts:
--------------------------------------------------------------------------------
1 | import ThemeProvider from './ThemeProvider'
2 |
3 | export { ThemeProvider }
4 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": false,
5 | "singleQuote": true
6 | }
--------------------------------------------------------------------------------
/src/ContentBox/types.ts:
--------------------------------------------------------------------------------
1 | export enum Opacity {
2 | Base = '1.0',
3 | Primary = '0.2',
4 | Secondary = '0.4',
5 | }
6 |
--------------------------------------------------------------------------------
/src/Price/index.ts:
--------------------------------------------------------------------------------
1 | import Price from './Price'
2 | import { Currency } from './types'
3 |
4 | export { Price, Currency }
5 |
--------------------------------------------------------------------------------
/src/locales/index.ts:
--------------------------------------------------------------------------------
1 | import de from './de/translations.json'
2 |
3 | const locales = { de }
4 |
5 | export { locales }
6 |
--------------------------------------------------------------------------------
/src/CircularProgress/types.ts:
--------------------------------------------------------------------------------
1 | export enum State {
2 | Determinate = 'determinate',
3 | Indeterminate = 'indeterminate',
4 | }
5 |
--------------------------------------------------------------------------------
/src/Typography/mixin/index.ts:
--------------------------------------------------------------------------------
1 | import status from './status'
2 | import variant from './variant'
3 |
4 | export { status, variant }
5 |
--------------------------------------------------------------------------------
/src/ContentBox/index.ts:
--------------------------------------------------------------------------------
1 | import ContentBox from './ContentBox'
2 | import { Opacity } from './types'
3 |
4 | export { ContentBox, Opacity }
5 |
--------------------------------------------------------------------------------
/src/ReactUtils/hooks/useTheme.ts:
--------------------------------------------------------------------------------
1 | import { theme, Theme } from '@mycompany/design-system/Styleguide'
2 |
3 | export default (): Theme => theme
4 |
--------------------------------------------------------------------------------
/src/Styleguide/value/index.ts:
--------------------------------------------------------------------------------
1 | import ColorValue from 'color'
2 | import SizeValue from './SizeValue'
3 |
4 | export { SizeValue, ColorValue }
5 |
--------------------------------------------------------------------------------
/src/CircularProgress/CircularProgressSVG.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export default styled.svg`
4 | display: block;
5 | `
6 |
--------------------------------------------------------------------------------
/src/Icon/base/index.ts:
--------------------------------------------------------------------------------
1 | import SvgIcon from './SvgIcon'
2 | import SvgIconRoot from './SvgIconRoot'
3 | import IconHover from './IconHover'
4 |
5 | export { SvgIcon, SvgIconRoot, IconHover }
6 |
--------------------------------------------------------------------------------
/src/Navigation/NavigationFooterLink.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const NavigationFooterLink = styled.a`
4 | &:hover {
5 | text-decoration: underline;
6 | }
7 | `
8 |
--------------------------------------------------------------------------------
/src/declarations.d.ts:
--------------------------------------------------------------------------------
1 | declare interface Window {
2 | DD_LOGS: any
3 | UC_UI?: { showSecondLayer: () => void }
4 | mycompany: {
5 | apiStore: {}
6 | initialLanguage: string
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "resolveJsonModule": true
5 | },
6 | "exclude": [
7 | "node_modules",
8 | "dist"
9 | ]
10 | }
--------------------------------------------------------------------------------
/src/Button/mixin/pill.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 |
3 | export default ({ pill }: StyledProps<{ pill: boolean }>) =>
4 | pill &&
5 | css`
6 | border-radius: 5rem;
7 | `
8 |
--------------------------------------------------------------------------------
/src/ReactUtils/hooks/index.ts:
--------------------------------------------------------------------------------
1 | import useTheme from './useTheme'
2 | import useBreakpoint from './useBreakpoint'
3 | import useDebounce from './useDebounce'
4 |
5 | export { useTheme, useBreakpoint, useDebounce }
6 |
--------------------------------------------------------------------------------
/src/Button/mixin/disabled.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 |
3 | export default ({ disabled }: StyledProps<{ disabled: boolean }>) =>
4 | disabled &&
5 | css`
6 | opacity: 0.7;
7 | `
8 |
--------------------------------------------------------------------------------
/src/Divider/types.ts:
--------------------------------------------------------------------------------
1 | export enum Orientation {
2 | Horizontal = 'horizontal',
3 | Vertical = 'vertical',
4 | }
5 |
6 | export enum Display {
7 | FullWidth = 'fullWidth',
8 | Middle = 'middle',
9 | }
10 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | # https://github.com/actions/labeler
2 |
3 | # Add 'gh-actions' label
4 | gh-actions:
5 | - .github/*
6 | - .github/**/*
7 |
8 | # Add 'documentation' label
9 | documentation:
10 | - '*.md'
11 |
--------------------------------------------------------------------------------
/src/Styleguide/mixin/index.ts:
--------------------------------------------------------------------------------
1 | import typography from './typography'
2 | import margin from './margin'
3 | import spacing from './spacing'
4 |
5 | export default {
6 | typography,
7 | margin,
8 | spacing,
9 | }
10 |
--------------------------------------------------------------------------------
/docs/preview/index.ts:
--------------------------------------------------------------------------------
1 | import SpacingPreview from './SpacingPreview'
2 | import ColorPreview from './ColorPreview'
3 | import clearThemeTypes from './clearThemeTypes'
4 |
5 | export { ColorPreview, SpacingPreview, clearThemeTypes }
6 |
--------------------------------------------------------------------------------
/src/ThemeProvider/styled.d.ts:
--------------------------------------------------------------------------------
1 | import {} from 'styled-components'
2 | import { Theme } from '@mycompany/design-system/Styleguide'
3 |
4 | declare module 'styled-components' {
5 | export interface DefaultTheme extends Theme {}
6 | }
7 |
--------------------------------------------------------------------------------
/src/ButtonGroup/index.ts:
--------------------------------------------------------------------------------
1 | import ButtonGroup from './ButtonGroup'
2 | import ButtonGroupRoot from './ButtonGroupRoot'
3 |
4 | const ButtonGroupComponents = {
5 | ButtonGroupRoot,
6 | }
7 |
8 | export { ButtonGroup, ButtonGroupComponents }
9 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/zIndex.ts:
--------------------------------------------------------------------------------
1 | import { ZIndexSet } from './types'
2 |
3 | const zIndex: ZIndexSet = {
4 | app: 500,
5 | drawer: 1200,
6 | modal: 1300,
7 | snackbar: 1400,
8 | tooltip: 1500,
9 | }
10 |
11 | export default zIndex
12 |
--------------------------------------------------------------------------------
/src/Button/mixin/iconOnly.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 |
3 | export default ({ iconOnly }: StyledProps<{ iconOnly: boolean }>) =>
4 | iconOnly &&
5 | css`
6 | &:hover {
7 | background: none;
8 | }
9 | `
10 |
--------------------------------------------------------------------------------
/src/CircularProgress/keyframe/circularRotateKeyframe.ts:
--------------------------------------------------------------------------------
1 | import { keyframes } from 'styled-components'
2 |
3 | export default keyframes`
4 | 0% {
5 | transform: rotate(0deg);
6 | }
7 | 100% {
8 | transform: rotate(360deg);
9 | }
10 | `
11 |
--------------------------------------------------------------------------------
/src/Grid/index.ts:
--------------------------------------------------------------------------------
1 | import Grid from './Grid'
2 | import GridRoot from './GridRoot'
3 | import type { GridSize } from './types'
4 |
5 | const GridComponents = {
6 | GridRoot,
7 | }
8 |
9 | export { Grid, GridComponents }
10 | export type { GridSize }
11 |
--------------------------------------------------------------------------------
/.releaserc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | branches: ['main'],
3 | plugins: [
4 | '@semantic-release/commit-analyzer',
5 | '@semantic-release/release-notes-generator',
6 | '@semantic-release/npm',
7 | '@semantic-release/github',
8 | ],
9 | }
10 |
--------------------------------------------------------------------------------
/src/Container/index.ts:
--------------------------------------------------------------------------------
1 | import Container from './Container'
2 | import ContainerRoot from './ContainerRoot'
3 | import { Behavior } from './types'
4 |
5 | const ContainerComponents = {
6 | ContainerRoot,
7 | }
8 |
9 | export { Container, ContainerComponents, Behavior }
10 |
--------------------------------------------------------------------------------
/src/Divider/index.ts:
--------------------------------------------------------------------------------
1 | import Divider from './Divider'
2 | import DividerRoot from './DividerRoot'
3 | import { Display, Orientation } from './types'
4 |
5 | const DividerComponents = {
6 | DividerRoot,
7 | }
8 |
9 | export { Divider, DividerComponents, Display, Orientation }
10 |
--------------------------------------------------------------------------------
/src/Styleguide/mixin/margin.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Size } from '../types'
3 |
4 | export default (props: StyledProps<{ spacing: Size | false }>) => css`
5 | margin: ${props.spacing ? props.theme.spacing.size[props.spacing].rem() : 0};
6 | `
7 |
--------------------------------------------------------------------------------
/src/Typography/index.ts:
--------------------------------------------------------------------------------
1 | import Typography from './Typography'
2 | import TypographyRoot from './TypographyRoot'
3 |
4 | import {TypographyAlign} from './types'
5 |
6 | const TypographyComponents = { TypographyRoot }
7 |
8 | export { Typography, TypographyComponents, TypographyAlign }
9 |
--------------------------------------------------------------------------------
/src/Styleguide/mixin/spacing.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Size } from '../types'
3 |
4 | export default (props: StyledProps<{ spacing: Size | false }>) => css`
5 | padding: ${props.spacing ? props.theme.spacing.size[props.spacing].rem() : 0};
6 | `
7 |
--------------------------------------------------------------------------------
/src/Drawer/DrawerContent.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import {} from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {}
5 |
6 | export default styled.div`
7 | display: flex;
8 | flex: 1;
9 | flex-direction: column;
10 | overflow: auto;
11 | `
12 |
--------------------------------------------------------------------------------
/src/Typography/types.ts:
--------------------------------------------------------------------------------
1 | import { TypographyUnit } from '@mycompany/design-system/Styleguide'
2 |
3 | export type DisplayComponentMap = {
4 | [key in TypographyUnit]: keyof JSX.IntrinsicElements
5 | }
6 |
7 | export enum TypographyAlign {
8 | Right = 'right',
9 | Left = 'left',
10 | }
11 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/shape.ts:
--------------------------------------------------------------------------------
1 | import { SizeValue } from '../value'
2 | import { Shape } from './types'
3 |
4 | const shape: Shape = {
5 | // atm it would be 5px, but Borstie suggested a 3px value
6 | borderRadius: new SizeValue(3),
7 | progressSickness: 3.6,
8 | }
9 |
10 | export default shape
11 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # example owners file:
2 | # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#example-of-a-codeowners-file
3 |
4 | # sre-team owns the repository settings managed by probot
5 | .github/settings.yml @mycompany/sre-team
6 |
--------------------------------------------------------------------------------
/src/Drawer/DrawerTitleRoot.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import {} from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {}
5 |
6 | export default styled.div`
7 | align-items: center;
8 | display: flex;
9 | padding: ${({ theme }) => theme.spacing.size.medium.rem()};
10 | `
11 |
--------------------------------------------------------------------------------
/src/ReactUtils/utils/index.ts:
--------------------------------------------------------------------------------
1 | import getCurrentBreakpoint from './getCurrentBreakpoint'
2 | import {
3 | isBreakpointGreaterThan,
4 | isBreakpointSmallerThan,
5 | } from './breakpointComparison'
6 |
7 | export {
8 | getCurrentBreakpoint,
9 | isBreakpointGreaterThan,
10 | isBreakpointSmallerThan,
11 | }
12 |
--------------------------------------------------------------------------------
/src/Drawer/DrawerTitleClose.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Button } from '@mycompany/design-system/Button'
3 |
4 | export default styled(Button)`
5 | color: ${({ theme }) => theme.color.variant.secondary.font.hex()};
6 | margin-bottom: 0;
7 | margin-top: 0;
8 | position: absolute;
9 | right: 15px;
10 | `
11 |
--------------------------------------------------------------------------------
/test-runner-jest.config.js:
--------------------------------------------------------------------------------
1 | const { getJestConfig } = require('@storybook/test-runner')
2 |
3 | module.exports = {
4 | // The default configuration comes from @storybook/test-runner
5 | ...getJestConfig(),
6 | /** Add your own overrides below
7 | * @see https://jestjs.io/docs/configuration
8 | */
9 | coverageProvider: 'v8',
10 | }
11 |
--------------------------------------------------------------------------------
/src/Button/mixin/fullWidth.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 |
4 | export default ({
5 | fullWidth,
6 | }: StyledProps<{ fullWidth: boolean; elementSpacing: Size | false }>) =>
7 | fullWidth &&
8 | css`
9 | display: flex;
10 | width: 100%;
11 | `
12 |
--------------------------------------------------------------------------------
/src/Styleguide/query/types.ts:
--------------------------------------------------------------------------------
1 | import { ThemedCssFunction, DefaultTheme } from 'styled-components'
2 | import { Size } from '../types'
3 |
4 | export interface QuerySet {
5 | min: (key: Size) => ThemedCssFunction
6 | max: (key: Size) => ThemedCssFunction
7 | between: (start: Size, end: Size) => ThemedCssFunction
8 | }
9 |
--------------------------------------------------------------------------------
/src/Typography/mixin/status.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Status } from '@mycompany/design-system/Styleguide'
3 |
4 | export default ({ status, theme }: StyledProps<{ status: Status }>) =>
5 | status &&
6 | status !== Status.Default &&
7 | css`
8 | color: ${theme.color.status[status].base.hex()};
9 | `
10 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "target": "es5",
5 | "allowSyntheticDefaultImports": true,
6 | "moduleResolution": "node",
7 | "baseUrl": "./",
8 | "paths": {
9 | "@mycompany/design-system/*": [
10 | "./src/*"
11 | ]
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # misc
10 | .DS_Store
11 | .env.local
12 | .env.development.local
13 | .env.test.local
14 | .env.production.local
15 | .vscode
16 |
17 | npm-debug.log*
18 | yarn-debug.log*
19 | yarn-error.log*
20 |
21 | #build/dist
22 | dist
23 | dist-storybook
24 | .jest-test-results.json
--------------------------------------------------------------------------------
/src/Drawer/DrawerTitleContent.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { mixin } from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {}
5 |
6 | export default styled.div`
7 | ${mixin.typography.headline3}
8 | display: flex;
9 | flex-grow: 1;
10 | overflow: hidden;
11 | padding-right: ${({ theme }) => theme.spacing.size.medium.rem()};
12 | `
13 |
--------------------------------------------------------------------------------
/src/Button/mixin/noBackground.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 |
3 | export default ({
4 | noBackground,
5 | theme,
6 | }: StyledProps<{
7 | noBackground: boolean
8 | }>) =>
9 | noBackground &&
10 | css`
11 | background: transparent;
12 | &:hover {
13 | background: none;
14 | color: ${theme.color.variant.primary.hover.hex()};
15 | }
16 | `
17 |
--------------------------------------------------------------------------------
/src/ReactUtils/hooks/useDebounce.ts:
--------------------------------------------------------------------------------
1 | export default () =>
2 | any>(func: F, waitFor: number) => {
3 | let timeout: ReturnType
4 |
5 | const debounced = (...args: any) => {
6 | clearTimeout(timeout)
7 | timeout = setTimeout(() => func(...args), waitFor)
8 | }
9 |
10 | return debounced as (...args: Parameters) => ReturnType
11 | }
12 |
--------------------------------------------------------------------------------
/src/CircularProgress/keyframe/circularDashKeyframe.ts:
--------------------------------------------------------------------------------
1 | import { keyframes } from 'styled-components'
2 |
3 | export default keyframes`
4 | 0% {
5 | stroke-dasharray: 1px, 200px;
6 | stroke-dashoffset: 0;
7 | }
8 | 50% {
9 | stroke-dasharray: 100px, 200px;
10 | stroke-dashoffset: -15px;
11 | }
12 | 100% {
13 | stroke-dasharray: 100px, 200px;
14 | stroke-dashoffset: -125px;
15 | }
16 | `
17 |
--------------------------------------------------------------------------------
/src/CssBaseline/CssBaseline.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 |
3 |
4 |
5 | # CssBaseline
6 |
7 | Injects global styles and normalization css
8 |
9 | ```tsx
10 | import CssBaseline from '@mycompany/design-system/CssBaseline'
11 |
12 | const MyApp = (
13 |
14 |
15 |
16 |
17 | )
18 | ```
19 |
--------------------------------------------------------------------------------
/src/Typography/mixin/variant.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Variant } from '@mycompany/design-system/Styleguide'
3 |
4 | export default ({
5 | theme,
6 | variant,
7 | }: StyledProps<{
8 | variant: Variant
9 | }>) => css`
10 | color: ${variant === Variant.Primary
11 | ? theme.color.variant[variant].base.hex()
12 | : theme.color.variant[variant].font.hex()};
13 | `
14 |
--------------------------------------------------------------------------------
/src/Button/ButtonLoadingIndicator.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { CircularProgress } from '@mycompany/design-system/CircularProgress'
3 | import { Size } from '@mycompany/design-system/Styleguide'
4 |
5 | export default styled(CircularProgress)<{ size: Size }>`
6 | margin-top: ${({ size, theme }) =>
7 | theme.spacing.size[size].multiply(-1).px()};
8 | position: absolute;
9 | top: 50%;
10 | `
11 |
--------------------------------------------------------------------------------
/src/Image/ImageRoot.tsx:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import { ObjectFit, Size } from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {
5 | spacing: Size
6 | src: string
7 | objectFit?: ObjectFit
8 | }
9 | export default styled.img`
10 | margin: 0;
11 | width: 100%;
12 |
13 | ${({ objectFit }) =>
14 | objectFit &&
15 | css`
16 | object-fit: ${objectFit};
17 | `}
18 | `
19 |
--------------------------------------------------------------------------------
/src/Styleguide/mixin/Mixin.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 |
3 |
4 |
5 | # Mixins
6 |
7 | There are multiple useable mixins for styled-components
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | import { mixin } from '@mycompany/design-system/Styleguide'
13 | import styled from 'styled-components'
14 |
15 | export default styled.div`
16 | ${mixin.typography.headline3}
17 | `
18 | ```
19 |
--------------------------------------------------------------------------------
/src/Drawer/DrawerProvider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { DEFAULT_IDENTIFIER } from './constants'
3 |
4 | type Props = React.PropsWithChildren<{
5 | identifier?: string
6 | }>
7 |
8 | const DrawerProvider: React.FC = React.memo(
9 | ({ identifier = DEFAULT_IDENTIFIER, children }: Props) => (
10 | <>
11 | {children}
12 | >
13 | )
14 | )
15 |
16 | export default DrawerProvider
17 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/elevation.ts:
--------------------------------------------------------------------------------
1 | import { Elevation } from './types'
2 |
3 | const elevation: Elevation = {
4 | none: 'none',
5 | primary:
6 | '0 0 3px rgba(0,0,0,0.2), 0 0px 3px rgba(0,0,0,0.14), 0 0px 3px rgba(0,0,0,0.14)',
7 | secondary: '0 2px 5px rgba(42,42,42,0.1)',
8 | modal:
9 | '0px 10px 13px -6px rgba(0,0,0,0.2),0px 20px 31px 3px rgba(0,0,0,0.14),0px 8px 38px 7px rgba(0,0,0,0.12)',
10 | }
11 |
12 | export default elevation
13 |
--------------------------------------------------------------------------------
/src/ReactUtils/utils/getCurrentBreakpoint.ts:
--------------------------------------------------------------------------------
1 | import { Size, theme } from '@mycompany/design-system/Styleguide'
2 |
3 | export default (windowWidth: number): Size => {
4 | let currentBreakpoint = Size.Tiny
5 | Object.keys(Size).some((key: string) => {
6 | if (theme.breakpoint[Size[key]].plain() > windowWidth) {
7 | return true
8 | }
9 | currentBreakpoint = Size[key]
10 | return false
11 | })
12 |
13 | return currentBreakpoint
14 | }
15 |
--------------------------------------------------------------------------------
/src/Button/ButtonLabel.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {
5 | size: Size
6 | isLoading: boolean
7 | }
8 |
9 | export default styled.span`
10 | display: flex;
11 | flex-grow: 1;
12 | font-size: inherit;
13 | justify-content: center;
14 | opacity: ${({ isLoading }) => (isLoading ? '0.3' : '1')};
15 | text-align: center;
16 | white-space: nowrap;
17 | `
18 |
--------------------------------------------------------------------------------
/src/Button/mixin/elevation.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { ElevationLevel } from '@mycompany/design-system/Styleguide'
3 |
4 | export default ({
5 | theme,
6 | elevation,
7 | }: StyledProps<{ elevation: ElevationLevel }>) =>
8 | elevation !== ElevationLevel.None &&
9 | css`
10 | box-shadow: ${theme.elevation[elevation]};
11 |
12 | &:active {
13 | box-shadow: ${theme.elevation[elevation]};
14 | }
15 | `
16 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
4 |
5 | ## Approach
6 |
7 | _How does this change address the problem?_
8 |
9 | ## Related Tickets
10 |
11 | Related #(ticket or issue)
12 |
13 | ## Stakeholder
14 |
15 | **Approved**
16 |
17 | - [ ] UX
18 | - [ ] Design
19 | - [ ] Product
20 |
--------------------------------------------------------------------------------
/src/Icon/ChevronUpIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const ChevronUpIcon: React.FC = () => (
6 |
13 |
14 |
15 | )
16 |
17 | export default ChevronUpIcon
18 |
--------------------------------------------------------------------------------
/src/Navigation/index.ts:
--------------------------------------------------------------------------------
1 | import NavigationItem from './NavigationItem'
2 | import Navigation from './Navigation'
3 | import { NavigationFooterLink } from './NavigationFooterLink'
4 | import { NavigationHeaderLink } from './NavigationHeaderLink'
5 |
6 | const NavigationComponents = {
7 | NavigationItem,
8 | Navigation,
9 | }
10 |
11 | export {
12 | NavigationComponents,
13 | Navigation,
14 | NavigationItem,
15 | NavigationHeaderLink,
16 | NavigationFooterLink,
17 | }
18 |
--------------------------------------------------------------------------------
/src/Icon/ChevronDownIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const ChevronDownIcon: React.FC = () => (
6 |
13 |
14 |
15 | )
16 |
17 | export default ChevronDownIcon
18 |
--------------------------------------------------------------------------------
/src/Button/index.ts:
--------------------------------------------------------------------------------
1 | import Button from './Button'
2 | import ButtonRoot from './ButtonRoot'
3 | import ButtonLabel from './ButtonLabel'
4 | import ButtonAttachment from './ButtonAttachment'
5 | import ButtonLoadingIndicator from './ButtonLoadingIndicator'
6 | import { AttachmentPos } from './types'
7 |
8 | const ButtonComponents = {
9 | ButtonRoot,
10 | ButtonLabel,
11 | ButtonAttachment,
12 | ButtonLoadingIndicator,
13 | }
14 |
15 | export { Button, ButtonComponents, AttachmentPos }
16 |
--------------------------------------------------------------------------------
/src/Icon/ChevronRightIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const ChevronRightIcon: React.FC = () => (
6 |
13 |
14 |
15 | )
16 |
17 | export default ChevronRightIcon
18 |
--------------------------------------------------------------------------------
/src/Navigation/NavigationItemRoot.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {
5 | spacing?: Size
6 | }
7 |
8 | export default styled.li`
9 | align-items: center;
10 | color: ${({ theme }) => theme.color.font.hex()};
11 | display: flex;
12 | width: 100%;
13 | ${({ theme, spacing }) =>
14 | spacing &&
15 | `
16 | padding: ${theme.spacing.size[spacing].rem()} 0;
17 | `}
18 | `
19 |
--------------------------------------------------------------------------------
/src/Icon/SearchIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const SearchIcon: React.FC = () => (
6 |
13 |
14 |
15 |
16 | )
17 |
18 | export default SearchIcon
19 |
--------------------------------------------------------------------------------
/docs/preview/SpacingPreview.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SizeValue } from '@mycompany/design-system/Styleguide'
3 | import './preview.css'
4 |
5 | type Props = {
6 | spacing: SizeValue
7 | }
8 |
9 | const SpacingPreview: React.FC = ({ spacing }: Props) => (
10 |
11 |
12 | {spacing.rem()}
13 |
14 | {spacing.px()}
15 |
16 |
17 | )
18 |
19 | export default SpacingPreview
20 |
--------------------------------------------------------------------------------
/src/CircularProgress/index.ts:
--------------------------------------------------------------------------------
1 | import CircularProgress from './CircularProgress'
2 | import CircularProgressRoot from './CircularProgressRoot'
3 | import CircularProgressCircle from './CircularProgressCircle'
4 | import CircularProgressSVG from './CircularProgressSVG'
5 | import { State as CircularProgressState } from './types'
6 |
7 | const CircularProgressComponents = {
8 | CircularProgressRoot,
9 | CircularProgressCircle,
10 | CircularProgressSVG,
11 | }
12 |
13 | export { CircularProgress, CircularProgressComponents, CircularProgressState }
14 |
--------------------------------------------------------------------------------
/src/CssGrid/index.ts:
--------------------------------------------------------------------------------
1 | import CssGrid from './CssGrid'
2 | import { CssGridRoot } from './CssGridStyles'
3 | import {
4 | GridAlignContent,
5 | GridAlignItems,
6 | GridJustify,
7 | GridDirection,
8 | GridArea,
9 | GridColumn,
10 | GridAutoFlow,
11 | } from './types'
12 |
13 | const CssGridComponents = {
14 | CssGridRoot,
15 | }
16 | export {
17 | GridAlignContent,
18 | GridAlignItems,
19 | GridJustify,
20 | GridDirection,
21 | GridArea,
22 | GridColumn,
23 | GridAutoFlow,
24 | }
25 | export { CssGrid, CssGridComponents }
26 |
--------------------------------------------------------------------------------
/src/Button/mixin/index.ts:
--------------------------------------------------------------------------------
1 | import disabled from './disabled'
2 | import fullWidth from './fullWidth'
3 | import pill from './pill'
4 | import size from './size'
5 | import status from './status'
6 | import variant from './variant'
7 | import elevation from './elevation'
8 | import link from './link'
9 | import noBackground from './noBackground'
10 | import iconOnly from './iconOnly'
11 |
12 | export {
13 | disabled,
14 | fullWidth,
15 | pill,
16 | size,
17 | status,
18 | variant,
19 | elevation,
20 | link,
21 | noBackground,
22 | iconOnly,
23 | }
24 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Transition.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 | import theme from './'
3 | import { Easing, Duration } from '../types'
4 | import { SpacingPreview } from '../../../docs/preview'
5 |
6 |
7 |
8 | # Transition
9 |
10 | ## Easing
11 |
12 |
13 | {Object.keys(Easing).map((name) => (
14 | {name}
15 | ))}
16 |
17 |
18 | ## Duration
19 |
20 |
21 | {Object.keys(Duration).map((name) => (
22 | {name}
23 | ))}
24 |
25 |
--------------------------------------------------------------------------------
/src/Icon/CheckboxUncheckedIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const CheckboxUncheckedIcon: React.FC = ({ fill }: Props) => (
6 |
14 |
15 |
16 | )
17 |
18 | export default CheckboxUncheckedIcon
19 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/breakpoint.ts:
--------------------------------------------------------------------------------
1 | import { Size } from '../types'
2 | import { BreakpointSet } from './types'
3 | import { SizeValue } from '../value'
4 |
5 | // Keep the order from small to big as we do build prioritized maps to compare sizes
6 | const breakpoint: BreakpointSet = {
7 | [Size.Zero]: new SizeValue(0),
8 | [Size.Tiny]: new SizeValue(380),
9 | [Size.Small]: new SizeValue(768),
10 | [Size.Medium]: new SizeValue(960),
11 | [Size.Large]: new SizeValue(1280),
12 | [Size.ExtraLarge]: new SizeValue(1600),
13 | }
14 |
15 | export default breakpoint
16 |
--------------------------------------------------------------------------------
/src/Button/ButtonAttachment.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {
5 | size: Size
6 | isLoading: boolean
7 | }
8 |
9 | export default styled.span`
10 | display: flex;
11 | font-size: inherit;
12 | opacity: ${({ isLoading }) => (isLoading ? '0.5' : '1')};
13 | ${({ size }) =>
14 | (size === Size.Small || size === Size.Tiny) &&
15 | `width: 20px;
16 | svg {
17 | width: 100%;
18 | }
19 | `}
20 | &:only-child {
21 | padding: 0;
22 | }
23 | `
24 |
--------------------------------------------------------------------------------
/src/Icon/CheckboxCheckedIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const CheckboxCheckedIcon: React.FC = () => (
6 |
13 |
14 |
15 |
16 | )
17 |
18 | export default CheckboxCheckedIcon
19 |
--------------------------------------------------------------------------------
/src/Icon/HeartIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const HeartIcon: React.FC = ({ fill }: Props) => (
6 |
13 |
14 |
15 | )
16 |
17 | export default HeartIcon
18 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/spacing.ts:
--------------------------------------------------------------------------------
1 | import { SizeValue } from '../value'
2 | import { Spacing } from './types'
3 | import { Size } from '../types'
4 |
5 | const baseScale = new SizeValue(8)
6 |
7 | const baseZero = new SizeValue(0)
8 |
9 | const spacing: Spacing = {
10 | base: baseScale,
11 | size: {
12 | [Size.Zero]: baseZero,
13 | [Size.Tiny]: baseScale.multiply(0.25),
14 | [Size.Small]: baseScale.multiply(0.5),
15 | [Size.Medium]: baseScale,
16 | [Size.Large]: baseScale.multiply(2),
17 | [Size.ExtraLarge]: baseScale.multiply(4),
18 | },
19 | }
20 |
21 | export default spacing
22 |
--------------------------------------------------------------------------------
/src/Button/mixin/link.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 |
3 | export default ({
4 | link,
5 | theme,
6 | }: StyledProps<{
7 | link: boolean
8 | }>) =>
9 | link &&
10 | css`
11 | background: transparent;
12 | color: ${theme.color.variant.primary.base.hex()};
13 | padding-left: 0;
14 | padding-right: 0;
15 | &:hover {
16 | background: transparent;
17 | color: ${theme.color.variant.primary.hover.hex()};
18 | }
19 | &:active {
20 | background: transparent;
21 | color: ${theme.color.variant.primary.active.hex()};
22 | }
23 | `
24 |
--------------------------------------------------------------------------------
/src/Styleguide/index.ts:
--------------------------------------------------------------------------------
1 | import theme, { Theme, TypographyUnit } from './theme'
2 | import { ColorValue, SizeValue } from './value'
3 | import mixin from './mixin'
4 | import query from './query'
5 |
6 | export {
7 | Duration,
8 | Easing,
9 | FlexboxAlignment,
10 | FlexboxDirection,
11 | FlexboxJustification,
12 | FlexboxAlignContent,
13 | FlexboxAlignItems,
14 | FlexboxWrap,
15 | FontWeight,
16 | Size,
17 | Status,
18 | Variant,
19 | ElevationLevel,
20 | ObjectFit,
21 | } from './types'
22 | export { theme, mixin, query, TypographyUnit, ColorValue, SizeValue }
23 | export type { Theme }
24 |
--------------------------------------------------------------------------------
/src/Navigation/NavigationHeaderLink.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Size } from '../Styleguide'
3 | type Props = {
4 | spacing?: Size
5 | first?: boolean
6 | }
7 | export const NavigationHeaderLink = styled.a`
8 | border-bottom: 2px solid transparent;
9 | color: inherit;
10 |
11 | ${({theme, spacing}) => spacing && `
12 | padding: 0 ${theme.spacing.size[spacing].rem()};
13 | `}
14 | ${({first}) => first && `
15 | padding-left: 0;
16 | `}
17 | &:hover {
18 | border-bottom-color: ${({ theme }) =>
19 | theme.color.variant.primary.base.hex()};
20 | }
21 | `
22 |
--------------------------------------------------------------------------------
/src/ThemeProvider/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { ThemeProvider as StyledComponentsThemeProvider } from 'styled-components'
4 | import { theme } from '@mycompany/design-system/Styleguide'
5 |
6 | type Props = React.PropsWithChildren<{}>
7 |
8 | const StyleProvider: React.FC = ({ children }: Props) => (
9 |
10 | {children}
11 |
12 | )
13 |
14 | StyleProvider.propTypes = {
15 | children: PropTypes.node.isRequired,
16 | }
17 |
18 | export default StyleProvider
19 |
--------------------------------------------------------------------------------
/src/Icon/UserIcon.tsx:
--------------------------------------------------------------------------------
1 | import { SvgIcon } from '@mycompany/design-system/Icon/base'
2 | import React, { FC } from 'react'
3 | import { IconName, Props } from './types'
4 |
5 | const UserIcon: FC = ({ fill }) => (
6 |
15 |
16 |
17 |
18 | )
19 |
20 | export default UserIcon
21 |
--------------------------------------------------------------------------------
/src/Hidden/Hidden.tsx:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import { Size } from '../Styleguide'
3 |
4 | type HiddenProps = {
5 | up?: Size | false
6 | down?: Size | false
7 | }
8 |
9 | const Hidden = styled.div`
10 | ${({ up, theme }) =>
11 | up &&
12 | css`
13 | @media (min-width: ${theme.breakpoint[up].px()}) {
14 | display: none;
15 | }
16 | `}
17 |
18 | ${({ down, theme }) =>
19 | down &&
20 | css`
21 | @media (max-width: ${theme.breakpoint[down].px()}) {
22 | display: none;
23 | }
24 | `}
25 | `
26 |
27 | export default Hidden
--------------------------------------------------------------------------------
/src/Icon/BurgerIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const BurgerIcon: React.FC = ({ fill }: Props) => (
6 |
15 |
16 |
17 |
18 |
19 | )
20 |
21 | export default BurgerIcon
22 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Shape.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 | import theme from './'
3 |
4 |
5 |
6 |
15 |
16 | # Shape
17 |
18 | ## Border Radius
19 |
20 |
21 | Global Border radius is {theme.shape.borderRadius.rem()} or{' '}
22 | {theme.shape.borderRadius.px()}
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Icon/base/IconHover.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size, Variant } from '@mycompany/design-system/Styleguide'
3 | import IconHoverRoot from './IconHoverRoot'
4 |
5 | type Props = {
6 | spacing?: Size | 0
7 | children?: React.ReactNode
8 | variant?: Variant
9 | }
10 |
11 | const defaultSpacing = Size.Medium
12 | const defaultVariant = Variant.Secondary
13 |
14 | const IconHover: React.FC = ({
15 | children,
16 | variant = defaultVariant,
17 | spacing = defaultSpacing,
18 | }: Props) => (
19 |
20 | {children}
21 |
22 | )
23 |
24 | export default IconHover
25 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Breakpoint.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 | import theme from './'
3 |
4 |
5 |
6 | # Breakpoint
7 |
8 | Breakpoints are always handled with px units to not interfere with the rem baseFont behavior and prevent flickering
9 |
10 |
11 |
12 |
13 | Name
14 | Size
15 |
16 |
17 |
18 | {Object.keys(theme.breakpoint).map((name) => (
19 |
20 | {name}
21 | {theme.breakpoint[name].px()}
22 |
23 | ))}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/Introduction.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 | import packageJson from '../package.json'
3 |
4 |
5 |
6 |
7 |
Version: {packageJson.version}
8 |
9 |
10 |
11 | The mycompany Design System was created for more efficiently. The Design
12 | System is the source of truth for Developer and Designer for Build
13 | consistency and quality Components for a better Userfaces.
14 |
15 |
16 | First Step please read Getting Started and read the Documentation for your
17 | division (Design/Develop)
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/index.ts:
--------------------------------------------------------------------------------
1 | import color from './color'
2 | import shape from './shape'
3 | import palette from './palette'
4 | import spacing from './spacing'
5 | import transition from './transition'
6 | import typography from './typography'
7 | import elevation from './elevation'
8 | import breakpoint from './breakpoint'
9 | import zIndex from './zIndex'
10 | import { Theme, TypographyUnit } from './types'
11 |
12 | const theme: Theme = {
13 | color,
14 | shape,
15 | typography,
16 | spacing,
17 | palette,
18 | transition,
19 | elevation,
20 | breakpoint,
21 | zIndex,
22 | }
23 |
24 | export type { Theme }
25 | export { TypographyUnit }
26 | export default theme
27 |
--------------------------------------------------------------------------------
/docs/preview/ColorPreview.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ColorValue } from '@mycompany/design-system/Styleguide'
3 | import './preview.css'
4 |
5 | type Props = {
6 | color: ColorValue
7 | }
8 |
9 | const ColorPreview: React.FC = ({ color }: Props) => (
10 |
11 |
16 |
17 |
18 |
19 | )
20 |
21 | export default ColorPreview
22 |
--------------------------------------------------------------------------------
/src/Drawer/index.ts:
--------------------------------------------------------------------------------
1 | import Drawer from './Drawer'
2 | import DrawerProvider from './DrawerProvider'
3 | import DrawerRoot from './DrawerRoot'
4 | import DrawerContent from './DrawerContent'
5 | import DrawerOverlay from './DrawerOverlay'
6 | import DrawerTitleRoot from './DrawerTitleRoot'
7 | import DrawerTitleContent from './DrawerTitleContent'
8 | import DrawerTitleClose from './DrawerTitleClose'
9 | import { DrawerOrientation } from './types'
10 |
11 | const DrawerComponents = {
12 | DrawerRoot,
13 | DrawerContent,
14 | DrawerOverlay,
15 | DrawerTitleRoot,
16 | DrawerTitleContent,
17 | DrawerTitleClose,
18 | }
19 |
20 | export { Drawer, DrawerProvider, DrawerComponents, DrawerOrientation }
21 |
--------------------------------------------------------------------------------
/src/ReactUtils/utils/breakpointComparison.ts:
--------------------------------------------------------------------------------
1 | import { Size } from '@mycompany/design-system/Styleguide'
2 |
3 | type BreakpointValueMap = { [key in Size]: number }
4 |
5 | const breakpointValueMap: BreakpointValueMap = Object.values(Size).reduce(
6 | (accumulator: BreakpointValueMap, currentSize: Size, index: number) => ({
7 | [currentSize]: index,
8 | ...accumulator,
9 | }),
10 | {} as BreakpointValueMap
11 | )
12 |
13 | export const isBreakpointGreaterThan = (size: Size, comparison: Size) =>
14 | breakpointValueMap[size] > breakpointValueMap[comparison]
15 |
16 | export const isBreakpointSmallerThan = (size: Size, comparison: Size) =>
17 | breakpointValueMap[size] < breakpointValueMap[comparison]
18 |
--------------------------------------------------------------------------------
/src/Drawer/DrawerRoot.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { animated } from 'react-spring'
3 | import { ElevationLevel, Size } from '@mycompany/design-system/Styleguide'
4 |
5 | type Props = {
6 | spacing?: Size
7 | elevation: ElevationLevel
8 | }
9 |
10 | export default styled(animated.div)`
11 | background-color: ${({ theme }) => theme.color.contentBackground.hex()};
12 | box-shadow: ${({ theme, elevation }) => theme.elevation[elevation]};
13 | display: flex;
14 | flex-direction: column;
15 | flex-grow: 1;
16 | max-width: 320px;
17 | width: 90vw;
18 | ${({ spacing, theme }) =>
19 | spacing &&
20 | `
21 | padding: ${theme.spacing.size[spacing].rem()};
22 | `}
23 | `
24 |
--------------------------------------------------------------------------------
/src/Icon/base/IconHoverRoot.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Size, Variant } from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {
5 | spacing: Size | 0
6 | variant: Variant
7 | }
8 | export default styled.span`
9 | ${({ theme, variant }) => `
10 | display: inline-block;
11 | padding: 10px;
12 | color: ${
13 | variant === Variant.Primary
14 | ? theme.color.variant[variant].base.hex()
15 | : theme.color.variant[variant].font.hex()
16 | };
17 | &:hover {
18 | border-radius: 50%;
19 | color: ${theme.color.variant[variant].font.hex()};
20 | background: ${theme.color.variant.secondary.base.hex()};
21 | }
22 | `}
23 | `
24 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/palette.ts:
--------------------------------------------------------------------------------
1 | import { ColorValue } from '../value'
2 | import { Palette } from './types'
3 |
4 | const palette: Palette = {
5 | patone: ColorValue('#7d9917'),
6 | white: ColorValue('#ffffff'),
7 | nero: ColorValue('#2A2A2A'),
8 | dim: ColorValue('#696969'),
9 | hillary: ColorValue('#afa88d'),
10 | alabaster: ColorValue('#F1F0EB'),
11 | raynolds: ColorValue('#990000'),
12 | pyroman: ColorValue('#BF5700'),
13 | peach: ColorValue('#E1B12C'),
14 | maverick: ColorValue('#C4BFAB'),
15 | lighten: ColorValue('#d3d3d3'),
16 | // New status colors
17 | soda: ColorValue('#F55D3E'),
18 | kelly: ColorValue('#43B929'),
19 | honey: ColorValue('#F6AE2D'),
20 | }
21 |
22 | export default palette
23 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/transition.ts:
--------------------------------------------------------------------------------
1 | import { Easing, Duration } from '../types'
2 | import { Transition } from './types'
3 |
4 | const transition: Transition = {
5 | easing: {
6 | [Easing.EaseIn]: 'cubic-bezier(0.4, 0, 1, 1)',
7 | [Easing.EaseInOut]: 'cubic-bezier(0.4, 0, 0.2, 1)',
8 | [Easing.EaseOut]: 'cubic-bezier(0.0, 0, 0.2, 1)',
9 | [Easing.Sharp]: 'cubic-bezier(0.4, 0, 0.6, 1)',
10 | },
11 | duration: {
12 | [Duration.Shortest]: 150,
13 | [Duration.Shorter]: 200,
14 | [Duration.Short]: 250,
15 | [Duration.Default]: 300,
16 | [Duration.Complex]: 375,
17 | [Duration.EnteringScreen]: 225,
18 | [Duration.LeavingScreen]: 195,
19 | },
20 | }
21 |
22 | export default transition
23 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Button'
2 | export * from './ButtonGroup'
3 | export * from './Container'
4 | export * from './ContentBox'
5 | export * from './CssBaseline'
6 | export * from './CssGrid'
7 | export * from './Drawer'
8 | export * from './CircularProgress'
9 | export * from './Divider'
10 | export * from './Grid'
11 | export * from './Hidden'
12 | export * from './Icon'
13 | export * from './Image'
14 | export * from './Layout'
15 | export * from './Navigation'
16 | export * from './ReactUtils'
17 | export * from './Stack'
18 | export * from './Styleguide'
19 | export * from './TextLink'
20 | export * from './ThemeProvider'
21 | export * from './Typography'
22 | export * from './locales'
23 | export * from './Price'
24 |
--------------------------------------------------------------------------------
/src/Icon/CloseIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const CloseIcon: React.FC = ({ fill }: Props) => (
6 |
12 |
13 |
20 |
27 |
28 | )
29 |
30 | export default CloseIcon
31 |
--------------------------------------------------------------------------------
/src/Button/mixin/size.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 |
4 | export default ({ theme, size }: StyledProps<{ size: Size }>) => css`
5 | ${size === Size.Zero &&
6 | `
7 | font-size: ${theme.typography.size.small.rem()};
8 | padding: 0;
9 | `}
10 | ${(size === Size.Small || size === Size.Tiny) &&
11 | `
12 | font-size: ${theme.typography.size.small.rem()};
13 | `}
14 |
15 | ${size === Size.Medium &&
16 | `
17 | font-size: ${theme.typography.size.medium.rem()};
18 | `}
19 |
20 | ${(size === Size.Large || size === Size.ExtraLarge) &&
21 | `
22 | font-size: ${theme.typography.size.large.rem()};
23 | `}
24 | `
25 |
--------------------------------------------------------------------------------
/src/Button/mixin/variant.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Variant } from '@mycompany/design-system/Styleguide'
3 |
4 | export default ({
5 | theme,
6 | variant,
7 | disabled,
8 | isLoading,
9 | }: StyledProps<{
10 | variant: Variant
11 | isLoading: boolean
12 | disabled: boolean
13 | }>) => css`
14 | background: ${theme.color.variant[variant].base.hex()};
15 | color: ${theme.color.variant[variant].font.hex()};
16 |
17 | ${!isLoading &&
18 | !disabled &&
19 | `
20 | &:hover {
21 | background: ${theme.color.variant[variant].hover.hex()};
22 | }
23 |
24 | &:active {
25 | background: ${theme.color.variant[variant].active.hex()};
26 | }
27 | `}
28 | `
29 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Theme.stories.mdx:
--------------------------------------------------------------------------------
1 | import ReactJson from 'react-json-view'
2 | import { Meta } from '@storybook/addon-docs'
3 | import { clearThemeTypes } from '../../../docs/preview'
4 | import {
5 | theme,
6 | ColorValue,
7 | SizeValue,
8 | } from '@mycompany/design-system/Styleguide'
9 |
10 |
11 |
12 | # Theme
13 |
14 | The theme contains all styleguide information and makes them accessable in the code all over the ecosystem
15 |
16 | ## Explorer
17 |
18 | {
24 | return field.src instanceof ColorValue || field.src instanceof SizeValue
25 | }}
26 | />
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | #### 0.0.1 (2022-04-07)
2 |
3 | ##### New Features
4 |
5 | - **BC-280:**
6 | - icon fix ([0e8d8853](https://github.com/mycompany/design-system/commit/0e8d8853587a59cd65ddcc2fa35bf3eb05e98e9b))
7 | - adding new icons ([cf78d1cb](https://github.com/mycompany/design-system/commit/cf78d1cbde23583da84c17f984f6c3d3a05df17f))
8 |
9 | ##### Other Changes
10 |
11 | - **migrate:** badge ([#140](https://github.com/mycompany/design-system/pull/140)) ([ffb76feb](https://github.com/mycompany/design-system/commit/ffb76feba5c12272fee848bba9a94c81c0fa75cb))
12 | - Gcloud Action Version Upgrade ([#139](https://github.com/mycompany/design-system/pull/139)) ([c770468e](https://github.com/mycompany/design-system/commit/c770468e14561661fdb92c754c19c9f148cc6ab4))
13 |
--------------------------------------------------------------------------------
/src/Navigation/NavigationItem.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useTranslation } from 'react-i18next'
3 | import PropTypes from 'prop-types'
4 | import { Size } from '@mycompany/design-system/Styleguide'
5 | import NavigationItemRoot from './NavigationItemRoot'
6 |
7 | type Props = React.PropsWithChildren<{
8 | spacing?: Size
9 | }>
10 |
11 | const NavigationItem: React.FC = ({ children, spacing }) => {
12 | const { t } = useTranslation()
13 | return (
14 |
15 | {typeof children === 'string' ? t(children) : children}
16 |
17 | )
18 | }
19 |
20 | NavigationItem.propTypes = {
21 | children: PropTypes.node.isRequired,
22 | }
23 |
24 | export default NavigationItem
25 |
--------------------------------------------------------------------------------
/src/Navigation/Navigation.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 | import NavigationRoot from './NavigationRoot'
4 |
5 | type Props = {
6 | orientation?: 'horizontal' | 'vertical'
7 | spacing?: Size | false
8 | size?: Size | false
9 | children: React.ReactNode
10 | }
11 |
12 | const defaultSpacing = Size.Medium
13 | const defaultSize = Size.Medium
14 |
15 | const Navigation = ({
16 | spacing = defaultSpacing,
17 | size = defaultSize,
18 | orientation,
19 | children,
20 | }: Props) => (
21 |
26 | {children}
27 |
28 | )
29 |
30 | export default Navigation
31 |
--------------------------------------------------------------------------------
/src/Button/mixin/status.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps } from 'styled-components'
2 | import { Status } from '@mycompany/design-system/Styleguide'
3 |
4 | export default ({
5 | theme,
6 | status,
7 | isLoading,
8 | disabled,
9 | }: StyledProps<{ status: Status; isLoading: boolean; disabled: boolean }>) =>
10 | status &&
11 | status !== Status.Default &&
12 | css`
13 | background: ${theme.color.status[status].base.hex()};
14 | color: ${theme.color.status[status].font.hex()};
15 |
16 | ${!isLoading &&
17 | !disabled &&
18 | `
19 | &:hover {
20 | background: ${theme.color.status[status].hover.hex()};
21 | }
22 |
23 | &:active {
24 | background: ${theme.color.status[status].active.hex()};
25 | }
26 | `}
27 | `
28 |
--------------------------------------------------------------------------------
/src/CircularProgress/CircularProgressCircle.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import { State } from './types'
3 | import circularDashKeyframe from './keyframe/circularDashKeyframe'
4 |
5 | type Props = {
6 | state: State
7 | }
8 |
9 | export default styled.circle`
10 | stroke: currentColor;
11 |
12 | ${({ state, theme }) =>
13 | state === State.Determinate &&
14 | css`
15 | transition: transform ${theme.transition.duration.default}
16 | ${theme.transition.easing.sharp};
17 | `}
18 |
19 | ${({ state }) =>
20 | state === State.Indeterminate &&
21 | css`
22 | animation: ${circularDashKeyframe} 1.4s ease-in-out infinite;
23 | stroke-dasharray: 80px, 200px;
24 | stroke-dashoffset: 0;
25 | `}
26 | `
27 |
--------------------------------------------------------------------------------
/src/Icon/base/SvgIconRoot.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | type Props = {
4 | title: string
5 | payments?: boolean
6 | social?: boolean
7 | width?: string
8 | height?: string
9 | }
10 |
11 | export default styled.svg`
12 | display: inline-flex;
13 | flex-shrink: 0;
14 | font-size: 24px;
15 | user-select: none;
16 | vertical-align: middle;
17 | ${({width}) => !width &&`
18 | width: 1em;
19 | `}
20 | ${({height}) => !height &&`
21 | height: 1em;
22 | `}
23 | ${({ social }) =>
24 | social &&
25 | `
26 | height: 1.9em;
27 | width: 1.9em;
28 | `}
29 | ${({ payments }) =>
30 | payments &&
31 | `
32 | height: 1.2em;
33 | width: 100%;
34 | `}
35 | &:focus {
36 | outline: 0;
37 | }
38 | `
39 |
--------------------------------------------------------------------------------
/src/Icon/types.ts:
--------------------------------------------------------------------------------
1 | export enum IconName {
2 | ChevronDown = 'chevronDown',
3 | Heart = 'heart',
4 | Search = 'search',
5 | Burger = 'burger',
6 | Cart = 'cart',
7 | Close = 'close',
8 | User = 'user',
9 | CheckboxUncheckedIcon = 'checkboxUncheckedIcon',
10 | CheckboxCheckedIcon = 'checkboxCheckedIcon',
11 | ChevronRightIcon = 'chevronRightIcon',
12 | ChevronUpIcon = 'chevronUpIcon',
13 | BankTransfer = 'bankTransfer',
14 | GiroPay = 'giropay',
15 | Visa = 'visa',
16 | MasterCard = 'masterCard',
17 | Amex = 'amex',
18 | SofortKlarna = 'sofortKlarna',
19 | TwitterIcon = 'twitter',
20 | FaceBookIcon = 'facebook',
21 | InstagramIcon = 'instagram',
22 | PinterestIcon = 'pinterest',
23 | GiftIcon = 'giftIcon'
24 | }
25 |
26 | export type Props = {
27 | fill?: boolean
28 | social?: boolean
29 | payments?: boolean
30 | }
31 |
--------------------------------------------------------------------------------
/docs/preview/clearThemeTypes.ts:
--------------------------------------------------------------------------------
1 | import { ColorValue, SizeValue } from '../../src/Styleguide/value'
2 |
3 | const iterate = (theme) => {
4 | // eslint-disable-next-line no-param-reassign
5 | theme = { ...theme }
6 | Object.keys(theme).forEach((key) => {
7 | if (theme[key] instanceof SizeValue) {
8 | // eslint-disable-next-line no-param-reassign
9 | theme[key] = `SizeValue(${theme[key].px()} | ${theme[key].rem()})`
10 | } else if (theme[key] instanceof ColorValue) {
11 | // eslint-disable-next-line no-param-reassign
12 | theme[key] = `ColorValue(${theme[key].rgb()} | ${theme[key].hex()})`
13 | } else if (typeof theme[key] === 'object') {
14 | // eslint-disable-next-line no-param-reassign
15 | theme[key] = iterate(theme[key])
16 | }
17 | })
18 |
19 | return theme
20 | }
21 |
22 | export default iterate
23 |
--------------------------------------------------------------------------------
/src/Image/Image.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ObjectFit, Size } from '@mycompany/design-system/Styleguide'
3 |
4 | import ImageRoot from './ImageRoot'
5 |
6 | type Props = React.PropsWithChildren<{
7 | spacing?: Size
8 | src: string
9 | className?: string
10 | objectFit?: ObjectFit
11 | }>
12 |
13 | const defaultSpacing = Size.Medium
14 |
15 | const Image: React.FC = React.forwardRef(
16 | (
17 | {
18 | src,
19 | spacing = defaultSpacing,
20 | className,
21 | objectFit,
22 | }: React.PropsWithChildren,
23 | ref
24 | ) => (
25 |
33 | )
34 | )
35 |
36 | export default Image
37 |
--------------------------------------------------------------------------------
/src/Styleguide/value/SizeValue.ts:
--------------------------------------------------------------------------------
1 | import theme from '../theme'
2 |
3 | class SizeValue {
4 | value: number
5 |
6 | constructor(value: number) {
7 | this.value = value
8 | }
9 |
10 | px(): string {
11 | return `${Math.round(this.value)}px`
12 | }
13 |
14 | rem(): string {
15 | return `${this.value / theme.typography.base.fontSize.plain()}rem`
16 | }
17 |
18 | plain(): number {
19 | return this.value
20 | }
21 |
22 | divide(divisor: number): SizeValue {
23 | return new SizeValue(this.value / divisor)
24 | }
25 |
26 | multiply(multiplier: number): SizeValue {
27 | return new SizeValue(this.value * multiplier)
28 | }
29 |
30 | add(value: number): SizeValue {
31 | return new SizeValue(this.value + value)
32 | }
33 |
34 | toString(): string {
35 | return this.px()
36 | }
37 | }
38 |
39 | export default SizeValue
40 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Spacing.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 | import theme from './'
3 | import { Size } from '../types'
4 | import { SpacingPreview } from '../../../docs/preview'
5 |
6 |
7 |
8 | # Spacing
9 |
10 | All spacing should have the same base as multiplier
11 |
12 | ## Base Spacing
13 |
14 |
15 |
16 | ## Size: Tiny
17 |
18 |
19 |
20 | ## Size: Small
21 |
22 |
23 |
24 | ## Size: Medium
25 |
26 |
27 |
28 | ## Size: Large
29 |
30 |
31 |
32 | ## Size: ExtraLarge
33 |
34 |
35 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.projectKey=mycompany_design-system
2 | sonar.organization=mycompany
3 |
4 | # This is the name and version displayed in the SonarCloud UI.
5 | sonar.projectName=design-system
6 | #sonar.projectVersion=1.0
7 |
8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
9 | sonar.sources=.
10 |
11 | # Encoding of the source code. Default is default system encoding
12 | sonar.sourceEncoding=UTF-8
13 |
14 | sonar.exclusions=spec/**/*,test/**/*,db/**/*,public/**/*,.github/**/*,bin/**/*,config/**/*,deployment/**/*,deployment-review/**/*,script/**/*,vendor/**/*,**/*.js
15 |
16 | sonar.coverage.exclusions=docs**/*,build/**/*,pages/**/*,spec/**/*,test/**/*,db/**/*,public/**/*,.github/**/*,bin/**/*,config/**/*,deployment/**/*,deployment-review/**/*,script/**/*,vendor/**/*,**/*.js,**/*.stories.tsx
17 |
18 | sonar.javascript.lcov.reportPaths=./coverage/merged/lcov.info
--------------------------------------------------------------------------------
/src/Image/Image.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Image } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Size } from '../Styleguide'
5 |
6 | export default {
7 | /* 👇 The title prop is optional.
8 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
9 | * to learn how to generate automatic titles
10 | */
11 | title: 'Component/Image',
12 | component: Image,
13 | argTypes: {
14 | spacing: {
15 | options: Size,
16 | control: { type: 'radio' },
17 | },
18 | src: {
19 | control: { type: 'string' },
20 | },
21 | },
22 | } as ComponentMeta
23 |
24 | const Template: ComponentStory = (args) =>
25 |
26 | export const Introduction: ComponentStory = Template.bind({})
27 |
28 | Introduction.args = {
29 | src: '#',
30 | }
31 |
--------------------------------------------------------------------------------
/src/ThemeProvider/ThemeProvider.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 |
3 |
4 |
5 | # ThemeProvider
6 |
7 | ## Styled Components
8 |
9 | Please use the `StyleProvider` to enable the global theme in styled-components.
10 |
11 | ```tsx
12 | import ThemeProvider from '@mycompany/design-system/ThemeProvider'
13 |
14 | const MyApp = (
15 |
16 |
17 |
18 | )
19 | ```
20 |
21 | Accessing the theme is done by the default styled-component context behavior https://styled-components.com/docs/advanced#theming.
22 |
23 | ```tsx
24 | import styled from 'styled-components'
25 |
26 | export default styled.button`
27 | border-radius: ${({ theme }) => theme.shape.borderRadius.rem()};
28 | font-weight: ${(props) => props.theme.typography.fontWeight.regular};
29 | margin: ${(props) => props.theme.spacing.size.medium.rem()};
30 | `
31 | ```
32 |
--------------------------------------------------------------------------------
/src/Icon/PinterestIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const PinterestIcon: React.FC = ({social}:Props) => (
6 |
13 |
17 |
18 | )
19 |
20 | export default PinterestIcon
21 |
--------------------------------------------------------------------------------
/src/Layout/index.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Canvas, Meta, Story } from '@storybook/addon-docs'
2 | import { Layout, LayoutItem } from '.'
3 | import { argTypesControl } from '../../docs/utils/arg-types-control'
4 |
5 |
6 |
7 | # Layout
8 |
9 | ## Example
10 |
11 |
12 |
13 | {(args) => (
14 |
15 |
16 |
17 | )}
18 |
19 |
20 |
21 | ## LayoutItem
22 |
23 | ### Example
24 |
25 |
26 |
35 | {(args) => (
36 |
37 |
38 |
39 | )}
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/ButtonGroup/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size, FlexboxJustification } from '@mycompany/design-system/Styleguide'
3 | import ButtonGroupRoot from './ButtonGroupRoot'
4 |
5 | type Props = React.PropsWithChildren<{
6 | justify?: FlexboxJustification
7 | squashed?: boolean
8 | spacing?: Size | false
9 | }>
10 |
11 | const defaultJustify = FlexboxJustification.FlexStart
12 | const defaultSpacing = Size.Medium
13 |
14 | const ButtonGroup = React.forwardRef(
15 | (
16 | {
17 | children,
18 | squashed = false,
19 | justify = defaultJustify,
20 | spacing = defaultSpacing,
21 | }: Props,
22 | ref
23 | ) => (
24 |
30 | {children}
31 |
32 | )
33 | )
34 |
35 | export default ButtonGroup
36 |
--------------------------------------------------------------------------------
/src/Icon/TwitterIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const TwitterIcon: React.FC = ({social}:Props) => (
6 |
13 |
14 |
18 |
19 | )
20 |
21 | export default TwitterIcon
22 |
--------------------------------------------------------------------------------
/src/Container/Container.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 | import { Behavior } from './types'
4 | import ContainerRoot from './ContainerRoot'
5 |
6 | type Props = React.PropsWithChildren<{
7 | behavior?: Behavior
8 | spacing?: Size | object | string
9 | component?: keyof JSX.IntrinsicElements
10 | }>
11 |
12 | const defaultBehavior = Behavior.Fluid
13 | const defaultSpacing = Size.Large
14 |
15 | const Container: React.FC = React.forwardRef(
16 | (
17 | {
18 | behavior = defaultBehavior,
19 | spacing = defaultSpacing,
20 | component = 'div',
21 | children,
22 | }: Props,
23 | ref
24 | ) => (
25 |
31 | {children}
32 |
33 | )
34 | )
35 |
36 | export default Container
37 |
--------------------------------------------------------------------------------
/docs/preview/preview.css:
--------------------------------------------------------------------------------
1 | .color-preview {
2 | text-align: right;
3 | padding: 16px;
4 | margin: 8px;
5 | border: 3px solid #333333;
6 | }
7 |
8 | .value-picker {
9 | margin: 8px;
10 | border: 0;
11 | border-radius: 0;
12 | resize: none;
13 | padding: 4px 8px;
14 | color: #ffffff;
15 | background: rgba(0, 0, 0, 0.5);
16 | }
17 |
18 | .spacing-preview {
19 | width: 5rem;
20 | height: 5rem;
21 | background: #7d9917;
22 | display: block;
23 | color: #ffffff;
24 | text-align: center;
25 | line-height: 2.5rem;
26 | font-family: 'Rubik';
27 | }
28 |
29 | .spacing-preview-container {
30 | background-size: 8px 8px;
31 | background-image: linear-gradient(to right, #333333 1px, transparent 1px),
32 | linear-gradient(to bottom, #333333 1px, transparent 1px);
33 | display: inline-block;
34 | border-left: 2px solid #333333;
35 | border-top: 2px solid #333333;
36 | border-right: 3px solid #333333;
37 | border-bottom: 3px solid #333333;
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/size_labels.yaml:
--------------------------------------------------------------------------------
1 | name: PR size labeler
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - master
7 | - main
8 | types:
9 | - opened
10 | - review_requested
11 | jobs:
12 | size-labels:
13 | runs-on: ubuntu-latest
14 | name: Label the PR size
15 | steps:
16 | # https://github.com/CodelyTV/pr-size-labeler
17 | - uses: codelytv/pr-size-labeler@v1.7.0
18 | with:
19 | IGNORED: ".*\n!.gitignore\npackage-lock.json"
20 | GITHUB_TOKEN: ${{ github.token }}
21 | xs_max_size: '10'
22 | s_max_size: '100'
23 | m_max_size: '500'
24 | l_max_size: '1000'
25 | fail_if_xl: 'false'
26 | message_if_xl: >
27 | 'This PR exceeds the recommended size of 1000 lines.
28 | Please make sure you are NOT addressing multiple issues with one PR.
29 | Note this PR might be rejected due to its size.’
30 | github_api_url: 'api.github.com'
31 |
--------------------------------------------------------------------------------
/src/ContentBox/ContentBox.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ContentBox } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Size } from '../Styleguide'
5 |
6 | export default {
7 | /* 👇 The title prop is optional.
8 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
9 | * to learn how to generate automatic titles
10 | */
11 | title: 'Component/ContentBox',
12 | component: ContentBox,
13 | argTypes: {
14 | spacing: {
15 | options: Size,
16 | control: { type: 'radio' },
17 | },
18 | border: {
19 | control: { type: 'boolean' },
20 | },
21 | },
22 | } as ComponentMeta
23 |
24 | const Template: ComponentStory = (args) => (
25 |
26 | contentBox
27 |
28 | )
29 |
30 | export const Introduction: ComponentStory = Template.bind({})
31 |
32 | Introduction.args = { border: true }
33 |
--------------------------------------------------------------------------------
/src/Layout/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import styled from 'styled-components'
3 | import { ElevationLevel, Size, Variant } from '../Styleguide'
4 |
5 | type LayoutItemProps = {
6 | elevationLevel?: ElevationLevel
7 | spacing?: Size
8 | stackLevel?: number
9 | variant?: Variant
10 | }
11 |
12 | export const Layout: FC = ({ children }) => <>{children}>
13 |
14 | export const LayoutItem: FC = styled.div`
15 | background-color: ${({ theme: { color }, variant }) =>
16 | (variant && color.variant[variant].base.hex()) ||
17 | color.contentBackground.hex()};
18 | box-shadow: ${({ elevationLevel, theme }) =>
19 | typeof elevationLevel === 'string' && theme.elevation[elevationLevel]};
20 | padding: ${({ spacing, theme }) =>
21 | spacing && theme.spacing.size[spacing].px()}
22 | 0;
23 | position: ${({ stackLevel }) => typeof stackLevel === 'number' && 'relative'};
24 | z-index: ${({ stackLevel }) => typeof stackLevel === 'number' && stackLevel};
25 | `
26 |
--------------------------------------------------------------------------------
/src/Icon/FacebookIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const FaceBookIcon: React.FC = ({social}:Props) => (
6 |
13 |
14 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 |
31 | export default FaceBookIcon
32 |
--------------------------------------------------------------------------------
/src/Icon/Amex.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const Amex: React.FC = ({ payments }: Props) => (
6 |
13 |
17 |
18 | )
19 |
20 | export default Amex
21 |
--------------------------------------------------------------------------------
/src/Divider/Divider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 | import DividerRoot from './DividerRoot'
4 | import { Orientation, Display } from './types'
5 |
6 | type Props = {
7 | orientation?: Orientation
8 | flexItem?: boolean
9 | display?: Display
10 | spacing?: Size
11 | inverted?: boolean
12 | }
13 |
14 | const defaultOrientation = Orientation.Horizontal
15 | const defaultDisplay = Display.FullWidth
16 | const defaultSpacing = Size.Medium
17 |
18 | const Divider: React.FC = React.forwardRef(
19 | (
20 | {
21 | orientation = defaultOrientation,
22 | flexItem = false,
23 | display = defaultDisplay,
24 | spacing = defaultSpacing,
25 | inverted = false,
26 | }: Props,
27 | ref
28 | ) => (
29 |
37 | )
38 | )
39 |
40 | export default Divider
41 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Elevation.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 | import theme from './'
3 |
4 |
5 |
6 |
17 |
18 | # Elevation
19 |
20 | We do have Primary, Secondary and Modal of elevation.
21 |
22 | Primary show a shadow left,right,top and bottom
23 | Secondary show a shadow only bottom such as Navbar
24 |
25 | Each component that has an elevation effect (box-shadow) should be configurable with an elevation property.
26 |
27 | Default = 0 elevation
28 |
29 | ## Preview
30 |
31 | <>
32 | {Object.keys(theme.elevation).map((name) => (
33 |
34 |
38 | {name}
39 |
40 |
41 | ))}
42 | >
43 |
--------------------------------------------------------------------------------
/src/Drawer/DrawerOverlay.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { animated } from 'react-spring'
3 | import { Size, query } from '@mycompany/design-system/Styleguide'
4 | import { DrawerOrientation } from './types'
5 |
6 | type Props = {
7 | closeable: boolean
8 | orientation: DrawerOrientation
9 | }
10 |
11 | export default styled(animated.div)`
12 | align-content: stretch;
13 | align-items: stretch;
14 | background-color: rgba(0, 0, 0, 0.3);
15 | bottom: 0;
16 | cursor: ${({ closeable }) => (closeable ? 'pointer' : 'default')};
17 | display: flex;
18 | justify-content: stretch;
19 | left: 0;
20 | position: fixed;
21 | right: 0;
22 | top: 0;
23 | z-index: ${({ theme }) => theme.zIndex.drawer};
24 |
25 | &:active,
26 | &:focus {
27 | outline: none;
28 | }
29 |
30 | ${query.min(Size.Small)`
31 | align-content: ${({ orientation }: Props) =>
32 | orientation === DrawerOrientation.Left ? 'flex-start' : 'flex-end'};
33 | align-items: stretch;
34 | justify-content: ${({ orientation }: Props) =>
35 | orientation === DrawerOrientation.Left ? 'flex-start' : 'flex-end'};
36 | `}
37 | `
38 |
--------------------------------------------------------------------------------
/src/Icon/CartIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const CartIcon: React.FC = ({ fill }: Props) => (
6 |
14 |
15 |
16 |
17 |
18 | )
19 |
20 | export default CartIcon
21 |
--------------------------------------------------------------------------------
/src/ContentBox/ContentBox.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size, Variant } from '@mycompany/design-system/Styleguide'
3 | import { Opacity } from '@mycompany/design-system/index'
4 |
5 | import ContentBoxRoot from './ContentBoxRoot'
6 |
7 | type Props = React.PropsWithChildren<{
8 | background?: Variant
9 | spacing?: Size
10 | opacity?: Opacity
11 | border?: boolean | false
12 | margin?: Size | string | object
13 | className?: string
14 | }>
15 |
16 | const defaultSpacing = Size.Medium
17 |
18 | const ContentBox: React.FC = React.forwardRef(
19 | (
20 | {
21 | border,
22 | background,
23 | spacing = defaultSpacing,
24 | margin = Size.Zero,
25 | className,
26 | opacity = Opacity.Base,
27 | children,
28 | }: React.PropsWithChildren,
29 | ref
30 | ) => (
31 |
41 | {children}
42 |
43 | )
44 | )
45 |
46 | export default ContentBox
47 |
--------------------------------------------------------------------------------
/src/Price/PriceRoot.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import {
3 | Size,
4 | FlexboxWrap,
5 | FlexboxDirection,
6 | FlexboxAlignItems,
7 | } from '@mycompany/design-system/Styleguide'
8 |
9 | type PriceRootProps = {
10 | spacing?: Size | false
11 | productPrice?: string | null
12 | discountPrice?: string
13 | discount?: string | null
14 | wrap?: FlexboxWrap
15 | align?: FlexboxAlignItems
16 | }
17 |
18 | export const PriceRoot = styled.div`
19 | align-items: flex-end;
20 | display: flex;
21 | justify-content: flex-end;
22 | width: 100%;
23 | white-space: nowrap;
24 | gap: 0 5px;
25 | flex-direction: column;
26 |
27 | ${({ align }) =>
28 | align &&
29 | css`
30 | align-items: ${align};
31 | `}
32 |
33 | ${({ wrap }) =>
34 | wrap &&
35 | css`
36 | flex-wrap: ${wrap};
37 | `}
38 | }
39 | `
40 |
41 | type PriceListProps = {
42 | direction?: FlexboxDirection
43 | align?: FlexboxAlignItems
44 | }
45 |
46 | export const PriceList = styled.div`
47 | display: flex;
48 | flex-direction: ${({ direction }) => direction || 'row'};
49 | gap: 0 5px;
50 | align-items: ${({ align }) => align || 'flex-start'};
51 | `
52 |
--------------------------------------------------------------------------------
/src/Divider/Divider.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Divider } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Display, Orientation } from './types'
5 |
6 | export default {
7 | /* 👇 The title prop is optional.
8 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
9 | * to learn how to generate automatic titles
10 | */
11 | title: 'Component/Divider',
12 | component: Divider,
13 | } as ComponentMeta
14 |
15 | const Template: ComponentStory = (args) =>
16 |
17 | export const Introduction: ComponentStory = Template.bind({})
18 |
19 | Introduction.args = { display: Display.FullWidth }
20 |
21 | export const OrientationHorizontal: ComponentStory =
22 | Template.bind({})
23 | OrientationHorizontal.args = { orientation: Orientation.Horizontal }
24 |
25 | export const OrientationVertical: ComponentStory = (args) => (
26 |
29 | )
30 | Template.bind({})
31 | OrientationVertical.args = { orientation: Orientation.Vertical }
32 |
--------------------------------------------------------------------------------
/src/__tests__/utils/render.tsx:
--------------------------------------------------------------------------------
1 | import { render, RenderOptions } from '@testing-library/react';
2 | import React, { FC, ReactElement } from 'react';
3 | import { I18nextProvider, initReactI18next } from 'react-i18next';
4 | import { ThemeProvider } from '../..';
5 | import * as data from '../../locales/de/translations.json';
6 | import i18n from 'i18next';
7 | import '@testing-library/jest-dom'
8 |
9 | i18n
10 | .use(initReactI18next)
11 | .init({
12 | resources: {
13 | de: {
14 | translation: {data}
15 | }
16 | },
17 | fallbackLng: 'de',
18 | lng: 'de',
19 | load: "languageOnly",
20 | react: { useSuspense: false },
21 | interpolation: {
22 | escapeValue: false,
23 | },
24 | debug: false,
25 | });
26 |
27 | const AllProviders: FC<{}> = ({children}) => {
28 | return (
29 |
30 |
31 | {children}
32 |
33 |
34 | );
35 | };
36 |
37 | export const customRender = (
38 | ui: ReactElement,
39 | wrapperProps = {},
40 | options?: Omit
41 | ) => render(ui, { wrapper: (props) => , ...options });
42 |
43 | export { customRender as render };
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist/esm",
4 | "resolveJsonModule": true,
5 | "sourceMap": true,
6 | "rootDirs": [
7 | "src",
8 | "dist"
9 | ],
10 | "module": "esnext",
11 | "target": "es5",
12 | "lib": [
13 | "es6",
14 | "dom",
15 | "es2016",
16 | "es2017"
17 | ],
18 | "jsx": "react",
19 | "declaration": true,
20 | "moduleResolution": "node",
21 | "noUnusedLocals": true,
22 | "noUnusedParameters": true,
23 | "esModuleInterop": true,
24 | "noImplicitReturns": true,
25 | "noImplicitThis": true,
26 | "noImplicitAny": true,
27 | "strictNullChecks": true,
28 | "suppressImplicitAnyIndexErrors": true,
29 | "allowSyntheticDefaultImports": true,
30 | "downlevelIteration": true,
31 | "paths": {
32 | "@mycompany/design-system/*": [
33 | "./src/*"
34 | ],
35 | }
36 | },
37 | "include": [
38 | "src"
39 | ],
40 | "exclude": [
41 | "node_modules",
42 | "dist",
43 | "src/__tests__/**",
44 | "**/*.test.tsx"
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/src/Hidden/Hidden.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Hidden } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Size } from '../Styleguide'
5 |
6 | export default {
7 | /* 👇 The title prop is optional.
8 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
9 | * to learn how to generate automatic titles
10 | */
11 | title: 'Component/CategorieBanner',
12 | component: Hidden,
13 | argTypes: {
14 | up: {
15 | options: { None: false, ...Size },
16 | control: { type: 'radio' },
17 | },
18 | down: {
19 | options: { None: false, ...Size },
20 | control: { type: 'radio' },
21 | },
22 | },
23 | } as ComponentMeta
24 |
25 | export const Introduction: ComponentStory = (args) => (
26 | I am visible
27 | )
28 |
29 | export const Up: ComponentStory = (args) => (
30 | I am visible on >= Size.Medium
31 | )
32 | Up.args = {
33 | up: Size.Medium,
34 | }
35 |
36 | export const Down: ComponentStory = (args) => (
37 | I am visible on <= Size.Medium
38 | )
39 | Down.args = {
40 | down: Size.Medium,
41 | }
42 |
--------------------------------------------------------------------------------
/src/Stack/index.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Canvas, Meta, Story } from '@storybook/addon-docs'
2 | import { Stack, StackItem } from '.'
3 | import { argTypesControl } from '../../docs/utils/arg-types-control'
4 |
5 |
6 |
7 | # Stack
8 |
9 | ## Example
10 |
11 |
12 |
23 | {(args) => (
24 |
25 | 1
26 | 2
27 | 3
28 |
29 | )}
30 |
31 |
32 |
33 | ## StackItem
34 |
35 | ### Example
36 |
37 |
38 |
45 | {(args) => (
46 |
47 | 1
48 | 2
49 | 3
50 |
51 | )}
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/TextLink/TextLinkRoot.tsx:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import { Size, Variant } from '@mycompany/design-system/Styleguide'
3 |
4 | type Props = {
5 | spacing: Size | 0
6 | href: string
7 | variant: Variant
8 | fullWidth?: boolean
9 | attachment?: React.ReactNode
10 | underline?: boolean
11 | }
12 | export default styled.a`
13 | ${({ theme, variant, underline, attachment, spacing, fullWidth }) =>
14 | css`
15 | color: ${variant === Variant.Primary
16 | ? theme.color.variant[variant].base.hex()
17 | : theme.color.variant[variant].font.hex()};
18 | cursor: pointer;
19 | display: inline-block;
20 | padding: ${spacing ? theme.spacing.size[spacing].px() : 0};
21 | text-decoration: ${underline ? 'underline' : 'none'};
22 | &:hover {
23 | ${underline &&
24 | `
25 | color: ${theme.color.variant[variant].hover.hex()};
26 | `}
27 | ${!underline &&
28 | `
29 | text-decoration: underline;
30 | `}
31 | }
32 | ${fullWidth &&
33 | `
34 | width: 100%;
35 | `}
36 | ${attachment &&
37 | `
38 | align-items: center;
39 | display: inline-flex;
40 | grid-gap: 0 ${spacing ? theme.spacing.size[spacing].px() : 0};
41 | `}
42 | `}
43 | `
44 |
--------------------------------------------------------------------------------
/src/Stack/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import {
3 | FlexboxAlignment,
4 | FlexboxDirection,
5 | FlexboxJustification,
6 | FlexboxWrap,
7 | Size,
8 | } from '../Styleguide'
9 |
10 | type StackProps = {
11 | alignment?: FlexboxAlignment
12 | as?: keyof JSX.IntrinsicElements
13 | direction?: FlexboxDirection
14 | justification?: FlexboxJustification
15 | spacing?: Size
16 | wrap?: FlexboxWrap
17 | }
18 |
19 | type StackItemProps = {
20 | as?: keyof JSX.IntrinsicElements
21 | growth?: number
22 | }
23 |
24 | export const Stack = styled('div').withConfig({
25 | shouldForwardProp: (prop) =>
26 | !['alignment', 'direction', 'justification', 'spacing', 'wrap'].includes(
27 | prop
28 | ),
29 | })`
30 | align-items: ${({ alignment }) => alignment};
31 | display: flex;
32 | flex-direction: ${({ direction }) => direction};
33 | flex-wrap: ${({ wrap }) => wrap};
34 | justify-content: ${({ justification }) => justification};
35 | list-style: none;
36 | padding: 0;
37 | ${({ theme, spacing }) =>
38 | spacing &&
39 | `
40 | grid-gap: ${theme.spacing.size[spacing].rem()};
41 | `};
42 | `
43 |
44 | export const StackItem = styled('div').withConfig({
45 | shouldForwardProp: (prop) => !['growth'].includes(prop),
46 | })`
47 | flex-grow: ${({ growth }) => growth};
48 | `
49 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Palette.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 | import theme from './'
3 |
4 |
5 |
6 |
24 |
25 | # Palette
26 |
27 | <>
28 | {Object.keys(theme.palette).map((name) => (
29 |
30 |
{name}
31 |
35 |
40 |
45 |
50 |
51 |
52 | ))}
53 | >
54 |
--------------------------------------------------------------------------------
/src/Styleguide/Types.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 |
3 |
4 |
5 | # Enum
6 |
7 | A selection of Typescript Types to guarantee a consitent property handling all over the ecosystem and to create a common sense of vocabulary to use in terms of talking about component states
8 |
9 | ## Enums
10 |
11 | ### Size
12 |
13 | `Size.Tiny`
14 | `Size.Small`
15 | `Size.Medium`
16 | `Size.Large`
17 | `Size.ExtraLarge`
18 |
19 | ### Status
20 |
21 | `Status.Default`
22 | `Status.Success`
23 | `Status.Warning`
24 | `Status.Error`
25 |
26 | ### Variant
27 |
28 | `Variant.Primary`
29 | `Variant.Secondary`
30 |
31 | ### FontWeight
32 |
33 | `FontWeight.Light`
34 | `FontWeight.Regular`
35 | `FontWeight.Bold`
36 |
37 | ### Easing
38 |
39 | `Easing.EaseIn`
40 | `Easing.EaseOut`
41 | `Easing.EaseInOut`
42 | `Easing.Sharp`
43 |
44 | ### Duration
45 |
46 | `Duration.Shortest`
47 | `Duration.Shorter`
48 | `Duration.Short`
49 | `Duration.Default`
50 | `Duration.Complex`
51 | `Duration.EnteringScreen`
52 | `Duration.LeavingScreen`
53 |
54 | ## Usage
55 |
56 | ```tsx
57 | import React from 'react'
58 | import { Size } from '@mycompany/design-system/Styleguide'
59 |
60 | type Props = {
61 | size: Size
62 | }
63 |
64 | export default ({ size }: Props) => {
65 | if (size === Size.Large) {
66 | return Large
67 | }
68 |
69 | return Default
70 | }
71 | ```
72 |
--------------------------------------------------------------------------------
/src/Divider/DividerRoot.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 | import { Orientation, Display } from './types'
4 |
5 | type Props = {
6 | orientation: Orientation
7 | display: Display
8 | flexItem: boolean
9 | spacing: Size
10 | inverted: boolean
11 | }
12 |
13 | export default styled.hr`
14 | background-color: ${({ theme, inverted }) =>
15 | inverted ? theme.color.dividerInverted.hex() : theme.color.divider.hex()};
16 | border: 0;
17 | box-sizing: border-box;
18 | flex-shrink: 0;
19 |
20 | margin-bottom: ${({ spacing, theme }) => theme.spacing.size[spacing].rem()};
21 | margin-left: ${({ spacing, theme, display }) =>
22 | display === Display.Middle ? theme.spacing.size[spacing].rem() : '0'};
23 | margin-right: ${({ spacing, theme, display }) =>
24 | display === Display.Middle ? theme.spacing.size[spacing].rem() : '0'};
25 | margin-top: ${({ spacing, theme }) => theme.spacing.size[spacing].rem()};
26 |
27 | ${({ flexItem }) =>
28 | flexItem &&
29 | css`
30 | align-self: stretch;
31 | `}
32 |
33 | ${({ orientation }) =>
34 | orientation === Orientation.Horizontal &&
35 | css`
36 | height: 1px;
37 | width: auto;
38 | `}
39 |
40 | ${({ orientation }) =>
41 | orientation === Orientation.Vertical &&
42 | css`
43 | height: auto;
44 | width: 1px;
45 | `};
46 | `
47 |
--------------------------------------------------------------------------------
/src/TextLink/TextLink.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size, Variant } from '@mycompany/design-system/Styleguide'
3 | import TextLinkRoot from './TextLinkRoot'
4 |
5 | type Props = React.PropsWithChildren<{
6 | spacing?: Size | 0
7 | href: string
8 | fullWidth?: boolean
9 | className?: string
10 | attachment?: React.ReactNode
11 | variant?: Variant
12 | underline?: boolean | false
13 | }>
14 |
15 | const defaultSpacing = 0
16 | const defaultVariant = Variant.Primary
17 | const defaultUnderline = true
18 |
19 | const TextLink: React.FC = React.forwardRef(
20 | (
21 | {
22 | href,
23 | attachment,
24 | spacing = defaultSpacing,
25 | className,
26 | fullWidth = false,
27 | variant = defaultVariant,
28 | underline = defaultUnderline,
29 | children,
30 | ...htmlAttributes
31 | }: React.PropsWithChildren,
32 | ref
33 | ) => (
34 |
46 | {attachment !== null && attachment}
47 | {children !== null && children}
48 |
49 | )
50 | )
51 |
52 | export default TextLink
53 |
--------------------------------------------------------------------------------
/src/Icon/Visa.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const Visa: React.FC = ({ payments }: Props) => (
6 |
13 |
17 |
18 | )
19 |
20 | export default Visa
21 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/Color.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/addon-docs'
2 | import theme from './'
3 | import { ColorPreview } from '../../../docs/preview'
4 | import { ColorValue } from '../value'
5 |
6 |
7 |
8 | # Colors
9 |
10 | ## Misc
11 |
12 | <>
13 | {Object.keys(theme.color).map(
14 | (name) =>
15 | theme.color[name] instanceof ColorValue && (
16 |
17 |
{name}
18 |
19 |
20 | )
21 | )}
22 | >
23 |
24 | ## Variant
25 |
26 | <>
27 | {Object.keys(theme.color.variant).map((variantName) => (
28 |
29 |
{variantName}
30 | {Object.keys(theme.color.variant[variantName]).map((name) => (
31 |
32 |
{name}
33 |
34 |
35 | ))}
36 |
37 | ))}
38 | >
39 |
40 | ## Status
41 |
42 | <>
43 | {Object.keys(theme.color.status).map((statusName) => (
44 |
45 |
{statusName}
46 | {Object.keys(theme.color.status[statusName]).map((name) => (
47 |
48 |
{name}
49 |
50 |
51 | ))}
52 |
53 | ))}
54 | >
55 |
--------------------------------------------------------------------------------
/src/Navigation/NavigationRoot.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 | import NavigationItemRoot from './NavigationItemRoot'
4 |
5 | type Props = {
6 | gridSpacing: Size | false
7 | gridSize: Size | false
8 | orientation?: 'horizontal' | 'vertical'
9 | }
10 |
11 | export default styled.ul`
12 | display: flex;
13 |
14 | flex-direction: ${(props) =>
15 | props.orientation === 'vertical' ? 'column' : 'row'};
16 | & ${NavigationItemRoot} {
17 | flex-basis: auto;
18 | padding: ${({ gridSpacing, theme }) =>
19 | gridSpacing ? theme.spacing.size[gridSpacing].rem() : 0}
20 | 0;
21 | ${({ orientation, gridSize, theme }) =>
22 | orientation === 'horizontal' &&
23 | `
24 | width: fit-content;
25 | margin: 0 ${gridSize ? theme.spacing.size[gridSize].rem() : 0};
26 | `}
27 |
28 | ${({ orientation, theme, gridSpacing }) =>
29 | orientation === 'vertical' &&
30 | `
31 | padding-right: ${gridSpacing && theme.spacing.size[Size.Medium].rem()};
32 | justify-content: space-between;
33 | &:first-child {
34 | border-top: 1px solid ${theme.color.divider.hex()};
35 | }
36 | border-bottom: 1px solid ${theme.color.divider.hex()};
37 | `}
38 | }
39 | margin: ${({ gridSize, theme }) =>
40 | gridSize ? theme.spacing.size[gridSize].rem() : 0}
41 | 0;
42 | padding: 0;
43 | `
44 |
--------------------------------------------------------------------------------
/src/ButtonGroup/ButtonGroup.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ButtonGroup } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Button } from '../Button'
5 | import { FlexboxJustification } from '../Styleguide'
6 |
7 | export default {
8 | /* 👇 The title prop is optional.
9 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
10 | * to learn how to generate automatic titles
11 | */
12 | title: 'Component/ButtonGroup',
13 | component: ButtonGroup,
14 | argTypes: {
15 | justify: {
16 | options: FlexboxJustification,
17 | control: { type: 'radio' },
18 | },
19 | squashed: {
20 | control: { type: 'boolean' },
21 | },
22 | },
23 | } as ComponentMeta
24 |
25 | const Template: ComponentStory = (args) => (
26 |
27 |
28 |
29 |
30 |
31 | )
32 |
33 | export const Introduction: ComponentStory = Template.bind(
34 | {}
35 | )
36 |
37 | export const Squashed: ComponentStory = Template.bind({})
38 |
39 | Squashed.args = {
40 | squashed: true,
41 | }
42 |
43 | export const Justify: ComponentStory = Template.bind({})
44 |
45 | Justify.args = {
46 | justify: FlexboxJustification.Center,
47 | }
48 |
--------------------------------------------------------------------------------
/src/ButtonGroup/ButtonGroupRoot.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import { ButtonComponents } from '@mycompany/design-system/Button'
3 | import { DividerComponents } from '@mycompany/design-system/Divider'
4 | import { Size, FlexboxJustification } from '@mycompany/design-system/Styleguide'
5 |
6 | type Props = {
7 | squashed: boolean
8 | justify: FlexboxJustification
9 | elementSpacing: Size | false
10 | }
11 |
12 | export default styled.div`
13 | box-sizing: border-box;
14 | display: flex;
15 | justify-content: ${({ justify }) => justify};
16 | margin: ${({ theme, elementSpacing }) =>
17 | elementSpacing ? theme.spacing.size[elementSpacing].rem() : 0};
18 |
19 | & ${ButtonComponents.ButtonRoot}, & ${DividerComponents.DividerRoot} {
20 | margin-bottom: 0;
21 | margin-top: 0;
22 | }
23 |
24 | & ${ButtonComponents.ButtonRoot}:last-child {
25 | margin-right: 0;
26 | }
27 |
28 | & ${ButtonComponents.ButtonRoot}:first-child {
29 | margin-left: 0;
30 | }
31 |
32 | ${({ squashed }) =>
33 | squashed &&
34 | css`
35 | & ${ButtonComponents.ButtonRoot}:not(:first-child) {
36 | border-bottom-left-radius: 0;
37 | border-top-left-radius: 0;
38 | margin-left: 0;
39 | }
40 |
41 | & ${ButtonComponents.ButtonRoot}:not(:last-child) {
42 | border-bottom-right-radius: 0;
43 | border-top-right-radius: 0;
44 | margin-right: 0;
45 | }
46 | `};
47 | `
48 |
--------------------------------------------------------------------------------
/src/Icon/MasterCard.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const MasterCard: React.FC = ({ payments }: Props) => (
6 |
13 |
14 |
21 |
22 |
29 |
33 |
37 |
41 |
42 | )
43 |
44 | export default MasterCard
45 |
--------------------------------------------------------------------------------
/src/CircularProgress/CircularProgressRoot.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import {
3 | Variant,
4 | Status,
5 | Size,
6 | ColorValue,
7 | mixin,
8 | } from '@mycompany/design-system/Styleguide'
9 | import { State } from './types'
10 | import circularRotateKeyframe from './keyframe/circularRotateKeyframe'
11 |
12 | type Props = {
13 | state: State
14 | variant: Variant
15 | inherit: boolean
16 | spacing: Size | false
17 | status: Status
18 | overrideColor?: ColorValue
19 | }
20 |
21 | export default styled.span`
22 | color: ${({ theme, variant, inherit }) =>
23 | inherit
24 | ? theme.color.variant[variant].font.hex()
25 | : theme.color.variant[variant].base.hex()};
26 | display: inline-flex;
27 |
28 | ${({ status, theme, inherit }) =>
29 | status &&
30 | status !== Status.Default &&
31 | css`
32 | color: ${
33 | inherit
34 | ? theme.color.status[status].font.hex()
35 | : theme.color.status[status].base.hex()
36 | };};
37 | `}
38 |
39 | ${({ state, theme }) =>
40 | state === State.Determinate &&
41 | css`
42 | transition: transform ${theme.transition.duration.default}
43 | ${theme.transition.easing.sharp};
44 | `}
45 |
46 | ${({ state }) =>
47 | state === State.Indeterminate &&
48 | css`
49 | animation: ${circularRotateKeyframe} 1.4s linear infinite;
50 | `}
51 |
52 |
53 | ${({ theme, spacing }) => mixin.spacing({ spacing, theme })}
54 | `
55 |
--------------------------------------------------------------------------------
/src/CssBaseline/CssBaseline.tsx:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components'
2 | import { normalize } from 'styled-normalize'
3 | import { theme, mixin, query, Size } from '@mycompany/design-system/Styleguide'
4 |
5 | export default createGlobalStyle`
6 | ${normalize}
7 |
8 | body, html {
9 | margin: 0;
10 | padding: 0;
11 | font-family: ${theme.typography.mobile.fontFamily};
12 | font-size: ${theme.typography.mobile.fontSize.px()};
13 | line-height: ${theme.typography.mobile.lineHeight};
14 | color: ${theme.color.font.hex()};
15 | background-color: ${theme.color.appBackground.hex()};
16 | }
17 |
18 | ${query.min(Size.Medium)`
19 | body, html {
20 | font-size: ${theme.typography.base.fontSize.px()};
21 | font-family: ${theme.typography.base.fontFamily};
22 | line-height: ${theme.typography.base.lineHeight};
23 | }
24 | `}
25 |
26 | * {
27 | box-sizing: border-box;
28 | }
29 |
30 | a {
31 | color: ${theme.color.variant.primary.base.hex()};
32 | text-decoration: none;
33 | ${mixin.typography.action}
34 | }
35 |
36 | h1 {
37 | ${mixin.typography.headline1}
38 | }
39 |
40 | h2 {
41 | ${mixin.typography.headline2}
42 | }
43 |
44 | h3 {
45 | ${mixin.typography.headline3}
46 | }
47 |
48 | h4 {
49 | ${mixin.typography.headline4}
50 | }
51 |
52 | h5 {
53 | ${mixin.typography.headline5}
54 | }
55 |
56 | p {
57 | ${mixin.typography.body1}
58 | }
59 |
60 | code, pre {
61 | ${mixin.typography.caption}
62 | }
63 | `
64 |
--------------------------------------------------------------------------------
/src/Icon/GiftIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const GiftIcon: React.FC = () => (
6 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | )
25 |
26 | export default GiftIcon
27 |
--------------------------------------------------------------------------------
/src/CssGrid/types.ts:
--------------------------------------------------------------------------------
1 |
2 | export enum GridJustify {
3 | FlexStart = 'flex-start',
4 | FlexEnd = 'flex-end',
5 | Center = 'center',
6 | SpaceBetween = 'space-between',
7 | SpaceAround = 'space-around',
8 | SpaceEvenly = 'space-evenly',
9 | Stretch = 'stretch'
10 | }
11 |
12 | export enum GridAlignItems {
13 | FlexStart = 'flex-start',
14 | FlexEnd = 'flex-end',
15 | Center = 'center',
16 | Stretch = 'stretch',
17 | Baseline = 'baseline',
18 | }
19 |
20 | export enum GridDirection {
21 | Column = 'column',
22 | ColumnReverse = 'column-reverse',
23 | Row = 'row',
24 | RowReverse = 'row-reverse',
25 | }
26 |
27 | export enum GridAlignContent {
28 | FlexStart = 'flex-start',
29 | FlexEnd = 'flex-end',
30 | Center = 'center',
31 | Stretch = 'stretch',
32 | SpaceBetween = 'space-between',
33 | SpaceAround = 'space-around',
34 | }
35 |
36 | export enum GridArea {
37 | Warenkorb = 'content sidebar',
38 | Sidebar = 'sidebar',
39 | Content = 'content',
40 | }
41 |
42 | export enum GridColumn {
43 | CartTiles = 'minmax(70px, 100px) auto auto',
44 | WarenkorbTemplate = 'auto minmax(150px,400px)',
45 | LineItemsAction = '70px repeat(auto-fit, 90px)',
46 | HeaderNavigation = 'auto auto',
47 | HeaderLogo = 'minmax(100px, 255px) auto',
48 | Footer = 'repeat(3, 190px)',
49 | FooterWrapper = '600px minmax(100px,330px)',
50 | FooterImprint = 'auto minmax(300px, 600px)',
51 | }
52 |
53 | export enum GridAutoFlow {
54 | Dense = 'dense',
55 | Column = 'column',
56 | Row = 'row',
57 | RowDense = 'row dense',
58 | ColumnDense = 'column dense',
59 | }
60 |
--------------------------------------------------------------------------------
/src/Container/Container.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Container } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Size } from '../Styleguide'
5 | import { Behavior } from './types'
6 |
7 | export default {
8 | /* 👇 The title prop is optional.
9 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
10 | * to learn how to generate automatic titles
11 | */
12 | title: 'Component/Container',
13 | component: Container,
14 | argTypes: {
15 | behavior: {
16 | options: Behavior,
17 | control: { type: 'radio' },
18 | },
19 | spacing: {
20 | options: { None: false, ...Size },
21 | control: { type: 'radio' },
22 | },
23 | },
24 | decorators: [
25 | (storyFn) => (
26 |
27 |
33 | {storyFn()}
34 |
35 |
36 | ),
37 | ],
38 | } as ComponentMeta
39 |
40 | const Template: ComponentStory = (args) => (
41 |
42 |
43 |
44 | )
45 |
46 | export const Introduction: ComponentStory = Template.bind({})
47 |
48 | export const BehaviorFixed: ComponentStory = Template.bind({})
49 | BehaviorFixed.args = {
50 | behavior: Behavior.Fixed,
51 | }
52 |
--------------------------------------------------------------------------------
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | # Set update schedule for GitHub Actions
2 |
3 | version: 2
4 | # Uncomment when you use ruby gems
5 | # registries:
6 | # # sample registry of the mycompany hosted gems
7 | # ruby-github:
8 | # type: rubygems-server
9 | # url: https://rubygems.pkg.github.com/mycompany
10 | # token: ${{ secrets.AVS_PIPELINE_PAT }}
11 |
12 | updates:
13 | # updates to
14 | - package-ecosystem: 'github-actions'
15 | directory: '/'
16 | schedule:
17 | # Check for updates to GitHub Actions every week
18 | interval: 'monthly'
19 | # assignees: []
20 | reviewers:
21 | - 'mycompany/frontend-team'
22 | - 'mycompany/sre-team'
23 | - 'mycompany/b2c-team'
24 | # Labels on pull requests for security and version updates
25 | labels:
26 | - dependencies
27 | - gh-actions
28 | # Maintain dependencies for npm
29 | - package-ecosystem: 'npm'
30 | directory: '/'
31 | open-pull-requests-limit: 10 # default is 5
32 | schedule:
33 | interval: 'weekly'
34 | reviewers:
35 | - 'mycompany/frontend-team'
36 | labels:
37 | - dependencies
38 | - js
39 | # Uncomment when you use ruby gems
40 | # - package-ecosystem: 'bundler'
41 | # open-pull-requests-limit: 10 # default is 5
42 | # insecure-external-code-execution: allow
43 | # registries:
44 | # - ruby-github
45 | # directory: '/'
46 | # schedule:
47 | # # Check for updates to Gemfile every week
48 | # interval: 'weekly'
49 | # assignees: []
50 | # reviewers: []
51 | # labels:
52 | # - dependencies
53 | # - ruby
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
Test Coverage Demo for storybook test-runner Issue #148
3 | https://github.com/storybookjs/test-runner/issues/148#issuecomment-1186507400
4 |
5 |
6 | ## Prerequisites
7 |
8 | **NodeJS**
9 |
10 | Install NodeJS `^16.13.0`
11 |
12 | **Yarn**
13 |
14 | Install yarn globally
15 | `npm i -g yarn`
16 |
17 | **GitHub Package Registry**
18 |
19 | Login to GPR: https://docs.github.com/en/packages/guides/configuring-npm-for-use-with-github-packages
20 |
21 | ## Setup
22 |
23 | Install the dependencies
24 |
25 | ```
26 | yarn
27 | ```
28 |
29 | Run `prepare` hook manually as yarn@2 is not supporting lifetime hooks of npm
30 | Run your local storybook
31 |
32 | ```
33 | yarn start
34 | ```
35 |
36 | ## Create Test Coverage Reports
37 |
38 | ```
39 | yarn run coverage
40 | ```
41 |
42 | To be able to run tests, storybook `yarn start` must be run in a separate terminal window.
43 |
44 | This runs jest unit tests and storybook test runner tests.
45 | The test-runner coverage json file is then converted into lcov format.
46 |
47 | Tests coverage results merged.
48 |
49 | ### Coverage results
50 |
51 | Jest Unit tests are saved to `./coverage/unit/`
52 |
53 | Storybook test runner coverage results are saved to `./coverage/storybook`
54 |
55 | Merged test resulst are saved to `./coverage/merged`
56 |
57 | ## Info to validate errors mentioned in Merge results coverage ticket https://github.com/storybookjs/test-runner/issues/148#issuecomment-1186507400
58 |
59 | 1. run `yarn run coverage`
60 | 2. Open `./coverage/unit/lcov-report/src/Navigation/NavigationRoot.ts.html`
61 | 3. Open `./coverage/storybook/lcov-report/src/Navigation/NavigationRoot.ts.html`
62 | 4. Compare file coverage and the number of total lines listed in both tests
63 |
--------------------------------------------------------------------------------
/src/Container/ContainerRoot.ts:
--------------------------------------------------------------------------------
1 | import styled, { css, DefaultTheme } from 'styled-components'
2 | import { query, Size } from '@mycompany/design-system/Styleguide'
3 | import { Behavior } from './types'
4 |
5 | type Props = {
6 | behavior: Behavior
7 | spacing: Size | object | string
8 | theme: DefaultTheme
9 | }
10 |
11 | const padding = ({ theme, ...props }: Props) => {
12 | let spacingStyle = ''
13 | if (typeof props.spacing === 'object') {
14 | Object.entries(props.spacing).forEach(([key, value]) => {
15 | spacingStyle += `
16 | @media (min-width: ${theme.breakpoint[key].px()}) {
17 | padding: ${value}
18 | }
19 | `
20 | })
21 | return css`
22 | ${spacingStyle}
23 | `
24 | }
25 | if (typeof props.spacing === 'string') {
26 | return css`
27 | padding: ${theme.spacing.size[props.spacing].px() || '0'};
28 | `
29 | }
30 |
31 | return false
32 | }
33 |
34 | export default styled.div`
35 | box-sizing: border-box;
36 | display: block;
37 | margin-left: auto;
38 | margin-right: auto;
39 | max-width: ${({ theme }) => theme.breakpoint[Size.ExtraLarge].px()};
40 | ${padding}
41 | ${({ behavior }) =>
42 | behavior === Behavior.Fixed &&
43 | css`
44 | ${query.min(Size.Small)`
45 | max-width: ${({ theme }) => theme.breakpoint[Size.Small].px()};
46 | `}
47 |
48 | ${query.min(Size.Medium)`
49 | max-width: ${({ theme }) => theme.breakpoint[Size.Medium].px()};
50 | `}
51 |
52 | ${query.min(Size.Large)`
53 | max-width: ${({ theme }) => theme.breakpoint[Size.Large].px()};
54 | `}
55 |
56 | ${query.min(Size.ExtraLarge)`
57 | max-width: ${({ theme }) => theme.breakpoint[Size.ExtraLarge].px()};
58 | `}
59 | `}
60 | `
61 |
--------------------------------------------------------------------------------
/src/Icon/base/SvgIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SvgIconRoot from './SvgIconRoot'
3 | import { IconName } from '../types'
4 |
5 | type Props = {
6 | stroke?: string
7 | strokeWidth?: string
8 | strokeLinejoin?: CanvasLineJoin
9 | strokeLinecap?: CanvasLineCap
10 | fill?: boolean | object
11 | viewBox?: string
12 | width?: string
13 | height?: string
14 | payments?: boolean
15 | social?: boolean
16 | children: React.ReactNode
17 | displayName: IconName
18 | className?: string
19 | }
20 |
21 | const SvgIcon: React.FC = React.forwardRef(
22 | (
23 | {
24 | fill,
25 | width,
26 | height,
27 | viewBox = '0 0 24 24',
28 | payments = false,
29 | strokeLinejoin,
30 | strokeLinecap,
31 | stroke,
32 | social = false,
33 | strokeWidth,
34 | children,
35 | displayName,
36 | className,
37 | ...htmlAttributes
38 | }: Props,
39 | ref
40 | ) => (
41 |
62 | {children}
63 |
64 | )
65 | )
66 |
67 | SvgIcon.defaultProps = {
68 | className: undefined,
69 | fill: false,
70 | }
71 |
72 | export default SvgIcon
73 |
--------------------------------------------------------------------------------
/src/Styleguide/query/query.ts:
--------------------------------------------------------------------------------
1 | import {
2 | css,
3 | ThemedCssFunction,
4 | DefaultTheme,
5 | CSSObject,
6 | SimpleInterpolation,
7 | } from 'styled-components'
8 | import { Size } from '../types'
9 | import { QuerySet } from './types'
10 | import theme from '../theme'
11 |
12 | const { breakpoint } = theme
13 |
14 | const mediaQueryWrapperFactory: (
15 | query: string
16 | ) => ThemedCssFunction =
17 | (query: string) =>
18 | (
19 | strings: TemplateStringsArray | CSSObject,
20 | values: SimpleInterpolation,
21 | ...args: any
22 | ) =>
23 | css`
24 | @media ${query} {
25 | ${css(strings, values, ...args)}
26 | }
27 | `
28 |
29 | type MinBreakpointSize =
30 | | Size.Zero
31 | | Size.Tiny
32 | | Size.Small
33 | | Size.Medium
34 | | Size.Large
35 | | Size.ExtraLarge
36 | type MaxBreakpointSize =
37 | | Size.Zero
38 | | Size.Tiny
39 | | Size.Small
40 | | Size.Medium
41 | | Size.Large
42 |
43 | const nextMap: { [key in MaxBreakpointSize]: MinBreakpointSize } = {
44 | [Size.Zero]: Size.Tiny,
45 | [Size.Tiny]: Size.Small,
46 | [Size.Small]: Size.Medium,
47 | [Size.Medium]: Size.Large,
48 | [Size.Large]: Size.ExtraLarge,
49 | }
50 |
51 | const query: QuerySet = {
52 | min: (key: MinBreakpointSize) =>
53 | mediaQueryWrapperFactory(`(min-width: ${breakpoint[key].px()})`),
54 | max: (key: MaxBreakpointSize) =>
55 | mediaQueryWrapperFactory(
56 | `(max-width: ${breakpoint[nextMap[key]].add(-1).px()})`
57 | ),
58 | between: (start: MinBreakpointSize, end: MaxBreakpointSize) =>
59 | mediaQueryWrapperFactory(
60 | `(min-width: ${breakpoint[start].px()}) and (max-width: ${breakpoint[
61 | nextMap[end]
62 | ]
63 | .add(-1)
64 | .px()})`
65 | ),
66 | }
67 |
68 | export default query
69 |
--------------------------------------------------------------------------------
/docs/utils/arg-types-control.ts:
--------------------------------------------------------------------------------
1 | import {
2 | FlexboxAlignment,
3 | FlexboxDirection,
4 | FlexboxJustification,
5 | FlexboxWrap,
6 | Size,
7 | theme,
8 | Variant,
9 | ElevationLevel,
10 | } from '../../src'
11 |
12 | type EnumNames =
13 | | 'FlexboxAlignment'
14 | | 'FlexboxDirection'
15 | | 'FlexboxJustification'
16 | | 'FlexboxWrap'
17 | | 'Size'
18 | | 'Variant'
19 | | 'ElevationLevel'
20 |
21 | function getEnum(enumName: EnumNames) {
22 | switch (enumName) {
23 | case 'FlexboxAlignment':
24 | return FlexboxAlignment
25 | case 'FlexboxDirection':
26 | return FlexboxDirection
27 | case 'FlexboxJustification':
28 | return FlexboxJustification
29 | case 'FlexboxWrap':
30 | return FlexboxWrap
31 | case 'Size':
32 | return Size
33 | case 'Variant':
34 | return Variant
35 | case 'ElevationLevel':
36 | return ElevationLevel
37 | default:
38 | throw new Error(`[arg-types-control]: Enum "${enumName}" not found!`)
39 | }
40 | }
41 |
42 | function getControl(enumName: EnumNames) {
43 | const enumeration = getEnum(enumName)
44 | const mapping = {}
45 |
46 | Object.keys(enumeration).forEach((key) => (mapping[key] = enumeration[key]))
47 |
48 | return {
49 | control: { type: 'radio' },
50 | mapping,
51 | options: Object.keys(enumeration),
52 | table: { type: { summary: enumName } },
53 | }
54 | }
55 |
56 | export const argTypesControl = {
57 | elevation:getControl('ElevationLevel'),
58 | flexboxAlignment: getControl('FlexboxAlignment'),
59 | flexboxDirection: getControl('FlexboxDirection'),
60 | flexboxJustification: getControl('FlexboxJustification'),
61 | flexboxWrap: getControl('FlexboxWrap'),
62 | number: { control: { type: 'number' } },
63 | size: getControl('Size'),
64 | text: { control: { type: 'text' } },
65 | variant: getControl('Variant'),
66 | }
67 |
--------------------------------------------------------------------------------
/src/Grid/Grid.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Size,
4 | FlexboxDirection,
5 | FlexboxWrap,
6 | FlexboxJustification,
7 | FlexboxAlignContent,
8 | FlexboxAlignItems,
9 | } from '@mycompany/design-system/Styleguide'
10 | import { GridSize } from './types'
11 | import GridRoot from './GridRoot'
12 |
13 | type Props = React.PropsWithChildren<{
14 | item?: boolean
15 | container?: boolean
16 | gridGap?: Size | false
17 | zero?: GridSize | false
18 | tiny?: GridSize | false
19 | small?: GridSize | false
20 | medium?: GridSize | false
21 | large?: GridSize | false
22 | xLarge?: GridSize | false
23 | alignContent?: FlexboxAlignContent | false
24 | alignItems?: FlexboxAlignItems | false
25 | justify?: FlexboxJustification | false | object
26 | direction?: FlexboxDirection | false
27 | wrap?: FlexboxWrap | false
28 | spacing?: Size
29 | component?: keyof JSX.IntrinsicElements
30 | }>
31 |
32 | const defaultSpacing = Size.Medium
33 | const defaultGap = Size.Zero
34 |
35 | const Grid = React.forwardRef(
36 | (
37 | {
38 | alignContent = false,
39 | alignItems = false,
40 | justify = false,
41 | direction = false,
42 | wrap = false,
43 | item = false,
44 | container = false,
45 | gridGap = defaultGap,
46 | spacing = defaultSpacing,
47 | component = 'div',
48 | children,
49 | ...sizeProps
50 | }: Props,
51 | ref
52 | ) => (
53 |
67 | {children}
68 |
69 | )
70 | )
71 |
72 | export default Grid
73 |
--------------------------------------------------------------------------------
/src/CssGrid/CssGridStyles.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 |
4 | import {
5 | justify,
6 | alignItems,
7 | alignContent,
8 | alignSelf,
9 | gridGap,
10 | gridColumns,
11 | gridItems,
12 | gridTemplate,
13 | gridRow,
14 | gridArea,
15 | gridAutoFlow,
16 | spacing,
17 | } from './mixin/options'
18 |
19 | import {
20 | GridAlignContent,
21 | GridAlignItems,
22 | GridJustify,
23 | GridArea,
24 | GridColumn,
25 | GridDirection,
26 | GridAutoFlow,
27 | } from './types'
28 |
29 | type Props = {
30 | alignContent?: GridAlignContent | false
31 | alignItems?: GridAlignItems | false | object
32 | alignSelf?: GridAlignContent | false | object
33 | justify?: GridJustify | false | object
34 | direction?: GridDirection
35 | container?: boolean
36 | item?: boolean
37 | spacing?: Size | false
38 | gridGap?: Size | object
39 | gridAutoFlow?: GridAutoFlow | object
40 | gridRow?: string | object
41 | gridArea?: GridArea | object
42 | gridTemplate?: GridArea | object
43 | gridColumns?: number | object | GridColumn | string | false
44 | gridItems?: string | false | object
45 | gridAutoFill?: number
46 | }
47 |
48 | const CssGridRoot = styled.div`
49 | ${({ container, gridAutoFill }) =>
50 | container &&
51 | css`
52 | margin: 0;
53 | box-sizing: border-box;
54 | display: grid;
55 | grid-auto-flow: column;
56 | ${gridAutoFill &&
57 | `
58 | grid-template-columns: repeat(auto-fit, minmax(${gridAutoFill}px, 1fr));
59 | `}
60 | `}
61 |
62 | ${css`
63 | ${spacing}
64 | ${justify}
65 | ${alignItems}
66 | ${alignContent}
67 | ${alignSelf}
68 | ${gridGap}
69 | ${gridColumns}
70 | ${gridItems}
71 | ${gridTemplate}
72 | ${gridRow}
73 | ${gridArea}
74 | ${gridAutoFlow}
75 | `}
76 | `
77 |
78 | export { CssGridRoot }
79 |
--------------------------------------------------------------------------------
/src/ReactUtils/hooks/useBreakpoint.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 | import useDebounce from './useDebounce'
4 | import {
5 | getCurrentBreakpoint,
6 | isBreakpointGreaterThan,
7 | isBreakpointSmallerThan,
8 | } from '../utils'
9 |
10 | type CurrentSize = {
11 | windowWidth: number
12 | windowHeight: number
13 | currentBreakpoint: Size
14 | isGreaterThan: (size: Size) => boolean
15 | isSmallerThan: (size: Size) => boolean
16 | }
17 |
18 | const getCurrentSize = (sizeFallback: Size): CurrentSize => {
19 | const currentBreakpoint =
20 | typeof window !== 'undefined'
21 | ? getCurrentBreakpoint(window.innerWidth)
22 | : sizeFallback
23 |
24 | return {
25 | windowWidth: typeof window !== 'undefined' ? window.innerWidth : 0,
26 | windowHeight: typeof window !== 'undefined' ? window.innerHeight : 0,
27 | currentBreakpoint,
28 | isGreaterThan: (size: Size) =>
29 | isBreakpointGreaterThan(currentBreakpoint, size),
30 | isSmallerThan: (size: Size) =>
31 | isBreakpointSmallerThan(currentBreakpoint, size),
32 | }
33 | }
34 |
35 | export default (
36 | initialSize: Size = Size.Tiny,
37 | bind: boolean = true,
38 | debounceOffset: number = 100
39 | ): CurrentSize => {
40 | const debounce = useDebounce()
41 | const [currentSize, setCurrentSize] = useState(
42 | getCurrentSize(initialSize)
43 | )
44 |
45 | if (bind) {
46 | useEffect(() => {
47 | const handleResize = debounce(() => {
48 | setCurrentSize(getCurrentSize(initialSize))
49 | }, debounceOffset)
50 |
51 | if (typeof window !== 'undefined') {
52 | window.addEventListener('resize', handleResize)
53 | }
54 |
55 | return () => {
56 | if (typeof window !== 'undefined') {
57 | window.removeEventListener('resize', handleResize)
58 | }
59 | }
60 | })
61 | }
62 |
63 | return currentSize
64 | }
65 |
--------------------------------------------------------------------------------
/src/Icon/Icon.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Fragment } from 'react'
2 | import {
3 | Story,
4 | Meta,
5 | ArgsTable,
6 | Preview,
7 | Canvas,
8 | Source,
9 | } from '@storybook/addon-docs'
10 | import { HeartIcon, iconMap } from '@mycompany/design-system/Icon'
11 | import IconHover from './base/IconHover'
12 |
13 |
14 |
15 | # Icons
16 |
17 | {(args) => }
18 |
19 | ## Overview
20 |
21 |
22 |
23 |
24 | Name
25 | Default
26 | Fill
27 | Usage
28 |
29 |
30 |
31 | {Object.keys(iconMap).map((name) => {
32 | const Component = iconMap[name];
33 | return (
34 |
35 | {name}
36 |
37 |
38 |
39 |
44 |
45 |
46 |
47 | )
48 | })}
49 |
50 |
51 |
52 |
53 | ## Usage
54 |
55 | ```tsx
56 | import { HeartIcon } from '@mycompany/design-system/Icon'
57 | ```
58 |
59 |
60 | {(args) => }
61 |
62 |
63 | ### Fill
64 |
65 |
66 | {(args) => }
67 |
68 |
69 | ### Hover
70 |
71 |
72 |
73 | {(args) => (
74 | <>
75 |
76 |
77 |
78 | >
79 | )}
80 |
81 |
82 |
83 | ## Props
84 |
85 |
86 |
--------------------------------------------------------------------------------
/.github/workflows/pull_request.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request
2 | on:
3 | pull_request:
4 | branches:
5 | - main
6 |
7 | jobs:
8 | linter:
9 | name: Lint
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v2
15 |
16 | - name: Setup Node
17 | uses: actions/setup-node@v2.5.1
18 | with:
19 | node-version: '16.x'
20 |
21 | - name: Install Yarn
22 | run: npm install -g yarn
23 |
24 | - name: Install Dependencies
25 | run: yarn
26 |
27 | - name: Run Linter
28 | run: yarn lint
29 |
30 | test_build:
31 | name: Test & Build
32 | runs-on: ubuntu-latest
33 |
34 | steps:
35 | - name: Checkout
36 | uses: actions/checkout@v2
37 |
38 | - name: Setup Node
39 | uses: actions/setup-node@v2.5.1
40 | with:
41 | node-version: '16.x'
42 |
43 | - name: Install Yarn
44 | run: npm install -g yarn
45 |
46 | - name: Install Dependencies
47 | run: yarn
48 |
49 | - name: Run Unit-Tests
50 | run: yarn coverage:unit
51 |
52 | - name: Install Playwright
53 | run: npx playwright install --with-deps
54 |
55 | - name: Run Build storybook
56 | run: yarn storybook:build
57 |
58 | - name: Serve Storybook and run tests
59 | run: |
60 | npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
61 | "npx http-server dist-storybook --port 6006 --silent" \
62 | "npx wait-on tcp:6006 && yarn run coverage:storybook"
63 |
64 | - name: Run Build
65 | run: yarn build
66 |
67 | - name: Merge Test Coverage
68 | run: "yarn run coverage:merge && yarn run coverage:merge-report"
69 |
70 | - name: Run sonar scanner
71 | uses: SonarSource/sonarcloud-github-action@v1.6
72 | env:
73 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
75 |
--------------------------------------------------------------------------------
/src/Typography/TypographyRoot.tsx:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import {
3 | mixin as globalMixin,
4 | TypographyUnit,
5 | Size,
6 | Status,
7 | Variant,
8 | } from '@mycompany/design-system/Styleguide'
9 | import { TypographyAlign } from '@mycompany/design-system/index'
10 | import * as mixin from './mixin'
11 |
12 | type Props = {
13 | display: TypographyUnit
14 | lighten?: number | false
15 | status: Status
16 | align?: TypographyAlign
17 | through?: boolean
18 | variant?: Variant
19 | spacing: Size | false
20 | }
21 |
22 | export default styled.span`
23 | ${({ align }) =>
24 | align &&
25 | css`
26 | text-align: ${align};
27 | `}
28 | ${({ through }) =>
29 | through &&
30 | css`
31 | text-decoration: line-through;
32 | `}
33 | ${({ display, theme, lighten }) =>
34 | (display === TypographyUnit.Headline1 ||
35 | display === TypographyUnit.Headline2 ||
36 | display === TypographyUnit.Headline3 ||
37 | display === TypographyUnit.Headline4) &&
38 | css`
39 | color: ${lighten
40 | ? theme.color.variant.primary.base.lighten(lighten).hex()
41 | : theme.color.variant.primary.base.hex()};
42 | `}
43 | ${({ display, theme, lighten }) =>
44 | display === TypographyUnit.Action &&
45 | css`
46 | color: ${lighten
47 | ? theme.color.variant.primary.base.lighten(lighten).hex()
48 | : theme.color.variant.primary.base.hex()};
49 | display: inline-block;
50 | `}
51 |
52 | ${({ display, theme, lighten }) =>
53 | display === TypographyUnit.Caption &&
54 | css`
55 | color: ${lighten
56 | ? theme.color.font.lighten(lighten).hex()
57 | : theme.color.font.lighten(1).hex()};
58 | `}
59 |
60 | /* Global Mixins */
61 | ${({ display }) => globalMixin.typography[display]}
62 | ${({ spacing, theme }) => globalMixin.margin({ spacing, theme })}
63 |
64 | /* States */
65 | ${mixin.status}
66 | ${({ variant }) =>
67 | variant &&
68 | css`
69 | ${mixin.variant}
70 | `}
71 | `
72 |
--------------------------------------------------------------------------------
/src/ContentBox/ContentBoxRoot.tsx:
--------------------------------------------------------------------------------
1 | import styled, { css, DefaultTheme } from 'styled-components'
2 | import { mixin as globalMixin, Size } from '@mycompany/design-system/Styleguide'
3 | import { Opacity, Variant } from '@mycompany/design-system/index'
4 |
5 | type Props = {
6 | background?: Variant
7 | spacing: Size
8 | opacity?: Opacity
9 | border?: boolean | false
10 | margin: string | object
11 | theme: DefaultTheme
12 | }
13 |
14 | const margin = ({ theme, ...props }: Props) => {
15 | let marginStyle = ''
16 | if (typeof props.margin === 'object') {
17 | Object.entries(props.margin).forEach(([key, value]) => {
18 | const checkKey = Object.keys(Size).filter(
19 | (items) =>
20 | items.toLowerCase() === (key === 'xLarge' ? 'extralarge' : key)
21 | )
22 | if (checkKey.length === 0) {
23 | marginStyle += `
24 | margin-${key}: ${
25 | /\d/.test(value) ? value : theme.spacing.size[value].rem()
26 | };
27 | `
28 | }
29 |
30 | if (checkKey.length === 1) {
31 | marginStyle += `
32 | @media (min-width: ${theme.breakpoint[key].px()}) {
33 | margin: ${value}
34 | }
35 | `
36 | }
37 | })
38 | return css`
39 | ${marginStyle}
40 | `
41 | }
42 |
43 | if (typeof props.margin === 'string') {
44 | return css`
45 | margin: ${theme.spacing.size[props.margin].px() || '0'};
46 | `
47 | }
48 |
49 | return false
50 | }
51 |
52 | export default styled.div`
53 | ${({ opacity }) =>
54 | opacity &&
55 | `
56 | opacity: ${opacity};
57 | `}
58 | ${({ background, theme }) =>
59 | background &&
60 | background === Variant.Secondary &&
61 | `
62 | background: ${theme.color.appBackground.hex()};
63 | `}
64 | ${({ border, theme }) =>
65 | border &&
66 | css`
67 | border: 1px solid ${theme.color.variant.secondary.base.hex()};
68 | `}
69 | ${({ spacing, theme }) => globalMixin.spacing({ spacing, theme })}
70 | ${css`
71 | ${margin}
72 | `}
73 | `
74 |
--------------------------------------------------------------------------------
/src/Button/ButtonRoot.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import {
3 | mixin as globalMixin,
4 | Size,
5 | Variant,
6 | ElevationLevel,
7 | Status,
8 | } from '@mycompany/design-system/Styleguide'
9 | import * as mixin from './mixin'
10 |
11 | import { AttachmentPos } from './types'
12 |
13 | type Props = {
14 | size: Size
15 | variant: Variant
16 | status: Status
17 | disabled: boolean
18 | elevation: ElevationLevel
19 | fullWidth: boolean
20 | pill: boolean
21 | link: boolean
22 | noBackground: boolean
23 | attachmentPos: AttachmentPos
24 | elementSpacing: Size | false
25 | isLoading: boolean
26 | hasLabel: boolean
27 | iconOnly: boolean
28 | }
29 |
30 | export default styled.button`
31 | -webkit-appearance: none;
32 | -webkit-tap-highlight-color: transparent;
33 | align-items: center;
34 | border: 0;
35 |
36 | border-radius: ${({ theme }) => theme.shape.borderRadius.rem()};
37 | cursor: ${({ isLoading, disabled }) =>
38 | (disabled && 'not-allowed') || (isLoading && 'wait') || 'pointer'};
39 |
40 | display: inline-flex;
41 | flex-direction: ${({ attachmentPos }) => attachmentPos};
42 | justify-content: center;
43 | line-height: 1;
44 | min-width: ${({ hasLabel }) => (hasLabel ? '64px' : 'none')};
45 | outline: 0;
46 | position: relative;
47 | text-decoration: none;
48 |
49 | /* Global Mixins */
50 | ${globalMixin.typography.action}
51 |
52 | /* States */
53 | ${mixin.variant}
54 | ${mixin.status}
55 | ${mixin.size}
56 | ${mixin.pill}
57 | ${mixin.link}
58 | ${mixin.noBackground}
59 | ${mixin.fullWidth}
60 | ${mixin.disabled}
61 | ${mixin.elevation}
62 | ${mixin.iconOnly}
63 | transition: background ${({ theme }) => theme.transition.duration.shortest}ms
64 | ${({ theme }) => theme.transition.easing.easeIn},
65 | box-shadow ${({ theme }) => theme.transition.duration.shortest}ms
66 | ${({ theme }) => theme.transition.easing.easeIn};
67 | vertical-align: middle;
68 | ${({ theme, elementSpacing }) =>
69 | elementSpacing &&
70 | `
71 | grid-gap: ${theme.spacing.size[elementSpacing].rem()};
72 | padding: ${theme.spacing.size[elementSpacing].rem()};
73 | `}
74 | `
75 |
--------------------------------------------------------------------------------
/src/Styleguide/mixin/typography.ts:
--------------------------------------------------------------------------------
1 | import { css, StyledProps, FlattenSimpleInterpolation } from 'styled-components'
2 | import { TypographyUnit } from '../theme/types'
3 |
4 | type TypographyMixins = {
5 | [key in TypographyUnit]: (
6 | props: StyledProps<{}>
7 | ) => FlattenSimpleInterpolation
8 | }
9 |
10 | const typographyMixinFactory =
11 | (typographyUnit: TypographyUnit) => (props: StyledProps<{}>) =>
12 | css`
13 | color: ${props.theme.typography[typographyUnit].color?.hex()};
14 | font-family: ${props.theme.typography[typographyUnit].fontFamily};
15 | font-size: ${props.theme.typography[typographyUnit].fontSize.rem()};
16 | font-weight: ${props.theme.typography[typographyUnit].fontWeight};
17 | line-height: ${props.theme.typography[typographyUnit].lineHeight};
18 | `
19 |
20 | const mixinSet: TypographyMixins = {
21 | [TypographyUnit.Base]: typographyMixinFactory(TypographyUnit.Base),
22 | [TypographyUnit.Mobile]: typographyMixinFactory(TypographyUnit.Mobile),
23 | [TypographyUnit.Body1]: typographyMixinFactory(TypographyUnit.Body1),
24 | [TypographyUnit.Body2]: typographyMixinFactory(TypographyUnit.Body2),
25 | [TypographyUnit.Caption]: typographyMixinFactory(TypographyUnit.Caption),
26 | [TypographyUnit.Headline1]: typographyMixinFactory(TypographyUnit.Headline1),
27 | [TypographyUnit.Headline2]: typographyMixinFactory(TypographyUnit.Headline2),
28 | [TypographyUnit.Headline3]: typographyMixinFactory(TypographyUnit.Headline3),
29 | [TypographyUnit.Headline4]: typographyMixinFactory(TypographyUnit.Headline4),
30 | [TypographyUnit.Headline5]: typographyMixinFactory(TypographyUnit.Headline5),
31 | [TypographyUnit.Action]: typographyMixinFactory(TypographyUnit.Action),
32 | [TypographyUnit.FootNote]: typographyMixinFactory(TypographyUnit.FootNote),
33 | [TypographyUnit.Input]: typographyMixinFactory(TypographyUnit.Input),
34 | [TypographyUnit.Label]: typographyMixinFactory(TypographyUnit.Label),
35 | [TypographyUnit.FooterHeading]: typographyMixinFactory(
36 | TypographyUnit.FooterHeading
37 | ),
38 | [TypographyUnit.FooterPhrasing]: typographyMixinFactory(
39 | TypographyUnit.FooterPhrasing
40 | ),
41 | }
42 |
43 | export default mixinSet
44 |
--------------------------------------------------------------------------------
/src/CssGrid/CssGrid.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Size } from '@mycompany/design-system/Styleguide'
3 | import {
4 | GridAlignContent,
5 | GridAlignItems,
6 | GridJustify,
7 | GridDirection,
8 | GridArea,
9 | GridColumn,
10 | GridAutoFlow,
11 | } from './types'
12 |
13 | import { CssGridRoot } from './CssGridStyles'
14 |
15 | type Props = React.PropsWithChildren<{
16 | alignContent?: GridAlignContent | false
17 | alignItems?: GridAlignItems | false
18 | alignSelf?: GridAlignContent | false | object
19 | justify?: GridJustify | false | object
20 | direction?: GridDirection
21 | spacing?: Size
22 | gridRow?: string | object
23 | gridArea?: GridArea | object
24 | gridGap?: Size | object
25 | gridAutoFlow?: GridAutoFlow | object
26 | gridColumns?: number | object | GridColumn | string
27 | gridTemplate?: GridArea | object
28 | gridItems?: string | false | object
29 | gridAutoFill?: number
30 | container?: boolean
31 | item?: boolean
32 | component?: keyof JSX.IntrinsicElements
33 | }>
34 |
35 | const defaultSpacing = Size.Zero
36 | const defaultGap = Size.Medium
37 |
38 | const CssGrid: React.FC = React.forwardRef(
39 | (
40 | {
41 | alignContent = false,
42 | alignItems = false,
43 | alignSelf = false,
44 | justify = false,
45 | direction,
46 | gridGap = defaultGap,
47 | container = false,
48 | item = false,
49 | spacing = defaultSpacing,
50 | gridRow,
51 | gridAutoFlow,
52 | gridArea,
53 | gridTemplate,
54 | gridColumns,
55 | gridItems,
56 | gridAutoFill,
57 | children,
58 | component = 'div',
59 | }: Props,
60 | ref
61 | ) => (
62 |
82 | {children}
83 |
84 | )
85 | )
86 |
87 | export default CssGrid
88 |
--------------------------------------------------------------------------------
/src/CssGrid/CssGrid.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { CssGrid } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Size } from '../Styleguide'
5 |
6 | export default {
7 | /* 👇 The title prop is optional.
8 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
9 | * to learn how to generate automatic titles
10 | */
11 | title: 'Component/CssGrid',
12 | component: CssGrid,
13 | } as ComponentMeta
14 |
15 | import { GridArea, GridColumn } from './types'
16 |
17 | export const Introduction: ComponentStory = (args) => (
18 |
19 |
20 | Default
21 |
22 |
23 | Default
24 |
25 |
26 | Default
27 |
28 |
29 | Default
30 |
31 |
32 | Default
33 |
34 |
35 | )
36 |
37 | export const GridAutoFill: ComponentStory = () => (
38 |
39 |
40 | auto fill 250
41 |
42 |
43 | auto fill 250
44 |
45 |
46 | auto fill 250
47 |
48 |
49 | auto fill 250
50 |
51 |
52 | auto fill 250
53 |
54 |
55 | )
56 |
57 | export const GridTemplateWarenkorb: ComponentStory = () => (
58 |
63 |
64 | content
65 |
66 |
67 | sidebar
68 |
69 |
70 | )
71 |
--------------------------------------------------------------------------------
/src/Typography/Typography.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Status,
4 | TypographyUnit,
5 | Size,
6 | Variant,
7 | } from '@mycompany/design-system/Styleguide'
8 |
9 | import TypographyRoot from './TypographyRoot'
10 |
11 | import { DisplayComponentMap, TypographyAlign } from './types'
12 |
13 | type Props = React.PropsWithChildren<{
14 | align?: TypographyAlign
15 | display: TypographyUnit
16 | lighten?: number | false
17 | through?: boolean
18 | component?: keyof JSX.IntrinsicElements
19 | status?: Status
20 | variant?: Variant
21 | spacing?: Size | false
22 | className?: string
23 | }>
24 |
25 | /* @TODO inline constants, only a storybook react-docgen-typescript quickfix */
26 | const defaultStatus = Status.Default
27 | const defaultSpacing = Size.Zero
28 |
29 | const Typography: React.FC = React.forwardRef(
30 | (
31 | {
32 | display,
33 | lighten,
34 | component,
35 | children,
36 | align = TypographyAlign.Left,
37 | through = false,
38 | spacing = defaultSpacing,
39 | status = defaultStatus,
40 | variant,
41 | className,
42 | ...htmlAttributes
43 | }: React.PropsWithChildren,
44 | ref
45 | ) => (
46 |
60 | {children}
61 |
62 | )
63 | )
64 |
65 | const componentMap: DisplayComponentMap = {
66 | [TypographyUnit.Base]: 'p',
67 | [TypographyUnit.Mobile]: 'p',
68 | [TypographyUnit.Body1]: 'p',
69 | [TypographyUnit.Body2]: 'p',
70 | [TypographyUnit.Headline1]: 'h1',
71 | [TypographyUnit.Headline2]: 'h2',
72 | [TypographyUnit.Headline3]: 'h3',
73 | [TypographyUnit.Headline4]: 'h4',
74 | [TypographyUnit.Headline5]: 'h5',
75 | [TypographyUnit.Action]: 'span',
76 | [TypographyUnit.Input]: 'input',
77 | [TypographyUnit.Caption]: 'p',
78 | [TypographyUnit.FootNote]: 'p',
79 | [TypographyUnit.Label]: 'label',
80 | [TypographyUnit.FooterHeading]: 'span',
81 | [TypographyUnit.FooterPhrasing]: 'span',
82 | }
83 |
84 | export default Typography
85 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "parserOptions": {
4 | "ecmaVersion": 2020,
5 | "sourceType": "module",
6 | "ecmaFeatures": {
7 | "jsx": true
8 | }
9 | },
10 | "settings": {
11 | "react": {
12 | "version": "detect"
13 | },
14 | "import/resolver": {
15 | "node": {
16 | "extensions": [
17 | ".js",
18 | ".jsx",
19 | ".ts",
20 | ".tsx"
21 | ]
22 | },
23 | "alias": {
24 | "extensions": [
25 | ".js",
26 | ".jsx",
27 | ".ts",
28 | ".tsx"
29 | ],
30 | "map": [
31 | [
32 | "@mycompany/design-system/*",
33 | "./src/*"
34 | ]
35 | ]
36 | }
37 | }
38 | },
39 | "extends": [
40 | "react-app",
41 | "airbnb",
42 | "plugin:jsx-a11y/recommended",
43 | "prettier",
44 | "prettier/react",
45 | "plugin:styled-components-a11y/recommended"
46 | ],
47 | "plugins": [
48 | "jsx-a11y",
49 | "prettier",
50 | "better-styled-components",
51 | "styled-components-a11y"
52 | ],
53 | "rules": {
54 | "import/no-unresolved": "off",
55 | "import/prefer-default-export": "off",
56 | "react/jsx-props-no-spreading": "off",
57 | "import/no-extraneous-dependencies": [
58 | "error",
59 | {
60 | "devDependencies": [
61 | "src/__tests__/**",
62 | "**/*.stories.{js,ts,tsx}",
63 | "**/*.test.{js,ts,tsx}",
64 | "**/*.spec.{js,ts,tsx}"
65 | ]
66 | }
67 | ],
68 | "no-undef": "off",
69 | "prettier/prettier": [
70 | "error"
71 | ],
72 | "no-use-before-define": "off",
73 | "react/jsx-filename-extension": [
74 | 2,
75 | {
76 | "extensions": [
77 | ".js",
78 | ".jsx",
79 | ".ts",
80 | ".tsx"
81 | ]
82 | }
83 | ],
84 | "better-styled-components/sort-declarations-alphabetically": 2,
85 | "@typescript-eslint/no-use-before-define": [
86 | "error"
87 | ],
88 | "react/require-default-props": "off",
89 | "no-unused-vars": "off",
90 | "no-shadow": "off",
91 | "import/extensions": [
92 | "error",
93 | "ignorePackages",
94 | {
95 | "js": "never",
96 | "jsx": "never",
97 | "ts": "never",
98 | "tsx": "never"
99 | }
100 | ]
101 | }
102 | }
--------------------------------------------------------------------------------
/src/Navigation/Navigation.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ComponentStory, ComponentMeta } from '@storybook/react'
3 | import { ChevronRightIcon } from '@mycompany/design-system/Icon'
4 | import { Size } from '@mycompany/design-system/Styleguide'
5 | import { Navigation, NavigationItem } from './index'
6 |
7 | export default {
8 | /* 👇 The title prop is optional.
9 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
10 | * to learn how to generate automatic titles
11 | */
12 | title: 'Component/Navigation',
13 | component: Navigation,
14 | argTypes: {
15 | spacing: {
16 | options: { None: false, ...Size },
17 | control: { type: 'radio' },
18 | },
19 | },
20 | } as ComponentMeta
21 |
22 | const Template: ComponentStory = (args) => (
23 | <>
24 |
25 |
26 | {`Das Beste fürs Baby`}
27 |
28 |
29 |
30 | {`Deko & Wohnen`}
31 |
32 |
33 |
34 | {`Grüne Küche`}
35 |
36 |
37 |
38 | {`Rucksäcke`}
39 |
40 |
41 |
42 | {`Sneaker`}
43 |
44 |
45 |
46 | {`Vegan`}
47 |
48 |
49 |
50 | {`Yoga`}
51 |
52 |
53 |
54 | >
55 | )
56 |
57 | export const Introduction: ComponentStory = Template.bind({})
58 |
59 | export const Vertical: ComponentStory = Template.bind({})
60 | Vertical.args = {
61 | orientation: 'vertical',
62 | }
63 |
64 | export const Horizontal: ComponentStory = (args) => (
65 | <>
66 |
67 | {`Das Beste fürs Baby`}
68 | {`Deko & Wohnen`}
69 | {`Grüne Küche`}
70 | {`Rucksäcke`}
71 | {`Sneaker`}
72 | {`Vegan`}
73 | {`Yoga`}
74 |
75 | >
76 | )
77 | Horizontal.args = {
78 | orientation: 'horizontal',
79 | }
80 |
--------------------------------------------------------------------------------
/src/Styleguide/theme/color.ts:
--------------------------------------------------------------------------------
1 | import { Variant, Status } from '../types'
2 | import palette from './palette'
3 | import { Color } from './types'
4 |
5 | const color: Color = {
6 | font: palette.nero,
7 | fontLight: palette.nero.lighten(1),
8 | appBackground: palette.alabaster,
9 | contentBackground: palette.white,
10 | divider: palette.maverick,
11 | dividerInverted: palette.white,
12 | variant: {
13 | [Variant.Primary]: {
14 | base: palette.patone,
15 | font: palette.white,
16 | hover: palette.patone.darken(0.1),
17 | active: palette.patone.darken(0.2),
18 | },
19 | [Variant.Secondary]: {
20 | base: palette.alabaster,
21 | font: palette.nero,
22 | hover: palette.alabaster.darken(0.1),
23 | active: palette.alabaster.darken(0.2),
24 | },
25 | [Variant.Tertiary]: {
26 | base: palette.nero,
27 | font: palette.nero.lighten(1),
28 | hover: palette.nero.lighten(1.2),
29 | active: palette.nero.darken(1.2),
30 | }
31 | },
32 | status: {
33 | [Status.SoldOut]: {
34 | base: palette.soda,
35 | font: palette.soda,
36 | hover: palette.soda.lighten(0.1),
37 | active: palette.soda.lighten(0.2),
38 | },
39 | [Status.Discount]: {
40 | base: palette.pyroman,
41 | font: palette.pyroman,
42 | hover: palette.pyroman.lighten(0.1),
43 | active: palette.pyroman.lighten(0.2),
44 | },
45 | [Status.Lighten]: {
46 | base: palette.lighten,
47 | font: palette.lighten,
48 | hover: palette.pyroman.darken(0.1),
49 | active: palette.pyroman.darken(0.2),
50 | },
51 | [Status.Success]: {
52 | base: palette.kelly,
53 | font: palette.nero,
54 | hover: palette.kelly.darken(0.1),
55 | active: palette.kelly.darken(0.2),
56 | },
57 | [Status.Warning]: {
58 | base: palette.honey,
59 | font: palette.nero,
60 | hover: palette.honey.darken(0.1),
61 | active: palette.honey.darken(0.2),
62 | },
63 | [Status.Error]: {
64 | base: palette.soda,
65 | font: palette.nero,
66 | hover: palette.soda.darken(0.1),
67 | active: palette.soda.darken(0.2),
68 | },
69 | },
70 | breadcrumbs: {
71 | anchor: palette.dim,
72 | divider: palette.hillary,
73 | },
74 | form: {
75 | borderColor: palette.maverick,
76 | backgroundColor: palette.white,
77 | backgroundColorDisabled: palette.white.darken(0.02),
78 | font: palette.nero,
79 | placeholderFont: palette.maverick,
80 | },
81 | list: palette.dim,
82 | rating: {
83 | empty: palette.dim.lighten(1.1),
84 | filled: palette.patone,
85 | },
86 | }
87 |
88 | export default color
89 |
--------------------------------------------------------------------------------
/src/Icon/index.ts:
--------------------------------------------------------------------------------
1 | import { FC } from 'react'
2 | import { SvgIcon, SvgIconRoot, IconHover } from './base'
3 | import { IconName, Props } from './types'
4 | import ChevronDownIcon from './ChevronDownIcon'
5 | import CheckboxUncheckedIcon from './CheckboxUncheckedIcon'
6 | import CheckboxCheckedIcon from './CheckboxCheckedIcon'
7 | import ChevronRightIcon from './ChevronRightIcon'
8 | import ChevronUpIcon from './ChevronUpIcon'
9 | import HeartIcon from './HeartIcon'
10 | import CartIcon from './CartIcon'
11 | import SearchIcon from './SearchIcon'
12 | import CloseIcon from './CloseIcon'
13 | import UserIcon from './UserIcon'
14 | import BankTransfer from './BankTransfer'
15 | import GiroPay from './GiroPay'
16 | import Visa from './Visa'
17 | import MasterCard from './MasterCard'
18 | import Amex from './Amex'
19 | import SofortKlarna from './SofortKlarna'
20 | import BurgerIcon from './BurgerIcon'
21 | import FaceBookIcon from './FacebookIcon'
22 | import TwitterIcon from './TwitterIcon'
23 | import PinterestIcon from './PinterestIcon'
24 | import InstagramIcon from './InstagramIcon'
25 | import GiftIcon from './GiftIcon'
26 |
27 | const iconMap: {
28 | [key in IconName]: FC
29 | } = {
30 | [IconName.GiftIcon]: GiftIcon,
31 | [IconName.FaceBookIcon]: FaceBookIcon,
32 | [IconName.PinterestIcon]: PinterestIcon,
33 | [IconName.InstagramIcon]: InstagramIcon,
34 | [IconName.TwitterIcon]: TwitterIcon,
35 | [IconName.SofortKlarna]: SofortKlarna,
36 | [IconName.Burger]: BurgerIcon,
37 | [IconName.MasterCard]: MasterCard,
38 | [IconName.Amex]: Amex,
39 | [IconName.Visa]: Visa,
40 | [IconName.GiroPay]: GiroPay,
41 | [IconName.BankTransfer]: BankTransfer,
42 | [IconName.Cart]: CartIcon,
43 | [IconName.ChevronDown]: ChevronDownIcon,
44 | [IconName.CheckboxUncheckedIcon]: CheckboxUncheckedIcon,
45 | [IconName.CheckboxCheckedIcon]: CheckboxCheckedIcon,
46 | [IconName.ChevronRightIcon]: ChevronRightIcon,
47 | [IconName.ChevronUpIcon]: ChevronUpIcon,
48 | [IconName.Close]: CloseIcon,
49 | [IconName.Heart]: HeartIcon,
50 | [IconName.Search]: SearchIcon,
51 | [IconName.User]: UserIcon,
52 | }
53 | const IconComponents = {
54 | SvgIconRoot,
55 | }
56 |
57 | export {
58 | iconMap,
59 | IconName,
60 | SvgIcon,
61 | FaceBookIcon,
62 | PinterestIcon,
63 | InstagramIcon,
64 | GiftIcon,
65 | TwitterIcon,
66 | GiroPay,
67 | Visa,
68 | MasterCard,
69 | Amex,
70 | SofortKlarna,
71 | IconHover,
72 | BankTransfer,
73 | CheckboxUncheckedIcon,
74 | CheckboxCheckedIcon,
75 | ChevronDownIcon,
76 | ChevronRightIcon,
77 | ChevronUpIcon,
78 | HeartIcon,
79 | SearchIcon,
80 | CartIcon,
81 | CloseIcon,
82 | UserIcon,
83 | IconComponents,
84 | BurgerIcon,
85 | }
86 |
--------------------------------------------------------------------------------
/src/Styleguide/types.ts:
--------------------------------------------------------------------------------
1 | export enum Size {
2 | Zero = 'zero',
3 | Tiny = 'tiny',
4 | Small = 'small',
5 | Medium = 'medium',
6 | Large = 'large',
7 | ExtraLarge = 'xLarge',
8 | }
9 |
10 | export enum Variant {
11 | Primary = 'primary',
12 | Secondary = 'secondary',
13 | Tertiary = 'tertiary'
14 | }
15 |
16 | export enum Status {
17 | SoldOut = 'soldOut',
18 | Discount = 'discount',
19 | Default = 'default',
20 | Success = 'success',
21 | Warning = 'warning',
22 | Error = 'error',
23 | Lighten = 'lighten'
24 | }
25 |
26 | export enum FontWeight {
27 | Light = 'light',
28 | Regular = 'regular',
29 | Bold = 'bold',
30 | }
31 |
32 | export enum Easing {
33 | EaseIn = 'easeIn',
34 | EaseOut = 'easeOut',
35 | EaseInOut = 'easeInOut',
36 | Sharp = 'sharp',
37 | }
38 |
39 | export enum Duration {
40 | Shortest = 'shortest',
41 | Shorter = 'shorter',
42 | Short = 'short',
43 | Default = 'default',
44 | Complex = 'complex',
45 | EnteringScreen = 'enteringScreen',
46 | LeavingScreen = 'leavingScreen',
47 | }
48 |
49 | export enum ElevationLevel {
50 | None = 'none',
51 | Primary = 'primary',
52 | Secondary = 'secondary',
53 | Modal = 'modal',
54 | }
55 |
56 | export enum FlexboxAlignment {
57 | Baseline = 'baseline',
58 | Center = 'center',
59 | FlexEnd = 'flex-end',
60 | FlexStart = 'flex-start',
61 | Stretch = 'stretch',
62 | }
63 |
64 | export enum FlexboxAlignContent {
65 | FlexStart = 'flex-start',
66 | FlexEnd = 'flex-end',
67 | Center = 'center',
68 | Stretch = 'stretch',
69 | SpaceBetween = 'space-between',
70 | SpaceAround = 'space-around',
71 | }
72 |
73 | export enum FlexboxAlignItems {
74 | FlexStart = 'flex-start',
75 | FlexEnd = 'flex-end',
76 | Center = 'center',
77 | Stretch = 'stretch',
78 | SpaceBetween = 'space-between',
79 | SpaceAround = 'space-around',
80 | }
81 |
82 | export enum FlexboxDirection {
83 | Column = 'column',
84 | ColumnReverse = 'column-reverse',
85 | Row = 'row',
86 | RowReverse = 'row-reverse',
87 | }
88 |
89 | export enum FlexboxJustification {
90 | Center = 'center',
91 | FlexEnd = 'flex-end',
92 | FlexStart = 'flex-start',
93 | SpaceAround = 'space-around',
94 | SpaceBetween = 'space-between',
95 | SpaceEvenly = 'space-evenly',
96 | }
97 |
98 | export enum FlexboxWrap {
99 | Nowrap = 'nowrap',
100 | Wrap = 'wrap',
101 | WrapReverse = 'wrap-reverse',
102 | }
103 |
104 | export enum ObjectFit {
105 | Initial = 'initial',
106 | Cover = 'cover',
107 | Contain = 'contain',
108 | Fill = 'fill',
109 | ScaleDown = 'scale-down',
110 | Revert = 'revert',
111 | Unset = 'unset',
112 | }
--------------------------------------------------------------------------------
/src/TextLink/TextLink.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Meta, ArgsTable, Canvas } from '@storybook/addon-docs'
2 | import { Typography } from '@mycompany/design-system/Typography'
3 | import {
4 | Size,
5 | Variant,
6 | Status,
7 | TypographyUnit,
8 | } from '@mycompany/design-system/Styleguide'
9 | import { HeartIcon } from '@mycompany/design-system/Icon'
10 | import TextLink from './TextLink'
11 |
12 |
29 |
30 | # Default
31 |
32 |
33 |
34 | {(args) => (
35 |
36 |
37 | Verkauf und Versand durch
38 |
39 |
40 | )}
41 |
42 |
43 |
44 |
45 |
46 | {(args) => (
47 |
48 |
49 | Verkauf und Versand durch
50 |
51 |
52 | )}
53 |
54 |
55 |
56 |
57 |
58 | {(args) => (
59 |
60 |
61 | Verkauf und Versand durch
62 |
63 |
64 | )}
65 |
66 |
67 |
68 | # Icon Text
69 |
70 |
71 | , variant: Variant.Secondary }}
74 | >
75 | {(args) => (
76 |
77 |
78 | Verkauf und Versand durch
79 |
80 |
81 | )}
82 |
83 |
84 |
85 | # Icon Only
86 |
87 |
88 | , spacing: Size.Medium }}
91 | >
92 | {(args) => }
93 |
94 |
95 |
96 |
97 | }}
100 | >
101 | {(args) => }
102 |
103 |
104 |
--------------------------------------------------------------------------------
/.github/workflows/push.yml:
--------------------------------------------------------------------------------
1 | name: Push
2 | on:
3 | push:
4 | branches:
5 | - main
6 | env:
7 | BUCKET_NAME: design-system-staging-mycompany
8 |
9 | jobs:
10 | release:
11 | name: Release
12 | runs-on: ubuntu-18.04
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v2
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v2.5.1
21 | with:
22 | node-version: '16.x'
23 |
24 | - name: Install Yarn
25 | run: npm install -g yarn
26 |
27 | - name: Install Dependencies
28 | run: yarn
29 |
30 | - name: Run Unit-Tests
31 | run: yarn coverage:unit
32 |
33 | - name: Install Playwright
34 | run: npx playwright install --with-deps
35 |
36 | - name: Run Build storybook
37 | run: yarn storybook:build
38 |
39 | - name: Serve Storybook and run tests
40 | run: |
41 | npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
42 | "npx http-server dist-storybook --port 6006 --silent" \
43 | "npx wait-on tcp:6006 && yarn test:storybook --config-dir build/storybook"
44 |
45 | - name: Merge Test Coverage
46 | run: 'yarn run coverage:merge && yarn run coverage:merge-report'
47 |
48 | - name: Run sonar scanner
49 | uses: SonarSource/sonarcloud-github-action@v1.6
50 | env:
51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
53 |
54 | - name: Run Build
55 | run: yarn build
56 |
57 | - name: Release
58 | env:
59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60 | NPM_TOKEN: ${{ secrets.AVS_PIPELINE_PAT }}
61 | SEMANTIC_RELEASE_PACKAGE: '@mycompany/design-system'
62 | run: npx semantic-release
63 |
64 | deploy_storybook:
65 | name: Deploy Storybook
66 | runs-on: ubuntu-latest
67 |
68 | steps:
69 | - name: Checkout
70 | uses: actions/checkout@v2
71 |
72 | - name: Setup Node
73 | uses: actions/setup-node@v2.5.1
74 | with:
75 | node-version: '16.x'
76 |
77 | - name: Install Yarn
78 | run: npm install -g yarn
79 |
80 | - name: Install Dependencies
81 | run: yarn
82 |
83 | - name: Run Build
84 | run: yarn storybook:build
85 |
86 | - name: Setup GCloud Credentials
87 | uses: google-github-actions/setup-gcloud@v0
88 | with:
89 | service_account_key: ${{ secrets.GCP_PIPELINE_SA_MARKETPLACE_STAGING }}
90 | export_default_credentials: true
91 |
92 | - name: Upload Files
93 | uses: google-github-actions/upload-cloud-storage@main
94 | with:
95 | path: dist-storybook
96 | destination: ${{ env.BUCKET_NAME }}
97 | parent: false
98 |
--------------------------------------------------------------------------------
/src/Price/Price.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | // import { Price } from './'
3 | // import { render } from '../__tests__/utils/render'
4 | // import { screen } from '@testing-library/react'
5 | // import { Currency } from '@mycompany/design-system/index'
6 | // import { render } from 'react-dom'
7 | // import { render } from '@testing-library/react';
8 | import {
9 | SimplePrice,
10 | WithoutUvp,
11 | FullPrice,
12 | WithoutBestPriceDiscount,
13 | } from './Price.stories'
14 | import { render } from '../__tests__/utils/render'
15 | import { screen } from '@testing-library/react'
16 |
17 | describe('``', () => {
18 | it('should only render product price', () => {
19 | render( )
20 |
21 | expect(screen.getByTestId('price')).toBeVisible()
22 |
23 | expect(screen.getByTestId('price').textContent).toContain(
24 | SimplePrice.args?.productPrice
25 | )
26 |
27 | expect(screen.getByTestId('product-price')).toBeVisible()
28 | expect(screen.queryByTestId('recommended-price')).not.toBeInTheDocument()
29 | expect(screen.queryByTestId('base-price')).not.toBeInTheDocument()
30 | expect(screen.queryByTestId('best-price')).not.toBeInTheDocument()
31 | })
32 |
33 | it('should render price tracking data', () => {
34 | const { container } = render( )
35 |
36 | expect(
37 | container.querySelector(
38 | `[data-tracking-price="${SimplePrice.args?.productPrice}"]`
39 | )
40 | ).toBeInTheDocument()
41 | })
42 |
43 | it('should render all price information', () => {
44 | // render( )
45 | const { getByTestId } = render( )
46 |
47 | expect(getByTestId('price')).toBeVisible()
48 |
49 | expect(getByTestId('product-price').textContent).toContain(
50 | FullPrice.args?.productPrice
51 | )
52 |
53 | expect(screen.getByTestId('price')).toBeVisible()
54 | expect(screen.getByTestId('product-price')).toBeVisible()
55 | expect(screen.getByTestId('recommended-price')).toBeVisible()
56 | expect(screen.getByTestId('recommended-price').textContent).toBe(
57 | 'UVP: 100€ '
58 | )
59 | expect(screen.getByTestId('base-price')).toBeVisible()
60 | expect(screen.getByTestId('best-price')).toBeVisible()
61 | })
62 |
63 | it('should render price without uvp', () => {
64 | render( )
65 | expect(screen.getByTestId('recommended-price').textContent).toBe(' 100€ ')
66 | expect(screen.getByTestId('recommended-price').textContent).not.toContain(
67 | 'UVP'
68 | )
69 | })
70 |
71 | it('should render best price without best price discount', () => {
72 | render( )
73 | expect(screen.getByTestId('best-price').textContent).toContain(
74 | WithoutBestPriceDiscount.args?.bestPrice
75 | )
76 | })
77 | })
78 |
--------------------------------------------------------------------------------
/src/Icon/SofortKlarna.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const SofortKlarna: React.FC = ({ payments }: Props) => (
6 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | )
25 |
26 | export default SofortKlarna
27 |
--------------------------------------------------------------------------------
/src/Grid/GridRoot.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components'
2 | import {
3 | Size,
4 | FlexboxDirection,
5 | FlexboxWrap,
6 | FlexboxJustification,
7 | FlexboxAlignContent,
8 | FlexboxAlignItems,
9 | } from '@mycompany/design-system/Styleguide'
10 |
11 | import {
12 | flexWrap,
13 | justify,
14 | alignItems,
15 | alignContent,
16 | height,
17 | gridGap,
18 | } from './mixin/options'
19 |
20 | import { GridSize } from './types'
21 |
22 | type GridSizeProps = {
23 | [key in Size]?: GridSize | false
24 | }
25 |
26 | type Props = GridSizeProps & {
27 | container: boolean
28 | gridGap?: Size | false
29 | item: boolean
30 | alignContent: FlexboxAlignContent | false
31 | alignItems: FlexboxAlignItems | false | object
32 | justify: FlexboxJustification | false | object
33 | flexDirection: FlexboxDirection | false
34 | flexWrap: FlexboxWrap | false
35 | spacing?: Size | false
36 | }
37 |
38 | const getFlexWidth = (size: GridSize): string => {
39 | if (size === 'auto') {
40 | return size
41 | }
42 | return `${Math.floor((100 / 12) * size * 1000000) / 1000000}%`
43 | }
44 |
45 | const setFlexWidth = (size: GridSize, param: string) =>
46 | size === 'auto' ? param : getFlexWidth(size as GridSize)
47 |
48 | const setQueryGrid = (theme: any, props: Props) => {
49 | let setQueryGridStyles = ''
50 | if (props.item) {
51 | Object.keys(Size).forEach((item) => {
52 | if (props[Size[item]]) {
53 | setQueryGridStyles += `
54 | @media (min-width: ${theme.breakpoint[Size[item]].px()}) {
55 | flex-basis: ${setFlexWidth(props[Size[item]], 'auto')};
56 | flex-grow: ${props[Size[item]] === 'auto' ? 1 : 0};
57 | max-width: ${setFlexWidth(props[Size[item]], 'none')};
58 | }
59 | `
60 | }
61 | })
62 | }
63 | return css`
64 | ${setQueryGridStyles}
65 | `
66 | }
67 |
68 | export default styled.div`
69 | ${({ container, spacing, theme, flexDirection }) =>
70 | container &&
71 | css`
72 | box-sizing: border-box;
73 | display: flex;
74 | flex-direction: ${flexDirection || 'row'};
75 | ${justify}
76 | flex-wrap: wrap;
77 | margin: 0
78 | ${spacing ? theme.spacing.size[spacing].multiply(-1).rem() : '0'};
79 | padding: ${spacing ? theme.spacing.size[spacing].rem() : '0'};
80 | width: ${spacing
81 | ? `calc(100% + ${theme.spacing.size[spacing].multiply(2).rem()})`
82 | : '100%'};
83 | `}
84 |
85 | ${css`
86 | ${flexWrap}
87 | ${justify}
88 | ${alignItems}
89 | ${alignContent}
90 | ${height}
91 | ${gridGap}
92 | `}
93 | ${({ item, flexDirection }) =>
94 | item &&
95 | css`
96 | flex-direction: ${flexDirection || 'row'};
97 | `}
98 | ${({ item, spacing, theme }) =>
99 | item &&
100 | css`
101 | box-sizing: border-box;
102 | padding: ${spacing ? theme.spacing.size[spacing].rem() : '0'};
103 | `}
104 |
105 | ${({ theme, item, ...props }) =>
106 | css`
107 | ${setQueryGrid(theme, { item, ...props })}
108 | `}
109 | `
110 |
--------------------------------------------------------------------------------
/src/Drawer/Drawer.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { ComponentStory, ComponentMeta } from '@storybook/react'
3 | import { Button } from '@mycompany/design-system/Button'
4 | import Drawer from './Drawer'
5 | import DrawerProvider from './DrawerProvider'
6 |
7 | import { within, userEvent } from '@storybook/testing-library'
8 |
9 | import { expect } from '@storybook/jest'
10 |
11 | export default {
12 | /* 👇 The title prop is optional.
13 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
14 | * to learn how to generate automatic titles
15 | */
16 | title: 'Component/Drawer',
17 | component: Drawer,
18 | argTypes: {},
19 | decorators: [
20 | (storyFn, context) => {
21 | const [isVisible, setIsVisible] = useState(false)
22 | return (
23 |
24 | setIsVisible(true)} />
25 | {storyFn({
26 | visible: isVisible,
27 | onClose: () => setIsVisible(false),
28 | identifier: context.id,
29 | })}
30 |
31 | )
32 | },
33 | ],
34 | } as ComponentMeta
35 |
36 | const Template: ComponentStory = (
37 | args: any,
38 | { identifier, visible, onClose }: any
39 | ) => (
40 |
41 |
42 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
43 | eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
44 | voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
45 | clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
46 | amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
47 | nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed
48 | diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
49 | Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor
50 | sit amet.
51 |
52 |
53 | )
54 |
55 | export const Introduction: ComponentStory = Template.bind({})
56 |
57 | export const Opened: ComponentStory = Template.bind({})
58 | Opened.play = async ({ canvasElement }: any) => {
59 | const canvas = within(canvasElement)
60 |
61 | expect(canvas.queryByTestId('drawer-content')).not.toBeInTheDocument()
62 | userEvent.click(canvasElement.querySelector('button'))
63 | expect(canvas.queryByTestId('drawer-content')).toBeInTheDocument()
64 | }
65 |
66 | export const OpenAndClose: ComponentStory = Template.bind({})
67 | OpenAndClose.play = async ({ canvasElement }: any) => {
68 | const canvas = within(canvasElement)
69 |
70 | expect(canvas.queryByTestId('drawer-content')).not.toBeInTheDocument()
71 | userEvent.click(canvasElement.querySelector('button'))
72 | expect(canvas.queryByTestId('drawer-content')).toBeInTheDocument()
73 | await new Promise((res) => setTimeout(res, 1000)) // wait 1 second before closing drawer
74 | userEvent.click(canvasElement.querySelectorAll('button')[1])
75 | expect(canvas.queryByTestId('drawer-content')).not.toBeInTheDocument()
76 | }
77 |
--------------------------------------------------------------------------------
/src/ReactUtils/ReactUtils.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs'
2 |
3 |
4 |
5 | # ReactUtils
6 |
7 | ## Custom Hooks
8 |
9 | ### `useTheme()`
10 |
11 | Custom Hook to access the theme in a functional React Component
12 |
13 | ```tsx
14 | import React from 'react'
15 | import { useTheme } from '@mycompany/design-system/ReactUtils'
16 |
17 | export default () => {
18 | const theme = useTheme()
19 |
20 | return Primary color is: {theme.color.variant.primary.base.rgb()}
21 | }
22 | ```
23 |
24 | Use Object destructuring for more explicit access
25 |
26 | ```tsx
27 | import React from 'react'
28 | import { useTheme } from '@mycompany/design-system/ReactUtils'
29 |
30 | export default () => {
31 | const { color } = useTheme()
32 |
33 | return Primary color is: {color.variant.primary.base.rgb()}
34 | }
35 | ```
36 |
37 | ### `useDebounce()`
38 |
39 | If you are not familar with the debounce conecpt check [This Article](https://medium.com/@jamischarles/what-is-debouncing-2505c0648ff1)
40 |
41 | ```tsx
42 | import React, { useState } from 'react'
43 | import { useDebounce } from '@mycompany/design-system/ReactUtils'
44 |
45 | export default () => {
46 | const debounce = useDebounce()
47 | // Use debounce wrapper at any function
48 |
49 | return
50 | }
51 | ```
52 |
53 | ### `useBreakpoint()`
54 |
55 | Get information over the current breakpoint the client viewport is rendered in
56 |
57 | ```tsx
58 | import React, { useState } from 'react'
59 | import { useBreakpoint } from '@mycompany/design-system/ReactUtils'
60 |
61 | export default () => {
62 | const {
63 | currentBreakpoint,
64 | isGreaterThan,
65 | isSmallerThan,
66 | windowWidth,
67 | windowHeight,
68 | } = useBreakpoint()
69 | // Use debounce wrapper at any function
70 |
71 | return
72 | }
73 | ```
74 |
75 | #### Params
76 |
77 | `useBreakpoint( initialSize: Size = Size.Tiny, bind: boolean = true, debounceOffset: number = 100 )`
78 |
79 | - **initialSize** The size that is used if the window object is not available (yet)
80 | - Usefull to control the view in a SSR context
81 | - Default Tiny to support the Mobile First philosphy
82 | - **bind** if `false` there will be no window resize listener and only the initial size is available
83 | - **debounceOffset** The debounce delay for the window resize event
84 |
85 | ## Helper
86 |
87 | ### `getCurrentBreakpoint(windowWidth: number)`
88 |
89 | ```tsx
90 | import { getCurrentBreakpoint } from '@mycompany/design-system/ReactUtils'
91 |
92 | console.log(getCurrentBreakpoint(800)) // Size.Small
93 | ```
94 |
95 | ### `isBreakpointGreaterThan(size: Size, comparison: Size)`
96 |
97 | ```tsx
98 | import { isBreakpointGreaterThan } from '@mycompany/design-system/ReactUtils'
99 | import { Size } from '@mycompany/design-system/Styleguide'
100 |
101 | console.log(isBreakpointGreaterThan(Size.Large, Size.Medium)) // true
102 | ```
103 |
104 | ### `isBreakpointSmallerThan(size: Size, comparison: Size)`
105 |
106 | ```tsx
107 | import { isBreakpointSmallerThan } from '@mycompany/design-system/ReactUtils'
108 | import { Size } from '@mycompany/design-system/Styleguide'
109 |
110 | console.log(isBreakpointGreaterThan(Size.Large, Size.Medium)) // false
111 | ```
112 |
--------------------------------------------------------------------------------
/src/CircularProgress/CircularProgress.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { CircularProgress } from './index'
3 | import { ComponentStory, ComponentMeta } from '@storybook/react'
4 | import { Size, Status, Variant } from '../Styleguide'
5 | import { State } from './types'
6 |
7 | export default {
8 | /* 👇 The title prop is optional.
9 | * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
10 | * to learn how to generate automatic titles
11 | */
12 | title: 'Component/CircularProgressnew',
13 | component: CircularProgress,
14 | argTypes: {
15 | state: {
16 | options: State,
17 | control: { type: 'radio' },
18 | },
19 | value: {
20 | control: { type: 'range', min: 0, max: 100, step: 0.1 },
21 | },
22 | variant: {
23 | options: Variant,
24 | control: { type: 'radio' },
25 | },
26 | size: {
27 | options: Size,
28 | control: { type: 'radio' },
29 | },
30 | status: {
31 | options: Status,
32 | control: { type: 'radio' },
33 | },
34 | spacing: {
35 | options: { None: false, ...Size },
36 | control: { type: 'radio' },
37 | },
38 | },
39 | } as ComponentMeta
40 |
41 | const Template: ComponentStory = (args) => (
42 |
43 | )
44 |
45 | export const Introduction: ComponentStory =
46 | Template.bind({})
47 |
48 | Introduction.args = { state: State.Indeterminate }
49 |
50 | /**
51 | * States
52 | */
53 | export const StateDeterminate: ComponentStory =
54 | Template.bind({})
55 |
56 | StateDeterminate.args = { state: State.Determinate, value: 42 }
57 |
58 | export const StateIndeterminate: ComponentStory =
59 | Template.bind({})
60 |
61 | StateIndeterminate.args = {
62 | ...StateDeterminate.args,
63 | state: State.Indeterminate,
64 | }
65 |
66 | /**
67 | * Variants
68 | */
69 | export const VariantPrimary: ComponentStory =
70 | Template.bind({})
71 |
72 | VariantPrimary.args = {
73 | ...StateIndeterminate.args,
74 | variant: Variant.Primary,
75 | }
76 |
77 | export const VariantSecondary: ComponentStory =
78 | Template.bind({})
79 |
80 | VariantSecondary.args = {
81 | ...StateIndeterminate.args,
82 | variant: Variant.Secondary,
83 | }
84 |
85 | /**
86 | * Status
87 | */
88 | export const StatusSuccess: ComponentStory =
89 | Template.bind({})
90 |
91 | StatusSuccess.args = {
92 | ...StateIndeterminate.args,
93 | status: Status.Success,
94 | }
95 |
96 | export const StatusWarning: ComponentStory =
97 | Template.bind({})
98 |
99 | StatusWarning.args = {
100 | ...StateIndeterminate.args,
101 | status: Status.Warning,
102 | }
103 |
104 | export const StatusError: ComponentStory =
105 | Template.bind({})
106 |
107 | StatusError.args = {
108 | ...StateIndeterminate.args,
109 | status: Status.Error,
110 | }
111 |
112 | export const Inherit: ComponentStory = Template.bind(
113 | {}
114 | )
115 |
116 | Inherit.args = {
117 | ...StateIndeterminate.args,
118 | variant: Variant.Secondary,
119 | inherit: true,
120 | }
121 |
--------------------------------------------------------------------------------
/src/Icon/GiroPay.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const GiroPay: React.FC = ({ payments }: Props) => (
6 |
13 |
14 |
18 |
22 |
26 |
27 |
33 |
34 | )
35 |
36 | export default GiroPay
37 |
--------------------------------------------------------------------------------
/src/CircularProgress/CircularProgress.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Variant,
4 | ColorValue,
5 | Status,
6 | Size,
7 | } from '@mycompany/design-system/Styleguide'
8 | import { useTheme } from '@mycompany/design-system/ReactUtils'
9 | import { State } from './types'
10 | import CircularProgressRoot from './CircularProgressRoot'
11 | import CircularProgressSVG from './CircularProgressSVG'
12 | import CircularProgressCircle from './CircularProgressCircle'
13 |
14 | const SVG_VIEWBOX_SIZE = 44
15 |
16 | type Props = {
17 | state: State
18 | value?: number
19 | size?: Size
20 | variant?: Variant
21 | overrideColor?: ColorValue
22 | inherit?: boolean
23 | status?: Status
24 | spacing?: Size | false
25 | className?: string
26 | }
27 |
28 | const defaultSize = Size.Medium
29 | const defaultVariant = Variant.Primary
30 | const defaultStatus = Status.Default
31 | const defaultSpacing = Size.Medium
32 |
33 | const CircularProgress: React.FC = React.forwardRef<
34 | HTMLSpanElement,
35 | Props
36 | >(
37 | (
38 | {
39 | state,
40 | value = 0,
41 | size = defaultSize,
42 | variant = defaultVariant,
43 | overrideColor,
44 | spacing = defaultSpacing,
45 | status = defaultStatus,
46 | inherit = false,
47 | className,
48 | ...htmlAttributes
49 | }: Props,
50 | ref
51 | ) => {
52 | const theme = useTheme()
53 | const circleStyle: React.CSSProperties = {}
54 | const rootStyle: React.CSSProperties = {}
55 | const rootProps: React.HTMLAttributes = {}
56 |
57 | if (state === State.Determinate) {
58 | const circumference =
59 | 2 * Math.PI * ((SVG_VIEWBOX_SIZE - theme.shape.progressSickness) / 2)
60 | circleStyle.strokeDasharray = circumference.toFixed(3)
61 | circleStyle.strokeDashoffset = `${(
62 | ((100 - value) / 100) *
63 | circumference
64 | ).toFixed(3)}px`
65 | rootStyle.transform = 'rotate(-90deg)'
66 |
67 | // A11y
68 | rootProps['aria-valuenow'] = Math.round(value)
69 | rootProps['aria-valuemin'] = 0
70 | rootProps['aria-valuemax'] = 100
71 | }
72 |
73 | if (overrideColor) {
74 | rootStyle.color = overrideColor.hex()
75 | }
76 |
77 | const actualSize = theme.spacing.size[size].multiply(2).rem()
78 |
79 | return (
80 |
93 |
98 |
107 |
108 |
109 | )
110 | }
111 | )
112 |
113 | export default CircularProgress
114 |
--------------------------------------------------------------------------------
/src/Grid/mixin/options.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 | import {
3 | Size,
4 | theme,
5 | FlexboxDirection,
6 | FlexboxAlignContent,
7 | FlexboxAlignItems,
8 | FlexboxWrap,
9 | FlexboxJustification,
10 | } from '@mycompany/design-system/Styleguide'
11 | import { GridSize } from '../types'
12 |
13 | type GridSizeProps = {
14 | [key in Size]?: GridSize | false
15 | }
16 |
17 | type Props = GridSizeProps & {
18 | displayFlex: string | false
19 | fullHeight?: boolean
20 | flex: string | false
21 | container: boolean
22 | gridGap?: Size | false
23 | item: boolean
24 | columnItem?: string | false
25 | alignContent: FlexboxAlignContent | false
26 | alignItems: FlexboxAlignItems | false
27 | justify: FlexboxJustification | false | object
28 | flexDirection: FlexboxDirection | false
29 | flexWrap: FlexboxWrap | false
30 | spacing?: Size | false
31 | }
32 |
33 | const displayFlex = ({ ...props }: Props) =>
34 | props.displayFlex &&
35 | css`
36 | display: ${props.displayFlex || 'unset'};
37 | `
38 |
39 | const flexWrap = ({ ...props }: Props) =>
40 | props.flexWrap &&
41 | css`
42 | flex-wrap: ${props.flexWrap || 'wrap'};
43 | `
44 |
45 | const height = ({ ...props }: Props) =>
46 | props.fullHeight &&
47 | css`
48 | height: ${props.fullHeight ? '100%' : 'auto'};
49 | `
50 |
51 | const gridGap = ({ ...props }: Props) =>
52 | props.gridGap &&
53 | css`
54 | grid-gap: ${props.gridGap ? theme.spacing.size[props.gridGap].rem() : '0'};
55 | `
56 |
57 | const alignContent = ({ ...props }: Props) => {
58 | let alignContentStyle = ''
59 | if (typeof props.alignContent === 'object') {
60 | Object.entries(props.alignContent).forEach(([key, value]) => {
61 | alignContentStyle += `
62 | @media (min-width: ${theme.breakpoint[key].px()}) {
63 | align-content: ${value || 'flex-start'};
64 | }
65 | `
66 | })
67 | return css`
68 | ${alignContentStyle}
69 | `
70 | }
71 | if (typeof props.alignContent === 'string') {
72 | return css`
73 | align-content: ${props.alignContent || 'flex-start'};
74 | `
75 | }
76 | return false
77 | }
78 |
79 | const alignItems = ({ ...props }: Props) => {
80 | let alignItemsStyle = ''
81 | if (typeof props.alignItems === 'object') {
82 | Object.entries(props.alignItems).forEach(([key, value]) => {
83 | alignItemsStyle += `
84 | @media (min-width: ${theme.breakpoint[key].px()}) {
85 | align-items: ${value || 'flex-start'};
86 | }
87 | `
88 | })
89 | return css`
90 | ${alignItemsStyle}
91 | `
92 | }
93 | if (typeof props.alignItems === 'string') {
94 | return css`
95 | align-items: ${props.alignItems || 'flex-start'};
96 | `
97 | }
98 | return false
99 | }
100 |
101 | const justify = ({ ...props }: Props) => {
102 | let justifyStyles = ''
103 | if (typeof props.justify === 'object') {
104 | Object.entries(props.justify).forEach(([key, value]) => {
105 | justifyStyles += `
106 | @media (min-width: ${theme.breakpoint[key].px()}) {
107 | justify-content: ${value || 'flex-start'};
108 | }
109 |
110 | `
111 | })
112 | return css`
113 | ${justifyStyles}
114 | `
115 | }
116 | if (typeof props.justify === 'string') {
117 | return css`
118 | justify-content: ${props.justify || 'flex-start'};
119 | `
120 | }
121 | return false
122 | }
123 |
124 | export {
125 | displayFlex,
126 | flexWrap,
127 | justify,
128 | alignItems,
129 | alignContent,
130 | height,
131 | gridGap,
132 | }
133 |
--------------------------------------------------------------------------------
/src/Price/Price.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Size,
4 | TypographyUnit,
5 | FlexboxWrap,
6 | FlexboxDirection,
7 | FlexboxAlignItems,
8 | Variant,
9 | } from '@mycompany/design-system/Styleguide'
10 | import { Typography, Currency, Status } from '@mycompany/design-system/index'
11 |
12 | import { useTranslation } from 'react-i18next'
13 | import { PriceList, PriceRoot } from './PriceRoot'
14 |
15 | type Props = React.PropsWithChildren<{
16 | spacing?: Size | false
17 | uvp?: boolean
18 | currency?: Currency
19 | display?: TypographyUnit
20 | productPrice?: string | null
21 | basePrice?: string | null
22 | discount?: string | null
23 | recommendedPrice?: string | null
24 | bestPrice?: string | null
25 | bestPriceDiscount?: string | null
26 | direction?: FlexboxDirection
27 | wrap?: FlexboxWrap
28 | align?: FlexboxAlignItems
29 | }>
30 |
31 | const defaultSpacing = Size.Medium
32 |
33 | const Price: React.FC = React.forwardRef(
34 | (
35 | {
36 | spacing = defaultSpacing,
37 | display = TypographyUnit.Base,
38 | productPrice,
39 | basePrice,
40 | wrap,
41 | uvp = false,
42 | currency = Currency.Euro,
43 | direction,
44 | discount,
45 | recommendedPrice,
46 | bestPrice,
47 | bestPriceDiscount,
48 | align = FlexboxAlignItems.FlexStart,
49 | }: React.PropsWithChildren,
50 | ref
51 | ) => {
52 | const { t } = useTranslation(['translation'])
53 |
54 | const currencyString = currency !== Currency.None ? currency : false
55 |
56 | const hasSale = recommendedPrice && recommendedPrice !== '0,00 €'
57 |
58 | return (
59 |
69 |
70 |
75 |
76 | {productPrice} {currency && currencyString}
77 |
78 |
79 | {basePrice && (
80 |
85 | ({basePrice})
86 |
87 | )}
88 | {recommendedPrice && (
89 | <>
90 |
95 |
96 | {uvp && 'UVP:'} {recommendedPrice}{' '}
97 | {currency && currencyString}
98 |
99 |
100 | >
101 | )}
102 |
103 |
104 | {bestPrice && (
105 |
110 | {t('cart.items.bestPrice')}*: {bestPrice}{' '}
111 | {currency && currencyString}
112 | {bestPriceDiscount && ({bestPriceDiscount}) }
113 |
114 | )}
115 |
116 | )
117 | }
118 | )
119 |
120 | export default Price
121 |
--------------------------------------------------------------------------------
/src/Icon/InstagramIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SvgIcon } from './base'
3 | import { IconName, Props } from './types'
4 |
5 | const InstagramIcon: React.FC = ({social}:Props) => (
6 |
7 |
8 |
16 |
17 |
18 |
19 |
20 |
21 |
29 |
30 |
31 |
32 |
33 |
34 |
39 |
44 |
49 |
50 | )
51 |
52 | export default InstagramIcon
53 |
--------------------------------------------------------------------------------
/src/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Ink from 'react-ink'
3 | import { useTranslation } from 'react-i18next'
4 | import {
5 | Size,
6 | Variant,
7 | Status,
8 | ElevationLevel,
9 | } from '@mycompany/design-system/Styleguide'
10 | import { CircularProgressState } from '@mycompany/design-system/CircularProgress'
11 | import ButtonRoot from './ButtonRoot'
12 | import ButtonAttachement from './ButtonAttachment'
13 | import ButtonLoadingIndicator from './ButtonLoadingIndicator'
14 | import ButtonLabel from './ButtonLabel'
15 | import { AttachmentPos } from './types'
16 |
17 | type CleanButtonAttributes = Omit<
18 | Omit<
19 | Omit, 'size'>, 'type'>,
20 | 'onPointerEnterCapture'
21 | >,
22 | 'onPointerLeaveCapture'
23 | >
24 |
25 | type Props = {
26 | label?: string
27 | size?: Size
28 | variant?: Variant
29 | status?: Status
30 | iconOnly?: boolean
31 | pill?: boolean
32 | ripple?: boolean
33 | link?: boolean
34 | noBackground?: boolean
35 | fullWidth?: boolean
36 | disabled?: boolean
37 | submit?: boolean
38 | elevation?: ElevationLevel
39 | spacing?: Size | false
40 | loading?: boolean
41 | attachment?: React.ReactNode
42 | attachmentPos?: AttachmentPos
43 | onClick?: (event: React.MouseEvent) => void
44 | className?: string
45 | } & CleanButtonAttributes
46 |
47 | // prebinding only required to avoid a storybook bug. Should be inlined
48 | const defaultSize = Size.Medium
49 | const defaultSpacing = Size.Medium
50 | const defaultVariant = Variant.Primary
51 | const defaultStatus = Status.Default
52 | const defaultElevation = ElevationLevel.None
53 |
54 | const Button: React.FC = React.forwardRef(
55 | (
56 | {
57 | onClick = () => {},
58 | label,
59 | attachmentPos = AttachmentPos.Right,
60 | attachment,
61 | disabled = false,
62 | fullWidth = false,
63 | loading = false,
64 | iconOnly = false,
65 | pill = false,
66 | ripple = false,
67 | link = false,
68 | noBackground = false,
69 | submit = false,
70 | elevation = defaultElevation,
71 | size = defaultSize,
72 | spacing = defaultSpacing,
73 | variant = defaultVariant,
74 | status = defaultStatus,
75 | className,
76 | ...htmlAttributes
77 | }: Props,
78 | ref
79 | ) => {
80 | const { t } = useTranslation()
81 |
82 | return (
83 |
103 | !disabled && !loading && onClick(event)
104 | }
105 | className={className}
106 | {...htmlAttributes}
107 | >
108 | {label && (
109 |
110 | {t(label)}
111 |
112 | )}
113 | {attachment && (
114 |
115 | {attachment}
116 |
117 | )}
118 | {loading && (
119 |
126 | )}
127 | {ripple && }
128 |
129 | )
130 | }
131 | )
132 |
133 | export default Button
134 |
--------------------------------------------------------------------------------