Skip to content
This repository was archived by the owner on Aug 7, 2020. 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
4 changes: 3 additions & 1 deletion packages/oui-angular/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -73,5 +74,6 @@ angular.module("oui", [
"oui.tile",
"oui.guide-menu",
"oui.header-tabs",
"oui.progress"
"oui.progress",
"oui.switch"
]);
1 change: 1 addition & 0 deletions packages/oui-angular/src/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions packages/oui-field/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@
</oui-field>
</form>
```
### Switch

```html:preview
<form novalidate name="switchForm">
<oui-field label="Switch options" help-text="Helper text">
<oui-switch
name="switch"
model="$ctrl.switch"
required></oui-switch>
</oui-field>
</form>
```

### Radio

Expand Down
68 changes: 68 additions & 0 deletions packages/oui-switch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Switch

<component-status cx-design="complete" ux="rc"></component-status>

## Usage

### Basic

```html:preview
<oui-switch id="switch" name="switch-name"
model="false">
</oui-switch>
<oui-switch id="switch2" name="switch-name"
model="true">
</oui-switch>
```

### Disabled

```html:preview
<oui-switch
model="false"
disabled>
</oui-switch>
<oui-switch
model="true"
disabled>
</oui-switch>
```

### 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
<div style="margin-bottom: 5px;">
<oui-switch
model="$ctrl.onChangeValue"
on-change="$ctrl.lastOnChangeValue = modelValue">
</oui-switch>
</div>
<span>Last onChange value: {{ $ctrl.lastOnChangeValue || ($ctrl.lastOnChangeValue === false && "false") || "undefined" }}</span>
```

### Validation in form

```html:preview
<form name="switchForm">
<div ng-init="$ctrl.agree = false">
<oui-switch
model="$ctrl.agree"
required>
</oui-switch>
</div>
Is this form valid? : {{ switchForm.$valid ? "yes" : "no" }}
</form>
```

## API

| Attribute | Type | Binding | One-time Binding | Values | Default | Description
| ---- | ---- | ---- | ---- | ---- | ---- | ----
| disabled | boolean | <? | | `true`, `false` | false | disabled flag |
| id | string | @? | `true` | | | id attribute of the switch |
| model | nullable&lt;boolean&gt; | =? | | `true`, `false`, `null` | | model bound to component |
| name | string | @? | `true` | | | name attribute of the switch |
| on-change | function | &? | | | | function to call when model changes |
| required | boolean | <? | | `true`, `false` | false | `true` if the switch should be actived |
4 changes: 4 additions & 0 deletions packages/oui-switch/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Switch from "./switch.component.js";

angular.module("oui.switch", [])
.component("ouiSwitch", Switch);
195 changes: 195 additions & 0 deletions packages/oui-switch/src/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
describe("ouiSwitch", () => {
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("<oui-switch></oui-switch>");
$timeout.flush();
expect(getSwitchInputElement(element)).toBeTruthy();
});
});

describe("id attribute", () => {
it("should generate an id for the input when undefined", () => {
const element = TestUtils.compileTemplate("<oui-switch></oui-switch>");

$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("<oui-switch id=\"test\"></oui-switch>");

$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("<oui-switch name=\"test\"></oui-switch>");

$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("<oui-switch></oui-switch>");

$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("<oui-switch model=\"$ctrl.checked\"></oui-switch>", {
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("<oui-switch model=\"$ctrl.checked\"></oui-switch>", {
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("<oui-switch model=\"$ctrl.currentModel\"></oui-switch>", {
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("<oui-switch></oui-switch>");

$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("<oui-switch disabled></oui-switch>");

$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("<oui-switch disabled=\"$ctrl.disabled\"></oui-switch>", {
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("<oui-switch on-change=\"$ctrl.onChange(modelValue)\"></oui-switch>", {
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("<oui-switch required></oui-switch>");

$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(`
<oui-switch required="$ctrl.isRequired"></oui-switch>
`, {
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(`
<oui-switch required="$ctrl.isRequired"></oui-switch>
`, {
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(`<form name="form">
<oui-switch required="$ctrl.isRequired"></oui-switch>
</form>
`, {
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();
});
});
});
});
15 changes: 15 additions & 0 deletions packages/oui-switch/src/switch.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import controller from "./switch.controller";
import template from "./switch.html";

export default {
controller,
template,
bindings: {
disabled: "<?",
id: "@?",
model: "=?",
name: "@?",
onChange: "&?",
required: "<?"
}
};
27 changes: 27 additions & 0 deletions packages/oui-switch/src/switch.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { addBooleanParameter, addDefaultParameter } from "@oui-angular/common/component-utils";

export default class {
constructor ($attrs, $element, $scope, $timeout) {
"ngInject";

this.$attrs = $attrs;
this.$element = $element;
this.$scope = $scope;
this.$timeout = $timeout;
}

$onInit () {
addBooleanParameter(this, "disabled");
addBooleanParameter(this, "required");
addDefaultParameter(this, "id", `ouiSwitch${this.$scope.$id}`);
}

$postLink () {
this.$timeout(() =>
this.$element
.addClass("oui-switch")
.removeAttr("id")
.removeAttr("name")
);
}
}
8 changes: 8 additions & 0 deletions packages/oui-switch/src/switch.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<label class="oui-switch__label-container">
<input class="oui-switch__checkbox" type="checkbox" id="{{:: $ctrl.id }}" name="{{:: $ctrl.name }}"
ng-change="$ctrl.onChange({ modelValue: $ctrl.model })"
ng-disabled="$ctrl.disabled"
ng-model="$ctrl.model"
ng-required="$ctrl.required">
<span class="oui-switch__slider" aria-hidden="true"></span>
</label>