Skip to content

Commit

Permalink
feat(list): improves the List component
Browse files Browse the repository at this point in the history
Improves the design of the list and simplifies some of the

fix #195
  • Loading branch information
stuarthendren committed Jul 28, 2021
1 parent 117279a commit 6780c46
Show file tree
Hide file tree
Showing 12 changed files with 374 additions and 218 deletions.
1 change: 1 addition & 0 deletions .eslintcache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"/workspaces/components/src/stitches.config.ts":"1"},{"size":17197,"mtime":1627388069412,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"3iazni","/workspaces/components/src/stitches.config.ts",[]]
14 changes: 14 additions & 0 deletions src/components/Accordion/Accordion.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,17 @@ export const Default: Story<React.ComponentProps<typeof Accordion>> = ({
</Accordion>
)
}

/** By setting `type` to multiple the accordion can have multiple sections open at once. */
export const Multiple = Default.bind({})
Multiple.args = {
type: 'multiple',
}

/** By setting `collapsible` to `false` and provided a `defaultValue` we can create an always one open accordion. */
export const OneOpen = Default.bind({})
OneOpen.args = {
type: 'single',
collapsible: false,
defaultValue: 'item-1',
}
40 changes: 31 additions & 9 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type { CSSProps } from '../../stitches.config'
import { keyframes, styled } from '../../stitches.config'
import { ChevronDown } from '../Icons'
import { paperStyles } from '../Paper'
import { buttonInteractionStyles } from '../Button/Button'
import { PartialPick } from '../../typings'

const slideDown = keyframes({
from: { height: 0 },
Expand All @@ -24,13 +26,32 @@ const Chevron = styled(ChevronDown, {
},
})

export const Accordion = (styled(Root, {
const StyledRoot = styled(Root, {
...paperStyles,
boxShadow: '$1',
borderTop: '1px solid $colors$grey4',
}) as unknown) as Polymorphic.ForwardRefComponent<
})

type AccordionProps = Partial<Polymorphic.OwnProps<typeof Root>> &
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This type is allowed when type single.
PartialPick<Polymorphic.OwnProps<typeof Root>, 'collapsible'> &
CSSProps

export const Accordion = forwardRef<HTMLDivElement, AccordionProps>(
({ collapsible = true, type = 'single', ...props }, forwardedRef) => (
<StyledRoot
type={type}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This type is allowed when type single.
collapsible={collapsible}
ref={forwardedRef}
{...props}
/>
)
) as Polymorphic.ForwardRefComponent<
Polymorphic.IntrinsicElement<typeof Root>,
Polymorphic.OwnProps<typeof Root> & CSSProps
AccordionProps
>
// Typed explicitly to get props in storybook

Expand All @@ -44,19 +65,20 @@ const AccordionTrigger = styled(Trigger, {
padding: '$4',
flex: 1,
textAlign: 'left',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
color: '$text',
width: '100%',
outline: 'none',

'&:hover': {
backgroundColor: '$grey3',
},
'&:focus': {
backgroundColor: '$grey4',
...buttonInteractionStyles,

pointerEvents: 'auto',

'&[aria-disabled="true"]': {
cursor: 'auto',
pointerEvents: 'none',
},
})

Expand Down
27 changes: 16 additions & 11 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ export const disabled = {
opacity: 0.4,
}

export const buttonInteractionStyles = {
cursor: 'pointer',
transition: 'background 0.5s',
backgroundPosition: 'center',

$$active: '$colors$defaultActive',
$$lowlight: '$colors$defaultLowlight',
$$hover: '$colors$defaultHighlight',

'&:hover': hover,
'&:focus': focus,
'&:active': active,
'&:disabled': disabled,
}

export const mainVariants = {
brand: {
$$active: '$colors$brandActive',
Expand Down Expand Up @@ -90,10 +105,8 @@ export const buttonBaseStyle = {
$$main: '$colors$primary',
$$mainHover: '$colors$primaryHighlight',
$$contrast: '$colors$primaryContrast',
$$active: '$colors$defaultActive',
$$default: '$colors$default',
$$defaultHover: '$colors$defaultHighlight',
$$lowlight: '$colors$defaultLowlight',

// Reset
alignItems: 'center',
Expand All @@ -119,19 +132,11 @@ export const buttonBaseStyle = {
// Defaults
fontSize: '$0',
borderRadius: '$default',
cursor: 'pointer',
backgroundColor: 'transparent',
border: 'none',
width: 'fit-content; width: -moz-fit-content',

// Actions
transition: 'background 0.5s',
backgroundPosition: 'center',

'&:hover': hover,
'&:focus': focus,
'&:active': active,
'&:disabled': disabled,
...buttonInteractionStyles,
}

const buttonVariants = {
Expand Down
207 changes: 134 additions & 73 deletions src/components/List/List.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,153 @@
import React from 'react'
import { mdiBluetooth, mdiComment, mdiWifi } from '@mdi/js'
import { action } from '@storybook/addon-actions'
import { Meta } from '@storybook/react'
import React from 'react'
import {
List,
ListItem,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
} from '.'
import { Box, Button, Flex } from '..'

import { Check } from '../Icons'
import { Checkbox, Svg, Switch } from '..'

export default {
title: 'Components/List',
component: List,
subcomponents: {
ListItem,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
},
} as Meta

export const Default: React.FC = () => {
return (
<Flex css={{ background: '$background', justifyContent: 'center' }}>
<Box css={{ background: '$paper', minWidth: '500px', margin: '$3' }}>
<List as="nav" aria-label="main mailbox folders">
<ListItem interactive>
<ListItemIcon>
{/* drafts */}
<Check />
</ListItemIcon>
<ListItemText text="Inbox" />
</ListItem>
<ListItem interactive>
<ListItemIcon>
{/* inbox */}
<Check />
</ListItemIcon>
<ListItemText text="Drafts" />
</ListItem>
</List>
<hr />
<List as="nav" aria-label="secondary mailbox folders">
<ListItem interactive>
<ListItemText text="Trash" />
</ListItem>
<ListItem interactive>
<ListItemText text="Spam" />
</ListItem>
</List>
</Box>
</Flex>
)
}
export const Default: React.FC = () => (
<List>
<ListItem>
<ListItemText text="Item 1" />
</ListItem>
<ListItem>
<ListItemText text="Item 2" />
</ListItem>
</List>
)

export const WithIcons: React.FC = () => {
return (
<Flex css={{ background: '$background', justifyContent: 'center' }}>
<Box css={{ background: '$paper', minWidth: '500px', margin: '$3' }}>
<List>
<ListItem interactive>
<ListItemIcon>
{/*
button in a button
causes browser to lockup
<Checkbox/>
*/}
</ListItemIcon>
<ListItemText text={`Line item`} />
<ListItemSecondaryAction>
<Button aria-label="comments">
{/* comment */}
<Check />
</Button>
</ListItemSecondaryAction>
</ListItem>
</List>
</Box>
</Flex>
)
}
export const Interactive: React.FC = () => (
<List>
<ListItem interactive onClick={action('Item 1')}>
<ListItemText text={`Line item 1`} />
</ListItem>
<ListItem interactive onClick={action('Item 2')}>
<ListItemText text={`Line item 2`} />
</ListItem>
<ListItem interactive disabled onClick={action('Item 3')}>
<ListItemText text={`Line item 3`} />
</ListItem>
</List>
)

export const WithSecondary: React.FC = () => {
/** List items can be marked as selected and the color can be controlled through the css variable `$$selectionColor` */
export const Selectable: React.FC = () => {
const [selected, setSelected] = React.useState('')
return (
<Flex css={{ background: '$background', justifyContent: 'center' }}>
<Box css={{ background: '$paper', minWidth: '500px', margin: '$3' }}>
<List>
<ListItem>
<ListItemText text="Item" subtext="secondary" />
</ListItem>
</List>
</Box>
</Flex>
<List>
<ListItem
interactive
selected={selected === 'Item 1'}
onClick={() => setSelected('Item 1')}
>
<ListItemText text={`Line item 1`} />
</ListItem>
<ListItem
css={{ $$selectionColor: '$colors$errorHighlight' }}
interactive
selected={selected === 'Item 2'}
onClick={() => setSelected('Item 2')}
>
<ListItemText text={`Line item 2`} />
</ListItem>
<ListItem
interactive
disabled
selected={selected === 'Item 3'}
onClick={() => setSelected('Item 3')}
>
<ListItemText text={`Line item 3`} />
</ListItem>
</List>
)
}

export const WithBorder: React.FC = () => (
<List border>
<ListItem>
<ListItemText text="Item 1" />
</ListItem>
<ListItem>
<ListItemText text="Item 2" />
</ListItem>
</List>
)

export const WithSecondaryActions: React.FC = () => (
<List>
<ListItem>
<ListItemText text={`Line item 1`} />
<ListItemSecondaryAction
path={mdiComment}
onClick={action('Secondary Item 1')}
/>
</ListItem>
<ListItem>
<ListItemText text={`Line item 2`} />
<ListItemSecondaryAction
path={mdiComment}
onClick={action('Secondary Item 2')}
/>
</ListItem>
</List>
)

export const WithSubtext: React.FC = () => (
<List>
<ListItem>
<ListItemText text="Item 1" subtext="Secondary item text" />
</ListItem>
<ListItem>
<ListItemText text="Item 2" subtext="Secondary item text" />
</ListItem>
</List>
)

export const WithSwitches: React.FC = () => (
<List>
<ListItem>
<ListItemIcon>
<Svg path={mdiWifi} />
</ListItemIcon>
<ListItemText text="Wi-Fi" />
<Switch />
</ListItem>
<ListItem>
<ListItemIcon>
<Svg path={mdiBluetooth} />
</ListItemIcon>
<ListItemText text="Bluetooth" />
<Switch />
</ListItem>
</List>
)

export const WithCheckbox: React.FC = () => (
<List>
<ListItem>
<ListItemText text="Wi-Fi" />
<Checkbox />
</ListItem>
<ListItem>
<ListItemText text="Bluetooth" />
<Checkbox />
</ListItem>
</List>
)
Loading

0 comments on commit 6780c46

Please sign in to comment.