Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

Commit

Permalink
Alert: Refactor + Fancify (#385)
Browse files Browse the repository at this point in the history
* Alert: Refactor + Fancify

This update refactors the Alert component to use the latest Blue conventions,
including namespacing, propConnect, and type support.

It also implements CSS-in-JS styling via Fancy

* Remove @seed/seed-alert

Styles are now rendered via JS

* Fix tests

* Refactor Alert to TS
  • Loading branch information
Jon Quach committed Dec 7, 2018
1 parent ba9c417 commit 476f737
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 268 deletions.
129 changes: 129 additions & 0 deletions src/components/Alert/Alert.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import styled from '../styled'
import baseStyles from '../../styles/resets/baseStyles.css.js'
import { STATUSES } from '../../styles/configs/constants'
import { getColor } from '../../styles/utilities/color'
import forEach from '../../styles/utilities/forEach'

export const config = {
backgroundColor: getColor('state.warning.backgroundColor'),
color: getColor('state.warning.color'),
boxShadow: `inset 4px 0 0 0 ${getColor('state.warning.borderColor')}`,
}

export const AlertUI = styled('div')`
${baseStyles};
background-color: ${config.backgroundColor};
color: ${config.color};
box-shadow: ${config.boxShadow};
padding: 8px 16px;
margin-bottom: 16px;
text-align: left;
> *:first-child {
margin-top: 0;
}
> *:last-child {
margin-bottom: 0;
}
a:not(.c-button, .c-Button) {
color: inherit;
text-decoration: underline;
&:hover {
color: inherit;
}
}
&.is-noMargin {
margin-bottom: 0;
}
${makeStateStyles};
`

export const ContentUI = styled('div')`
align-items: flex-start;
display: flex;
> * {
max-width: 100%;
min-width: 0;
+ * {
margin-left: 12px;
}
}
`

export const BadgeWrapperUI = styled('div')`
padding: 4px 0;
+ * {
margin-left: 8px;
}
`

export const BlockUI = styled('div')`
line-height: 28px;
max-width: 100%;
min-height: 28px;
min-width: 0;
.c-Heading,
.c-Text,
.c-Link,
h1,
h2,
h3,
h4,
h5,
h6,
p,
ol,
ul {
color: inherit;
}
ul {
margin-bottom: 8px;
li:last-child {
margin-bottom: 0;
}
}
> p {
margin: 5px 0;
}
`

export const IconWrapperUI = styled('div')`
padding: 3px 0;
+ * {
margin-left: 8px;
}
`

export const CloseWrapperUI = styled('div')`
margin-left: auto;
margin-right: -4px;
padding-left: 8px;
`

function makeStateStyles(): string {
return forEach(
STATUSES,
state => `
&.is-${state} {
background-color: ${getColor('state', state, 'backgroundColor')};
box-shadow: inset 4px 0 0 0 ${getColor('state', state, 'borderColor')};
color: ${getColor('state', state, 'color')};
.c-Badge,
.c-badge {
background-color: ${getColor('state', state, 'borderColor')};
}
}
`
)
}
160 changes: 160 additions & 0 deletions src/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import * as React from 'react'
import { UIStatus } from '../../constants/types'
import getValidProps from '@helpscout/react-utils/dist/getValidProps'
import propConnect from '../PropProvider/propConnect'
import Badge from '../Badge'
import Animate from '../Animate'
import Collapsible from '../Collapsible'
import CloseButton from '../CloseButton'
import Icon from '../Icon'
import classNames from '../../utilities/classNames'
import { noop } from '../../utilities/other'
import { COMPONENT_KEY } from './Alert.utils'
import {
AlertUI,
BadgeWrapperUI,
BlockUI,
CloseWrapperUI,
ContentUI,
IconWrapperUI,
} from './Alert.css'

export interface Props {
actionRight?: any
badge?: string
className?: string
closeLabel?: string
dismissible: boolean
icon: boolean
noMargin: boolean
onDismiss: () => void
status: UIStatus
}

export interface State {
dismissed: boolean
}

export const classNameSpace = 'c-Alert'
export const cx = {
main: classNameSpace,
actionRight: `${classNameSpace}__actionRight`,
badge: `${classNameSpace}__badge`,
block: `${classNameSpace}__block`,
closeButton: `${classNameSpace}__closeButton`,
content: `${classNameSpace}__content`,
icon: `${classNameSpace}__icon`,
}

export class Alert extends React.PureComponent<Props, State> {
static defaultProps = {
dismissible: false,
icon: false,
noMargin: false,
onDismiss: noop,
status: 'warning',
}

state = {
dismissed: false,
}

handleOnDismiss = () => {
this.setState({
dismissed: true,
})
this.props.onDismiss()
}

getLeftContentMarkup = () => {
const { badge, icon, status } = this.props

const leftContentMarkup = badge ? (
<BadgeWrapperUI className={cx.badge}>
<Badge status={status}>{badge}</Badge>
</BadgeWrapperUI>
) : icon ? (
<IconWrapperUI className={cx.icon}>
<Icon name="alert" size="20" />
</IconWrapperUI>
) : null

return leftContentMarkup
}

getContentMarkup = () => {
const { actionRight, dismissible, children } = this.props

const actionRightMarkup = actionRight && (
<div className={cx.actionRight}>{actionRight}</div>
)

const closeButtonMarkup = dismissible && (
<CloseWrapperUI className={cx.closeButton}>
<CloseButton onClick={this.handleOnDismiss} seamless size="sm" />
</CloseWrapperUI>
)

return (
<ContentUI className={cx.content}>
{this.getLeftContentMarkup()}
<BlockUI className={cx.block}>{children}</BlockUI>
{actionRightMarkup}
{closeButtonMarkup}
</ContentUI>
)
}

render() {
const {
actionRight,
badge,
closeLabel,
dismissible,
children,
className,
icon,
onDismiss,
noMargin,
status,
...rest
} = this.props

const { dismissed } = this.state

const componentClassName = classNames(
cx.main,
actionRight && 'has-actionRight',
badge && 'has-badge',
dismissible && 'is-dismissible',
icon && 'has-icon',
noMargin && 'is-noMargin',
status && `is-${status}`,
className
)

const componentMarkup = (
<AlertUI
role="alert"
{...getValidProps(rest)}
className={componentClassName}
>
{this.getContentMarkup()}
</AlertUI>
)

return dismissible ? (
<Collapsible duration={200} isOpen={!dismissed}>
<Animate in={!dismissed} sequence={['fade']} duration={100}>
{componentMarkup}
</Animate>
</Collapsible>
) : (
componentMarkup
)
}
}

const PropConnectedComponent = propConnect(COMPONENT_KEY)(Alert)

export default PropConnectedComponent
1 change: 1 addition & 0 deletions src/components/Alert/Alert.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const COMPONENT_KEY = 'Alert'

0 comments on commit 476f737

Please sign in to comment.