+```
### 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
+
+```
+
+## 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<boolean> | =? | | `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 |
diff --git a/packages/oui-switch/src/index.js b/packages/oui-switch/src/index.js
new file mode 100644
index 00000000..8c2eafae
--- /dev/null
+++ b/packages/oui-switch/src/index.js
@@ -0,0 +1,4 @@
+import Switch from "./switch.component.js";
+
+angular.module("oui.switch", [])
+ .component("ouiSwitch", Switch);
diff --git a/packages/oui-switch/src/index.spec.js b/packages/oui-switch/src/index.spec.js
new file mode 100644
index 00000000..2403417d
--- /dev/null
+++ b/packages/oui-switch/src/index.spec.js
@@ -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("");
+ $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: "",
+ id: "@?",
+ model: "=?",
+ name: "@?",
+ onChange: "&?",
+ required: ""
+ }
+};
diff --git a/packages/oui-switch/src/switch.controller.js b/packages/oui-switch/src/switch.controller.js
new file mode 100644
index 00000000..3fad47ae
--- /dev/null
+++ b/packages/oui-switch/src/switch.controller.js
@@ -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")
+ );
+ }
+}
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 @@
+