Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tab): Get tabs by their ID #4149

Merged
merged 6 commits into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/mdc-tab-bar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Method Signature | Description
`getTabListLength() => number` | Returns the number of child Tab components.
`getPreviousActiveTabIndex() => number` | Returns the index of the previously active Tab.
`getFocusedTabIndex() => number` | Returns the index of the focused Tab.
`getIndexOfTab(tab: MDCTab) => number` | Returns the index of the given Tab instance.
`getIndexOfTabById(id: string) => number` | Returns the index of the given Tab ID.
`notifyTabActivated(index: number) => void` | Emits the `MDCTabBar:activated` event.

### `MDCTabBarFoundation`
Expand Down
5 changes: 2 additions & 3 deletions packages/mdc-tab-bar/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

/* eslint-disable no-unused-vars */
import {MDCTabDimensions} from '@material/tab/adapter';
import {MDCTab} from '@material/tab/index';
/* eslint-enable no-unused-vars */

/**
Expand Down Expand Up @@ -134,10 +133,10 @@ class MDCTabBarAdapter {

/**
* Returns the index of the given tab
* @param {!MDCTab} tab The tab whose index to determin
* @param {string} id The ID of the tab whose index to determine
* @return {number}
*/
getIndexOfTab(tab) {}
getIndexOfTabById(id) {}

/**
* Emits the MDCTabBar:activated event
Expand Down
4 changes: 2 additions & 2 deletions packages/mdc-tab-bar/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class MDCTabBarFoundation extends MDCFoundation {
getTabDimensionsAtIndex: () => {},
getPreviousActiveTabIndex: () => {},
getFocusedTabIndex: () => {},
getIndexOfTab: () => {},
getIndexOfTabById: () => {},
getTabListLength: () => {},
notifyTabActivated: () => {},
});
Expand Down Expand Up @@ -174,7 +174,7 @@ class MDCTabBarFoundation extends MDCFoundation {
* @param {!Event} evt
*/
handleTabInteraction(evt) {
this.adapter_.setActiveTab(this.adapter_.getIndexOfTab(evt.detail.tab));
this.adapter_.setActiveTab(this.adapter_.getIndexOfTabById(evt.detail.tabId));
}

/**
Expand Down
60 changes: 44 additions & 16 deletions packages/mdc-tab-bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {MDCTabScroller} from '@material/tab-scroller/index';
import MDCTabBarAdapter from './adapter';
import MDCTabBarFoundation from './foundation';

let tabIdCounter = 0;

/**
* @extends {MDCComponent<!MDCTabBarFoundation>}
* @final
Expand All @@ -43,15 +45,9 @@ class MDCTabBar extends MDCComponent {
/** @private {!Array<!MDCTab>} */
this.tabList_;

/** @type {(function(!Element): !MDCTab)} */
this.tabFactory_;

/** @private {?MDCTabScroller} */
this.tabScroller_;

/** @type {(function(!Element): !MDCTabScroller)} */
this.tabScrollerFactory_;

/** @private {?function(?Event): undefined} */
this.handleTabInteraction_;

Expand Down Expand Up @@ -82,15 +78,8 @@ class MDCTabBar extends MDCComponent {
initialize(
tabFactory = (el) => new MDCTab(el),
tabScrollerFactory = (el) => new MDCTabScroller(el)) {
this.tabFactory_ = tabFactory;
this.tabScrollerFactory_ = tabScrollerFactory;

this.tabList_ = this.getTabElements_().map((el) => this.tabFactory_(el));

const tabScrollerElement = this.root_.querySelector(MDCTabBarFoundation.strings.TAB_SCROLLER_SELECTOR);
if (tabScrollerElement) {
this.tabScroller_ = this.tabScrollerFactory_(tabScrollerElement);
}
this.tabList_ = this.instantiateTabs_(tabFactory);
this.tabScroller_ = this.instantiateTabScroller_(tabScrollerFactory);
}

initialSyncWithDOM() {
Expand Down Expand Up @@ -147,7 +136,14 @@ class MDCTabBar extends MDCComponent {
const activeElement = document.activeElement;
return tabElements.indexOf(activeElement);
},
getIndexOfTab: (tabToFind) => this.tabList_.indexOf(tabToFind),
getIndexOfTabById: (id) => {
for (let i = 0; i < this.tabList_.length; i++) {
if (this.tabList_[i].id === id) {
return i;
}
}
return -1;
},
getTabListLength: () => this.tabList_.length,
notifyTabActivated: (index) => this.emit(MDCTabBarFoundation.strings.TAB_ACTIVATED_EVENT, {index}, true),
})
Expand All @@ -170,9 +166,41 @@ class MDCTabBar extends MDCComponent {
this.foundation_.scrollIntoView(index);
}

/**
* Returns all the tab elements in a nice clean array
* @return {!Array<!Element>}
* @private
*/
getTabElements_() {
return [].slice.call(this.root_.querySelectorAll(MDCTabBarFoundation.strings.TAB_SELECTOR));
}

/**
* Instantiates tab components on all child tab elements
* @param {(function(!Element): !MDCTab)} tabFactory
* @return {!Array<!MDCTab>}
* @private
*/
instantiateTabs_(tabFactory) {
return this.getTabElements_().map((el) => {
el.id = el.id || `mdc-tab-${++tabIdCounter}`;
return tabFactory(el);
});
}

/**
* Instantiates tab scroller component on the child tab scroller element
* @param {(function(!Element): !MDCTabScroller)} tabScrollerFactory
* @return {?MDCTabScroller}
* @private
*/
instantiateTabScroller_(tabScrollerFactory) {
const tabScrollerElement = this.root_.querySelector(MDCTabBarFoundation.strings.TAB_SCROLLER_SELECTOR);
if (tabScrollerElement) {
return tabScrollerFactory(tabScrollerElement);
}
return null;
}
}

export {MDCTabBar, MDCTabBarFoundation};
2 changes: 1 addition & 1 deletion packages/mdc-tab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Method Signature | Description

Event Name | Event Data Structure | Description
--- | --- | ---
`MDCTab:interacted` | `{"detail": {"tab": MDCTab}}` | Emitted when the Tab is interacted with, regardless of its active state. Used by parent components to know which Tab to activate.
`MDCTab:interacted` | `{"detail": {"tabId": string}}` | Emitted when the Tab is interacted with, regardless of its active state. Used by parent components to know which Tab to activate.

## Usage within Web Frameworks

Expand Down
12 changes: 11 additions & 1 deletion packages/mdc-tab/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
*/
let MDCTabDimensions;

/**
* @typedef {{
* detail: {
* tabId: string,
* },
* bubbles: boolean,
* }}
*/
let MDCTabInteractionEventType;

/**
* Adapter for MDC Tab.
*
Expand Down Expand Up @@ -112,4 +122,4 @@ class MDCTabAdapter {
focus() {}
}

export {MDCTabDimensions, MDCTabAdapter};
export {MDCTabDimensions, MDCTabInteractionEventType, MDCTabAdapter};
9 changes: 7 additions & 2 deletions packages/mdc-tab/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import MDCComponent from '@material/base/component';
/* eslint-disable no-unused-vars */
import {MDCRipple, MDCRippleFoundation, RippleCapableSurface} from '@material/ripple/index';
import {MDCTabIndicator, MDCTabIndicatorFoundation} from '@material/tab-indicator/index';
import {MDCTabAdapter, MDCTabDimensions} from './adapter';
import {MDCTabAdapter, MDCTabDimensions, MDCTabInteractionEventType} from './adapter';
/* eslint-enable no-unused-vars */

import MDCTabFoundation from './foundation';
Expand All @@ -41,6 +41,9 @@ class MDCTab extends MDCComponent {
*/
constructor(...args) {
super(...args);

/** @type {string} */
this.id;
/** @private {?MDCRipple} */
this.ripple_;
/** @private {?MDCTabIndicator} */
Expand All @@ -63,6 +66,7 @@ class MDCTab extends MDCComponent {
initialize(
rippleFactory = (el, foundation) => new MDCRipple(el, foundation),
tabIndicatorFactory = (el) => new MDCTabIndicator(el)) {
this.id = this.root_.id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the element's id guaranteed to be set by the time this runs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is guaranteed to be set. I based this off chips which is working appropriately.

const rippleSurface = this.root_.querySelector(MDCTabFoundation.strings.RIPPLE_SELECTOR);
const rippleAdapter = Object.assign(MDCRipple.createAdapter(/** @type {!RippleCapableSurface} */ (this)), {
addClass: (className) => rippleSurface.classList.add(className),
Expand Down Expand Up @@ -101,7 +105,8 @@ class MDCTab extends MDCComponent {
hasClass: (className) => this.root_.classList.contains(className),
activateIndicator: (previousIndicatorClientRect) => this.tabIndicator_.activate(previousIndicatorClientRect),
deactivateIndicator: () => this.tabIndicator_.deactivate(),
notifyInteracted: () => this.emit(MDCTabFoundation.strings.INTERACTED_EVENT, {tab: this}, true /* bubble */),
notifyInteracted: () => this.emit(
MDCTabFoundation.strings.INTERACTED_EVENT, {tabId: this.id}, true /* bubble */),
getOffsetLeft: () => this.root_.offsetLeft,
getOffsetWidth: () => this.root_.offsetWidth,
getContentOffsetLeft: () => this.content_.offsetLeft,
Expand Down
2 changes: 1 addition & 1 deletion test/unit/mdc-tab-bar/foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ test('defaultAdapter returns a complete adapter implementation', () => {
'getOffsetWidth', 'isRTL', 'setActiveTab',
'activateTabAtIndex', 'deactivateTabAtIndex', 'focusTabAtIndex',
'getTabIndicatorClientRectAtIndex', 'getTabDimensionsAtIndex',
'getPreviousActiveTabIndex', 'getFocusedTabIndex', 'getIndexOfTab', 'getTabListLength',
'getPreviousActiveTabIndex', 'getFocusedTabIndex', 'getIndexOfTabById', 'getTabListLength',
'notifyTabActivated',
]);
});
Expand Down
6 changes: 4 additions & 2 deletions test/unit/mdc-tab-bar/mdc-tab-bar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ test('attachTo returns an MDCTabBar instance', () => {
assert.isOk(MDCTabBar.attachTo(getFixture()) instanceof MDCTabBar);
});

let fakeTabIdCounter = 0;
class FakeTab {
constructor() {
this.id = `mdc-tab-${++fakeTabIdCounter}`;
this.destroy = td.function();
this.activate = td.function();
this.deactivate = td.function();
Expand Down Expand Up @@ -206,10 +208,10 @@ test('#adapter.getPreviousActiveTabIndex returns the index of the active tab', (
assert.strictEqual(component.getDefaultFoundation().adapter_.getPreviousActiveTabIndex(), 1);
});

test('#adapter.getIndexOfTab returns the index of the given tab', () => {
test('#adapter.getIndexOfTabById returns the index of the given tab', () => {
const {component} = setupTest();
const tab = component.tabList_[2];
assert.strictEqual(component.getDefaultFoundation().adapter_.getIndexOfTab(tab), 2);
assert.strictEqual(component.getDefaultFoundation().adapter_.getIndexOfTabById(tab.id), 2);
});

test('#adapter.getTabListLength returns the length of the tab list', () => {
Expand Down