chore: update storybook docs

This commit is contained in:
Hossein Mehrabi 2023-10-13 15:28:37 +03:30
parent efa4792a2a
commit ade4af1856
No known key found for this signature in database
GPG Key ID: 45C04964191AFAA1
46 changed files with 1013 additions and 46 deletions

View File

@ -1,7 +1,9 @@
import { dirname, join } from 'path'
module.exports = {
stories: [
'../src/components/**/*.stories.mdx',
'../src/docs/**/*.mdx',
'../src/components/**/*.mdx',
'../src/components/**/*.stories.@(js|jsx|ts|tsx)',
],

View File

@ -1,6 +1,15 @@
import {
Canvas,
Controls,
Description,
Subtitle,
Title,
useOf,
} from '@storybook/blocks'
import type { Preview } from '@storybook/react'
import React from 'react'
import { THEME_TYPOGRAPHY_FONT_CATEGORIES } from '../src/components/Theme/constants'
import { storybookThemes } from './themes'
import { docTheme, storybookThemes } from './themes'
import { withTheme } from './withTheme.decorator'
const preview: Preview = {
@ -32,6 +41,27 @@ const preview: Preview = {
),
},
},
docs: {
theme: docTheme,
page: () => {
const resolvedOf = useOf('meta', ['meta'])
const { stories, meta, moduleExports } = resolvedOf.csfFile
const controls =
typeof meta?.parameters?.docs?.controls === 'undefined' ||
meta?.parameters?.docs?.controls === true
return (
<>
<Title />
<Subtitle />
<Description />
<Canvas sourceState="shown" />
{controls && <Controls />}
</>
)
},
},
},
decorators: [withTheme],
argTypes: {

View File

@ -118,13 +118,13 @@ export const extractMetadata = async (dir: string) => {
})
const components = await fromStories(stories.default, assetsDir)
const globalTypes = await extractGlobalTypes(assetsDir)
// const globalTypes = await extractGlobalTypes(assetsDir)
fs.unlinkSync(path.join(assetsDir, 'package.json'))
return {
components,
globalTypes,
// globalTypes,
}
}

View File

@ -1,4 +1,5 @@
import { Parameters, StoryContext } from '@storybook/react'
import { create } from '@storybook/theming/create'
import { GlobalTypes } from '@storybook/types'
import { CreateThemeProps, createTheme, defaultThemes } from '../src'
import { THEME_TYPOGRAPHY_FONT_CATEGORIES } from '../src/components/Theme/constants'
@ -104,3 +105,30 @@ const createThemes = () => {
}
export const storybookThemes = createThemes()
const darkTheme = defaultThemes.dark
export const docTheme = create({
base: 'dark',
fontBase: darkTheme.typographyGlobal.genericFontFamily,
fontCode: darkTheme.typographyGlobal.genericFontFamily,
colorPrimary: `rgb(${darkTheme.palette.primary})`,
colorSecondary: `rgb(${darkTheme.palette.secondary})`,
appBg: `rgb(${darkTheme.palette.surface.primary})`,
appContentBg: `rgb(${darkTheme.palette.surface.primary})`,
appBorderColor: `rgb(${darkTheme.palette.border.primary})`,
appBorderRadius: 0,
textColor: `rgb(${darkTheme.palette.text.primary})`,
textInverseColor: `rgb(${darkTheme.palette.text.secondary})`,
barTextColor: `rgb(${darkTheme.palette.text.primary})`,
barSelectedColor: `rgb(${darkTheme.palette.text.primary})`,
barBg: `rgb(${darkTheme.palette.surface.primary})`,
inputBg: `rgb(${darkTheme.palette.surface.secondary})`,
inputBorder: `rgb(${darkTheme.palette.border.primary})`,
inputTextColor: `rgb(${darkTheme.palette.text.secondary})`,
inputBorderRadius: 0,
booleanBg: `rgb(${darkTheme.palette.surface.primary})`,
booleanSelectedBg: `rgb(${darkTheme.palette.surface.primary})`,
buttonBorder: `rgb(${darkTheme.palette.border.primary})`,
buttonBg: `rgb(${darkTheme.palette.surface.secondary})`,
textMutedColor: `rgb(${darkTheme.palette.text.primary})`,
})

View File

@ -1,3 +1,4 @@
import { Global, css } from '@emotion/react'
import { useGlobals } from '@storybook/preview-api'
import { Decorator } from '@storybook/react'
import React, { useEffect } from 'react'
@ -6,9 +7,9 @@ import { storybookThemes } from './themes'
export const withTheme: Decorator = (Story, context) => {
const StoryComponent = Story as any as React.ComponentType
const isDoc = context.viewMode === 'docs'
const theme = storybookThemes.getTheme(context)
const [globals, setGlobals] = useGlobals()
useEffect(() => {
@ -28,8 +29,19 @@ export const withTheme: Decorator = (Story, context) => {
return (
<div>
<ThemeProvider theme={theme}>
<ThemeProvider theme={theme} injectCssVars={true}>
<StoryComponent />
{isDoc && (
<Global
styles={css`
.docs-story {
${theme.cssVars}
background: rgb(var(--lsd-surface-primary));
}
`}
/>
)}
</ThemeProvider>
</div>
)

View File

@ -1,12 +1,26 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import shuffle from 'lodash/shuffle'
import { Autocomplete, AutocompleteProps } from './Autocomplete'
const list = JSON.parse(
'["BitTorrent","VANIG","BenjiRolls","Status Network Token","GigTricks","Vechain","Insureum","Instant Sponsor Token","IslaCoin","Kcash","Litecoin","BoutsPro","Valorem","Artcoin","Insureum","Zen Protocol","CryptoPennies","MediBond","WishFinance","BlockNet","Big Data Block","Loom Network","Gamedex","Universal Recognition Token","Soarcoin","IZX","aelf","Bitcoin Private","TerraCoin","Coinnec","Revolution VR","SecureCoin","Swarm Fund","Fan360","LakeBanker","16BitCoin","CyberTrust","Actinium","ZestCoin","Monero 0","DACash","Bitswift","Bethereum","Hedge Token","Megastake","HeelCoin","RealChain","LiteBitcoin","SexCoin","Suretly","Internet of People","Rate3","Invictus","WorldPay","iOlite","Cashaa","NovaCoin","U Network","Gimli","Chynge.net","BMChain","Ethereum Premium","BlackShadowCoin","Javvy","Befund","Bancor Network Token","BOONSCoin","Pantos","IDEX Membership","BitStation","Lynx","Encrybit","HighVibe.Network","Credo","HiCoin","RiptideCoin","BitBoss","NXTTY","Presale Ventures","Urbit Data","Xeonbit","Newbium","Mint","Crypto Wine Exchange","HARA","Pioneer Coin","Ethereum Dark","FazzCoin","Fitrova","The EFFECT Network","CargoX","PolicyPal Network","Vechain","President Clinton","GenesysCoin","Trivver","Bitdeal","ShareMeAll","Primas","RoboAdvisorCoin","Liquid","Napoleon X","NOKU Master token","Liqnet","ZeroState","0chain","DarkCash","Sudan Gold Coin","PokerSports","Bankera","Think And Get Rich Coin","ELTCOIN","USOAMIC","XDNA","Autoria","Cosmo","Bigbom","EagsCoin","Stakinglab","SaffronCoin","BnrtxCoin","FoodCoin","PutinCoin","SID Token","Quartz","IOU1","Spend","NEO Gold","High Voltage Coin","Unobtanium","Sandcoin","FrazCoin","Sudan Gold Coin","VeriCoin","Aurora","NANJCOIN","Muse","DuckDuckCoin","Saifu","CryCash","Rustbits","BitFlip","Cpollo","Monkey Project","Coin Analyst","Scanetchain Token","Aditus","LendConnect","FOREXCOIN","Crypto Improvement Fund","Electra","Psilocybin","GameLeagueCoin","Switcheo","BitQuark","BinaryCoin","CyberVein","KATZcoin","BtcEX","ByteCoin","Shping Coin","Liquid","Stacktical","Tezos","ParkByte","Imbrex","Pinmo","Sigil","LePenCoin","OmiseGO Classic","Digix DAO","ShareRing","CryptoCarbon","CDX Network","SONM","PhoenixCoin","Incent","Tokyo Coin","Premium","Unattanium","Biotron","WETH","Rublebit","KRCoin","Aegis","Legends Cryptocurrency","VARcrypt","Witcoin","GlowShares","HOQU","VARcrypt","Linx","BlackholeCoin","NumbersCoin","ZayedCoin","CarVertical","Securosys","ElliotCoin","Zelcash","AcesCoin","EtherInc"]',
)
const list = shuffle(
JSON.parse(
'["BitTorrent","VANIG","BenjiRolls","Status Network Token","GigTricks","Vechain","Insureum","Instant Sponsor Token","IslaCoin","Kcash","Litecoin","BoutsPro","Valorem","Artcoin","Insureum","Zen Protocol","CryptoPennies","MediBond","WishFinance","BlockNet","Big Data Block","Loom Network","Gamedex","Universal Recognition Token","Soarcoin","IZX","aelf","Bitcoin Private","TerraCoin","Coinnec","Revolution VR","SecureCoin","Swarm Fund","Fan360","LakeBanker","16BitCoin","CyberTrust","Actinium","ZestCoin","Monero 0","DACash","Bitswift","Bethereum","Hedge Token","Megastake","HeelCoin","RealChain","LiteBitcoin","SexCoin","Suretly","Internet of People","Rate3","Invictus","WorldPay","iOlite","Cashaa","NovaCoin","U Network","Gimli","Chynge.net","BMChain","Ethereum Premium","BlackShadowCoin","Javvy","Befund","Bancor Network Token","BOONSCoin","Pantos","IDEX Membership","BitStation","Lynx","Encrybit","HighVibe.Network","Credo","HiCoin","RiptideCoin","BitBoss","NXTTY","Presale Ventures","Urbit Data","Xeonbit","Newbium","Mint","Crypto Wine Exchange","HARA","Pioneer Coin","Ethereum Dark","FazzCoin","Fitrova","The EFFECT Network","CargoX","PolicyPal Network","Vechain","President Clinton","GenesysCoin","Trivver","Bitdeal","ShareMeAll","Primas","RoboAdvisorCoin","Liquid","Napoleon X","NOKU Master token","Liqnet","ZeroState","0chain","DarkCash","Sudan Gold Coin","PokerSports","Bankera","Think And Get Rich Coin","ELTCOIN","USOAMIC","XDNA","Autoria","Cosmo","Bigbom","EagsCoin","Stakinglab","SaffronCoin","BnrtxCoin","FoodCoin","PutinCoin","SID Token","Quartz","IOU1","Spend","NEO Gold","High Voltage Coin","Unobtanium","Sandcoin","FrazCoin","Sudan Gold Coin","VeriCoin","Aurora","NANJCOIN","Muse","DuckDuckCoin","Saifu","CryCash","Rustbits","BitFlip","Cpollo","Monkey Project","Coin Analyst","Scanetchain Token","Aditus","LendConnect","FOREXCOIN","Crypto Improvement Fund","Electra","Psilocybin","GameLeagueCoin","Switcheo","BitQuark","BinaryCoin","CyberVein","KATZcoin","BtcEX","ByteCoin","Shping Coin","Liquid","Stacktical","Tezos","ParkByte","Imbrex","Pinmo","Sigil","LePenCoin","OmiseGO Classic","Digix DAO","ShareRing","CryptoCarbon","CDX Network","SONM","PhoenixCoin","Incent","Tokyo Coin","Premium","Unattanium","Biotron","WETH","Rublebit","KRCoin","Aegis","Legends Cryptocurrency","VARcrypt","Witcoin","GlowShares","HOQU","VARcrypt","Linx","BlackholeCoin","NumbersCoin","ZayedCoin","CarVertical","Securosys","ElliotCoin","Zelcash","AcesCoin","EtherInc"]',
),
).slice(0, 20)
const subtitle = `Input`
const description = `Autocomplete is an enhanced version of a TextField component, that displays suggested options as users type, allowing them to make a selection from the list.`
export default {
title: 'Autocomplete',
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
component: Autocomplete,
argTypes: {
size: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Badge, BadgeProps } from './Badge'
import { Meta, StoryObj } from '@storybook/react'
import { useStorybookIconComponent } from '../../utils/storybook.utils'
import { Badge, BadgeProps } from './Badge'
const subtitle = `Data Display`
const description = `Badge component is used to display concise pieces of information, such as notifications, counts, statuses, and more. Typically, it is positioned near other UI elements to convey information associated with them.`
export default {
title: 'Badge',
component: Badge,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
variant: {
type: {

View File

@ -1,8 +1,19 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { Breadcrumb, BreadcrumbProps } from './Breadcrumb'
const subtitle = `Navigation`
const description = `Breadcrumbs show users their current location on the website or app and allow them to quickly navigate back to a parent level or a previous step.`
export default {
title: 'Breadcrumb',
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
component: Breadcrumb,
argTypes: {
size: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { useStorybookIconComponent } from '../../utils/storybook.utils'
import { Button, ButtonProps } from './Button'
const subtitle = `Input`
const description = `Button component is used to trigger an action or event, it is labeled to convey what will happen upon interaction.`
export default {
title: 'Button',
component: Button,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,11 +1,22 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { CardBody } from '../CardBody'
import { CardHeader } from '../CardHeader'
import { Card, CardProps } from './Card'
const subtitle = `Surface`
const description = `Card is a flexible component designed to group and present content about a single subject in a clear and concise format, often including associated actions.`
export default {
title: 'Card',
component: Card,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { CardBody, CardBodyProps } from './CardBody'
const subtitle = ``
const description = ``
export default {
title: 'CardBody',
component: CardBody,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { CardHeader, CardHeaderProps } from './CardHeader'
const subtitle = ``
const description = ``
export default {
title: 'CardHeader',
component: CardHeader,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { Checkbox, CheckboxProps } from './Checkbox'
const subtitle = `Input`
const description = `Checkbox component allows users to select one or multiple items from a list. It is ideal for cases where multiple selections are required from a range of options.`
export default {
title: 'Checkbox',
component: Checkbox,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { Checkbox } from '../Checkbox'
import { CheckboxGroup, CheckboxGroupProps } from './CheckboxGroup'
const subtitle = `Input`
const description = `CheckboxGroup component groups multiple Checkbox components, inheriting all their available styles.`
export default {
title: 'CheckboxGroup',
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
component: CheckboxGroup,
argTypes: {
size: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { Typography } from '../Typography'
import { Collapse, CollapseProps } from './Collapse'
const subtitle = `Surface`
const description = `A collapsible and expandable content section that can be used to group or hide content.`
export default {
title: 'Collapse',
component: Collapse,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { CollapseHeader, CollapseHeaderProps } from './CollapseHeader'
const subtitle = ``
const description = ``
export default {
title: 'CollapseHeader',
component: CollapseHeader,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { DateField, DateFieldProps } from './DateField'
const subtitle = `Input`
const description = `DateField component allows users to manually enter a specific date.`
export default {
title: 'DateField',
component: DateField,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { DatePicker, DatePickerProps } from './DatePicker'
const subtitle = `Input`
const description = `DatePicker component allows users select a date.`
export default {
title: 'DatePicker',
component: DatePicker,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { Dropdown, DropdownProps } from './Dropdown'
const subtitle = `Input`
const description = `Dropdown is a menu of hidden options that a user can reveal by clicking on the component. It allows users to select one or multiple options, which can represent values in forms or serve as actions to filter or sort existing content.`
export default {
title: 'Dropdown',
component: Dropdown,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
variant: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { DropdownItem, DropdownItemProps } from './DropdownItem'
const subtitle = ``
const description = ``
export default {
title: 'DropdownItem',
component: DropdownItem,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { useStorybookIconComponent } from '../../utils/storybook.utils'
import { IconButton, IconButtonProps } from './IconButton'
const subtitle = `Input`
const description = `IconButton component is used to trigger an action or event, it is labeled with an icon.`
export default {
title: 'IconButton',
component: IconButton,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,11 +1,22 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { useStorybookIconComponent } from '../../utils/storybook.utils'
import { IconButton } from '../IconButton/IconButton'
import { IconButtonGroup, IconButtonGroupProps } from './IconButtonGroup'
const subtitle = `Input`
const description = `IconButtonGroup component groups multiple IconButton components, inheriting all their available styles.`
export default {
title: 'IconButtonGroup',
component: IconButtonGroup,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,13 +1,24 @@
import { Meta, StoryObj } from '@storybook/react'
import { useState } from 'react'
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Modal, ModalProps } from './Modal'
import { Button } from '../Button'
import { ModalBody } from '../ModalBody'
import { ModalFooter } from '../ModalFooter'
import { Modal, ModalProps } from './Modal'
const subtitle = `Feedback`
const description = `A modal is a window positioned above the page content to direct the user's attention solely toward a single task or piece of information.`
export default {
title: 'Modal',
component: Modal,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { ModalBody, ModalBodyProps } from './ModalBody'
const subtitle = ``
const description = ``
export default {
title: 'ModalBody',
component: ModalBody,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
table: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { ModalFooter, ModalFooterProps } from './ModalFooter'
import { Meta, StoryObj } from '@storybook/react'
import { Button } from '../Button'
import { ModalFooter, ModalFooterProps } from './ModalFooter'
const subtitle = ``
const description = ``
export default {
title: 'ModalFooter',
component: ModalFooter,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { NumberInput, NumberInputProps } from './NumberInput'
const subtitle = `Input`
const description = `NumberInput allows users to input a numeric value and adjust the value incrementally using a two-segment control.`
export default {
title: 'NumberInput',
component: NumberInput,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { Quote, QuoteProps } from './Quote'
const subtitle = `Surface`
const description = `Quote component is used to display a quotation.`
export default {
title: 'Quote',
component: Quote,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
mode: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { RadioButton, RadioButtonProps } from './RadioButton'
const subtitle = `Input`
const description = `Radio buttons enable users to make a single selection from a list of options, particularly suitable for scenarios where you have a set of mutually exclusive choices.`
export default {
title: 'RadioButton',
component: RadioButton,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { RadioButton } from '../RadioButton'
import { RadioButtonGroup, RadioButtonGroupProps } from './RadioButtonGroup'
const subtitle = `Input`
const description = `RadioButtonGroup component groups multiple RadioButton components, inheriting all their available styles.`
export default {
title: 'RadioButtonGroup',
component: RadioButtonGroup,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { useStorybookIconComponent } from '../../utils/storybook.utils'
import { TabItem, TabItemProps } from './TabItem'
const subtitle = ``
const description = ``
export default {
title: 'TabItem',
component: TabItem,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,4 +1,4 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { useState } from 'react'
import { Button } from '../Button'
import { Dropdown } from '../Dropdown'
@ -8,9 +8,20 @@ import { TableItem } from '../TableItem'
import { TableRow } from '../TableRow'
import { Table, TableProps } from './Table'
const subtitle = `Data Display`
const description = `Table component is used to efficiently organise and display sets of information in rows and columns, facilitating easy scanning for patterns and insights. Component allows for customisation with additional functionality.`
export default {
title: 'Table',
component: Table,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { TableBody, TableBodyProps } from './TableBody'
const subtitle = ``
const description = ``
export default {
title: 'TableBody',
component: TableBody,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { TableHeader, TableHeaderProps } from './TableHeader'
const subtitle = ``
const description = ``
export default {
title: 'TableHeader',
component: TableHeader,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { TableItem, TableItemProps } from './TableItem'
const subtitle = ``
const description = ``
export default {
title: 'TableItem',
component: TableItem,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -1,9 +1,20 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { TableRow, TableRowProps } from './TableRow'
const subtitle = ``
const description = ``
export default {
title: 'TableRow',
component: TableRow,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
type: {
type: {

View File

@ -1,11 +1,22 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { useStorybookIconComponent } from '../../utils/storybook.utils'
import { TabItem } from '../TabItem'
import { Tabs, TabsProps } from './Tabs'
const subtitle = `Navigation`
const description = `Tabs are a navigational component that efficiently organizes related content, enabling users to switch between different groups of information within the same context.`
export default {
title: 'Tabs',
component: Tabs,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {
@ -62,5 +73,6 @@ export const Root: StoryObj<
fullWidth: false,
scrollControls: true,
onChange: undefined,
tabs: 4,
},
}

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { FolderIcon } from '../Icons'
import { Tag, TagProps } from './Tag'
const subtitle = `Data Display`
const description = `Tags are used to label, categorise and organise items with descriptive keywords.`
export default {
title: 'Tag',
component: Tag,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
variant: {
type: {

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { useStorybookIconComponent } from '../../utils/storybook.utils'
import { TextField, TextFieldProps } from './TextField'
const subtitle = `Input`
const description = `TextField component allows users to enter free-form text data, accommodating both long and short-form entries.`
export default {
title: 'TextField',
component: TextField,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
size: {
type: {

View File

@ -0,0 +1,61 @@
import styled from '@emotion/styled'
import { Meta, StoryObj } from '@storybook/react'
import { get } from 'lodash'
import React from 'react'
import { Typography } from '../Typography'
import { ThemeProvider, ThemeProviderProps } from './ThemeProvider'
import { Theme, TypographyVariants } from './types'
import { useTheme } from './useTheme'
import {
ColorDesignTokens,
TypographyDesignTokens,
SpacingDesignTokens,
} from '../../docs/components/DesignTokens'
const subtitle = ``
const description = ``
export default {
title: 'ThemeProvider',
parameters: {
componentSubtitle: subtitle,
docs: {
controls: false,
description: {
component: description,
},
},
},
component: ThemeProvider,
} as Meta
export const Root: StoryObj<ThemeProviderProps> = {
render: (args) => {
return (
<div>
<Typography variant="h6" component="h2">
Colour
</Typography>
<ColorDesignTokens />
<Typography variant="h6" component="h2">
Spacing
</Typography>
<SpacingDesignTokens />
<Typography variant="h6" component="h2">
Typography
</Typography>
<TypographyDesignTokens />
</div>
)
},
parameters: {
docs: {
source: {
code: null,
},
},
},
args: {},
}

View File

@ -21,6 +21,9 @@ export const THEME_TYPOGRAPHY_VARIANTS = [
'h6',
'subtitle1',
'subtitle2',
'subtitle3',
'subtitle4',
'subtitle5',
'body1',
'body2',
'body3',

View File

@ -1,10 +1,21 @@
import { StoryObj, Meta, StoryFn } from '@storybook/react'
import { Meta, StoryObj } from '@storybook/react'
import { THEME_TYPOGRAPHY_VARIANTS } from '../Theme'
import { Typography, TypographyProps } from './Typography'
const subtitle = `Data Display`
const description = `Typography component lets you apply LSD typography styles of a specific variant to the elements in your app.`
export default {
title: 'Typography',
component: Typography,
parameters: {
componentSubtitle: subtitle,
docs: {
description: {
component: description,
},
},
},
argTypes: {
variant: {
type: {
@ -28,5 +39,6 @@ export const Root: StoryObj<TypographyProps> = {
args: {
color: 'primary',
variant: 'body1',
},
}

View File

@ -0,0 +1,382 @@
import styled from '@emotion/styled'
import { Meta, StoryObj } from '@storybook/react'
import { get } from 'lodash'
import React from 'react'
import { Theme, TypographyVariants, useTheme } from '../../../components/Theme'
import { Typography } from '../../../components/Typography'
type Token = (
| {
type: 'color'
rgb: string
hex: string
}
| {
type: 'spacing'
value: string
}
| {
type: 'typography'
value: string
}
) & {
name: string
varName: string
}
type TokenGroup<T = Token> = {
name: string
tokens: T[]
}
type Colors = TokenGroup<Extract<Token, { type: 'color' }>>[]
type Spacing = TokenGroup<Extract<Token, { type: 'spacing' }>>[]
type TypographyTokens = TokenGroup<Extract<Token, { type: 'typography' }>>[]
const getDesignTokens = (
theme: Theme,
): {
colors: Colors
spacing: Spacing
typography: TypographyTokens
} => {
const rgbToHex = (r: number, g: number, b: number) =>
'#' +
[r, g, b]
.map((val) => val.toString(16))
.map((hex) => (hex.length === 1 ? `0${hex}` : hex))
.join('')
const remToPx = (rem: string) => parseFloat(rem.replace('rem', '')) * 16
const keys = ['surface', 'border', 'text', 'icon']
const colors: Colors = [
{
name: 'primitives',
tokens: [
{
type: 'color',
name: 'primary',
varName: `--lsd-theme-primary`,
rgb: theme.palette.primary,
hex: rgbToHex(
...(theme.palette.primary
.split(',')
.map((x) => parseInt(x, 10)) as [number, number, number]),
),
},
{
type: 'color',
name: 'secondary',
varName: '--lsd-theme-secondary',
rgb: theme.palette.secondary,
hex: rgbToHex(
...(theme.palette.secondary
.split(',')
.map((x) => parseInt(x, 10)) as [number, number, number]),
),
},
],
},
...keys.map(
(key) =>
({
name: key,
tokens: ['primary', 'secondary'].map((ck) => ({
name: ck,
type: 'color',
varName: `--lsd-${key}-${ck}`,
rgb: get(theme.palette, key + '.' + ck),
hex: rgbToHex(
...(get(theme.palette, key + '.' + ck)
.split(',')
.map((x: string) => parseInt(x, 10)) as [
number,
number,
number,
]),
),
})),
} as Colors[number]),
),
]
const spacing: Spacing = theme.spacing.map((spacing) => ({
name: spacing.toString(),
tokens: [
{
name: spacing.toString(),
type: 'spacing',
varName: `--lsd-spacing-${spacing}`,
value: `${spacing}px`,
},
],
}))
const typography: TypographyTokens = Object.entries(theme.typography).map(
([name, settings]) => ({
name: name,
tokens: [
{
name: 'fontSize',
type: 'typography',
value: `${settings.fontSize} (${remToPx(
(settings.fontSize as string) || '',
)}px)`,
varName: `--lsd-typography-${name}-fontSize`,
},
{
name: 'lineHeight',
type: 'typography',
value: `${settings.lineHeight} (${remToPx(
(settings.lineHeight as string) || '',
)}px)`,
varName: `--lsd-${name}-lineHeight`,
},
],
}),
)
return {
colors,
spacing,
typography,
}
}
export const SpacingDesignTokens = () => {
const theme = useTheme()
const { spacing } = getDesignTokens(theme)
return <Spacing spacing={spacing}></Spacing>
}
export const TypographyDesignTokens = () => {
const theme = useTheme()
const { typography } = getDesignTokens(theme)
return <TypographyTable typography={typography}></TypographyTable>
}
export const ColorDesignTokens = () => {
const theme = useTheme()
const { colors } = getDesignTokens(theme)
return (
<>
{colors.map((group) => (
<ColorGroup name={group.name} tokens={group.tokens} />
))}
</>
)
}
const ColorGroup: React.FC<Colors[number]> = ({ name, tokens }) => {
return (
<ColorGroupRoot>
<Typography className="color-group__name" component="div" variant="body1">
{name}
</Typography>
<div className="color-group__tokens">
{tokens.map((token) => (
<ColorCard key={token.name} groupName={name} {...token} />
))}
</div>
</ColorGroupRoot>
)
}
const ColorGroupRoot = styled.div`
margin-bottom: var(--lsd-spacing-32);
.color-group__name {
width: calc(50% - var(--lsd-spacing-16));
padding: var(--lsd-spacing-16) 0;
border-bottom: 1px solid rgba(var(--lsd-border-primary), 0.2);
}
.color-group__tokens {
display: flex;
flex-direction: row;
gap: var(--lsd-spacing-16);
margin-top: var(--lsd-spacing-16);
}
`
const ColorCard: React.FC<
Colors[number]['tokens'][number] & { groupName: string }
> = ({ groupName, name, varName, rgb, hex }) => {
return (
<ColorCardRoot>
<div
className="color-card__color"
style={{
background: `rgb(var(${varName}))`,
}}
></div>
<div className="color-card__details">
<Typography
genericFontFamily="monospace"
variant="label2"
className="color-card__var"
component="div"
>
<p>
{varName}: {rgb}
</p>
<p style={{ marginTop: 4 }}>
theme.palette
{groupName === 'primitives' ? `.${name}` : `.${groupName}.${name}`}
</p>
</Typography>
<div className="color-card__value">
<Typography variant="label2" className="color-card__rgb">
{name}
</Typography>
<Typography variant="label2" className="color-card__hex">
{hex}
</Typography>
</div>
</div>
</ColorCardRoot>
)
}
const ColorCardRoot = styled.div`
width: 50%;
border: 1px solid rgba(var(--lsd-border-primary), 0.2);
.color-card__color {
width: 100%;
height: 100px;
border-bottom: 1px solid rgba(var(--lsd-border-primary), 0.2);
}
.color-card__details {
margin-top: var(--lsd-spacing-8);
padding: var(--lsd-spacing-8);
}
.color-card__value {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-top: var(--lsd-spacing-8);
}
`
export const Spacing: React.FC<{ spacing: Spacing }> = ({ spacing }) => {
return (
<Table>
<thead>
<th>Example</th>
<th>CSS variable</th>
<th>Value</th>
</thead>
<tbody>
{spacing
.flatMap((s) => s.tokens)
.map((s, index) => (
<tr key={s.name}>
<td>
<div
style={{
width: s.value,
height: s.value,
boxSizing: 'border-box',
border: '1px solid rgb(var(--lsd-border-primary))',
}}
></div>
</td>
<td>
<Typography variant="label1" genericFontFamily="monospace">
{s.varName}
</Typography>
</td>
<td>
<Typography variant="label1" genericFontFamily="monospace">
{s.value}
</Typography>
</td>
</tr>
))}
</tbody>
</Table>
)
}
export const TypographyTable: React.FC<{ typography: TypographyTokens }> = ({
typography,
}) => {
return (
<Table>
<thead>
<th>variant</th>
<th>tokens</th>
<th>Value</th>
</thead>
<tbody>
{typography.map((t) => (
<>
<tr>
<td rowSpan={t.tokens.length}>
<Typography variant={t.name as TypographyVariants}>
{t.name}
</Typography>
</td>
{t.tokens.slice(0, 1).map((token) => (
<>
<td>
<Typography variant="body2" genericFontFamily="monospace">
{token.varName}
</Typography>
</td>
<td>
<Typography variant="body2" genericFontFamily="monospace">
{token.value}
</Typography>
</td>
</>
))}
</tr>
{t.tokens.slice(1).map((token) => (
<tr>
<td>
<Typography variant="body2" genericFontFamily="monospace">
{token.varName}
</Typography>
</td>
<td>
<Typography variant="body2" genericFontFamily="monospace">
{token.value}
</Typography>
</td>
</tr>
))}
</>
))}
</tbody>
</Table>
)
}
const Table = styled.table`
border-collapse: collapse;
width: 100%;
margin-top: var(--lsd-spacing-32);
margin-bottom: var(--lsd-spacing-32);
th {
font-weight: normal;
}
th,
td {
border: 1px solid rgba(var(--lsd-border-primary), 0.2);
padding: var(--lsd-spacing-8);
text-align: left;
color: rgb(var(--lsd-text-primary));
}
`

View File

@ -0,0 +1 @@
export * from './DesignTokens'

View File

@ -0,0 +1,11 @@
import { Meta, Story } from '@storybook/blocks'
import { useTheme } from '../../'
import { Root } from '../../components/Theme/ThemeProvider.stories'
<Meta title="Overview/Design Tokens" />
# Design tokens
Design tokens are variables that store values for the base layer of LSD, like colour and typography. They're used in components, so changes on this level will resonate throughout the whole system.
<Story of={Root} />

View File

@ -0,0 +1,51 @@
import { Meta } from '@storybook/blocks'
<Meta title="Overview/Quick Start" />
## 1. Installation
Install the latest version of `@acid-info/lsd-react`
```shell
yarn add @acid-info/lsd-react @emotion/react @emotion/styled
```
## 2. Setup
To use LSD theme and design tokens in your React app, wrap your app with the ThemeProvider component. This component:
- Creates a CSS baseline for the components using CSS-in-JS and inserts it into the DOM.
- Injects the LSD CSS variables into the DOM for the theme prop.
The ThemeProvider component should be at the root of your app, as shown below:
```tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { ThemeProvider, defaultThemes } from '@acid-info/lsd-react'
import App from './App'
ReactDOM.render(
<ThemeProvider theme={defaultThemes.dark}>
<App />
</ThemeProvider>,
document.getElementById('root'),
)
```
## 3. Usage
Import LSD components from `@acid-info/lsd-react` and use them in your React app!
```tsx
import { Button } from '@acid-info/lsd-react'
function App() {
return (
<div>
<Button>Button</Button>
</div>
)
}
```

View File

@ -0,0 +1,7 @@
import { Meta } from '@storybook/blocks'
<Meta title="Overview/Styling Components" />
# Styling Components
LSD components are styled with CSS-in-JS using the @emotion/react library, but we've made sure that you can still style them however you like.