Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion packages/mdc-linear-progress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ npm install @material/linear-progress
## Basic Usage

### HTML Structure

```html
<div role="progressbar" class="mdc-linear-progress">
<div role="progressbar" class="mdc-linear-progress" aria-label="Example Progress Bar" aria-valuemin="0" aria-valuemax="1" aria-valuenow="0">
<div class="mdc-linear-progress__buffering-dots"></div>
<div class="mdc-linear-progress__buffer"></div>
<div class="mdc-linear-progress__bar mdc-linear-progress__primary-bar">
Expand All @@ -52,6 +53,19 @@ npm install @material/linear-progress
</div>
```

### Accessibility

Progress bars conform to the [WAI-ARIA Progressbar Specification](https://www.w3.org/TR/wai-aria/#progressbar). The supported ARIA attributes for this progress bar are:

| Attribute | Description |
| --------- | ----------- |
| `aria-label` | Label indicating how the progress bar should be announced to the user. |
| `aria-valuemin` | The minimum numeric value of the progress bar, which should always be `0`. |
| `aria-valuemax` | The maximum numeric value of the progress bar, which should always be `1`. |
| `aria-valuenow` | A numeric value between `aria-valuemin` and `aria-valuemax` indicating the progress value of the primary progress bar. This attribute is removed in indeterminate progress bars. |

Note that `aria-label`, `aria-valuemin`, and `aria-valuemax` are static and must be configured in the HTML. `aria-valuenow` is updated dynamically by the foundation when the progress value is updated in determinate progress bars.

### Styles
```scss
@import "@material/linear-progress/mdc-linear-progress";
Expand Down Expand Up @@ -93,11 +107,13 @@ The adapter for linear progress must provide the following functions, with corre
| Method Signature | Description |
| --- | --- |
| `addClass(className: string) => void` | Adds a class to the root element. |
| `removeAttribute(attributeName: string) => void` | Removes the specified attribute from the root element. |
| `removeClass(className: string) => void` | Removes a class from the root element. |
| `hasClass(className: string) => boolean` | Returns boolean indicating whether the root element has a given class. |
| `forceLayout() => void` | Force-trigger a layout on the root element. This is needed to restart animations correctly. |
| `getPrimaryBar() => Element` | Returns the primary bar element. |
| `getBuffer() => Element` | Returns the buffer element. |
| `setAttribute(attributeName: string, value: string) => void` | Sets the specified attribute on the root element. |
| `setStyle(el: Element, styleProperty: string, value: string) => void` | Sets the inline style on the given element. |

### MDCLinearProgressFoundation API
Expand Down
2 changes: 2 additions & 0 deletions packages/mdc-linear-progress/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ export interface MDCLinearProgressAdapter {
getPrimaryBar(): HTMLElement | null;
hasClass(className: string): boolean;
removeClass(className: string): void;
removeAttribute(name: string): void;
setAttribute(name: string, value: string): void;
setStyle(el: HTMLElement, styleProperty: string, value: string): void;
}
6 changes: 6 additions & 0 deletions packages/mdc-linear-progress/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,13 @@ export class MDCLinearProgress extends MDCComponent<MDCLinearProgressFoundation>
getBuffer: () => this.root_.querySelector(MDCLinearProgressFoundation.strings.BUFFER_SELECTOR),
getPrimaryBar: () => this.root_.querySelector(MDCLinearProgressFoundation.strings.PRIMARY_BAR_SELECTOR),
hasClass: (className: string) => this.root_.classList.contains(className),
removeAttribute: (attributeName: string) => {
this.root_.removeAttribute(attributeName);
},
removeClass: (className: string) => this.root_.classList.remove(className),
setAttribute: (attributeName: string, value: string) => {
this.root_.setAttribute(attributeName, value);
},
setStyle: (el: HTMLElement, styleProperty: string, value: string) => el.style.setProperty(styleProperty, value),
};
return new MDCLinearProgressFoundation(adapter);
Expand Down
1 change: 1 addition & 0 deletions packages/mdc-linear-progress/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const cssClasses = {
};

export const strings = {
ARIA_VALUENOW: 'aria-valuenow',
BUFFER_SELECTOR: '.mdc-linear-progress__buffer',
PRIMARY_BAR_SELECTOR: '.mdc-linear-progress__primary-bar',
};
5 changes: 5 additions & 0 deletions packages/mdc-linear-progress/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ export class MDCLinearProgressFoundation extends MDCFoundation<MDCLinearProgress
getBuffer: () => null,
getPrimaryBar: () => null,
hasClass: () => false,
removeAttribute: () => undefined,
removeClass: () => undefined,
setAttribute: () => undefined,
setStyle: () => undefined,
};
}
Expand All @@ -68,6 +70,7 @@ export class MDCLinearProgressFoundation extends MDCFoundation<MDCLinearProgress

if (this.isDeterminate_) {
this.adapter_.removeClass(cssClasses.INDETERMINATE_CLASS);
this.adapter_.setAttribute(strings.ARIA_VALUENOW, this.progress_.toString());
this.setScale_(this.adapter_.getPrimaryBar(), this.progress_);
this.setScale_(this.adapter_.getBuffer(), this.buffer_);
} else {
Expand All @@ -83,6 +86,7 @@ export class MDCLinearProgressFoundation extends MDCFoundation<MDCLinearProgress
}

this.adapter_.addClass(cssClasses.INDETERMINATE_CLASS);
this.adapter_.removeAttribute(strings.ARIA_VALUENOW);
this.setScale_(this.adapter_.getPrimaryBar(), 1);
this.setScale_(this.adapter_.getBuffer(), 1);
}
Expand All @@ -92,6 +96,7 @@ export class MDCLinearProgressFoundation extends MDCFoundation<MDCLinearProgress
this.progress_ = value;
if (this.isDeterminate_) {
this.setScale_(this.adapter_.getPrimaryBar(), value);
this.adapter_.setAttribute(strings.ARIA_VALUENOW, value.toString());
}
}

Expand Down
21 changes: 17 additions & 4 deletions test/unit/mdc-linear-progress/foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {setupFoundationTest} from '../helpers/setup';
import {verifyDefaultAdapter} from '../helpers/foundation';
import {MDCLinearProgressFoundation} from '../../../packages/mdc-linear-progress/foundation';

const {cssClasses} = MDCLinearProgressFoundation;
const {cssClasses, strings} = MDCLinearProgressFoundation;

suite('MDCLinearProgressFoundation');

Expand All @@ -42,13 +42,21 @@ test('exports cssClasses', () => {

test('defaultAdapter returns a complete adapter implementation', () => {
verifyDefaultAdapter(MDCLinearProgressFoundation, [
'addClass', 'getPrimaryBar', 'forceLayout', 'getBuffer', 'hasClass', 'removeClass', 'setStyle',
'addClass',
'getPrimaryBar',
'forceLayout',
'getBuffer',
'hasClass',
'removeAttribute',
'removeClass',
'setAttribute',
'setStyle',
]);
});

const setupTest = () => setupFoundationTest(MDCLinearProgressFoundation);

test('#setDeterminate adds class and resets transforms', () => {
test('#setDeterminate false adds class, resets transforms, and removes aria-valuenow', () => {
const {foundation, mockAdapter} = setupTest();
td.when(mockAdapter.hasClass(cssClasses.INDETERMINATE_CLASS)).thenReturn(false);
const primaryBar = {};
Expand All @@ -60,6 +68,7 @@ test('#setDeterminate adds class and resets transforms', () => {
td.verify(mockAdapter.addClass(cssClasses.INDETERMINATE_CLASS));
td.verify(mockAdapter.setStyle(primaryBar, 'transform', 'scaleX(1)'));
td.verify(mockAdapter.setStyle(buffer, 'transform', 'scaleX(1)'));
td.verify(mockAdapter.removeAttribute(strings.ARIA_VALUENOW));
});

test('#setDeterminate removes class', () => {
Expand Down Expand Up @@ -87,6 +96,7 @@ test('#setDeterminate restores previous progress value after toggled from false
foundation.setDeterminate(false);
foundation.setDeterminate(true);
td.verify(mockAdapter.setStyle(primaryBar, 'transform', 'scaleX(0.123)'), {times: 2});
td.verify(mockAdapter.setAttribute(strings.ARIA_VALUENOW, '0.123'), {times: 2});
});

test('#setDeterminate restores previous buffer value after toggled from false to true', () => {
Expand All @@ -109,16 +119,18 @@ test('#setDeterminate updates progress value set while determinate is false afte
foundation.setProgress(0.123);
foundation.setDeterminate(true);
td.verify(mockAdapter.setStyle(primaryBar, 'transform', 'scaleX(0.123)'));
td.verify(mockAdapter.setAttribute(strings.ARIA_VALUENOW, '0.123'));
});

test('#setProgress sets transform', () => {
test('#setProgress sets transform and aria-valuenow', () => {
const {foundation, mockAdapter} = setupTest();
td.when(mockAdapter.hasClass(cssClasses.INDETERMINATE_CLASS)).thenReturn(false);
const primaryBar = {};
td.when(mockAdapter.getPrimaryBar()).thenReturn(primaryBar);
foundation.init();
foundation.setProgress(0.5);
td.verify(mockAdapter.setStyle(primaryBar, 'transform', 'scaleX(0.5)'));
td.verify(mockAdapter.setAttribute(strings.ARIA_VALUENOW, '0.5'));
});

test('#setProgress on indeterminate does nothing', () => {
Expand All @@ -129,6 +141,7 @@ test('#setProgress on indeterminate does nothing', () => {
foundation.init();
foundation.setProgress(0.5);
td.verify(mockAdapter.setStyle(), {times: 0, ignoreExtraArgs: true});
td.verify(mockAdapter.setAttribute(), {times: 0, ignoreExtraArgs: true});
});

test('#setBuffer sets transform', () => {
Expand Down
5 changes: 4 additions & 1 deletion test/unit/mdc-linear-progress/mdc-linear-progress.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {MDCLinearProgress, MDCLinearProgressFoundation} from '../../../packages/

function getFixture() {
return bel`
<div role="progressbar" class="mdc-linear-progress">
<div role="progressbar" class="mdc-linear-progress" aria-label="Unit Test Progress Bar" aria-valuemin="0"
aria-valuemax="1" aria-valuenow="0">
<div class="mdc-linear-progress__buffering-dots"></div>
<div class="mdc-linear-progress__buffer"></div>
<div class="mdc-linear-progress__bar mdc-linear-progress__primary-bar">
Expand Down Expand Up @@ -58,6 +59,7 @@ test('set indeterminate', () => {

component.determinate = false;
assert.isOk(root.classList.contains('mdc-linear-progress--indeterminate'));
assert.equal(undefined, root.getAttribute(MDCLinearProgressFoundation.strings.ARIA_VALUENOW));
});

test('set progress', () => {
Expand All @@ -66,6 +68,7 @@ test('set progress', () => {
component.progress = 0.5;
const primaryBar = root.querySelector(MDCLinearProgressFoundation.strings.PRIMARY_BAR_SELECTOR);
assert.equal('scaleX(0.5)', primaryBar.style.transform);
assert.equal('0.5', root.getAttribute(MDCLinearProgressFoundation.strings.ARIA_VALUENOW));
});

test('set buffer', () => {
Expand Down