Popover content
@@ -83,22 +134,34 @@ describe("ouiPopover", () => {
`
);
- const rootElement = element[0].querySelector(".oui-popover");
- const $rootElement = angular.element(rootElement);
- const trigger = element[0].querySelector("[oui-popover-trigger]");
- const $trigger = angular.element(trigger);
- const closeButton = element[0].querySelector(".oui-popover__close-button");
- const $closeButton = angular.element(closeButton);
-
- expect($rootElement.hasClass("oui-popover_active")).toBeFalsy();
- $trigger.triggerHandler("click");
- expect($rootElement.hasClass("oui-popover_active")).toBeTruthy();
- $trigger.triggerHandler("click");
- expect($rootElement.hasClass("oui-popover_active")).toBeFalsy();
- $trigger.triggerHandler("click");
- expect($rootElement.hasClass("oui-popover_active")).toBeTruthy();
- $closeButton.triggerHandler("click");
- expect($rootElement.hasClass("oui-popover_active")).toBeFalsy();
+ $timeout.flush();
+
+ const controller = element.controller("ouiPopover");
+ controller.openPopover();
+
+ expect(controller.popper.options.placement).toEqual("bottom-start");
+ });
+
+ it("should set aria-expanded when trigger is clicked", () => {
+ const element = testUtils.compileTemplate(`
+
+ Popover trigger
+
+ Popover content
+
+ `
+ );
+
+ $timeout.flush();
+
+ const trigger = angular.element(element[0].querySelector("[oui-popover-trigger]"));
+ expect(trigger.attr("aria-expanded")).toBe("false");
+
+ trigger.triggerHandler("click");
+ expect(trigger.attr("aria-expanded")).toBe("true");
+
+ trigger.triggerHandler("click");
+ expect(trigger.attr("aria-expanded")).toBe("false");
});
});
});
diff --git a/packages/oui-popover/src/popover-content.html b/packages/oui-popover/src/popover-content.html
deleted file mode 100644
index 6e784b4e..00000000
--- a/packages/oui-popover/src/popover-content.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/packages/oui-popover/src/popover-trigger.directive.js b/packages/oui-popover/src/popover-trigger.directive.js
deleted file mode 100644
index 485713fd..00000000
--- a/packages/oui-popover/src/popover-trigger.directive.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const popoverTriggerClass = "oui-popover__trigger";
-
-export default () => {
- "ngInject";
-
- return {
- restrict: "AE",
- require: "^ouiPopover",
- scope: {},
- link: (scope, element, attrs, ctrl) => {
- const triggerElement = element;
-
- triggerElement.addClass(popoverTriggerClass);
-
- triggerElement.attr("id", ctrl.id);
- triggerElement.attr({ "aria-haspopup": true, "aria-expanded": false });
-
- triggerElement.on("click", () => ctrl.onTriggerClick());
-
- scope.$on("oui:popover:afterOpen", (e, id) => {
- if (id !== ctrl.id) {
- return;
- }
-
- triggerElement.attr("aria-expanded", true);
- });
-
- scope.$on("oui:popover:afterClose", (e, id) => {
- if (id !== ctrl.id) {
- return;
- }
-
- triggerElement.attr("aria-expanded", false);
- });
-
- scope.$on("$destroy", () => {
- triggerElement.off("click");
- });
- }
- };
-};
diff --git a/packages/oui-popover/src/popover.component.js b/packages/oui-popover/src/popover.component.js
deleted file mode 100644
index 6531221c..00000000
--- a/packages/oui-popover/src/popover.component.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import controller from "./popover.controller";
-import template from "./popover.html";
-
-export default {
- template,
- controller,
- bindings: {
- placement: "@?"
- },
- transclude: true
-};
diff --git a/packages/oui-popover/src/popover.controller.js b/packages/oui-popover/src/popover.controller.js
index b22df046..c7624775 100644
--- a/packages/oui-popover/src/popover.controller.js
+++ b/packages/oui-popover/src/popover.controller.js
@@ -1,39 +1,91 @@
+import { addDefaultParameter } from "@ovh-ui/common/component-utils";
import Popper from "popper.js";
+import template from "./popover.html";
const KEY_ESCAPE = 27;
export default class PopoverController {
- constructor ($scope, $element, $attrs, $document, $timeout) {
+ constructor ($attrs, $compile, $document, $element, $scope, $timeout) {
"ngInject";
- this.$scope = $scope;
- this.$element = $element;
this.$attrs = $attrs;
+ this.$compile = $compile;
this.$document = $document;
+ this.$element = $element;
+ this.$scope = $scope;
this.$timeout = $timeout;
}
$onInit () {
- this.isPopoverOpen = false;
+ // Deprecated: Support for component `oui-popover`
+ // Check if directive is an attribute or a component
+ this.isComponent = angular.isUndefined(this.$attrs.ouiPopover);
+
+ // Deprecated: Support for `placement` attribute
+ this.placement = this.placement || this.$attrs.placement;
- // Use internal id to map trigger
this.id = `ouiPopover${this.$scope.$id}`;
+ this.isPopoverOpen = false;
- if (angular.isUndefined(this.placement)) {
- this.placement = "right";
- }
+ addDefaultParameter(this, "placement", "right");
}
$postLink () {
- this.triggerElement = this.$element[0].querySelector(".oui-popover__trigger");
- this.popperElement = this.$element[0].querySelector(".oui-popover__content");
- this.arrowElement = this.$element[0].querySelector(".oui-popover__arrow");
+ this.setPopover();
+ this.setTrigger();
}
$destroy () {
this.closePopover();
}
+ setPopover () {
+ this.$timeout(() => {
+ // Deprecated: Support for component `oui-popover-content`
+ if (this.isComponent) {
+ this.popperElement = this.$element[0].querySelector(".oui-popover");
+ this.arrowElement = this.$element[0].querySelector(".oui-popover__arrow");
+ return;
+ }
+
+ // Support for attribute `oui-popover`
+ // Create a new scope to compile the popover next to the trigger
+ const popoverScope = angular.extend(this.$scope.$new(true), { $popoverCtrl: this });
+ const popoverTemplate = this.$compile(template)(popoverScope);
+
+ // Add compiled template after $element
+ this.$element
+ .removeAttr("title") // Remove title to avoid native tooltip
+ .after(popoverTemplate);
+
+ this.popperElement = this.$element.next()[0];
+ this.arrowElement = this.popperElement.querySelector(".oui-popover__arrow");
+ });
+ }
+
+ setTrigger () {
+ this.$timeout(() => {
+ // Deprecated: Support for component `oui-popover-trigger`
+ if (this.isComponent) {
+ this.triggerElement = this.$element[0].querySelector(".oui-popover__trigger");
+ this.$triggerElement = angular.element(this.triggerElement);
+ return;
+ }
+
+ // Support for attribute `oui-popover`
+ this.triggerElement = this.$element[0];
+ this.$triggerElement = angular.element(this.triggerElement);
+
+ this.$triggerElement
+ .addClass("oui-popover__trigger")
+ .attr({
+ "aria-haspopup": true,
+ "aria-expanded": false
+ })
+ .on("click", () => this.onTriggerClick());
+ });
+ }
+
onTriggerClick () {
if (!this.isPopoverOpen) {
this.openPopover();
@@ -52,20 +104,33 @@ export default class PopoverController {
openPopover () {
this.isPopoverOpen = true;
- angular.element(this.$element.children()[0]).addClass("oui-popover_active");
this.updatePopper();
this.$document.on("keydown", evt => this.triggerKeyHandler(evt));
- this.$scope.$broadcast("oui:popover:afterOpen", this.id);
+
+ // Deprecated: Support for component `oui-popover-trigger`
+ if (this.isComponent) {
+ this.$triggerElement.attr("aria-expanded", true);
+ return;
+ }
+
+ // Support for attribute `oui-popover`
+ this.$element.attr("aria-expanded", true);
}
closePopover () {
this.isPopoverOpen = false;
- angular.element(this.$element.children()[0]).removeClass("oui-popover_active");
- this.destroyPopper();
this.$document.off("keydown", evt => this.triggerKeyHandler(evt));
- this.$scope.$broadcast("oui:popover:afterClose", this.id);
+
+ // Deprecated: Support for component `oui-popover-trigger`
+ if (this.isComponent) {
+ this.$triggerElement.attr("aria-expanded", false);
+ return;
+ }
+
+ // Support for attribute `oui-popover`
+ this.$element.attr("aria-expanded", false);
}
createPopper () {
diff --git a/packages/oui-popover/src/popover.directive.js b/packages/oui-popover/src/popover.directive.js
new file mode 100644
index 00000000..b388e7d5
--- /dev/null
+++ b/packages/oui-popover/src/popover.directive.js
@@ -0,0 +1,17 @@
+import controller from "./popover.controller";
+
+export default () => {
+ "ngInject";
+
+ return {
+ restrict: "AE",
+ bindToController: {
+ text: "@ouiPopover",
+ title: "@?",
+ placement: "@?ouiPopoverPlacement",
+ template: "@?ouiPopoverTemplate"
+ },
+ controller,
+ controllerAs: "$popoverCtrl"
+ };
+};
diff --git a/packages/oui-popover/src/popover.html b/packages/oui-popover/src/popover.html
index ecfd3b09..460f5661 100644
--- a/packages/oui-popover/src/popover.html
+++ b/packages/oui-popover/src/popover.html
@@ -1,3 +1,18 @@
-
+
diff --git a/packages/oui-popover/src/trigger/popover-trigger.controller.js b/packages/oui-popover/src/trigger/popover-trigger.controller.js
new file mode 100644
index 00000000..9f664c2d
--- /dev/null
+++ b/packages/oui-popover/src/trigger/popover-trigger.controller.js
@@ -0,0 +1,26 @@
+// Deprecated: Support only for old use
+export default class {
+ constructor ($element, $scope, $timeout) {
+ "ngInject";
+
+ this.$element = $element;
+ this.$scope = $scope;
+ this.$timeout = $timeout;
+ }
+
+ $postLink () {
+ this.$timeout(() =>
+ this.$element
+ .addClass("oui-popover__trigger")
+ .attr({
+ "aria-haspopup": true,
+ "aria-expanded": false
+ })
+ .on("click", () => this.popover.onTriggerClick())
+ );
+ }
+
+ $onDestroy () {
+ this.$element.off("click");
+ }
+}
diff --git a/packages/oui-popover/src/trigger/popover-trigger.directive.js b/packages/oui-popover/src/trigger/popover-trigger.directive.js
new file mode 100644
index 00000000..78be2ab2
--- /dev/null
+++ b/packages/oui-popover/src/trigger/popover-trigger.directive.js
@@ -0,0 +1,16 @@
+import controller from "./popover-trigger.controller";
+
+// Deprecated: Support only for old use
+export default () => {
+ "ngInject";
+
+ return {
+ restrict: "AE",
+ require: {
+ popover: "^ouiPopover"
+ },
+ controller,
+ bindToController: true,
+ scope: {}
+ };
+};
diff --git a/packages/oui-tooltip/src/tooltip.controller.js b/packages/oui-tooltip/src/tooltip.controller.js
index 8f308029..93d9b64a 100644
--- a/packages/oui-tooltip/src/tooltip.controller.js
+++ b/packages/oui-tooltip/src/tooltip.controller.js
@@ -17,8 +17,11 @@ export default class {
addDefaultParameter(this, "placement", "top");
}
- $postLink () {
+ $onDestroy () {
+ this.destroyPopper();
+ }
+ $postLink () {
this.$timeout(() => {
if (this.title) {
addDefaultParameter(this, "text", this.title);
@@ -49,4 +52,13 @@ export default class {
placement: this.placement
});
}
+
+ destroyPopper () {
+ if (!this.popper) {
+ return;
+ }
+
+ this.popper.destroy();
+ this.popper = null;
+ }
}
From b776a2b1592783662080ff55ca0b2e9bcbe26c02 Mon Sep 17 00:00:00 2001
From: Axel Peter <15101925+AxelPeter@users.noreply.github.com>
Date: Wed, 31 Oct 2018 10:02:13 +0100
Subject: [PATCH 06/12] fix(oui-calendar): fix boolean parameters (#302)
* docs(oui-calendar): add enable-time example
* fix(oui-calendar): fix boolean parameters
---
packages/oui-calendar/README.md | 13 ++++++++++++-
packages/oui-calendar/src/calendar.controller.js | 2 ++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/packages/oui-calendar/README.md b/packages/oui-calendar/README.md
index c223aac9..0d54e3cd 100644
--- a/packages/oui-calendar/README.md
+++ b/packages/oui-calendar/README.md
@@ -61,6 +61,17 @@
```
+### Enabling time
+
+Use `enable-time` to show time selection after a date is selected.
+
+```html:preview
+
+
+```
+
+**Note**: See [Flatpickr documentation](https://flatpickr.js.org/examples/#time-picker) for more information.
+
### Disabling dates
Use `disable-date` to make certain dates unavailable for selection.
@@ -168,7 +179,7 @@ Use `mode` to set a different selection mode for the calendar
| `min-date` | object | | yes | See [Supplying Dates](https://flatpickr.js.org/examples/#supplying-dates-for-flatpickr) | n/a | specifies the minimum/earliest date (inclusively) allowed for selection
| `disable-date` | array | | yes | See [Supplying Dates](https://flatpickr.js.org/examples/#supplying-dates-for-flatpickr) | n/a | make certain dates unavailable for selection
| `enable-date` | array | | yes | See [Supplying Dates](https://flatpickr.js.org/examples/#supplying-dates-for-flatpickr) | n/a | make certain dates only available for selection
-| `enable-time` | boolean | | yes | See [Options](https://flatpickr.js.org/options/) | n/a | enables time picker
+| `enable-time` | boolean | | yes | `true`, `false` | `false` | enables time selection
| `week-numbers` | boolean | | yes | `true`, `false` | `false` | week numbers flag
| `disabled` | boolean | | no | `true`, `false` | `false` | disabled flag
| `required` | boolean | | no | `true`, `false` | `false` | required flag
diff --git a/packages/oui-calendar/src/calendar.controller.js b/packages/oui-calendar/src/calendar.controller.js
index 773c6adc..794485bb 100644
--- a/packages/oui-calendar/src/calendar.controller.js
+++ b/packages/oui-calendar/src/calendar.controller.js
@@ -82,8 +82,10 @@ export default class {
$onInit () {
addBooleanParameter(this, "appendToBody");
addBooleanParameter(this, "disabled");
+ addBooleanParameter(this, "enableTime");
addBooleanParameter(this, "inline");
addBooleanParameter(this, "required");
+ addBooleanParameter(this, "static");
addBooleanParameter(this, "weekNumbers");
this.initCalendarInstance();
From c39dc82d753396527398c457b5f9bb8e6a60eb3b Mon Sep 17 00:00:00 2001
From: Marie JONES <14836007+marie-j@users.noreply.github.com>
Date: Wed, 31 Oct 2018 17:16:04 +0100
Subject: [PATCH 07/12] fix(oui-select): prevent required validation to trigger
on select (#303)
Error status is triggered for an instant on option selection
---
packages/oui-select/src/select.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/oui-select/src/select.html b/packages/oui-select/src/select.html
index e428e973..3fc01767 100644
--- a/packages/oui-select/src/select.html
+++ b/packages/oui-select/src/select.html
@@ -3,7 +3,7 @@
ng-model="$ctrl.model"
search-enabled="false"
ng-attr-title="{{::$ctrl.title}}"
- ng-required="$ctrl.required"
+ ng-required="$ctrl.required && !$select.open"
ng-disabled="$ctrl.disabled"
on-blur="$ctrl.onUiSelectBlur()"
on-focus="$ctrl.onUiSelectFocus()"
From 9824bbb34a81e9f48e1a1448fd099472822b07eb Mon Sep 17 00:00:00 2001
From: Axel Peter <15101925+AxelPeter@users.noreply.github.com>
Date: Mon, 5 Nov 2018 14:59:50 +0100
Subject: [PATCH 08/12] feat(oui-inline-adder): add inline-adder component
(#304)
refactor(oui-inline-adder): component improvements
---
packages/oui-angular/src/index.js | 2 +
packages/oui-angular/src/index.spec.js | 1 +
packages/oui-inline-adder/README.md | 168 +++++++++++++++++
.../src/field/inline-adder-field.component.js | 8 +
.../field/inline-adder-field.controller.js | 25 +++
packages/oui-inline-adder/src/index.js | 12 ++
packages/oui-inline-adder/src/index.spec.js | 176 ++++++++++++++++++
.../src/inline-adder.component.js | 15 ++
.../src/inline-adder.controller.js | 57 ++++++
.../oui-inline-adder/src/inline-adder.html | 24 +++
.../src/inline-adder.provider.js | 25 +++
.../src/row/inline-adder-row.component.js | 16 ++
12 files changed, 529 insertions(+)
create mode 100644 packages/oui-inline-adder/README.md
create mode 100644 packages/oui-inline-adder/src/field/inline-adder-field.component.js
create mode 100644 packages/oui-inline-adder/src/field/inline-adder-field.controller.js
create mode 100644 packages/oui-inline-adder/src/index.js
create mode 100644 packages/oui-inline-adder/src/index.spec.js
create mode 100644 packages/oui-inline-adder/src/inline-adder.component.js
create mode 100644 packages/oui-inline-adder/src/inline-adder.controller.js
create mode 100644 packages/oui-inline-adder/src/inline-adder.html
create mode 100644 packages/oui-inline-adder/src/inline-adder.provider.js
create mode 100644 packages/oui-inline-adder/src/row/inline-adder-row.component.js
diff --git a/packages/oui-angular/src/index.js b/packages/oui-angular/src/index.js
index 2b5f6d77..c32f6756 100644
--- a/packages/oui-angular/src/index.js
+++ b/packages/oui-angular/src/index.js
@@ -14,6 +14,7 @@ import Field from "@ovh-ui/oui-field";
import FormActions from "@ovh-ui/oui-form-actions";
import GuideMenu from "@ovh-ui/oui-guide-menu";
import HeaderTabs from "@ovh-ui/oui-header-tabs";
+import InlineAdder from "@ovh-ui/oui-inline-adder";
import Message from "@ovh-ui/oui-message";
import Modal from "@ovh-ui/oui-modal";
import Navbar from "@ovh-ui/oui-navbar";
@@ -53,6 +54,7 @@ export default angular
FormActions,
GuideMenu,
HeaderTabs,
+ InlineAdder,
Message,
Modal,
Navbar,
diff --git a/packages/oui-angular/src/index.spec.js b/packages/oui-angular/src/index.spec.js
index ed8ea126..67b46a72 100644
--- a/packages/oui-angular/src/index.spec.js
+++ b/packages/oui-angular/src/index.spec.js
@@ -16,6 +16,7 @@ loadTests(require.context("../../oui-field/src/", true, /.*((\.spec)|(index))$/)
loadTests(require.context("../../oui-form-actions/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-inline-adder/src/", true, /.*((\.spec)|(index))$/));
loadTests(require.context("../../oui-message/src/", true, /.*((\.spec)|(index))$/));
loadTests(require.context("../../oui-modal/src/", true, /.*((\.spec)|(index))$/));
loadTests(require.context("../../oui-navbar/src/", true, /.*((\.spec)|(index))$/));
diff --git a/packages/oui-inline-adder/README.md b/packages/oui-inline-adder/README.md
new file mode 100644
index 00000000..76292d0f
--- /dev/null
+++ b/packages/oui-inline-adder/README.md
@@ -0,0 +1,168 @@
+# Inline adder
+
+
+
+## Usage
+
+### Basic
+
+```html:preview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Multiple rows
+
+```html:preview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Adaptive fields
+
+**Note**: Fields with `adaptive` attribute will adapt their size to their content.
+
+```html:preview
+
+
+
+
+
+
+
+
+
+
+
+ Select the OS
+ FreeBSD
+ Linux
+ OSX
+ Windows
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Events
+
+#### `on-add`
+
+**Note**: If you want to access the form inside `on-add` callback, you need to use the `form` variable as below.
+
+```html:preview
+
+
+
+
+
+
+
+
+
+
+
On Add
+
{{$ctrl.addedForm | json}}
+
+```
+
+#### `on-remove`
+
+**Note**: If you want to access the form inside `on-remove` callback, you need to use the `form` variable as below.
+
+```html:preview
+
+
+
+
+
+
+
+
+
+
+
On Remove
+
{{$ctrl.removedForm | json}}
+
+```
+
+#### `on-change`
+
+**Note**: If you want to access the forms array inside `on-change` callback, you need to use the `forms` variable as below.
+
+```html:preview
+
+
+
+
+
+
+
+
+
+
+
On Change
+
{{$ctrl.changedForms | json}}
+
+```
+
+## API
+
+### oui-inline-adder
+
+| Attribute | Type | Binding | One-time binding | Values | Default | Description
+| ---- | ---- | ---- | ---- | ---- | ---- | ----
+| `on-add` | function | & | no | n/a | n/a | handler triggered when a row is added
+| `on-remove` | function | & | no | n/a | n/a | handler triggered when a row is removed
+| `on-change` | function | & | no | n/a | n/a | handler triggered when rows have changed
+
+### oui-inline-adder-field
+
+| Attribute | Type | Binding | One-time binding | Values | Default | Description
+| ---- | ---- | ---- | ---- | ---- | ---- | ----
+| `adaptive` | boolean | | yes | `true`, `false` | `false` | adaptive field flag
diff --git a/packages/oui-inline-adder/src/field/inline-adder-field.component.js b/packages/oui-inline-adder/src/field/inline-adder-field.component.js
new file mode 100644
index 00000000..669b5e7a
--- /dev/null
+++ b/packages/oui-inline-adder/src/field/inline-adder-field.component.js
@@ -0,0 +1,8 @@
+import controller from "./inline-adder-field.controller";
+
+export default {
+ bindings: {
+ adaptive: ""
+ },
+ controller
+};
diff --git a/packages/oui-inline-adder/src/field/inline-adder-field.controller.js b/packages/oui-inline-adder/src/field/inline-adder-field.controller.js
new file mode 100644
index 00000000..dd2b31dc
--- /dev/null
+++ b/packages/oui-inline-adder/src/field/inline-adder-field.controller.js
@@ -0,0 +1,25 @@
+import { addBooleanParameter } from "@ovh-ui/common/component-utils";
+
+export default class {
+ constructor ($attrs, $element, $timeout) {
+ "ngInject";
+
+ this.$attrs = $attrs;
+ this.$element = $element;
+ this.$timeout = $timeout;
+ }
+
+ $onInit () {
+ addBooleanParameter(this, "adaptive");
+ }
+
+ $postLink () {
+ this.$timeout(() => {
+ this.$element.addClass("oui-inline-adder__field");
+
+ if (this.adaptive) {
+ this.$element.addClass("oui-inline-adder__field_adaptive");
+ }
+ });
+ }
+}
diff --git a/packages/oui-inline-adder/src/index.js b/packages/oui-inline-adder/src/index.js
new file mode 100644
index 00000000..d2b791a2
--- /dev/null
+++ b/packages/oui-inline-adder/src/index.js
@@ -0,0 +1,12 @@
+import InlineAdder from "./inline-adder.component";
+import InlineAdderField from "./field/inline-adder-field.component";
+import InlineAdderProvider from "./inline-adder.provider";
+import InlineAdderRow from "./row/inline-adder-row.component";
+
+export default angular
+ .module("oui.inline-adder", [])
+ .component("ouiInlineAdder", InlineAdder)
+ .component("ouiInlineAdderField", InlineAdderField)
+ .component("ouiInlineAdderRow", InlineAdderRow)
+ .provider("ouiInlineAdderConfiguration", InlineAdderProvider)
+ .name;
diff --git a/packages/oui-inline-adder/src/index.spec.js b/packages/oui-inline-adder/src/index.spec.js
new file mode 100644
index 00000000..f3a16ac3
--- /dev/null
+++ b/packages/oui-inline-adder/src/index.spec.js
@@ -0,0 +1,176 @@
+describe("ouiInlineAdder", () => {
+ let TestUtils;
+ let $timeout;
+ let configuration;
+
+ beforeEach(angular.mock.module("oui.inline-adder"));
+ beforeEach(angular.mock.module("oui.inline-adder.configuration"));
+ beforeEach(angular.mock.module("oui.test-utils"));
+
+ beforeEach(inject((_TestUtils_, _$timeout_) => {
+ TestUtils = _TestUtils_;
+ $timeout = _$timeout_;
+ }));
+
+ describe("Provider", () => {
+ angular.module("oui.inline-adder.configuration", [
+ "oui.inline-adder"
+ ]).config(ouiInlineAdderConfigurationProvider => {
+ ouiInlineAdderConfigurationProvider.setTranslations({
+ foo: "bar"
+ });
+ });
+
+ beforeEach(inject(_ouiInlineAdderConfiguration_ => {
+ configuration = _ouiInlineAdderConfiguration_;
+ }));
+
+ it("should have custom options", () => {
+ expect(configuration.translations.foo).toEqual("bar");
+ });
+ });
+
+ describe("Component", () => {
+ describe("oui-inline-adder", () => {
+ it("should have a default classname", () => {
+ const element = TestUtils.compileTemplate(`
+
+
+
+
+
+
+
+
+ `);
+
+ $timeout.flush();
+
+ expect(element.hasClass("oui-inline-adder")).toBeTruthy();
+ });
+
+ it("should have a form with a default id/name", () => {
+ const element = TestUtils.compileTemplate(`
+
+
+
+
+
+
+
+
+ `);
+
+ const form = element.find("form");
+ const id = `${element.controller("ouiInlineAdder").id}_0`;
+ const name = `${element.controller("ouiInlineAdder").name}_0`;
+
+ expect(form.length).toBe(1);
+ expect(form.attr("id")).toBe(id);
+ expect(form.attr("name")).toBe(name);
+ });
+
+ it("should create a new form when submitted", () => {
+ const element = TestUtils.compileTemplate(`
+
+
+
+
+
+
+
+
+ `);
+
+ let length = element.find("form").length;
+
+ // Will have 2 rows
+ angular.element(element.find("form")[length - 1]).triggerHandler("submit");
+ expect(element.find("form").length).toBe(length += 1);
+
+ // Will have 3 rows
+ angular.element(element.find("form")[length - 1]).triggerHandler("submit");
+ expect(element.find("form").length).toBe(length += 1);
+ });
+
+ it("should hide a form when removed", () => {
+ const element = TestUtils.compileTemplate(`
+
+
+
+
+
+
+
+
+ `);
+
+ let length = element.find("form").length;
+ const form = angular.element(element.find("form")[0]);
+
+ // Will have 2 rows
+ form.triggerHandler("submit");
+ expect(element.find("form").length).toBe(length += 1);
+
+ // Will have 1 row hidden
+ form.find("button").triggerHandler("click");
+ expect(form.hasClass("ng-hide")).toBeTruthy();
+ });
+ });
+
+ describe("oui-inline-adder-row", () => {
+ it("should have a default classname", () => {
+ const element = TestUtils.compileTemplate(`
+
+
+
+
+
+
+
+
+ `);
+
+ $timeout.flush();
+
+ expect(element.find("oui-inline-adder-row").hasClass("oui-inline-adder__row")).toBeTruthy();
+ });
+ });
+
+ describe("oui-inline-adder-field", () => {
+ it("should have a default classname", () => {
+ const element = TestUtils.compileTemplate(`
+
+
+
+
+
+
+
+
+ `);
+
+ $timeout.flush();
+
+ expect(element.find("oui-inline-adder-field").hasClass("oui-inline-adder__field")).toBeTruthy();
+ });
+
+ it("should have a variant classname", () => {
+ const element = TestUtils.compileTemplate(`
+
+
+
+
+
+
+
+
+ `);
+
+ $timeout.flush();
+
+ expect(element.find("oui-inline-adder-field").hasClass("oui-inline-adder__field_adaptive")).toBeTruthy();
+ });
+ });
+ });
+});
diff --git a/packages/oui-inline-adder/src/inline-adder.component.js b/packages/oui-inline-adder/src/inline-adder.component.js
new file mode 100644
index 00000000..0a60a478
--- /dev/null
+++ b/packages/oui-inline-adder/src/inline-adder.component.js
@@ -0,0 +1,15 @@
+import controller from "./inline-adder.controller.js";
+import template from "./inline-adder.html";
+
+export default {
+ bindings: {
+ id: "@?",
+ name: "@?",
+ onAdd: "&",
+ onChange: "&",
+ onRemove: "&"
+ },
+ controller,
+ template,
+ transclude: true
+};
diff --git a/packages/oui-inline-adder/src/inline-adder.controller.js b/packages/oui-inline-adder/src/inline-adder.controller.js
new file mode 100644
index 00000000..e8bf4b53
--- /dev/null
+++ b/packages/oui-inline-adder/src/inline-adder.controller.js
@@ -0,0 +1,57 @@
+import { addDefaultParameter } from "@ovh-ui/common/component-utils";
+import filter from "lodash/filter";
+
+export default class {
+ constructor ($attrs, $element, $scope, $timeout, ouiInlineAdderConfiguration) {
+ "ngInject";
+
+ this.$attrs = $attrs;
+ this.$element = $element;
+ this.$scope = $scope;
+ this.$timeout = $timeout;
+ this.translations = ouiInlineAdderConfiguration.translations;
+ }
+
+ $onInit () {
+ this.forms = [true];
+ this.isDisabled = [false];
+
+ addDefaultParameter(this, "id", `ouiInlineAdderForm${this.$scope.$id}`);
+ addDefaultParameter(this, "name", `ouiInlineAdderForm${this.$scope.$id}`);
+ }
+
+ $postLink () {
+ this.$timeout(() =>
+ this.$element.addClass("oui-inline-adder")
+ );
+ }
+
+ onFormsChange () {
+ // Filter boolean values used for ngShow
+ const forms = filter(this.forms, (item) => angular.isObject(item));
+ this.onChange({ forms });
+ }
+
+ onFormSubmit (form, index) {
+ if (form.$valid) {
+ this.forms[index] = form;
+
+ // Create new instance of form
+ this.isDisabled[index] = true;
+ this.forms.push(true);
+
+ // Callbacks
+ this.onAdd({ form });
+ this.onFormsChange();
+ }
+ }
+
+ onFormRemove (form, index) {
+ // Hide removed form to avoid refreshing ngRepeat
+ this.forms[index] = false;
+
+ // Callback
+ this.onRemove({ form });
+ this.onFormsChange();
+ }
+}
diff --git a/packages/oui-inline-adder/src/inline-adder.html b/packages/oui-inline-adder/src/inline-adder.html
new file mode 100644
index 00000000..9502e156
--- /dev/null
+++ b/packages/oui-inline-adder/src/inline-adder.html
@@ -0,0 +1,24 @@
+
diff --git a/packages/oui-inline-adder/src/inline-adder.provider.js b/packages/oui-inline-adder/src/inline-adder.provider.js
new file mode 100644
index 00000000..c2810a0f
--- /dev/null
+++ b/packages/oui-inline-adder/src/inline-adder.provider.js
@@ -0,0 +1,25 @@
+import { merge } from "lodash";
+
+export default class {
+ constructor () {
+ this.translations = {
+ ariaAddItem: "Add Item",
+ ariaRemoveItem: "Remove Item"
+ };
+ }
+
+ /**
+ * Set the translations
+ * @param {Object} translations a map of translations
+ */
+ setTranslations (translations) {
+ this.translations = merge(this.translations, translations);
+ return this;
+ }
+
+ $get () {
+ return {
+ translations: this.translations
+ };
+ }
+}
diff --git a/packages/oui-inline-adder/src/row/inline-adder-row.component.js b/packages/oui-inline-adder/src/row/inline-adder-row.component.js
new file mode 100644
index 00000000..4a62ecae
--- /dev/null
+++ b/packages/oui-inline-adder/src/row/inline-adder-row.component.js
@@ -0,0 +1,16 @@
+export default {
+ controller: class {
+ constructor ($element, $timeout) {
+ "ngInject";
+
+ this.$element = $element;
+ this.$timeout = $timeout;
+ }
+
+ $postLink () {
+ this.$timeout(() =>
+ this.$element.addClass("oui-inline-adder__row")
+ );
+ }
+ }
+};
From 08420397cbd19eab91cd5f03decfb74eb3723808 Mon Sep 17 00:00:00 2001
From: Marie JONES <14836007+marie-j@users.noreply.github.com>
Date: Mon, 5 Nov 2018 15:41:27 +0100
Subject: [PATCH 09/12] feat(oui-navbar): allow actions on navbar menu links
(#305)
---
packages/oui-navbar/README.md | 2 ++
packages/oui-navbar/src/index.spec.js | 30 +++++++++++++++++++
.../src/link/navbar-link.component.js | 3 +-
packages/oui-navbar/src/link/navbar-link.html | 6 ++--
packages/oui-navbar/src/menu/navbar-menu.html | 6 ++--
packages/oui-navbar/src/navbar.html | 5 ++--
6 files changed, 44 insertions(+), 8 deletions(-)
diff --git a/packages/oui-navbar/README.md b/packages/oui-navbar/README.md
index 86558664..f90107e8 100644
--- a/packages/oui-navbar/README.md
+++ b/packages/oui-navbar/README.md
@@ -250,6 +250,7 @@ This property is only available for root links of `main-links`.
"title": String,
"url": String
,
"isPrimary": Boolean,
+ "click": Function,
"subLinks": Array[{
"label": String,
"title": String,
@@ -595,3 +596,4 @@ The property `name` **must be** `"user"`.
| `state` | string | @? | yes | n/a | n/a | state of the link
| `state-params` | object | | yes | n/a | n/a | state-params of the link
| `variant` | string | @? | yes | `primary`, `secondary`, `tertiary` | n/a | style modifier of the link
+| `on-click` | function | & | no | n/a | n/a | click callback
diff --git a/packages/oui-navbar/src/index.spec.js b/packages/oui-navbar/src/index.spec.js
index e5aa774b..76e0ca27 100644
--- a/packages/oui-navbar/src/index.spec.js
+++ b/packages/oui-navbar/src/index.spec.js
@@ -655,5 +655,35 @@ describe("ouiNavbar", () => {
expect(toggler.attr("aria-expanded")).toBe("false");
});
});
+
+ describe("Links", () => {
+ const data = mockData.mainLinks[0];
+
+ it("should call click callback", () => {
+ const onClickSpy = jasmine.createSpy("onClickSpy");
+ const navbar = testUtils.compileTemplate(
+ `
+
+
+
+
+ `, {
+ name: data.name,
+ title: data.title,
+ url: data.url,
+ onClick: onClickSpy
+ });
+
+ $timeout.flush();
+
+ const link = angular.element(navbar[0].querySelector(".oui-navbar-link"));
+ link.triggerHandler("click");
+
+ expect(onClickSpy).toHaveBeenCalled();
+ });
+ });
});
});
diff --git a/packages/oui-navbar/src/link/navbar-link.component.js b/packages/oui-navbar/src/link/navbar-link.component.js
index 65f2e78f..c6c38147 100644
--- a/packages/oui-navbar/src/link/navbar-link.component.js
+++ b/packages/oui-navbar/src/link/navbar-link.component.js
@@ -12,7 +12,8 @@ export default {
state: "@?",
stateParams: "",
label: "@?ariaLabel",
- variant: "@?"
+ variant: "@?",
+ onClick: "&"
},
controller,
template
diff --git a/packages/oui-navbar/src/link/navbar-link.html b/packages/oui-navbar/src/link/navbar-link.html
index 2c1e4fae..0d928cb8 100644
--- a/packages/oui-navbar/src/link/navbar-link.html
+++ b/packages/oui-navbar/src/link/navbar-link.html
@@ -9,7 +9,8 @@
'oui-navbar-link_tertiary': $ctrl.variant === 'tertiary'
}"
ng-href="{{::$ctrl.href}}"
- ng-bind="::$ctrl.text">
+ ng-bind="::$ctrl.text"
+ ng-click="$ctrl.onClick()">
+ ui-sref-active="oui-navbar-link_active"
+ ng-click="$ctrl.onClick()">
diff --git a/packages/oui-navbar/src/menu/navbar-menu.html b/packages/oui-navbar/src/menu/navbar-menu.html
index 5642d6e7..1e2cd02d 100644
--- a/packages/oui-navbar/src/menu/navbar-menu.html
+++ b/packages/oui-navbar/src/menu/navbar-menu.html
@@ -31,7 +31,7 @@
'oui-navbar_desktop-only': !!menuLink.subLinks,
'oui-navbar-link_external': !!menuLink.isExternal
}"
- ng-click="$ctrl.navbarCtrl.toggleMenu()"
+ ng-click="$ctrl.closeMenuWithCallback($event, menuLink.click)"
ng-href="{{::menuLink.url}}"
oui-navbar-group="{{::$ctrl.menuName}}"
oui-navbar-group-last="::$last">
@@ -50,7 +50,7 @@
ng-class="::{
'oui-navbar_desktop-only': !!menuLink.subLinks
}"
- ng-click="$ctrl.navbarCtrl.toggleMenu()"
+ ng-click="$ctrl.closeMenuWithCallback($event, menuLink.click)"
ng-bind="::menuLink.title"
ui-sref="{{::$ctrl.constructor.getFullSref(menuLink)}}"
oui-navbar-group="{{::$ctrl.menuName}}"
@@ -60,7 +60,7 @@
+ ng-class="::!!managerLink.class ? managerLink.class : null"
+ on-click="managerLink.click()">
@@ -51,7 +52,7 @@
aria-label="{{::!!asideLink.label ? asideLink.label : null}}"
icon-badge="(asideLink.subLinks | filter: {isActive: 'true', acknowledged: '!true'}).length"
icon-class="{{::!!asideLink.iconClass ? asideLink.iconClass : null}}"
- on-click="asideLink.onClick"
+ on-click="asideLink.onClick()"
ng-repeat="asideLink in $ctrl.asideLinks track by $index"
ng-class="::!!asideLink.class ? asideLink.class : null"
ng-switch="::asideLink.name">
From b01d260193e6dd0dc5e666adc27708090393cad9 Mon Sep 17 00:00:00 2001
From: Marie JONES <14836007+marie-j@users.noreply.github.com>
Date: Tue, 6 Nov 2018 12:53:24 +0100
Subject: [PATCH 10/12] feat(oui-dropdown): allow click on link items (#306)
---
packages/oui-dropdown/src/index.spec.js | 26 +++++++++++++++++++
.../oui-dropdown/src/item/dropdown-item.html | 4 ++-
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/packages/oui-dropdown/src/index.spec.js b/packages/oui-dropdown/src/index.spec.js
index f2fa14d1..0be80e1c 100644
--- a/packages/oui-dropdown/src/index.spec.js
+++ b/packages/oui-dropdown/src/index.spec.js
@@ -212,6 +212,32 @@ describe("ouiDropdown", () => {
expect(link.attr("target")).toBe("_blank");
expect(link.attr("rel")).toBe("noopener");
});
+
+ it("should call click callback", () => {
+ const onLinkClickSpy = jasmine.createSpy("onLinkClickSpy");
+ const onButtonClickSpy = jasmine.createSpy("onButtonClickSpy");
+ const element = TestUtils.compileTemplate(`
+