Skip to content

Commit

Permalink
feat(community-progress): implement new design standards
Browse files Browse the repository at this point in the history
feat: new size prop has been added with mini variant of progress bar
feat: progress bars now have proper aria attributes
BREAKING CHANGE: progress bar height has been adjusted and no longer changes with viewport
BREAKING CHANGE: allyLabel prop in Progress is required to aid assistive technology
BREAKING CHANGE: 'primary' variant is now named 'positive'
BREAKING CHANGE: 'error' variant is now named 'negative'
BREAKING CHANGE: removed 'secondary' variant
BREAKING CHANGE: styled components is now a peer dependency
  • Loading branch information
marcod1419 authored and jraff committed Feb 7, 2020
1 parent 35da02d commit 7eaf907
Show file tree
Hide file tree
Showing 14 changed files with 227 additions and 134 deletions.
53 changes: 44 additions & 9 deletions packages/Progress/Bar/Bar.jsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,81 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import { colorAccessibleGreen, colorCardinal, colorGainsboro } from '@tds/core-colours'
import { safeRest } from '@tds/util-helpers'

import styles from '../Progress.scss'
import Negative from './textures/Negative'
import Disabled from './textures/Disabled'

const MIN_NON_ZERO_PROGRESS_PERCENTAGE = 5
const MAX_PROGRESS_PERCENTAGE = 100

const ProgressBar = styled.div(({ progress, zIndex, variant }) => ({
width: `${progress}%`,
height: '100%',
zIndex,
display: 'block',
position: 'absolute',
top: 0,
left: 0,
overflow: 'hidden',
boxShadow: 'inset 0 -1px 1px rgba(255,255,255,0.3)',

...(variant === 'positive' && { backgroundColor: colorAccessibleGreen }),
...(variant === 'negative' && { backgroundColor: colorCardinal }),
...(variant === 'disabled' && { backgroundColor: colorGainsboro }),
}))

const constrainNonZeroPercentage = (minNonZeroPercentage, maxPercentage, percentage) => {
if (percentage <= 0) return 0
else if (percentage < minNonZeroPercentage) return minNonZeroPercentage
else if (percentage > maxPercentage) return maxPercentage
return percentage
}

const Bar = ({ percentage, variant, ...rest }) => {
const Bar = ({ percentage, variant, a11yLabel, ...rest }) => {
const percent = constrainNonZeroPercentage(
MIN_NON_ZERO_PROGRESS_PERCENTAGE,
MAX_PROGRESS_PERCENTAGE,
percentage
)
const zIndex = 100 - percent // z-index order is inverse of percentage
return (
<div
<ProgressBar
{...safeRest(rest)}
className={[styles.progressBar, styles[variant]].join(' ')}
style={{ width: `${percent}%`, zIndex }}
/>
variant={variant}
progress={percent}
zIndex={zIndex}
role="progressbar"
aria-valuenow={percentage}
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext={a11yLabel}
>
{variant === 'negative' && <Negative />}
{variant === 'disabled' && <Disabled />}
</ProgressBar>
)
}

Bar.propTypes = {
/**
* Specifies how much of the task has been completed
* A number from 0 to 100 that specifies amount of progress.
*/
percentage: PropTypes.number.isRequired,
variant: PropTypes.oneOf(['primary', 'secondary', 'error', 'disabled']),
/**
* The style of the `Progress.Bar`.
*/
variant: PropTypes.oneOf(['positive', 'negative', 'disabled']),
/**
* A label to be read by assistive technology. Meant to give context about the `Progress.Bar`.
*/
a11yLabel: PropTypes.string.isRequired,
}

Bar.defaultProps = {
variant: 'primary',
variant: 'positive',
}

export default Bar
14 changes: 3 additions & 11 deletions packages/Progress/Bar/Bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,14 @@ By default, Progress.Bar will be displayed in the **primary** variant. Use prima

```jsx
<Progress>
<Progress.Bar percentage={40} />
<Progress.Bar percentage={40} a11yLabel="Data Used" />
</Progress>
```

Specify a **variant** to create a progress bar for non-primary data
You can use the **negative** variant to indicate a level of failure or warning

```jsx
<Progress>
<Progress.Bar percentage={40} variant="secondary" />
</Progress>
```

You can use the **error** variant to indicate a level of failure or warning

```jsx
<Progress>
<Progress.Bar percentage={40} variant="error" />
<Progress.Bar percentage={40} variant="negative" a11yLabel="Data Used" />
</Progress>
```
10 changes: 5 additions & 5 deletions packages/Progress/Bar/__tests__/Bar.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { shallow } from 'enzyme'
import Bar from '../Bar'

describe('Progress', () => {
const defaultProps = { percentage: 3 }
const defaultProps = { percentage: 3, a11yLabel: 'Data Usage' }
const doShallow = (overrides = {}) => shallow(<Bar {...defaultProps} {...overrides} />)

it('renders', () => {
Expand Down Expand Up @@ -37,12 +37,12 @@ describe('Progress', () => {
})

it('has a width that is a percentage proportional to its value', () => {
const progressBar = doShallow({ percentage: 50 }).find('.progressBar')
expect(progressBar).toHaveStyle({ width: '50%' })
const progressBar = doShallow({ percentage: 50 })
expect(progressBar).toMatchSnapshot()
})

it('has a width of 5% if value is equal or less than 5', () => {
const progressBar = doShallow({ percentage: 5 }).find('.progressBar')
expect(progressBar).toHaveStyle({ width: '5%' })
const progressBar = doShallow({ percentage: 5 })
expect(progressBar).toMatchSnapshot()
})
})
43 changes: 35 additions & 8 deletions packages/Progress/Bar/__tests__/__snapshots__/Bar.spec.jsx.snap
Original file line number Diff line number Diff line change
@@ -1,13 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Progress has a width of 5% if value is equal or less than 5 1`] = `
<Bar__ProgressBar
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow={5}
aria-valuetext="Data Usage"
progress={5}
role="progressbar"
variant="positive"
zIndex={95}
/>
`;

exports[`Progress has a width that is a percentage proportional to its value 1`] = `
<Bar__ProgressBar
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow={50}
aria-valuetext="Data Usage"
progress={50}
role="progressbar"
variant="positive"
zIndex={50}
/>
`;

exports[`Progress renders 1`] = `
<div
className="progressBar primary"
style={
Object {
"width": "5%",
"zIndex": 95,
}
}
<Bar__ProgressBar
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow={3}
aria-valuetext="Data Usage"
progress={5}
role="progressbar"
variant="positive"
zIndex={95}
/>
`;
19 changes: 19 additions & 0 deletions packages/Progress/Bar/textures/Disabled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react'

const Disabled = () => (
<svg width="100%" height="24">
<defs>
<pattern id="zohoz" patternUnits="userSpaceOnUse" width="8" height="8">
<rect width="8" height="8" fill="#D8D8D8" />
<circle cx="4" cy="4" r="2" fill="#71757B" stroke="#71757B" strokeWidth="0" />
<circle cx="0" cy="0" r="2" fill="#71757B" stroke="#71757B" strokeWidth="0" />
<circle cx="0" cy="8" r="2" fill="#71757B" stroke="#71757B" strokeWidth="0" />
<circle cx="8" cy="0" r="2" fill="#71757B" stroke="#71757B" strokeWidth="0" />
<circle cx="8" cy="8" r="2" fill="#71757B" stroke="#71757B" strokeWidth="0" />
</pattern>
</defs>
<path d="M 0 0 L 0 140 L 2000 2000 L 2000 0 Z" style={{ fill: 'url("#zohoz")' }} />
</svg>
)

export default Disabled
21 changes: 21 additions & 0 deletions packages/Progress/Bar/textures/Negative.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'

const Negative = () => (
<svg width="100%" height="24">
<defs>
<pattern id="sasmd" patternUnits="userSpaceOnUse" width="8" height="8">
<rect width="8" height="8" fill="#C12335" />
<path
d="M 0,8 l 8,-8 M -2,2 l 4,-4 M 6,10 l 4,-4"
strokeWidth="2"
shapeRendering="auto"
stroke="#e7adb4"
strokeLinecap="square"
/>
</pattern>
</defs>
<path d="M 0 0 L 0 140 L 2000 2000 L 2000 0 Z" style={{ fill: 'url("#sasmd")' }} />
</svg>
)

export default Negative
32 changes: 27 additions & 5 deletions packages/Progress/Progress.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import { colorGainsboro, colorWhite } from '@tds/core-colours'
import { safeRest } from '@tds/util-helpers'

import styles from './Progress.scss'

import Bar from './Bar/Bar'

const ProgressBarContainer = styled.div(({ size }) => ({
display: 'inline-block',
position: 'relative',
background: colorWhite,
width: '100%',
height: size === 'mini' ? '12px' : '24px',
borderRadius: size === 'mini' ? '12px' : '24px',
borderWidth: 'thin',
borderStyle: 'solid',
borderColor: colorGainsboro,
overflow: 'hidden',
transform: 'translateZ(-99999999px)', // Fix for safari overflow bug
}))

/**
* @version ./package.json
*/

const Progress = ({ children, ...rest }) => (
<div {...safeRest(rest)} className={styles.progressBarContainer}>
const Progress = ({ size, children, ...rest }) => (
<ProgressBarContainer {...safeRest(rest)} size={size}>
{children}
</div>
</ProgressBarContainer>
)

Progress.propTypes = {
/**
* The size of the progress bar where the default is 24px tall and mini is 12px tall.
*/
size: PropTypes.oneOf(['default', 'mini']),
children: PropTypes.element.isRequired,
}

Progress.defaultProps = {
size: 'default',
}

Progress.Bar = Bar
export default Progress
60 changes: 54 additions & 6 deletions packages/Progress/Progress.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,67 @@
The Progress component is a wrapper-container for an number of Progress.Bar components.
The Progress Bar is a visual representation of linear progression. They provide simple but important information at a quick glance.

A Progress component can contain any number of progress bars, which will be
stacked where shorter bars appears in front of longer bars.
### Usage Criteria

- Use to indicate progress within a single limit
- Available in 2 sizes: `default` and `mini`
- Use the `default` size when displaying ProgressBar with supporting content
- Use the `mini` size when displaying ProgressBar with minimal supporting content in a condensed layout
- Limit the use of `mini` size for overview views
- Use variants to provide immediate recognition of status:
- Will display in Positive variant by default
- Positive variant indicates account in good standing
- Negative variant indicates account meeting or exceeding limit
- Disabled variant indicates account is paused, unavailable, or inactive
- Use the `percentage` prop to indicate completion
- Limit the use of stacked bars (displaying multiple progress/limits within a single `ProgressBar`)

### Minimal Usage

```jsx
<Progress>
<Progress.Bar percentage={20} variant="error" />
<Progress.Bar percentage={25} variant="positive" a11yLabel="Data Usage" />
</Progress>
<Progress>
<Progress.Bar percentage={50} variant="primary" />
<Progress.Bar percentage={50} variant="negative" a11yLabel="Data Usage" />
</Progress>
<Progress>
<Progress.Bar percentage={70} variant="secondary" />
<Progress.Bar percentage={100} variant="disabled" a11yLabel="Data Usage" />
</Progress>
<Progress>
<Progress.Bar percentage={100} variant="disabled" />
<Progress.Bar percentage={30} variant="positive" a11yLabel="Data Used" />
<Progress.Bar percentage={75} variant="negative" a11yLabel="Data Unavailable" />
</Progress>
```

### Mini Size

```jsx
<Progress size="mini">
<Progress.Bar percentage={25} variant="positive" a11yLabel="Data Usage" />
</Progress>
<Progress size="mini">
<Progress.Bar percentage={50} variant="negative" a11yLabel="Data Usage" />
</Progress>
<Progress size="mini">
<Progress.Bar percentage={100} variant="disabled" a11yLabel="Data Usage" />
</Progress>
<Progress size="mini">
<Progress.Bar percentage={30} variant="negative" a11yLabel="Data Used" />
<Progress.Bar percentage={75} variant="disabled" a11yLabel="Data Unavailable" />
</Progress>
```

### Accessibility

#### Accessibility features

- `a11yLabel` prop is required to add context to assistive technology
- This prop should be applied on a per `Progress.Bar` basis to provide maximum context for stacked bars
- Patterns provide additional visual difference between states aside from colour alone
- Progress Bars make use of the ‘progressbar’ role and set related attributes to communicate progress to assistive technology. We chose `<div>` elements instead of `<progress>` for visual parity across all target browsers

#### Accessibility guidelines

- Include a label adjacent or in close proximity to the ProgressBar to provide clear context
- Build components in logical reading order
Loading

0 comments on commit 7eaf907

Please sign in to comment.