diff --git a/packages/oui-angular/src/index.js b/packages/oui-angular/src/index.js index 214320dd..471a1e6e 100644 --- a/packages/oui-angular/src/index.js +++ b/packages/oui-angular/src/index.js @@ -34,6 +34,7 @@ import "@oui-angular/oui-page-header/src"; import "@oui-angular/oui-tile/src"; import "@oui-angular/oui-guide-menu/src"; import "@oui-angular/oui-header-tabs/src"; +import "@oui-angular/oui-progress/src"; angular.module("oui", [ "oui.button", @@ -71,5 +72,6 @@ angular.module("oui", [ "oui.page-header", "oui.tile", "oui.guide-menu", - "oui.header-tabs" + "oui.header-tabs", + "oui.progress" ]); diff --git a/packages/oui-angular/src/index.spec.js b/packages/oui-angular/src/index.spec.js index 624cda3e..3014b647 100644 --- a/packages/oui-angular/src/index.spec.js +++ b/packages/oui-angular/src/index.spec.js @@ -35,6 +35,7 @@ loadTests(require.context("../../oui-page-header/src/", true, /.*((\.spec)|(inde loadTests(require.context("../../oui-tile/src/", true, /.*((\.spec)|(index))$/)); loadTests(require.context("../../oui-guide-menu/src/", true, /.*((\.spec)|(index))$/)); loadTests(require.context("../../oui-header-tabs/src/", true, /.*((\.spec)|(index))$/)); +loadTests(require.context("../../oui-progress/src/", true, /.*((\.spec)|(index))$/)); function loadTests (context) { context.keys().forEach(context); diff --git a/packages/oui-progress/README.md b/packages/oui-progress/README.md new file mode 100644 index 00000000..c639db5c --- /dev/null +++ b/packages/oui-progress/README.md @@ -0,0 +1,134 @@ +# oui-progress + + + +## Usage + +### Simple + +```html:preview + + + + + + + + + + + + + + + +``` + +### With custom labels + +```html:preview + + + + +``` + +### Custom max value + +```html:preview + + + + +``` + +### Stacked + +```html:preview + + + + +``` + +### Thresholds + +```html:preview + + + + + + + + + + + + + + + + + + +``` + +### Compact mode + +```html:preview + + + + + + + + + + + + + + + + + + + +``` + +## API + +### oui-progress + +| Attribute | Type | Binding | One-time binding | Values | default | Description +| ---- | ---- | ---- | ---- | ---- | ---- | ---- +| compact | Boolean | { + this.$element + .attr("ariaValuenow", value); + + if (!this.compact) { + this.$element + .css("width", this.progressCtrl.getPercentageValue(value)); + } + }); + } + + $postLink () { + this.$timeout(() => { + this.$element + .addClass("oui-progress__bar") + .addClass(`oui-progress__bar_${this.type}`) + .attr("ariaValuenow", this.value) + .attr("ariaValuemin", this.minValue) + .attr("ariaValuemax", this.maxValue) + .attr("role", "progressbar"); + + if (this.text) { + this.$element + .attr("ariaValuetext", this.text); + } + + if (!this.compact) { + this.$element + .css("width", this.progressCtrl.getPercentageValue(this.value)); + } + }); + } +} diff --git a/packages/oui-progress/src/bar/progress-bar.html b/packages/oui-progress/src/bar/progress-bar.html new file mode 100644 index 00000000..9970e2a6 --- /dev/null +++ b/packages/oui-progress/src/bar/progress-bar.html @@ -0,0 +1,3 @@ + + diff --git a/packages/oui-progress/src/index.js b/packages/oui-progress/src/index.js new file mode 100644 index 00000000..f6cb9fa3 --- /dev/null +++ b/packages/oui-progress/src/index.js @@ -0,0 +1,8 @@ +import Progress from "./progress.component.js"; +import ProgressBar from "./bar/progress-bar.component.js"; +import ProgressThreshold from "./threshold/progress-threshold.component.js"; + +angular.module("oui.progress", []) + .component("ouiProgress", Progress) + .component("ouiProgressBar", ProgressBar) + .component("ouiProgressThreshold", ProgressThreshold); diff --git a/packages/oui-progress/src/index.spec.js b/packages/oui-progress/src/index.spec.js new file mode 100644 index 00000000..532b28de --- /dev/null +++ b/packages/oui-progress/src/index.spec.js @@ -0,0 +1,163 @@ +describe("ouiProgress", () => { + let TestUtils; + let $timeout; + + const progressClass = "oui-progress"; + const progressBarClass = `${progressClass}__bar`; + const progressLabelClass = `${progressClass}__label`; + const progressThresholdClass = `${progressClass}__threshold`; + + beforeEach(angular.mock.module("oui.progress")); + beforeEach(angular.mock.module("oui.test-utils")); + + beforeEach(inject((_TestUtils_, _$timeout_) => { + TestUtils = _TestUtils_; + $timeout = _$timeout_; + })); + + describe("ouiProgress Component", () => { + function getProgressBarComponent (element, type) { + if (type) { + return angular.element(element[0].querySelector(`.${progressBarClass}_${type}`)); + } + + return angular.element(element[0].querySelector(`.${progressBarClass}`)); + } + + function getProgressBarLabel (element) { + return angular.element(element[0].querySelector(`.${progressLabelClass}`)); + } + + function getProgressThreshold (element) { + return angular.element(element[0].querySelector(`.${progressThresholdClass}`)); + } + + it("should display a progress", () => { + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + expect(element.hasClass(progressClass)).toBeTruthy(); + expect(getProgressBarComponent(element).length).toBe(1); + }); + + it("should display a compact progress bar", () => { + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + expect(element.hasClass(`${progressClass}_compact`)).toBeTruthy(); + }); + + it("should set type info by default", () => { + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + expect(getProgressBarComponent(element, "info").length).toBe(1); + }); + + it("should display a progress bar of each type", () => { + const element = TestUtils.compileTemplate(` + + + + + + ` + ); + + $timeout.flush(); + + expect(getProgressBarComponent(element, "info").length).toBe(1); + expect(getProgressBarComponent(element, "success").length).toBe(1); + expect(getProgressBarComponent(element, "warning").length).toBe(1); + expect(getProgressBarComponent(element, "error").length).toBe(1); + }); + + it("should have the correct width set", () => { + const value = 5; + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + expect(getProgressBarComponent(element).css("width")).toBe(`${value}%`); + }); + + it("should have the correct width when max-value is used", () => { + const value = 10; + const expectedWidth = value / 2; + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + expect(getProgressBarComponent(element).css("width")).toBe(`${expectedWidth}%`); + }); + + it("should have the correct default label", () => { + const value = 5; + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + expect(getProgressBarLabel(element).text()).toBe(`${value}%`); + }); + + it("should have the correct label", () => { + const value = 5; + const text = `Progress: ${value}%`; + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + expect(getProgressBarLabel(element).text()).toBe(text); + }); + + describe("ouiProgressThreshold Component", () => { + it("should have the correct position according to value", () => { + const value = 10; + const maxValue = 200; + const leftPosition = value / (maxValue / 100); + const element = TestUtils.compileTemplate(` + + + ` + ); + + $timeout.flush(); + + const thresholdEl = getProgressThreshold(element); + expect(thresholdEl.length).toBe(1); + expect(thresholdEl.css("left")).toBe(`${leftPosition}%`); + }); + }); + }); +}); diff --git a/packages/oui-progress/src/progress.component.js b/packages/oui-progress/src/progress.component.js new file mode 100644 index 00000000..ee84eb62 --- /dev/null +++ b/packages/oui-progress/src/progress.component.js @@ -0,0 +1,10 @@ +import controller from "./progress.controller"; + +export default { + controller, + bindings: { + compact: " { + this.$element.addClass("oui-progress"); + + if (this.compact) { + this.$element.addClass("oui-progress_compact"); + } + }); + } + + getPercentageValue (value) { + const percent = 100; + const minValue = this.minValue; + const maxValue = Math.max(this.maxValue - this.minValue, minValue); + const currentValue = Math.max(value - this.minValue, minValue); + + return `${(currentValue / maxValue) * percent}%`; + } +} diff --git a/packages/oui-progress/src/threshold/progress-threshold.component.js b/packages/oui-progress/src/threshold/progress-threshold.component.js new file mode 100644 index 00000000..ff6c68ef --- /dev/null +++ b/packages/oui-progress/src/threshold/progress-threshold.component.js @@ -0,0 +1,11 @@ +import controller from "./progress-threshold.controller"; + +export default { + controller, + bindings: { + value: "<" + }, + require: { + progressCtrl: "^^ouiProgress" + } +}; diff --git a/packages/oui-progress/src/threshold/progress-threshold.controller.js b/packages/oui-progress/src/threshold/progress-threshold.controller.js new file mode 100644 index 00000000..9f1b64fa --- /dev/null +++ b/packages/oui-progress/src/threshold/progress-threshold.controller.js @@ -0,0 +1,16 @@ +export default class { + constructor ($element, $timeout) { + "ngInject"; + + this.$element = $element; + this.$timeout = $timeout; + } + + $postLink () { + this.$timeout(() => + this.$element + .addClass("oui-progress__threshold") + .css("left", this.progressCtrl.getPercentageValue(this.value)) + ); + } +}