From fbc565b6def8b1e08c086ba1e1a3534ce9c8b472 Mon Sep 17 00:00:00 2001 From: Marie JONES <14836007+marie-j@users.noreply.github.com> Date: Fri, 19 Oct 2018 15:05:49 +0200 Subject: [PATCH 01/12] feat(oui-select): allow items to be disabled (#291) --- packages/oui-select/README.md | 19 +++++++ packages/oui-select/src/index.spec.js | 54 +++++++++++++++++++ packages/oui-select/src/select.directive.js | 1 + packages/oui-select/src/select.html | 2 +- .../oui-select/src/templates/choices.html | 3 +- 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/packages/oui-select/README.md b/packages/oui-select/README.md index 23eb4613..d6b4e6a7 100644 --- a/packages/oui-select/README.md +++ b/packages/oui-select/README.md @@ -68,6 +68,24 @@ ``` +### Disabled Items + +```html:preview + + + +``` + +**Note**: For each `$item` in `items` array, `disable-item` will be called with current `$item` as an argument. If it returns true, `$item` will be disabled. + ### On Change **Note**: Model will not be refreshed until the `on-change` callback hasn't returned. If you want to access the new model inside the `on-change` callback you need to use the `modelValue` variable as below. @@ -110,6 +128,7 @@ | `placeholder` | string | @? | yes | n/a | n/a | placeholder displayed when model is undefined | `match` | string | @? | no | n/a | n/a | property of item to show as selected item | `items` | array | < | no | n/a | n/a | array used to populate the list +| `disable-items`| function | & | no | n/a | n/a | predicate to determine items to disable | `required` | boolean | { expect(onChange).toHaveBeenCalledWith(data[index2]); }); }); + + describe("Disable options", () => { + it("should disable corresponding items", () => { + const disableCountry = (item) => item.name === data[3].name; + const element = TestUtils.compileTemplate(` + + + `, { + countries: data, + disableCountry + }); + + $timeout.flush(); + + // Trigger click to update the options with corresponding dynamic attributes + getDropdownButton(element).click(); + expect(angular.element(getDropdownItem(element, 3)).prop("disabled")).toBeTruthy(); + }); + + it("should update item", () => { + const countries = data.concat({ name: "Imaginary country", code: "IC" }); + const disableCountry = (item) => item.code === ""; + const element = TestUtils.compileTemplate(` + + + `, { + countries, + disableCountry + }); + + $timeout.flush(); + + // Trigger click to update the options with corresponding dynamic attributes + getDropdownButton(element).click(); + expect(angular.element(getDropdownItem(element, data.length - 1)).prop("disabled")).toBeFalsy(); + + element.scope().$ctrl.countries[data.length - 1].code = ""; + $timeout.flush(); + + expect(angular.element(getDropdownItem(element, data.length - 1)).prop("disabled")).toBeTruthy(); + }); + }); }); }); diff --git a/packages/oui-select/src/select.directive.js b/packages/oui-select/src/select.directive.js index d2233f54..45a76cd9 100644 --- a/packages/oui-select/src/select.directive.js +++ b/packages/oui-select/src/select.directive.js @@ -17,6 +17,7 @@ export default () => ({ title: "@?", placeholder: "@?", items: "<", + disableItems: "&", match: "@?", groupBy: " - + diff --git a/packages/oui-select/src/templates/choices.html b/packages/oui-select/src/templates/choices.html index 39ca7a5f..80888d58 100644 --- a/packages/oui-select/src/templates/choices.html +++ b/packages/oui-select/src/templates/choices.html @@ -5,10 +5,11 @@ ng-attr-id="ui-select-choices-row-{{ $select.generatedId }}-{{$index}}" class="ui-select-choices-row oui-dropdown-option" ng-class="{ - active: $select.isActive(this), + active: $select.isActive(this) && !$select.isDisabled(this), selected: $select.isSelected(this), disabled: $select.isDisabled(this) }" + ng-disabled="$select.isDisabled(this)" role="option" type="button"> From 3cf97fc3179a49e29c14638d1f03366aaca9c94b Mon Sep 17 00:00:00 2001 From: Axel Peter <15101925+AxelPeter@users.noreply.github.com> Date: Mon, 22 Oct 2018 10:19:23 +0200 Subject: [PATCH 02/12] fix(oui-navbar): fix state attributes on component links (#295) --- packages/oui-navbar/src/navbar.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/oui-navbar/src/navbar.html b/packages/oui-navbar/src/navbar.html index 12cd08a9..86594ba8 100644 --- a/packages/oui-navbar/src/navbar.html +++ b/packages/oui-navbar/src/navbar.html @@ -29,6 +29,8 @@ name="{{::managerLink.name}}" text="{{::managerLink.heading || managerLink.title}}" href="{{::managerLink.href || managerLink.url}}" + state="{{::!!managerLink.state ? managerLink.state : null}}" + state-params="::!!managerLink.stateParams ? managerLink.stateParams : null" variant="{{::managerLink.isPrimary ? 'primary' : 'secondary'}}" aria-label="{{::!!managerLink.label ? managerLink.label : null}}" ng-repeat="managerLink in ::$ctrl.mainLinks track by $index" From b84e08ed8ea44b8e3ef6efef941b64aed07bfccc Mon Sep 17 00:00:00 2001 From: Marie JONES <14836007+marie-j@users.noreply.github.com> Date: Tue, 23 Oct 2018 14:43:21 +0200 Subject: [PATCH 03/12] feat(oui-datagrid): permit to access row index (#296) --- packages/oui-datagrid/README.md | 20 ++++++++++++ .../oui-datagrid/src/cell/cell.controller.js | 1 + packages/oui-datagrid/src/index.spec.js | 32 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/packages/oui-datagrid/README.md b/packages/oui-datagrid/README.md index 8c605725..a7640633 100644 --- a/packages/oui-datagrid/README.md +++ b/packages/oui-datagrid/README.md @@ -294,6 +294,26 @@ Or you can use the `page-size` property. It takes precedence over value configur ``` +### Access row index + +```html:preview + + + {{$rowIndex}} + + + {{$row.firstName}} {{$row.lastName}} + + + {{$value}} + + + + {{$value | date:shortDate}} + + +``` + ### Remote data ```html diff --git a/packages/oui-datagrid/src/cell/cell.controller.js b/packages/oui-datagrid/src/cell/cell.controller.js index d17d3cdd..ca62b284 100644 --- a/packages/oui-datagrid/src/cell/cell.controller.js +++ b/packages/oui-datagrid/src/cell/cell.controller.js @@ -40,6 +40,7 @@ export default class { this.cellScope.$row = this.row; this.cellScope.$column = this.column; this.cellScope.$value = this.row[this.column.name]; + this.cellScope.$rowIndex = this.index; if (this.column.compiledTemplate) { this.column.compiledTemplate(this.cellScope, clone => { diff --git a/packages/oui-datagrid/src/index.spec.js b/packages/oui-datagrid/src/index.spec.js index 0af9c61f..1871e269 100644 --- a/packages/oui-datagrid/src/index.spec.js +++ b/packages/oui-datagrid/src/index.spec.js @@ -1076,6 +1076,38 @@ describe("ouiDatagrid", () => { expect(actualCellHtml).toBe(`test: ${fakeData[0].lastName}`); }); + it("should support row index data binding inside cell", () => { + const element = TestUtils.compileTemplate(` + + + + test: {{ $rowIndex }} + + + `, { + rows: fakeData.slice(0, 5) + } + ); + + const $firstRow = getRow(element, 0); + expect( + getCell($firstRow, 1).children().children().html() + .trim()) + .toBe("test: 0"); + + const $middleRow = getRow(element, 2); + expect( + getCell($middleRow, 1).children().children().html() + .trim()) + .toBe("test: 2"); + + const $lastRow = getRow(element, 4); + expect( + getCell($lastRow, 1).children().children().html() + .trim()) + .toBe("test: 4"); + }); + it("should support parent binding inside cell", () => { const element = TestUtils.compileTemplate(` From 2fa7c1506b173a97426bbe822ac55c7680f10ebe Mon Sep 17 00:00:00 2001 From: Marie JONES <14836007+marie-j@users.noreply.github.com> Date: Tue, 30 Oct 2018 10:09:03 +0100 Subject: [PATCH 04/12] fix(oui-select-picker): prevent leaving blank space (#300) If no match value is defined an empty section is created --- packages/oui-select-picker/src/index.spec.js | 24 +++++++++++++++++++ .../oui-select-picker/src/select-picker.html | 15 +++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/oui-select-picker/src/index.spec.js b/packages/oui-select-picker/src/index.spec.js index 12a11be5..55353276 100644 --- a/packages/oui-select-picker/src/index.spec.js +++ b/packages/oui-select-picker/src/index.spec.js @@ -98,6 +98,30 @@ describe("ouiSelectPicker", () => { const radioElement = getRadioInputElement(element); expect(angular.element(radioElement).attr("value")).toEqual("aValue"); }); + + it("should allow to pick one of values attribute", () => { + const element = TestUtils.compileTemplate(''); + + const selectElement = element[0].querySelector("oui-select"); + expect(angular.element(selectElement)).not.toBeUndefined(); + + const selectValues = element[0].querySelectorAll(".oui-dropdown-option"); + expect(angular.element(selectValues[0]).text().trim()).toEqual("aValue"); + }); + + it("should display radio value according to match", () => { + const element = TestUtils.compileTemplate(''); + + const value = element[0].querySelectorAll(".oui-select-picker__value"); + expect(angular.element(value).text().trim()).toEqual("aValue"); + }); + + it("should display select values according to match", () => { + const element = TestUtils.compileTemplate(''); + + const selectValues = element[0].querySelectorAll(".oui-dropdown-option"); + expect(angular.element(selectValues[1]).text().trim()).toEqual("bValue"); + }); }); describe("disabled attribute", () => { diff --git a/packages/oui-select-picker/src/select-picker.html b/packages/oui-select-picker/src/select-picker.html index 630b2daa..ceecbfd3 100644 --- a/packages/oui-select-picker/src/select-picker.html +++ b/packages/oui-select-picker/src/select-picker.html @@ -28,10 +28,13 @@ ng-bind=":: $ctrl.description" ng-if="$ctrl.description"> + ng-if="$ctrl.values.length === 1 && $ctrl.match"> + ng-bind=":: $ctrl.getFirstValueMatch($ctrl.match)"> + + + - + data-align="end"> + + Date: Wed, 31 Oct 2018 10:00:26 +0100 Subject: [PATCH 05/12] feat(oui-popover): add ng-template and title attribute support (#301) * Refactor oui-popover to have the same functioning as oui-tooltip * Add deprecated support for the old functionning of oui-popover * Add title attribute support * Add ng-template support to load the content of a popover * Add unit tests --- packages/oui-popover/README.md | 157 +++++++++----- .../popover-content.directive.js | 6 +- .../src/content/popover-content.html | 5 + packages/oui-popover/src/index.js | 8 +- packages/oui-popover/src/index.spec.js | 205 ++++++++++++------ packages/oui-popover/src/popover-content.html | 7 - .../src/popover-trigger.directive.js | 41 ---- packages/oui-popover/src/popover.component.js | 11 - .../oui-popover/src/popover.controller.js | 97 +++++++-- packages/oui-popover/src/popover.directive.js | 17 ++ packages/oui-popover/src/popover.html | 19 +- .../src/trigger/popover-trigger.controller.js | 26 +++ .../src/trigger/popover-trigger.directive.js | 16 ++ .../oui-tooltip/src/tooltip.controller.js | 14 +- 14 files changed, 416 insertions(+), 213 deletions(-) rename packages/oui-popover/src/{ => content}/popover-content.directive.js (69%) create mode 100644 packages/oui-popover/src/content/popover-content.html delete mode 100644 packages/oui-popover/src/popover-content.html delete mode 100644 packages/oui-popover/src/popover-trigger.directive.js delete mode 100644 packages/oui-popover/src/popover.component.js create mode 100644 packages/oui-popover/src/popover.directive.js create mode 100644 packages/oui-popover/src/trigger/popover-trigger.controller.js create mode 100644 packages/oui-popover/src/trigger/popover-trigger.directive.js diff --git a/packages/oui-popover/README.md b/packages/oui-popover/README.md index 7a2a65a3..a9fec30b 100644 --- a/packages/oui-popover/README.md +++ b/packages/oui-popover/README.md @@ -1,10 +1,49 @@ # Popover - + ## Usage -### Simple case +### Using value of `oui-popover` attribute + +```html:preview + +``` + +### Using value of `title` attribute + +```html:preview + +``` + +### Using a template with `oui-popover-template` attribute + +```html:preview + + + +``` + +**Note**: This method use `ngInclude` to add the template in a popover. The content of your template will be compiled with a **new** scope. See [ngInclude](https://docs.angularjs.org/api/ng/directive/ngInclude). + +### Using `oui-popover` component ```html:preview @@ -15,83 +54,83 @@ ``` +**Note**: This use is **deprecated** and will be removed in the next major version + ### All directions ```html:preview - - - This is an awesome popover content. - - - - - This is an awesome popover content. - - - - - This is an awesome popover content. - - - - - This is an awesome popover content. - + + + + + + + ``` ### Alignments ```html:preview - - - This is an awesome popover content. - - - - - This is an awesome popover content. - + + + ``` ### Help Popover ```html:preview - - - This is an awesome popover content. - + ``` -## API - -### oui-popover - -The component for a popover. - -Availability: - - - Element - -| Attribute | Type | Binding | Values | Default | Description -| ---- | ---- | ---- | ---- | ---- | ---- -| `placement` | string | @? | See [Popper placements](https://popper.js.org/popper-documentation.html#Popper.placements) | `right` | modifier for alignment +### Dynamic popover text -For placement values, see Popper.JS documentation (https://popper.js.org/popper-documentation.html#Popper.placements) - -### oui-popover-trigger +```html:preview + +``` -The directive that triggers the popover apparition. +## API -Availability: +| Attribute | Type | Binding | One-time Binding | Values | Default | Description +| ---- | ---- | ---- | ---- | ---- | ---- | ---- +| `oui-popover` | string | @ | no | n/a | `title` attribute | popover content +| `oui-popover-template` | string | @? | no | n/a | n/a | popover content template +| `oui-popover-placement` | string | @? | yes | See [Popper placements](https://popper.js.org/popper-documentation.html#Popper.placements) | `right` | modifier for alignment - - Element - - Attribute +## Deprecated -### oui-popover-content +### Attributes -The directive that wrap the popover content. +* `placement`: Replaced by `oui-popover-placement` attribute -Availability: +### Components - - Element - - Attribute +* `oui-popover`: Replaced by `oui-popover` attribute +* `oui-popover-trigger`: Replaced by `oui-popover` attribute +* `oui-popover-content`: Replaced by `oui-popover` attribute diff --git a/packages/oui-popover/src/popover-content.directive.js b/packages/oui-popover/src/content/popover-content.directive.js similarity index 69% rename from packages/oui-popover/src/popover-content.directive.js rename to packages/oui-popover/src/content/popover-content.directive.js index c1873ac6..cd3952c8 100644 --- a/packages/oui-popover/src/popover-content.directive.js +++ b/packages/oui-popover/src/content/popover-content.directive.js @@ -1,5 +1,6 @@ import contentTemplate from "./popover-content.html"; +// Deprecated: Support only for old use export default () => { "ngInject"; @@ -13,6 +14,9 @@ export default () => { bindToController: true, scope: {}, template: contentTemplate, - transclude: true + transclude: true, + link: (scope, element) => { + element.addClass("oui-popover"); + } }; }; diff --git a/packages/oui-popover/src/content/popover-content.html b/packages/oui-popover/src/content/popover-content.html new file mode 100644 index 00000000..0f8066e4 --- /dev/null +++ b/packages/oui-popover/src/content/popover-content.html @@ -0,0 +1,5 @@ + +
+ diff --git a/packages/oui-popover/src/index.js b/packages/oui-popover/src/index.js index 97a7648c..f0afe3c0 100644 --- a/packages/oui-popover/src/index.js +++ b/packages/oui-popover/src/index.js @@ -1,10 +1,10 @@ -import Popover from "./popover.component.js"; -import PopoverContent from "./popover-content.directive"; -import PopoverTrigger from "./popover-trigger.directive"; +import Popover from "./popover.directive.js"; +import PopoverContent from "./content/popover-content.directive"; +import PopoverTrigger from "./trigger/popover-trigger.directive"; export default angular .module("oui.popover", []) - .component("ouiPopover", Popover) + .directive("ouiPopover", Popover) .directive("ouiPopoverContent", PopoverContent) .directive("ouiPopoverTrigger", PopoverTrigger) .name; diff --git a/packages/oui-popover/src/index.spec.js b/packages/oui-popover/src/index.spec.js index d286cd6c..0d12f7d2 100644 --- a/packages/oui-popover/src/index.spec.js +++ b/packages/oui-popover/src/index.spec.js @@ -1,81 +1,132 @@ describe("ouiPopover", () => { - let TestUtils; + let $timeout; + let testUtils; beforeEach(angular.mock.module("oui.popover")); beforeEach(angular.mock.module("oui.test-utils")); - beforeEach(inject((_TestUtils_) => { - TestUtils = _TestUtils_; + beforeEach(inject((_$timeout_, _TestUtils_) => { + $timeout = _$timeout_; + testUtils = _TestUtils_; })); - describe("Component", () => { - it("should display the trigger with correct class name", () => { - const element = TestUtils.compileTemplate(` - - - - Popover content - - ` - ); - - const trigger = element[0].querySelector("[oui-popover-trigger]"); - expect(angular.element(trigger).hasClass("oui-popover__trigger")).toBeTruthy(); - }); + describe("Directive", () => { + describe("oui-popover", () => { + it("should create a popover, next to the trigger, with the attribute value as text", () => { + const component = testUtils.compileTemplate('
'); - it("should display at right with arrow by default", () => { - const element = TestUtils.compileTemplate(` - - -
- Popover content -
-
` - ); - - const controller = element.controller("ouiPopover"); - controller.openPopover(); - - expect(controller.popper.options.placement).toEqual("right"); - expect(element[0].querySelector("[x-arrow]")).toBeDefined(); - }); + $timeout.flush(); + + const popover = angular.element(component[0].querySelector(".trigger")).next(); + + expect(popover.length).toBe(1); + expect(popover.hasClass("oui-popover")).toBe(true); + expect(popover.text().trim()).toBe("foo"); + }); + + it("should create a popover, next to the trigger, with the attribute value as text", () => { + const component = testUtils.compileTemplate('
'); + + $timeout.flush(); + + const popover = angular.element(component[0].querySelector(".trigger")).next(); + + expect(popover.length).toBe(1); + expect(popover.hasClass("oui-popover")).toBe(true); + expect(popover.text().trim()).toBe("foo"); + }); + + it("should position the popover with right direction when trigger is clicked, if there is no placement defined", () => { + const component = testUtils.compileTemplate('
'); + + $timeout.flush(); + + const trigger = angular.element(component[0].querySelector(".trigger")).triggerHandler("click"); + const popover = trigger.next(); + + expect(popover.attr("x-placement")).toBe("right"); + }); + + + it("should position the popover with placement attribute value, when trigger is clicked", () => { + const component = testUtils.compileTemplate('
'); + + $timeout.flush(); + + const trigger = angular.element(component[0].querySelector(".trigger")).triggerHandler("click"); + const popover = trigger.next(); + + expect(popover.attr("x-placement")).toBe("bottom-start"); + }); + + it("should create a popover, next to the trigger, with the content of the template", () => { + const component = testUtils.compileTemplate(`
+ + +
`); + + $timeout.flush(); + + const popover = angular.element(component[0].querySelector(".trigger")).next(); + + expect(popover.text().trim()).toBe("foo"); + }); + + it("should set aria-expanded when trigger is clicked", () => { + const component = testUtils.compileTemplate('
'); - it("should display the popover at bottom aligned the left border", () => { - const element = TestUtils.compileTemplate(` - - -
- Popover content -
-
` - ); + $timeout.flush(); - const controller = element.controller("ouiPopover"); - controller.openPopover(); + const trigger = angular.element(component[0].querySelector(".trigger")); + expect(trigger.attr("aria-expanded")).toBe("false"); - expect(controller.popper.options.placement).toEqual("bottom-start"); + trigger.triggerHandler("click"); + expect(trigger.attr("aria-expanded")).toBe("true"); + + trigger.triggerHandler("click"); + expect(trigger.attr("aria-expanded")).toBe("false"); + }); }); - describe("Events", () => { - it("should not be visible", () => { - const element = TestUtils.compileTemplate(` + describe("Deprecated support", () => { + it("should display the trigger with correct class name", () => { + const element = testUtils.compileTemplate(` - + + + Popover content + + ` + ); + + $timeout.flush(); + + const trigger = element[0].querySelector("[oui-popover-trigger]"); + expect(angular.element(trigger).hasClass("oui-popover__trigger")).toBeTruthy(); + }); + + it("should display at right with arrow by default", () => { + const element = testUtils.compileTemplate(` + +
Popover content
` ); - const popover = element[0].querySelector("[oui-popover-content]").parentNode; - const $popover = angular.element(popover); + $timeout.flush(); + + const controller = element.controller("ouiPopover"); + controller.openPopover(); - expect($popover.hasClass("oui-popover_active")).toBeFalsy(); + expect(controller.popper.options.placement).toEqual("right"); + expect(element[0].querySelector("[x-arrow]")).toBeDefined(); }); - it("should display and hide popover on click", () => { - const element = TestUtils.compileTemplate(` - + it("should display the popover at bottom aligned the left border", () => { + const element = testUtils.compileTemplate(` +
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 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 | 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 + + + + + + + + + + + + + + + + + + + +``` + +### 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 | { + 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 | { 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: " + 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 @@

@@ -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(` + + + + + + + + + `, { + onLinkClick: onLinkClickSpy, + onButtonClick: onButtonClickSpy + }); + + $timeout.flush(); + const items = angular.element(element[0].querySelectorAll("oui-dropdown-item")).children(); + angular.element(items[0]).triggerHandler("click"); + angular.element(items[1]).triggerHandler("click"); + + expect(onLinkClickSpy).toHaveBeenCalled(); + expect(onButtonClickSpy).toHaveBeenCalled(); + }); }); describe("Group", () => { diff --git a/packages/oui-dropdown/src/item/dropdown-item.html b/packages/oui-dropdown/src/item/dropdown-item.html index a7996605..4741b3df 100644 --- a/packages/oui-dropdown/src/item/dropdown-item.html +++ b/packages/oui-dropdown/src/item/dropdown-item.html @@ -12,7 +12,8 @@ ng-if="::!!$ctrl.href" ng-href="{{::$ctrl.href}}" ng-attr-target="{{::$ctrl.linkTarget}}" - ng-attr-rel="{{::$ctrl.linkRel}}"> + ng-attr-rel="{{::$ctrl.linkRel}}" + ng-click="$ctrl.onClick()"> {{::$ctrl.text}} {{::$ctrl.text}} From ac775804b84512345481de75e2650490b55ddf3c Mon Sep 17 00:00:00 2001 From: Marie JONES <14836007+marie-j@users.noreply.github.com> Date: Thu, 8 Nov 2018 09:54:31 +0100 Subject: [PATCH 11/12] feat(oui-navbar): allow to animate icon on aside links (#308) --- packages/oui-navbar/README.md | 4 ++++ .../oui-navbar/src/dropdown/navbar-dropdown.component.js | 1 + packages/oui-navbar/src/dropdown/navbar-dropdown.html | 6 ++++-- packages/oui-navbar/src/index.spec.data.json | 1 + packages/oui-navbar/src/index.spec.js | 6 ++++++ packages/oui-navbar/src/navbar.html | 1 + 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/oui-navbar/README.md b/packages/oui-navbar/README.md index f90107e8..26d6dca1 100644 --- a/packages/oui-navbar/README.md +++ b/packages/oui-navbar/README.md @@ -349,6 +349,7 @@ It defines the menu in reponsive mode. It will be visible only for screen resolu This property is only available for root links of `aside-links`. - `iconClass`: define `class` of the menu item icon. +- `iconAnimated`: define if the menu item icon should be animated ```json [{ @@ -357,6 +358,7 @@ This property is only available for root links of `aside-links`. "label": String, "title": String, "iconClass": String, + "iconAnimated": Boolean, "subLinks": Array[{ "label": String, "title": String, @@ -379,6 +381,7 @@ This property is only available for root links of `aside-links`. acknowledged: '!true' }).length" icon-class="{{asideLink.iconClass}}" + icon-animated="asideLink.name === 'notifications'" on-click="asideLink.onClick" ng-repeat="asideLink in $ctrl.asideLinks track by $index" ng-class="asideLink.class" @@ -548,6 +551,7 @@ The property `name` **must be** `"user"`. | `text` | string | @ | yes | n/a | n/a | text of the button | `aria-label` | string | @? | yes | n/a | n/a | accessibility label of the button | `icon-class` | string | @? | yes | n/a | n/a | classname of the button icon +| `icon-animated` | boolean | -