Skip to content

Commit

Permalink
feat: Add expand/collapse option to the callout component (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
floreks authored Jan 3, 2023
1 parent 2fa6f8f commit 8b3503a
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 11 deletions.
62 changes: 52 additions & 10 deletions src/components/Callout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import classNames from 'classnames'
import PropTypes from 'prop-types'
import { PropsWithChildren, forwardRef, useMemo } from 'react'
import {
Dispatch,
PropsWithChildren,
forwardRef,
useMemo,
} from 'react'
import styled, { useTheme } from 'styled-components'
import { ColorKey, Severity } from 'src/types'
import { Flex } from 'honorable'
import AnimateHeight from 'react-animate-height'

import { CaretDownIcon } from '../icons'

import {
FillLevel,
Expand All @@ -16,6 +25,7 @@ import ErrorIcon from './icons/ErrorIcon'
import InfoIcon from './icons/InfoIcon'
import StatusOkIcon from './icons/StatusOkIcon'
import WarningIcon from './icons/WarningIcon'
import IconFrame from './IconFrame'

const SEVERITIES = ['info', 'danger', 'warning', 'success'] as const

Expand Down Expand Up @@ -64,6 +74,9 @@ export type CalloutProps = PropsWithChildren<{
buttonProps?: ButtonProps
fillLevel?: FillLevel
className?: string
expandable?: boolean
expanded?: boolean
onExpand?: Dispatch<boolean>
}>

export function CalloutButton(props: ButtonProps) {
Expand All @@ -79,6 +92,9 @@ const Callout = forwardRef<HTMLDivElement, CalloutProps>(({
title,
severity = DEFAULT_SEVERITY,
size = 'full',
expandable = false,
expanded = false,
onExpand,
fillLevel,
className,
buttonProps,
Expand Down Expand Up @@ -120,6 +136,7 @@ ref) => {
$borderColorKey={borderColorKey}
$fillLevel={fillLevel}
$size={size}
$expanded={expanded}
ref={ref}
>
<div className="icon">
Expand All @@ -131,17 +148,34 @@ ref) => {
/>
</div>
<div>
<h6 className={classNames({ visuallyHidden: !title })}>
<h6 className={classNames({ visuallyHidden: !title, expandable })}>
<span className="visuallyHidden">{`${text}: `}</span>
{title}
</h6>
<div className="children">{children}</div>
{buttonProps && (
<div className="buttonArea">
<CalloutButton {...buttonProps} />
</div>
)}
<AnimateHeight height={(expandable && expanded) || !expandable ? 'auto' : 0}>
<div className="children">{children}</div>
{buttonProps && (
<div className="buttonArea">
<CalloutButton {...buttonProps} />
</div>
)}
</AnimateHeight>
</div>
{expandable && (
<Flex
grow={1}
justify="end"
>
<IconFrame
textValue=""
display="flex"
size="small"
clickable
onClick={() => onExpand && onExpand(!expanded)}
icon={<CaretDownIcon className="expandIcon" />}
/>
</Flex>
)}
</CalloutWrap>
</FillLevelProvider>
)
Expand All @@ -151,8 +185,9 @@ const CalloutWrap = styled.div<{
$borderColorKey: string
$size: CalloutSize
$fillLevel: FillLevel
$expanded: boolean
}>(({
theme, $size, $fillLevel, $borderColorKey,
theme, $size, $fillLevel, $borderColorKey, $expanded,
}) => ({
position: 'relative',
display: 'flex',
Expand All @@ -172,7 +207,11 @@ const CalloutWrap = styled.div<{
color: theme.colors.text,
margin: 0,
padding: 0,
marginBottom: theme.spacing.xxsmall,
marginBottom: theme.spacing.small,

'&.expandable': {
marginBottom: $expanded ? theme.spacing.small : 0,
},
},
'.children *:first-child': {
marginTop: '0',
Expand Down Expand Up @@ -224,6 +263,9 @@ const CalloutWrap = styled.div<{
'& a, & a:any-link': {
...theme.partials.text.inlineLink,
},
'.expandIcon': {
...theme.partials.dropdown.arrowTransition({ isOpen: $expanded }),
},
}))

Callout.propTypes = {
Expand Down
38 changes: 37 additions & 1 deletion src/stories/Callout.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Div } from 'honorable'
import { Div, Flex } from 'honorable'

import { useState } from 'react'

import { FillLevel } from '../components/contexts/FillLevelContext'
import { Callout, CalloutProps, Card } from '..'
Expand Down Expand Up @@ -49,6 +51,7 @@ const compactContent = (
function Template({
size,
withButton,
expandable,
title,
fillLevel,
onFillLevel,
Expand Down Expand Up @@ -82,6 +85,7 @@ function Template({
title={title}
fillLevel={fillLevel}
buttonProps={withButton ? { children: 'Button text' } : undefined}
expandable={expandable}
>
{size === 'compact' ? compactContent : fullContent}
</Callout>
Expand All @@ -90,6 +94,32 @@ function Template({
)
}

function ExpandableTemplate({ title }: CalloutProps) {
const [expanded, setExpanded] = useState(false)

return (
<Flex
flexDirection="column"
gap="large"
maxWidth={600}
>
{styles.map(style => (
<Callout
key={style}
severity={style}
title={title}
buttonProps={{ children: 'Learn more' }}
expandable
expanded={expanded}
onExpand={setExpanded}
>
{fullContent}
</Callout>
))}
</Flex>
)
}

export const Default = Template.bind({})
Default.args = {
title: '',
Expand Down Expand Up @@ -122,6 +152,11 @@ WithButton.args = {
onFillLevel: 0,
}

export const Expandable = ExpandableTemplate.bind({})
Expandable.args = {
title: 'Why do I need to authenticate with GitHub/GitLab?',
}

export const KitchenSink = Template.bind({})
KitchenSink.args = {
title: 'Title text - How to write a dummy title',
Expand All @@ -136,4 +171,5 @@ OnCard.args = {
size: 'full',
withButton: true,
onFillLevel: 1,
expandable: false,
}

0 comments on commit 8b3503a

Please sign in to comment.