Skip to content

Commit 473c75b

Browse files
Merge pull request #1376 from kchan902/progress-indicator-assistive-text
Fixed problem with progress indicator/assistive-text
2 parents 8fd3f2b + 8d63d17 commit 473c75b

File tree

29 files changed

+1319
-44
lines changed

29 files changed

+1319
-44
lines changed

components/progress-indicator/__docs__/storybook-stories.jsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,14 @@ const ExampleProgressIndicator = createReactClass({
4343

4444
render () {
4545
return (
46-
<div style={{ padding: '2rem 1rem 0px' }}>
46+
<div style={{ padding: '4rem 1rem 0px' }}>
4747
<ProgressIndicator
48+
id="example-progress-indicator"
4849
steps={this.props.steps}
4950
selectedStep={this.props.selectedStep}
51+
disabledSteps={this.props.disabledSteps}
52+
completedSteps={this.props.completedSteps}
53+
assistiveText={this.props.assistiveText}
5054
onStepClick={(event, data) => {
5155
console.log(data);
5256
}}
@@ -81,10 +85,25 @@ storiesOf(PROGRESS_INDICATOR, module)
8185
))
8286
.add('Step Error', () => (
8387
<StepError
88+
id="example-progress-indicator"
8489
steps={steps}
8590
selectedStep={steps[1]}
8691
completedSteps={steps.slice(0, 1)}
8792
errorSteps={steps.slice(1, 2)}
8893
/>
8994
))
90-
.add('In A Modal (With Step Error)', () => <Modal />);
95+
.add(
96+
'In A Modal (With Step Error) - Needs DOM',
97+
() => (typeof document !== 'undefined' ? <Modal /> : null)
98+
)
99+
.add('Completed Progress', () => (
100+
<ExampleProgressIndicator
101+
steps={steps}
102+
selectedStep={steps[steps.length - 2]}
103+
completedSteps={steps.slice(0, steps.length - 2)}
104+
assistiveText={{
105+
completedStep: 'Finished this step.',
106+
disabledStep: 'Unable to proceed on this step.',
107+
}}
108+
/>
109+
));
Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React from 'react';
2-
import createReactClass from 'create-react-class';
32
import IconSettings from '~/components/icon-settings';
43
import ProgressIndicator from '~/components/progress-indicator'; // `~` is replaced with design-system-react at runtime
54

@@ -15,33 +14,56 @@ const steps = [
1514
{ id: 4, label: 'tooltip label #5' },
1615
];
1716

18-
const handleStepEvent = function (event, data) {
19-
console.log(data);
20-
};
17+
/*
18+
* This example allows you to select the next step and only the next step. Typically, Progress Indicator should be paired with a modal and form errors should be explained outside of this component.
19+
*/
20+
class Example extends React.Component {
21+
constructor (props) {
22+
super(props);
23+
this.state = {
24+
steps,
25+
completedSteps: [],
26+
disabledSteps: steps.slice(2, steps.length),
27+
selectedStep: steps[0],
28+
};
29+
}
2130

22-
const Example = createReactClass({
23-
displayName: 'ProgressIndicatorDefault',
31+
handleStepEvent = (event, data) => {
32+
this.setState({
33+
completedSteps: steps.slice(0, data.step.id),
34+
disabledSteps:
35+
data.step.id < steps.length
36+
? steps.slice(data.step.id + 2, steps.length)
37+
: [],
38+
selectedStep: data.step,
39+
});
40+
};
2441

2542
render () {
2643
return (
2744
<IconSettings iconPath="/assets/icons">
2845
<div
2946
style={{
30-
padding: '2rem 1rem 0px',
47+
padding: '4rem 1rem 0px',
3148
background:
32-
this.props.variant === 'modal' ? 'rgb(244, 246, 249)' : '',
49+
this.props.variant === 'modal' ? 'rgb(244, 246, 249)' : undefined,
3350
}}
3451
>
3552
<ProgressIndicator
36-
steps={steps}
37-
selectedStep={steps[0]}
38-
onStepClick={handleStepEvent}
53+
id="example-progress-indicator"
54+
completedSteps={this.state.completedSteps}
55+
disabledSteps={this.state.disabledSteps}
56+
onStepClick={this.handleStepEvent}
57+
steps={this.state.steps}
58+
selectedStep={this.state.selectedStep}
3959
// tooltipIsOpenSteps={stepsBasic.slice(0, 2)}
4060
/>
4161
</div>
4262
</IconSettings>
4363
);
44-
},
45-
});
64+
}
65+
}
66+
67+
Example.displayName = 'ProgressIndicatorDefault';
4668

4769
export default Example; // export is replaced with `ReactDOM.render(<Example />, mountNode);` at runtime

components/progress-indicator/__examples__/step-error.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const Example = createReactClass({
77

88
render () {
99
return (
10-
<div style={{ padding: '2rem 1rem 0px' }}>
10+
<div style={{ padding: '4rem 1rem 0px' }}>
1111
<ProgressIndicator {...this.props} />
1212
</div>
1313
);

components/progress-indicator/index.jsx

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import React from 'react';
77
import PropTypes from 'prop-types';
88

9-
import assign from 'lodash.assign';
10-
119
import find from 'lodash.find';
1210

1311
// ### shortid
@@ -26,10 +24,17 @@ const propTypes = {
2624
/**
2725
* **Assistive text for accessibility**
2826
* This object is merged with the default props object on every render.
29-
* * `percentage`: Label for Progress Bar. The default is `Progress: [this.props.value]%`
27+
* * `completedStep`: Label for a completed step. The default is `Completed Step`
28+
* * `disabledStep`: Label for disabled step. The default is `Disabled Step`
29+
* * `errorStep`: Label for a step with an error. The default is `Error Step`
30+
* * `percentage`: Label for Progress Bar. The default is `Progress: [this.props.value]%`. You will need to calculate the percentage yourself if changing this string.
31+
* * `step`: Label for a step. It will be typically followed by the number of the step such as "Step 1".
3032
*/
3133
assistiveText: PropTypes.shape({
34+
completedStep: PropTypes.string,
35+
disabledStep: PropTypes.string,
3236
percentage: PropTypes.string,
37+
step: PropTypes.string,
3338
}),
3439
/**
3540
* CSS class names to be added to the container element. `array`, `object`, or `string` are accepted.
@@ -49,7 +54,7 @@ const propTypes = {
4954
*/
5055
disabledSteps: PropTypes.array,
5156
/**
52-
* Stores all error steps. It is an array of step objects and usually there is only one error step, the current step.
57+
* Stores all error steps. It is an array of step objects and usually there is only one error step, the current step. If an error occurs a second error icon should be placed to the left of related confirmation buttons (e.g. Cancel, Save) and an Error Popover should appear indicating there are errors. These additional items are NOT part of this component. This note was included for visibility purposes. Please refer to [SLDS website](https://www.lightningdesignsystem.com/components/progress-indicator/) for full details **
5358
*/
5459
errorSteps: PropTypes.array,
5560
/**
@@ -106,7 +111,12 @@ const defaultSteps = [
106111
];
107112

108113
const defaultProps = {
109-
assistiveText: {},
114+
assistiveText: {
115+
completedStep: 'Completed',
116+
disabledStep: 'Disabled',
117+
errorStep: 'Error',
118+
step: 'Step',
119+
},
110120
errorSteps: [],
111121
completedSteps: [],
112122
disabledSteps: [],
@@ -165,10 +175,17 @@ class ProgressIndicator extends React.Component {
165175

166176
render () {
167177
// Merge objects of strings with their default object
168-
const assistiveText = this.props
169-
? assign({}, defaultProps.assistiveText, this.props.assistiveText)
170-
: defaultProps.assistiveText;
171-
178+
const assistiveText = {
179+
...defaultProps.assistiveText,
180+
...this.props.assistiveText,
181+
};
182+
183+
const {
184+
selectedStep,
185+
disabledSteps,
186+
errorSteps,
187+
completedSteps,
188+
} = this.props;
172189
/** 1. preparing data */
173190
const allSteps = this.getSteps();
174191

@@ -199,13 +216,14 @@ class ProgressIndicator extends React.Component {
199216
>
200217
{allSteps.map((step, i) => (
201218
<Step
219+
assistiveText={assistiveText}
202220
key={`${this.getId()}-${step.id}`}
203221
id={this.getId()}
204222
index={i}
205-
isSelected={findStep(step, this.props.selectedStep)}
206-
isDisabled={findStep(step, this.props.disabledSteps)}
207-
isError={findStep(step, this.props.errorSteps)}
208-
isCompleted={findStep(step, this.props.completedSteps)}
223+
isSelected={findStep(step, selectedStep)}
224+
isDisabled={findStep(step, disabledSteps)}
225+
isError={findStep(step, errorSteps)}
226+
isCompleted={findStep(step, completedSteps)}
209227
onClick={this.props.onStepClick}
210228
onFocus={this.props.onStepFocus}
211229
step={step}

components/progress-indicator/private/progress-bar.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ class ProgressBar extends React.Component {
2828
aria-valuemax="100"
2929
aria-valuenow={this.props.value}
3030
role="progressbar"
31-
tabIndex={0}
3231
>
3332
<span
3433
className="slds-progress-bar__value"

components/progress-indicator/private/step.jsx

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@ const displayName = PROGRESS_INDICATOR_STEP;
1919

2020
// ### Prop Types
2121
const propTypes = {
22+
/**
23+
* **Assistive text for accessibility**
24+
* This object is merged with the default props object on every render.
25+
* * `completedStep`: Label for a completed step. The default is `Completed Step`
26+
* * `disabledStep`: Label for disabled step. The default is `Disabled Step`
27+
* * `errorStep`: Label for a step with an error. The default is `Error Step`
28+
* * `percentage`: Label for Progress Bar. The default is `Progress: [this.props.value]%`. You will need to calculate the percentage yourself if changing this string.
29+
* * `step`: Label for a step. It will be typically followed by the number of the step such as "Step 1".
30+
*/
31+
assistiveText: PropTypes.shape({
32+
completedStep: PropTypes.string,
33+
disabledStep: PropTypes.string,
34+
percentage: PropTypes.string,
35+
step: PropTypes.string,
36+
}),
2237
/**
2338
* Id for Steps, ranging in [0, steps.length).
2439
*/
@@ -30,6 +45,7 @@ const propTypes = {
3045
/**
3146
* Determines if the step has been completed
3247
*/
48+
3349
isCompleted: PropTypes.bool,
3450
/**
3551
* Determines if the step has been disabled
@@ -91,15 +107,15 @@ class Step extends React.Component {
91107
const icon = renderIcon ? (
92108
<ButtonIcon
93109
category="utility"
94-
name={this.props.isError ? 'warning' : 'success'}
110+
name={this.props.isError ? 'error' : 'success'}
95111
/>
96112
) : null;
97113

98114
const handleClick = (event) => props.onClick(event, data);
99115
const handleFocus = (event) => props.onFocus(event, data);
100116

101117
const stepButton = props.isDisabled ? (
102-
<span
118+
<a
103119
className={classNames(
104120
'slds-button',
105121
{ 'slds-button_icon': renderIcon },
@@ -113,9 +129,12 @@ class Step extends React.Component {
113129
>
114130
{icon}
115131
<span className="slds-assistive-text">
116-
{props.step.assistiveText || `Step ${props.index + 1}: ${status}`}
132+
{this.props.step.assistiveText ||
133+
`${props.assistiveText.step} ${props.index + 1}: ${
134+
props.step.label
135+
} - ${status}`}
117136
</span>
118-
</span>
137+
</a>
119138
) : (
120139
<button
121140
className={classNames(
@@ -128,10 +147,14 @@ class Step extends React.Component {
128147
onFocus={handleFocus}
129148
aria-describedby={`progress-indicator-tooltip-${this.props.step.id ||
130149
this.props.index}`}
150+
aria-current={this.props.isSelected ? 'step' : null}
131151
>
132152
{icon}
133153
<span className="slds-assistive-text">
134-
{props.step.assistiveText || `Step ${props.index + 1}: ${status}`}
154+
{this.props.step.assistiveText ||
155+
`${props.assistiveText.step} ${props.index + 1}: ${
156+
props.step.label
157+
}${status ? ` - ${status}` : ''}`}
135158
</span>
136159
</button>
137160
);
@@ -141,22 +164,21 @@ class Step extends React.Component {
141164

142165
render () {
143166
const renderIcon = this.props.isCompleted || this.props.isError;
144-
// step status (one of ['Error', 'Completed', 'Active', 'Uncompleted'])
145167
let status = '';
146168
if (this.props.isError) {
147-
status = 'Error';
169+
status = this.props.assistiveText.errorStep;
148170
} else if (this.props.isCompleted) {
149-
status = 'Completed';
150-
} else if (this.props.isSelected) {
151-
status = 'Active';
152-
} else status = 'Uncompleted';
171+
status = this.props.assistiveText.completedStep;
172+
} else if (this.props.isDisabled) {
173+
status = this.props.assistiveText.disabledStep;
174+
}
153175

154176
const tooltipProps = {
155177
align: 'top',
156178
id: `progress-indicator-tooltip-${this.props.step.id ||
157179
this.props.index}`,
158180
content: this.props.step.label,
159-
theme: this.props.isError ? 'error' : 'info',
181+
theme: 'info',
160182
triggerStyle: { display: !renderIcon ? 'flex' : '' },
161183
};
162184

components/story-based-tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export Tree from '../components/tree/__docs__/storybook-stories';
6464
// export Notification from '../components/notification/__docs__/storybook-stories';
6565
// export Panel from '../components/panel/__docs__/storybook-stories';
6666
// export Popover from '../components/popover/__docs__/storybook-stories';
67-
// export ProgressIndicator from '../components/progress-indicator/__docs__/storybook-stories';
67+
export ProgressIndicator from '../components/progress-indicator/__docs__/storybook-stories';
6868
// export Picklist from '../components/menu-picklist/__docs__/storybook-stories';
6969
// export Spinner from '../components/spinner/__docs__/storybook-stories';
7070
// export TimePicker from '../components/time-picker/__docs__/storybook-stories';

0 commit comments

Comments
 (0)