Skip to content

Commit

Permalink
feat(notification): Introduce components to color text and links for …
Browse files Browse the repository at this point in the history
…error notifications.

Add a Paragraph and ColoredText component to provide inherited color through React context.
  • Loading branch information
ryanoglesby08 committed Aug 18, 2017
1 parent a2a5736 commit d228b29
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 68 deletions.
14 changes: 11 additions & 3 deletions src/components/Link/Link.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import { warn } from '../../warn'

import styles from './Link.modules.scss'

const getClassName = (variant) => {
const getClassName = (variant, context) => {
if (context.inheritColor) {
return styles.inheritColor
}

return styles[variant]
}

/**
* <span class="docs--badge green">new!</span> <span class="docs--badge purple">v0.21.0</span>
*/
const Link = ({ reactRouterLinkComponent, variant, children, ...rest }) => {
const Link = ({ reactRouterLinkComponent, variant, children, ...rest }, context) => {
if (!(reactRouterLinkComponent && rest.to) && (reactRouterLinkComponent || rest.to)) {
warn('Link', 'The props `reactRouterLinkComponent` and `to` must be used together.')
}
Expand All @@ -24,7 +28,7 @@ const Link = ({ reactRouterLinkComponent, variant, children, ...rest }) => {
reactRouterLinkComponent || 'a',
{
...safeRest(rest),
className: getClassName(variant)
className: getClassName(variant, context)
},
children
)
Expand Down Expand Up @@ -61,6 +65,10 @@ Link.defaultProps = {
variant: 'base'
}

Link.contextTypes = {
inheritColor: PropTypes.bool
}

Link.Chevron = ChevronLink
Link.Button = ButtonLink

Expand Down
9 changes: 9 additions & 0 deletions src/components/Link/Link.modules.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,12 @@
color: $color-white;
}
}

.inheritColor {
composes: underlineOnHover;

&:link,
&:visited {
color: inherit;
}
}
46 changes: 28 additions & 18 deletions src/components/NotificationNew/Notification.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import safeRest from '../../safeRest'

import Container from '../Grid/Container/Container'
import Icon from '../Icon/Icon'
import ColoredText from '../Typography/ColoredText/ColoredText'
import Paragraph from '../Typography/Paragraph/Paragraph'

import styles from './Notification.modules.scss'

Expand All @@ -13,34 +15,42 @@ const iconByVariant = {
error: 'exclamation-point-circle'
}

const isSuccessOrError = variant => variant === 'success' || variant === 'error'
const isImportant = variant => variant === 'success' || variant === 'error'

const NotificationIcon = ({ variant }) => {
if (!isSuccessOrError(variant)) {
return null
}
const renderIcon = variant => (
<span className={styles.icon}>
<Icon glyph={iconByVariant[variant]} aria-hidden="true" />
</span>
)

return (
<span className={styles.icon}>
<Icon glyph={iconByVariant[variant]} aria-hidden="true" />
</span>
const renderContent = (variant, children) => {
const content = (
<Paragraph bold={isImportant(variant)}>
{children}
</Paragraph>
)
}
NotificationIcon.propTypes = {
variant: PropTypes.string.isRequired

if (variant === 'error') {
return (
<ColoredText colorClassName={styles.errorText}>
{content}
</ColoredText>
)
}

return content
}

/**
* A banner to highlight important notices.
* A banner that highlights important notices.
*/
const Notification = ({ variant, children, ...rest }) => (
<div {...safeRest(rest)} className={styles[variant]}>
<Container limitWidth>
<div className={styles.content}>
<NotificationIcon variant={variant} />
<p className={isSuccessOrError(variant) ? styles[`${variant}Text`] : styles.text}>
{children}
</p>
<div className={styles.flexRow}>
{isImportant(variant) ? renderIcon(variant) : undefined}

{renderContent(variant, children)}
</div>
</Container>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/NotificationNew/Notification.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Used for feedback or chat related messages.

```
<Notification variant="branded">
<strong>Tell us what you think</strong> It’s in our nature to listen. As TELUS.com continues to evolve, we’d love to hear more from you.
<strong>Tell us what you think</strong> It’s in our nature to listen. As TELUS.com continues to evolve, we’d love to <Link href="http://telus.com">hear more from you</Link>.
</Notification>
```

Expand All @@ -41,6 +41,6 @@ Used for error messages. Text is bolded to show importance.

```
<Notification variant="error">
Looks like our registration system is temporarily down. You’ll need to come back another time to register for My Account. In the meantime, return to TELUS.com.
Looks like our registration system is temporarily down. You’ll need to come back another time to register for My Account. In the meantime, return to <Link href="http://telus.com">TELUS.com</Link>.
</Notification>
```
25 changes: 1 addition & 24 deletions src/components/NotificationNew/Notification.modules.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ $tds-style-typography-html-tags: false;
background-color: $color-lavender-blush;
}

.content {
.flexRow {
display: flex;
flex-direction: row;
}
Expand All @@ -44,29 +44,6 @@ $tds-style-typography-html-tags: false;
margin-right: $spacing-base;
}


.textBase {
@extend %text--medium;

margin: 0;
}

.text {
composes: textBase;

color: $color-text;
}

.successText {
composes: textBase;

@extend %strong;
}

.errorText {
composes: textBase;

@extend %strong;

color: $color-cardinal;
}
54 changes: 40 additions & 14 deletions src/components/NotificationNew/__tests__/Notification.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { shallow } from 'enzyme'
import toJson from 'enzyme-to-json'

import Icon from '../../Icon/Icon'
import Paragraph from '../../Typography/Paragraph/Paragraph'

import Notification from '../Notification'
import ColoredText from '../../Typography/ColoredText/ColoredText'

describe('<Notification />', () => {
const doShallow = ({ ...overrides }) => (
shallow(<Notification {...overrides}>Some content</Notification>)
const defaultChildren = 'Some content'
const doShallow = (props = {}, children = defaultChildren) => (
shallow(<Notification {...props}>{children}</Notification>)
)

it('renders', () => {
Expand All @@ -25,23 +28,46 @@ describe('<Notification />', () => {
expect(notification).toHaveClassName('success')
})

it('adds an icon only to the error and success variants', () => {
let notification = doShallow({ variant: 'error' })
expect(notification.find('NotificationIcon').dive()).toContainReact(<Icon glyph="exclamation-point-circle" aria-hidden="true" />)

notification = doShallow({ variant: 'success' })
expect(notification.find('NotificationIcon').dive()).toContainReact(<Icon glyph="checkmark" aria-hidden="true" />)
it('does not have an icon by default', () => {
let notification = doShallow()
expect(notification.find(Icon)).toBeEmpty()

notification = doShallow({ variant: 'branded' })
expect(notification.find('NotificationIcon').dive()).toHaveText('')
expect(notification.find(Icon)).toBeEmpty()
})

it('styles the text only for success and error variants', () => {
let notification = doShallow({ variant: 'error' })
expect(notification.find('p')).toHaveClassName('errorText')
describe('successful variant', () => {
it('bolds the content', () => {
const notification = doShallow({ variant: 'success' }, 'A success message')

notification = doShallow({ variant: 'success' })
expect(notification.find('p')).toHaveClassName('successText')
expect(notification).toContainReact(<Paragraph bold>A success message</Paragraph>)
})

it('adds a checkmark icon', () => {
const notification = doShallow({ variant: 'success' })

expect(notification).toContainReact(<Icon glyph="checkmark" aria-hidden="true" />)
})
})

describe('error variant', () => {
it('bolds and colors the content', () => {
const notification = doShallow({ variant: 'error' }, 'An error message')

expect(notification).toContainReact(
<ColoredText colorClassName="errorText">
<Paragraph bold>An error message</Paragraph>
</ColoredText>
)
})

it('adds an exclamation point icon', () => {
const notification = doShallow({ variant: 'error' })

expect(notification).toContainReact(
<Icon glyph="exclamation-point-circle" aria-hidden="true" />
)
})
})

it('passes additional HTML attributes to the containing element', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ exports[`<Notification /> renders 1`] = `
limitWidth={true}
>
<div
className="content"
className="flexRow"
>
<NotificationIcon
variant="instructional"
/>
<p
className="text"
<Paragraph
bold={false}
>
Some content
</p>
</Paragraph>
</div>
</Container>
</div>
Expand Down
31 changes: 31 additions & 0 deletions src/components/Typography/ColoredText/ColoredText.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import PropTypes from 'prop-types'

class ColoredText extends React.Component {
getChildContext() {
return {
inheritColor: true
}
}

render() {
const { colorClassName, children } = this.props

return (
<div className={colorClassName}>
{children}
</div>
)
}
}

ColoredText.propTypes = {
colorClassName: PropTypes.string.isRequired,
children: PropTypes.node.isRequired
}

ColoredText.childContextTypes = {
inheritColor: PropTypes.bool
}

export default ColoredText
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { mount } from 'enzyme'

import ColoredText from '../ColoredText'
import Paragraph from '../../Paragraph/Paragraph'
import Link from '../../../Link/Link'

describe('ColoredText', () => {
it('allows text components to inherit their color', () => {
const coloredText = mount(
<ColoredText colorClassName="some-class-name">
<Paragraph>
Some content and a <Link>hyperlink</Link>
</Paragraph>
</ColoredText>
)

expect(coloredText.find('p')).toHaveClassName('inheritColor')
expect(coloredText.find('a')).toHaveClassName('inheritColor')
})
})
36 changes: 36 additions & 0 deletions src/components/Typography/Paragraph/Paragraph.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'

import styles from './Paragraph.modules.scss'

const Paragraph = ({ bold, children }, context) => {
const classes = classnames(
styles.base,
context.inheritColor ? styles.inheritColor : styles.color,
{
[styles.bold]: bold
}
)

return (
<p className={classes}>
{children}
</p>
)
}

Paragraph.propTypes = {
bold: PropTypes.bool,
children: PropTypes.node.isRequired
}

Paragraph.defaultProps = {
bold: false
}

Paragraph.contextTypes = {
inheritColor: PropTypes.bool
}

export default Paragraph
25 changes: 25 additions & 0 deletions src/components/Typography/Paragraph/Paragraph.modules.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
$tds-style-typography-html-tags: false;

@import '../../../scss/settings/colours';
@import '../../../scss/utility/responsive';
@import '../../../scss/settings/responsive-grid';
@import '../../../scss/settings/typography';
@import '../../../scss/foundation/typography';

.base {
@extend %text--medium;

margin: 0;
}

.bold {
@extend %strong;
}

.color {
color: $color-text;
}

.inheritColor {
color: inherit;
}
Loading

0 comments on commit d228b29

Please sign in to comment.