This repository was archived by the owner on Aug 7, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
feat(oui-tabs): add tabs component #319
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| # Tabs | ||
|
|
||
| <component-status cx-design="complete" ux="rc"></component-status> | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Basic | ||
|
|
||
| ```html:preview | ||
| <oui-tabs> | ||
| <oui-tabs-item heading="Basic1"> | ||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet congue urna. Praesent ultricies id ex convallis dictum. Pellentesque malesuada faucibus consectetur. Quisque vehicula tincidunt leo, quis auctor nisi luctus quis. Etiam purus lectus, placerat vitae vehicula at, molestie nec erat. Duis enim odio, maximus at laoreet in, finibus nec mi. Nam ultrices, lacus vitae egestas volutpat, libero odio gravida turpis, vitae dapibus ex mi nec quam. Pellentesque a tempor nibh.</p> | ||
| </oui-tabs-item> | ||
| <oui-tabs-item heading="Basic2"> | ||
| <p>Proin egestas fermentum lectus nec euismod. Vivamus eu congue dui. Pellentesque sit amet pellentesque quam. Morbi posuere sem nec rutrum placerat. Vestibulum porttitor arcu eu risus tempor consectetur. Fusce aliquam bibendum aliquet. Morbi semper egestas iaculis. Ut sit amet sem et neque porta cursus pellentesque nec augue. Nullam semper in metus et luctus. Nunc molestie non ipsum a consequat. Etiam pellentesque laoreet lectus ut luctus. Nulla maximus, leo a mattis gravida, ligula felis vulputate libero, vitae fringilla nibh mauris nec dui. Fusce sed massa at arcu euismod dictum id sit amet lorem. Aliquam sed viverra sem, quis vehicula ligula. Vivamus blandit varius condimentum.</p> | ||
| </oui-tabs-item> | ||
| <oui-tabs-item heading="Basic3"> | ||
| <p>Duis egestas nulla at euismod semper. Nullam bibendum auctor viverra. Sed posuere neque nulla, id cursus nisi molestie vel. Nulla ornare elit sit amet congue faucibus. Aliquam eget lorem id justo ornare pretium in sit amet lectus. Sed maximus odio id porttitor rhoncus. Quisque pulvinar mauris ut sapien dictum, ultrices fermentum orci efficitur. Cras nec auctor ante. Aliquam ornare eleifend neque, at condimentum lacus aliquet elementum. Mauris mattis porttitor tortor vel vehicula. Phasellus venenatis nibh nec viverra sollicitudin. Ut lobortis mattis mauris, vel euismod nibh faucibus a.</p> | ||
| </oui-tabs-item> | ||
| </oui-tabs> | ||
| ``` | ||
|
|
||
| ### Dynamic | ||
|
|
||
| ```html:preview | ||
| <p class="oui-doc-preview-only"> | ||
| <oui-button ng-init="$ctrl.hideDynamic1 = false" on-click="$ctrl.hideDynamic1 = !$ctrl.hideDynamic1"> | ||
| {{$ctrl.hideDynamic1 ? 'Show' : 'Hide'}} Dynamic1 tab | ||
| </oui-button> | ||
| <oui-button ng-init="$ctrl.hideDynamic2 = true" on-click="$ctrl.hideDynamic2 = !$ctrl.hideDynamic2"> | ||
| {{$ctrl.hideDynamic2 ? 'Show' : 'Hide'}} Dynamic2 tab | ||
| </oui-button> | ||
| <oui-button ng-init="$ctrl.hideDynamic3 = false" on-click="$ctrl.hideDynamic3 = !$ctrl.hideDynamic3"> | ||
| {{$ctrl.hideDynamic3 ? 'Show' : 'Hide'}} Dynamic3 tab | ||
| </oui-button> | ||
| </p> | ||
| <oui-tabs> | ||
| <oui-tabs-item heading="Dynamic1" ng-if="!$ctrl.hideDynamic1"> | ||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet congue urna. Praesent ultricies id ex convallis dictum. Pellentesque malesuada faucibus consectetur. Quisque vehicula tincidunt leo, quis auctor nisi luctus quis. Etiam purus lectus, placerat vitae vehicula at, molestie nec erat. Duis enim odio, maximus at laoreet in, finibus nec mi. Nam ultrices, lacus vitae egestas volutpat, libero odio gravida turpis, vitae dapibus ex mi nec quam. Pellentesque a tempor nibh.</p> | ||
| </oui-tabs-item> | ||
| <oui-tabs-item heading="Dynamic2" ng-if="!$ctrl.hideDynamic2"> | ||
| <p>Proin egestas fermentum lectus nec euismod. Vivamus eu congue dui. Pellentesque sit amet pellentesque quam. Morbi posuere sem nec rutrum placerat. Vestibulum porttitor arcu eu risus tempor consectetur. Fusce aliquam bibendum aliquet. Morbi semper egestas iaculis. Ut sit amet sem et neque porta cursus pellentesque nec augue. Nullam semper in metus et luctus. Nunc molestie non ipsum a consequat. Etiam pellentesque laoreet lectus ut luctus. Nulla maximus, leo a mattis gravida, ligula felis vulputate libero, vitae fringilla nibh mauris nec dui. Fusce sed massa at arcu euismod dictum id sit amet lorem. Aliquam sed viverra sem, quis vehicula ligula. Vivamus blandit varius condimentum.</p> | ||
| </oui-tabs-item> | ||
| <oui-tabs-item heading="Dynamic3" ng-if="!$ctrl.hideDynamic3"> | ||
| <p>Duis egestas nulla at euismod semper. Nullam bibendum auctor viverra. Sed posuere neque nulla, id cursus nisi molestie vel. Nulla ornare elit sit amet congue faucibus. Aliquam eget lorem id justo ornare pretium in sit amet lectus. Sed maximus odio id porttitor rhoncus. Quisque pulvinar mauris ut sapien dictum, ultrices fermentum orci efficitur. Cras nec auctor ante. Aliquam ornare eleifend neque, at condimentum lacus aliquet elementum. Mauris mattis porttitor tortor vel vehicula. Phasellus venenatis nibh nec viverra sollicitudin. Ut lobortis mattis mauris, vel euismod nibh faucibus a.</p> | ||
| </oui-tabs-item> | ||
| </oui-tabs> | ||
| ``` | ||
|
|
||
| ### Check marks | ||
|
|
||
| ```html:preview | ||
| <oui-tabs ng-init="$ctrl.checked1 = true"> | ||
| <oui-tabs-item heading="Lorem" checked="$ctrl.checked1"> | ||
| <oui-button on-click="$ctrl.checked1 = !$ctrl.checked1">Toggle check mark of tab 1</oui-button> | ||
| </oui-tabs-item> | ||
| <oui-tabs-item heading="Ipsum" checked="$ctrl.checked2"> | ||
| <oui-button on-click="$ctrl.checked2 = !$ctrl.checked2">Toggle check mark of tab 2</oui-button> | ||
| </oui-tabs-item> | ||
| <oui-tabs-item heading="Dolor sit amet" checked="$ctrl.checked3"> | ||
| <oui-button on-click="$ctrl.checked3 = !$ctrl.checked3">Toggle check mark of tab 3</oui-button> | ||
| </oui-tabs-item> | ||
| </oui-tabs> | ||
| ``` | ||
|
|
||
| ## API | ||
|
|
||
| ### oui-tabs | ||
|
|
||
| | Attribute | Type | Binding | One-time Binding | Values | Default | Description | ||
| | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ||
| | `aria-label` | string | @? | yes | n/a | n/a | accessibility label | ||
|
|
||
| ### oui-tabs-item | ||
|
|
||
| | Attribute | Type | Binding | One-time Binding | Values | Default | Description | ||
| | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ||
| | `id` | string | @? | yes | n/a | n/a | id attribute of the panel | ||
| | `heading` | string | @? | yes | n/a | n/a | heading text of the tab | ||
| | `aria-label` | string | @? | yes | n/a | n/a | accessibility label | ||
| | `checked` | booldean | <? | yes | `true`, `false` | n/a | check mark flag of the tab |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "name": "@ovh-ui/oui-tabs", | ||
| "version": "1.0.0", | ||
| "main": "./src/index.js", | ||
| "license": "BSD-3-Clause", | ||
| "author": "OVH SAS" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import Tabs from "./tabs.component"; | ||
| import TabsItem from "./item/tabs-item.component"; | ||
|
|
||
| export default angular | ||
| .module("oui.tabs", []) | ||
| .component("ouiTabs", Tabs) | ||
| .component("ouiTabsItem", TabsItem) | ||
| .name; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| describe("ouiTabs", () => { | ||
| let TestUtils; | ||
| let $timeout; | ||
|
|
||
| beforeEach(angular.mock.module("oui.tabs")); | ||
| beforeEach(angular.mock.module("oui.test-utils")); | ||
|
|
||
| beforeEach(inject((_TestUtils_, _$timeout_) => { | ||
| TestUtils = _TestUtils_; | ||
| $timeout = _$timeout_; | ||
| })); | ||
|
|
||
| describe("Components", () => { | ||
| it("should add default classname", () => { | ||
| const element = TestUtils.compileTemplate(` | ||
| <oui-tabs> | ||
| <oui-tabs-item></oui-tabs-item> | ||
| </oui-tabs>`); | ||
|
|
||
| $timeout.flush(); | ||
|
|
||
| expect(element.hasClass("oui-tabs")).toBeTruthy(); | ||
| expect(element.find("oui-tabs-item").hasClass("oui-tabs-item")).toBeTruthy(); | ||
| }); | ||
|
|
||
| it("should have accessibility attribute", () => { | ||
| const element = TestUtils.compileTemplate(` | ||
| <oui-tabs aria-label="tablist"> | ||
| <oui-tabs-item aria-label="heading"></oui-tabs-item> | ||
| </oui-tabs>`); | ||
|
|
||
| $timeout.flush(); | ||
|
|
||
| const content = angular.element(element[0].querySelector(".oui-tabs-item__content")); | ||
| const contentId = content.attr("id"); | ||
| expect(content.attr("role")).toBe("tabpanel"); | ||
| expect(content.attr("aria-hidden")).toBeDefined(); | ||
|
|
||
| const tablist = angular.element(element[0].querySelector(".oui-tabs__tablist")); | ||
| expect(tablist.attr("role")).toBe("tablist"); | ||
| expect(tablist.attr("aria-label")).toBe("tablist"); | ||
|
|
||
| const tab = angular.element(element[0].querySelector(".oui-tabs__tab")); | ||
| expect(tab.attr("aria-label")).toBe("heading"); | ||
| expect(tab.attr("aria-controls")).toBe(contentId); | ||
| expect(tab.attr("aria-selected")).toBeDefined(); | ||
|
|
||
| const button = angular.element(element[0].querySelector(".oui-tabs-item__button")); | ||
| expect(button.attr("aria-label")).toBe("heading"); | ||
| expect(button.attr("aria-controls")).toBe(contentId); | ||
| expect(button.attr("aria-expanded")).toBeDefined(); | ||
| }); | ||
|
|
||
| it("should set tabs with heading", () => { | ||
| const element = TestUtils.compileTemplate(` | ||
| <oui-tabs aria-label="tablist"> | ||
| <oui-tabs-item heading="lorem"></oui-tabs-item> | ||
| <oui-tabs-item heading="ipsum"></oui-tabs-item> | ||
| </oui-tabs>`); | ||
|
|
||
| $timeout.flush(); | ||
|
|
||
| const items = element.find("oui-tabs-item"); | ||
| const tabs = angular.element(element[0].querySelector(".oui-tabs__tablist")).find("button"); | ||
| expect(tabs.length).toBe(items.length); | ||
|
|
||
| angular.forEach(items, (item, index) => { | ||
| const heading = angular.element(item).attr("heading"); | ||
| const tab = angular.element(tabs[index]); | ||
| expect(tab.text().trim()).toBe(heading); | ||
| }); | ||
| }); | ||
|
|
||
| it("should update active tab", () => { | ||
| const element = TestUtils.compileTemplate(` | ||
| <oui-tabs aria-label="tablist"> | ||
| <oui-tabs-item heading="lorem"></oui-tabs-item> | ||
| <oui-tabs-item heading="ipsum"></oui-tabs-item> | ||
| </oui-tabs>`); | ||
|
|
||
| $timeout.flush(); | ||
|
|
||
| const items = element.find("oui-tabs-item"); | ||
| const button = angular.element(items[1]).find("button"); | ||
| button.triggerHandler("click"); | ||
|
|
||
| const id = button.next().attr("id"); | ||
| const tabsCtrl = element.controller("ouiTabs"); | ||
| expect(tabsCtrl.activeId).toBe(id); | ||
| }); | ||
|
|
||
| it("should update checkmark", () => { | ||
| const checked = [true, false]; | ||
| const element = TestUtils.compileTemplate(` | ||
| <oui-tabs aria-label="tablist"> | ||
| <oui-tabs-item heading="lorem" checked="${checked[0]}"></oui-tabs-item> | ||
| <oui-tabs-item heading="ipsum" checked="${checked[1]}"></oui-tabs-item> | ||
| </oui-tabs>`); | ||
|
|
||
| $timeout.flush(); | ||
|
|
||
| const items = element.find("oui-tabs-item"); | ||
| angular.forEach(items, (item, index) => { | ||
| const heading = angular.element(item).find("button"); | ||
| expect(heading.hasClass("oui-tabs-item__button_checked")).toBe(checked[index]); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| describe("Methods", () => { | ||
| it("should add item in items array", () => { | ||
| const element = TestUtils.compileTemplate("<oui-tabs></oui-tabs>"); | ||
| const tabsCtrl = element.controller("ouiTabs"); | ||
|
|
||
| tabsCtrl.items = ["ipsum"]; | ||
| tabsCtrl.addItem("lorem", 0); // Should be added at index 0 | ||
| tabsCtrl.addItem("dolor"); // Should be added at the end | ||
|
|
||
| expect(tabsCtrl.items.indexOf("lorem")).toBe(0); | ||
| expect(tabsCtrl.items.indexOf("dolor")).toBe(tabsCtrl.items.length - 1); | ||
| }); | ||
|
|
||
| it("should remove item in items array", () => { | ||
| const element = TestUtils.compileTemplate("<oui-tabs></oui-tabs>"); | ||
| const tabsCtrl = element.controller("ouiTabs"); | ||
|
|
||
| tabsCtrl.items = ["lorem", "ipsum", "dolor"]; | ||
| tabsCtrl.removeItem("ipsum"); | ||
|
|
||
| expect(tabsCtrl.items.indexOf("ipsum")).toBe(-1); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import controller from "./tabs-item.controller"; | ||
| import template from "./tabs-item.html"; | ||
|
|
||
| export default { | ||
| require: { | ||
| tabsCtrl: "^ouiTabs" | ||
| }, | ||
| bindings: { | ||
| id: "@?", | ||
| heading: "@?", | ||
| ariaLabel: "@?", | ||
| checked: "<?" | ||
| }, | ||
| controller, | ||
| template, | ||
| transclude: true | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { addBooleanParameter, addDefaultParameter } from "@ovh-ui/common/component-utils"; | ||
|
|
||
| export default class { | ||
| constructor ($attrs, $element, $scope, $timeout) { | ||
| "ngInject"; | ||
|
|
||
| this.$attrs = $attrs; | ||
| this.$element = $element; | ||
| this.$scope = $scope; | ||
| this.$timeout = $timeout; | ||
| } | ||
|
|
||
| setActive () { | ||
| this.tabsCtrl.activeId = this.id; | ||
| } | ||
|
|
||
| getNodeIndex () { | ||
| let i = 0; | ||
| let item = this.$element[0]; | ||
|
|
||
| // Return DOM node index | ||
| while (item.previousSibling) { | ||
| item = item.previousSibling; | ||
| if (item.nodeType === 1) { | ||
| i += 1; | ||
| } | ||
| } | ||
|
|
||
| return i; | ||
| } | ||
|
|
||
| $onInit () { | ||
| addBooleanParameter(this, "checked"); | ||
| addDefaultParameter(this, "id", `ouiTabsItem${this.$scope.$id}`); | ||
|
|
||
| this.hasCheckmark = angular.isDefined(this.$attrs.checked); | ||
|
|
||
| // Watch if hidden | ||
| this.$scope.$watch( | ||
| () => this.tabsCtrl.activeId, | ||
| (value) => { | ||
| this.isHidden = value !== this.id; | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| $onDestroy () { | ||
| if (this.tabsCtrl) { | ||
| this.tabsCtrl.removeItem(this); | ||
| } | ||
| } | ||
|
|
||
| $postLink () { | ||
| this.$timeout(() => | ||
| this.$element.addClass("oui-tabs-item") | ||
| ); | ||
|
|
||
| if (this.tabsCtrl) { | ||
| // Add item to parent oui-tabs and give node index | ||
| // To support ngIf directive, which delay the adding | ||
| this.tabsCtrl.addItem(this, this.getNodeIndex()); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <button class="oui-tabs-item__button" | ||
| ng-attr-aria-label="{{::$ctrl.ariaLabel}}" | ||
| ng-attr-aria-expanded="{{!$ctrl.isHidden}}" | ||
| ng-attr-aria-controls="{{::$ctrl.id}}" | ||
| ng-class="{ 'oui-tabs-item__button_checked': $ctrl.checked }" | ||
| ng-click="$ctrl.setActive()"> | ||
| <span class="oui-icon oui-icon-success" | ||
| ng-if="::$ctrl.hasCheckmark"> | ||
| </span> | ||
| <span class="oui-tabs-item__heading" role="heading" | ||
| ng-bind="::$ctrl.heading"> | ||
| </span> | ||
| <span class="oui-icon oui-icon-chevron-down oui-tabs-item__toggle-icon" aria-hidden="true"></span> | ||
| </button> | ||
| <div class="oui-tabs-item__content" role="tabpanel" | ||
| ng-attr-id="{{::$ctrl.id}}" | ||
| ng-attr-aria-hidden="{{$ctrl.isHidden}}" | ||
| ng-transclude> | ||
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import controller from "./tabs.controller"; | ||
| import template from "./tabs.html"; | ||
|
|
||
| export default { | ||
| controller, | ||
| template, | ||
| bindings: { | ||
| ariaLabel: "@?" | ||
| }, | ||
| transclude: { | ||
| item: "?ouiTabsItem" | ||
| } | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.