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): Add MDCTabDimensions computation method #3122

Merged
merged 2 commits into from
Jul 18, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 14 additions & 7 deletions packages/mdc-tab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ Property | Value Type | Description

Method Signature | Description
--- | ---
`activate(previousIndicatorClientRect: ClientRect=) => void` | Activates the indicator. `previousIndicatorClientRect` is an optional argument
`deactivate() => void` | Deactivates the indicator
`activate(previousIndicatorClientRect: ClientRect=) => void` | Activates the indicator. `previousIndicatorClientRect` is an optional argument.
`deactivate() => void` | Deactivates the indicator.
`computeIndicatorClientRect() => ClientRect` | Returns the bounding client rect of the indicator.
`computeDimensions() => MDCTabDimensions` | Returns the dimensions of the Tab.


### `MDCTabAdapter`
Expand All @@ -108,13 +110,18 @@ Method Signature | Description
`activateIndicator(previousIndicatorClientRect: ClientRect=) => void` | Activates the tab indicator subcomponent. `previousIndicatorClientRect` is an optional argument
`deactivateIndicator() => void` | Deactivates the tab indicator subcomponent
`computeIndicatorClientRect() => ClientRect` | Returns the tab indicator subcomponent's content bounding client rect
`getOffsetLeft() => number` | Returns the `offsetLeft` value of the root element
`getOffsetWidth() => number` | Returns the `offsetWidth` value of the root element
`getContentOffsetLeft() => number` | Returns the `offsetLeft` value of the content element
`getContentOffsetWidth() => number` | Returns the `offsetWidth` value of the content element

### `MDCTabFoundation`

Method Signature | Description
--- | ---
`handleTransitionEnd(evt: Event) => void` | Handles the logic for the `"transitionend"` event
`isActive() => boolean` | Returns whether the tab is active
`activate(previousIndicatorClientRect: ClientRect=) => void` | Activates the tab. `previousIndicatorClientRect` is an optional argument
`deactivate() => void` | Deactivates the tab
`computeIndicatorClientRect() => ClientRect` | Returns the tab indicator subcomponent's content bounding client rect
`handleTransitionEnd(evt: Event) => void` | Handles the logic for the `"transitionend"` event.
`isActive() => boolean` | Returns whether the tab is active.
`activate(previousIndicatorClientRect: ClientRect=) => void` | Activates the tab. `previousIndicatorClientRect` is an optional argument.
`deactivate() => void` | Deactivates the tab.
`computeIndicatorClientRect() => ClientRect` | Returns the tab indicator subcomponent's content bounding client rect.
`computeDimensions() => MDCTabDimensions` | Returns the dimensions of the tab.
38 changes: 35 additions & 3 deletions packages/mdc-tab/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@

/* eslint no-unused-vars: [2, {"args": "none"}] */

/**
* MDCTabDimensions provides details about the left and right edges of the Tab
* root element and the Tab content element. These values are used to determine
* the visual position of the Tab with respect it's parent container.
* @typedef {{rootLeft: number, rootRight: number, contentLeft: number, contentRight: number}}
*/
let MDCTabDimensions;

/**
* Adapter for MDC Tab.
*
Expand Down Expand Up @@ -74,14 +82,38 @@ class MDCTabAdapter {
*/
activateIndicator(previousIndicatorClientRect) {}

/** Deactivates the indicator */
/** Deactivates the indicator. */
deactivateIndicator() {}

/**
* Returns the client rect of the indicator
* Returns the client rect of the indicator.
* @return {!ClientRect}
*/
computeIndicatorClientRect() {}

/**
* Returns the offsetLeft value of the root element.
* @return {number}
*/
getOffsetLeft() {}

/**
* Returns the offsetWidth value of the root element.
* @return {number}
*/
getOffsetWidth() {}

/**
* Returns the offsetLeft of the content element.
* @return {number}
*/
getContentOffsetLeft() {}

/**
* Returns the offsetWidth of the content element.
* @return {number}
*/
getContentOffsetWidth() {}
}

export default MDCTabAdapter;
export {MDCTabDimensions, MDCTabAdapter};
1 change: 1 addition & 0 deletions packages/mdc-tab/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const cssClasses = {
const strings = {
ARIA_SELECTED: 'aria-selected',
RIPPLE_SELECTOR: '.mdc-tab__ripple',
CONTENT_SELECTOR: '.mdc-tab__content',
TAB_INDICATOR_SELECTOR: '.mdc-tab-indicator',
TABINDEX: 'tabIndex',
};
Expand Down
28 changes: 27 additions & 1 deletion packages/mdc-tab/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
*/

import MDCFoundation from '@material/base/foundation';
import MDCTabAdapter from './adapter';

/* eslint-disable no-unused-vars */
import {MDCTabAdapter, MDCTabDimensions} from './adapter';
/* eslint-enable no-unused-vars */

import {
cssClasses,
strings,
Expand Down Expand Up @@ -52,6 +56,10 @@ class MDCTabFoundation extends MDCFoundation {
activateIndicator: () => {},
deactivateIndicator: () => {},
computeIndicatorClientRect: () => {},
getOffsetLeft: () => {},
getOffsetWidth: () => {},
getContentOffsetLeft: () => {},
getContentOffsetWidth: () => {},
});
}

Expand Down Expand Up @@ -127,6 +135,24 @@ class MDCTabFoundation extends MDCFoundation {
computeIndicatorClientRect() {
return this.adapter_.computeIndicatorClientRect();
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason the foundation actually needs this (thus necessitating additions to foundation, adapter, and component), or could it actually just be done entirely at the component level? This is basically just doing straight-up DOM math. I'm not sure I'd even qualify that as "business logic".

/**
* Returns the dimensions of the Tab
* @return {!MDCTabDimensions}
*/
computeDimensions() {
const rootWidth = this.adapter_.getOffsetWidth();
const rootLeft = this.adapter_.getOffsetLeft();
const contentWidth = this.adapter_.getContentOffsetWidth();
const contentLeft = this.adapter_.getContentOffsetLeft();

return {
rootLeft,
rootRight: rootLeft + rootWidth,
contentLeft: rootLeft + contentLeft,
contentRight: rootLeft + contentLeft + contentWidth,
};
}
}

export default MDCTabFoundation;
17 changes: 16 additions & 1 deletion packages/mdc-tab/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ 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';
/* eslint-enable no-unused-vars */

import MDCTabAdapter from './adapter';
import MDCTabFoundation from './foundation';

/**
Expand All @@ -39,6 +39,8 @@ class MDCTab extends MDCComponent {
this.ripple_;
/** @private {?MDCTabIndicator} */
this.tabIndicator_;
/** @private {?Element} */
this.content_;
}

/**
Expand All @@ -63,6 +65,8 @@ class MDCTab extends MDCComponent {

const tabIndicatorElement = this.root_.querySelector(MDCTabFoundation.strings.TAB_INDICATOR_SELECTOR);
this.tabIndicator_ = tabIndicatorFactory(tabIndicatorElement);

this.content_ = this.root_.querySelector(MDCTabFoundation.strings.CONTENT_SELECTOR);
}

destroy() {
Expand All @@ -85,6 +89,10 @@ class MDCTab extends MDCComponent {
activateIndicator: (previousIndicatorClientRect) => this.tabIndicator_.activate(previousIndicatorClientRect),
deactivateIndicator: () => this.tabIndicator_.deactivate(),
computeIndicatorClientRect: () => this.tabIndicator_.computeContentClientRect(),
getOffsetLeft: () => this.root_.offsetLeft,
getOffsetWidth: () => this.root_.offsetWidth,
getContentOffsetLeft: () => this.content_.offsetLeft,
getContentOffsetWidth: () => this.content_.offsetWidth,
}));
}

Expand Down Expand Up @@ -118,6 +126,13 @@ class MDCTab extends MDCComponent {
computeIndicatorClientRect() {
return this.foundation_.computeIndicatorClientRect();
}

/**
* @return {!MDCTabDimensions}
*/
computeDimensions() {
return this.foundation_.computeDimensions();
}
}

export {MDCTab, MDCTabFoundation};
15 changes: 15 additions & 0 deletions test/unit/mdc-tab/foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ test('defaultAdapter returns a complete adapter implementation', () => {
'addClass', 'removeClass', 'hasClass',
'setAttr',
'activateIndicator', 'deactivateIndicator', 'computeIndicatorClientRect',
'getOffsetLeft', 'getOffsetWidth', 'getContentOffsetLeft', 'getContentOffsetWidth',
]);
});

Expand Down Expand Up @@ -176,3 +177,17 @@ test('on transitionend, do nothing when triggered by a pseudeo element', () => {
td.verify(mockAdapter.removeClass(MDCTabFoundation.cssClasses.ANIMATING_DEACTIVATE), {times: 0});
td.verify(mockAdapter.deregisterEventHandler('transitionend', td.matchers.isA(Function)), {times: 0});
});

test('#computeDimensions() returns the dimensions of the tab', () => {
const {foundation, mockAdapter} = setupTest();
td.when(mockAdapter.getOffsetLeft()).thenReturn(10);
td.when(mockAdapter.getOffsetWidth()).thenReturn(100);
td.when(mockAdapter.getContentOffsetLeft()).thenReturn(11);
td.when(mockAdapter.getContentOffsetWidth()).thenReturn(30);
assert.deepEqual(foundation.computeDimensions(), {
rootLeft: 10,
rootRight: 110,
contentLeft: 21,
contentRight: 51,
});
});
29 changes: 28 additions & 1 deletion test/unit/mdc-tab/mdc-tab.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ test('attachTo returns an MDCTab instance', () => {

function setupTest() {
const root = getFixture();
const content = root.querySelector(MDCTabFoundation.strings.CONTENT_SELECTOR);
const component = new MDCTab(root);
return {root, component};
return {root, content, component};
}

test('#destroy removes the ripple', () => {
Expand Down Expand Up @@ -121,6 +122,26 @@ test('#adapter.computeIndicatorClientRect returns the indicator element\'s bound
);
});

test('#adapter.getOffsetWidth() returns the offsetWidth of the root element', () => {
const {root, component} = setupTest();
assert.strictEqual(component.getDefaultFoundation().adapter_.getOffsetWidth(), root.offsetWidth);
});

test('#adapter.getOffsetLeft() returns the offsetLeft of the root element', () => {
const {root, component} = setupTest();
assert.strictEqual(component.getDefaultFoundation().adapter_.getOffsetLeft(), root.offsetLeft);
});

test('#adapter.getContentOffsetWidth() returns the offsetLeft of the content element', () => {
const {content, component} = setupTest();
assert.strictEqual(component.getDefaultFoundation().adapter_.getContentOffsetWidth(), content.offsetWidth);
});

test('#adapter.getContentOffsetLeft() returns the offsetLeft of the content element', () => {
const {content, component} = setupTest();
assert.strictEqual(component.getDefaultFoundation().adapter_.getContentOffsetLeft(), content.offsetLeft);
});

function setupMockFoundationTest(root = getFixture()) {
const MockFoundationConstructor = td.constructor(MDCTabFoundation);
const mockFoundation = new MockFoundationConstructor();
Expand Down Expand Up @@ -157,3 +178,9 @@ test('#computeIndicatorClientRect() calls computeIndicatorClientRect', () => {
component.computeIndicatorClientRect();
td.verify(mockFoundation.computeIndicatorClientRect(), {times: 1});
});

test('#computeDimensions() calls computeDimensions', () => {
const {component, mockFoundation} = setupMockFoundationTest();
component.computeDimensions();
td.verify(mockFoundation.computeDimensions(), {times: 1});
});