diff --git a/packages/oui-angular/src/index.js b/packages/oui-angular/src/index.js index 471a1e6e..4b7bd45c 100644 --- a/packages/oui-angular/src/index.js +++ b/packages/oui-angular/src/index.js @@ -35,6 +35,7 @@ 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"; +import "@oui-angular/oui-switch/src"; angular.module("oui", [ "oui.button", @@ -73,5 +74,6 @@ angular.module("oui", [ "oui.tile", "oui.guide-menu", "oui.header-tabs", - "oui.progress" + "oui.progress", + "oui.switch" ]); diff --git a/packages/oui-angular/src/index.spec.js b/packages/oui-angular/src/index.spec.js index 3014b647..e24586f1 100644 --- a/packages/oui-angular/src/index.spec.js +++ b/packages/oui-angular/src/index.spec.js @@ -36,6 +36,7 @@ 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))$/)); +loadTests(require.context("../../oui-switch/src/", true, /.*((\.spec)|(index))$/)); function loadTests (context) { context.keys().forEach(context); diff --git a/packages/oui-field/README.md b/packages/oui-field/README.md index be2e0e15..d20fcc7f 100644 --- a/packages/oui-field/README.md +++ b/packages/oui-field/README.md @@ -83,6 +83,18 @@ ``` +### Switch + +```html:preview +
+ + + +
+``` ### Radio diff --git a/packages/oui-switch/README.md b/packages/oui-switch/README.md new file mode 100644 index 00000000..e1170745 --- /dev/null +++ b/packages/oui-switch/README.md @@ -0,0 +1,68 @@ +# Switch + + + +## Usage + +### Basic + +```html:preview + + + + +``` + +### Disabled + +```html:preview + + + + +``` + +### On change + +**Note:** Model will not be refreshed until the `on-change` callback as not finished. If you want to access the new model inside the `on-change` callback you need to use the `modelValue` variable as below. + +```html:preview +
+ + +
+Last onChange value: {{ $ctrl.lastOnChangeValue || ($ctrl.lastOnChangeValue === false && "false") || "undefined" }} +``` + +### Validation in form + +```html:preview +
+
+ + +
+ Is this form valid? : {{ switchForm.$valid ? "yes" : "no" }} +
+``` + +## API + +| Attribute | Type | Binding | One-time Binding | Values | Default | Description +| ---- | ---- | ---- | ---- | ---- | ---- | ---- +| disabled | boolean | { + let TestUtils; + let $timeout; + + beforeEach(angular.mock.module("oui.switch")); + beforeEach(angular.mock.module("oui.test-utils")); + + beforeEach(inject((_TestUtils_, _$timeout_) => { + TestUtils = _TestUtils_; + $timeout = _$timeout_; + })); + + const getSwitchInputElement = (element) => element[0].querySelector("input[type=checkbox]"); + + describe("Component", () => { + + describe("input checkbox", () => { + it("should display a input (checkbox) element", () => { + const element = TestUtils.compileTemplate(""); + $timeout.flush(); + expect(getSwitchInputElement(element)).toBeTruthy(); + }); + }); + + describe("id attribute", () => { + it("should generate an id for the input when undefined", () => { + const element = TestUtils.compileTemplate(""); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("id")).toMatch(/^ouiSwitch\d+$/); + }); + + it("should set the id for the input when defined", () => { + const element = TestUtils.compileTemplate(""); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("id")).toBe("test"); + }); + }); + + describe("name attribute", () => { + it("should set the name attribute on input when defined", () => { + const element = TestUtils.compileTemplate(""); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("name")).toBe("test"); + }); + }); + + describe("model attribute", () => { + it("should display an unchecked switch when no model", () => { + const element = TestUtils.compileTemplate(""); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("checked")).toBe(false); + }); + + it("should display a on switch when true", () => { + const element = TestUtils.compileTemplate("", { + checked: true + }); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("checked")).toBe(true); + }); + + it("should display a an off switch when false", () => { + const element = TestUtils.compileTemplate("", { + checked: false + }); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("checked")).toBe(false); + }); + + it("should be updated when clicked", () => { + const element = TestUtils.compileTemplate("", { + currentModel: false + }); + + $timeout.flush(); + const $ctrl = TestUtils.getElementController(element); + const checkboxElement = getSwitchInputElement(element); + const $checkboxElement = angular.element(checkboxElement); + $checkboxElement.prop("checked", true); + $checkboxElement.triggerHandler("click"); + expect($ctrl.currentModel).toBe(true); + }); + }); + + describe("disabled attribute", () => { + it("should display an active switch when no attribute", () => { + const element = TestUtils.compileTemplate(""); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("disabled")).toBe(false); + }); + + it("should display a disabled switch when defined but no value", () => { + const element = TestUtils.compileTemplate(""); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("disabled")).toBe(true); + }); + + it("should display a disabled switch when true", () => { + const element = TestUtils.compileTemplate("", { + disabled: true + }); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("disabled")).toBe(true); + }); + }); + + describe("on-change attribute", () => { + it("should trigger callback when the switch is clicked", () => { + const onChangeSpy = jasmine.createSpy("onChangeSpy"); + + const element = TestUtils.compileTemplate("", { + onChange: onChangeSpy + }); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + const $checkboxElement = angular.element(checkboxElement); + $checkboxElement.prop("checked", true); + $checkboxElement.triggerHandler("click"); + expect(onChangeSpy).toHaveBeenCalledWith(true); + }); + }); + + describe("Validation", () => { + it("should apply a required validation with the required attribute without value", () => { + const element = TestUtils.compileTemplate(""); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("required")).toBe(true); + }); + + it("should apply a required validation with the required attribute when true", () => { + const element = TestUtils.compileTemplate(` + + `, { + isRequired: true + }); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("required")).toBe(true); + }); + + it("should not apply a required validation with the required attribute when false", () => { + const element = TestUtils.compileTemplate(` + + `, { + isRequired: false + }); + + $timeout.flush(); + const checkboxElement = getSwitchInputElement(element); + expect(angular.element(checkboxElement).prop("required")).toBe(false); + }); + + it("should be done if required attribute is defined", () => { + const element = TestUtils.compileTemplate(`
+ +
+ `, { + isRequired: true + }); + + $timeout.flush(); + const form = element.scope().form; + const checkboxElement = getSwitchInputElement(element); + const $checkboxElement = angular.element(checkboxElement); + expect(form.$valid).toBeFalsy(); + + $checkboxElement.prop("checked", true); + $checkboxElement.triggerHandler("click"); + expect(form.$valid).toBeTruthy(); + }); + }); + }); +}); diff --git a/packages/oui-switch/src/switch.component.js b/packages/oui-switch/src/switch.component.js new file mode 100644 index 00000000..39e639e3 --- /dev/null +++ b/packages/oui-switch/src/switch.component.js @@ -0,0 +1,15 @@ +import controller from "./switch.controller"; +import template from "./switch.html"; + +export default { + controller, + template, + bindings: { + disabled: " + this.$element + .addClass("oui-switch") + .removeAttr("id") + .removeAttr("name") + ); + } +} diff --git a/packages/oui-switch/src/switch.html b/packages/oui-switch/src/switch.html new file mode 100644 index 00000000..56e2d2ec --- /dev/null +++ b/packages/oui-switch/src/switch.html @@ -0,0 +1,8 @@ +