Skip to content

Commit

Permalink
chore(text-field): Pass subelement foundations through MDCTextField s…
Browse files Browse the repository at this point in the history
…uper constructor (#1684)

BREAKING CHANGE: Please update implementations of MDCTextField to pass in a map of subfoundations to the MDCTextFieldFoundation constructor. Methods getBottomLineFoundation() and getHelperTextFoundation() are no longer in MDCTextFieldAdapter. See the README for mdc-textfield/input for more information.
  • Loading branch information
bonniezhou committed Dec 5, 2017
1 parent a7505db commit 80223f2
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 133 deletions.
20 changes: 10 additions & 10 deletions packages/mdc-textfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ since it won't be added until that JS runs, adding it manually will prevent an i

### Validation

MDC TextField provides validity styling by using the `:invalid` and `:required` attributes provided
MDC Text Field provides validity styling by using the `:invalid` and `:required` attributes provided
by HTML5's form validation API.

```html
Expand All @@ -114,7 +114,7 @@ by HTML5's form validation API.
```

By default an input's validity is checked via `checkValidity()` on blur, and the styles are updated
accordingly. Set the MDCTextField.valid field to set the input's validity explicitly. MDC TextField
accordingly. Set the MDCTextField.valid field to set the input's validity explicitly. MDC Text Field
automatically appends an asterisk to the label text if the required attribute is set.

### Leading and Trailing Icons
Expand Down Expand Up @@ -175,8 +175,8 @@ do anything unexpected.
<div class="mdc-text-field mdc-text-field--fullwidth">
<input class="mdc-text-field__input"
type="text"
placeholder="Full-Width TextField"
aria-label="Full-Width TextField">
placeholder="Full-Width Text Field"
aria-label="Full-Width Text Field">
</div>

<div class="mdc-text-field mdc-text-field--fullwidth mdc-text-field--textarea">
Expand Down Expand Up @@ -227,7 +227,7 @@ This mixin customizes the border radius for a Text Field `textarea`.

### Using the JS component

MDC TextField ships with Component / Foundation classes which are used to provide a full-fidelity
MDC Text Field ships with Component / Foundation classes which are used to provide a full-fidelity
Material Design text field component.

#### Including in code
Expand Down Expand Up @@ -318,7 +318,7 @@ initializes when given an `mdc-text-field--box` root element. Otherwise, the fie

### Using the foundation class

Because MDC TextField is a feature-rich and relatively complex component, its adapter is a bit more
Because MDC Text Field is a feature-rich and relatively complex component, its adapter is a bit more
complicated.

| Method Signature | Description |
Expand All @@ -336,8 +336,8 @@ complicated.
| registerBottomLineEventHandler(evtType: string, handler: EventListener) => void | Registers an event listener on the bottom line element for a given event |
| deregisterBottomLineEventHandler(evtType: string, handler: EventListener) => void | Deregisters an event listener on the bottom line element for a given event |
| getNativeInput() => {value: string, disabled: boolean, badInput: boolean, checkValidity: () => boolean}? | Returns an object representing the native text input element, with a similar API shape. The object returned should include the `value`, `disabled` and `badInput` properties, as well as the `checkValidity()` function. We _never_ alter the value within our code, however we _do_ update the disabled property, so if you choose to duck-type the return value for this method in your implementation it's important to keep this in mind. Also note that this method can return null, which the foundation will handle gracefully. |
| getBottomLineFoundation() => MDCTextFieldBottomLineFoundation | Returns the instance of the bottom line element's foundation |
| getHelperTextFoundation() => MDCTextFieldHelperTextFoundation | Returns the instance of the helper text element's foundation |

MDC Text Field has multiple optional sub-elements: bottom line and helper text. The foundations of these sub-elements must be passed in as constructor arguments for the `MDCTextField` foundation. Since the `MDCTextField` component takes care of creating its foundation, we need to pass sub-element foundations through the `MDCTextField` component. This is typically done in the component's implementation of `getDefaultFoundation()`.

#### The full foundation API

Expand Down Expand Up @@ -376,7 +376,7 @@ Sets the content of the helper text, if it exists.

### Theming

MDC TextField components use the configured theme's primary color for its underline and label text
MDC Text Field components use the configured theme's primary color for its underline and label text
when the input is focused.

MDC TextField components support dark themes.
MDC Text Field components support dark themes.
24 changes: 9 additions & 15 deletions packages/mdc-textfield/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ import MDCTextFieldHelperTextFoundation from './helper-text/foundation';
*/
let NativeInputType;

/**
* @typedef {{
* bottomLine: (!MDCTextFieldBottomLineFoundation|undefined),
* helperText: (!MDCTextFieldHelperTextFoundation|undefined)
* }}
*/
let FoundationMapType;

/**
* Adapter for MDC Text Field.
*
Expand Down Expand Up @@ -142,20 +150,6 @@ class MDCTextFieldAdapter {
* @return {?Element|?NativeInputType}
*/
getNativeInput() {}

/**
* Returns the foundation for the bottom line element. Returns undefined if
* there is no bottom line element.
* @return {?MDCTextFieldBottomLineFoundation}
*/
getBottomLineFoundation() {}

/**
* Returns the foundation for the helper text element. Returns undefined if
* there is no helper text element.
* @return {?MDCTextFieldHelperTextFoundation}
*/
getHelperTextFoundation() {}
}

export {MDCTextFieldAdapter, NativeInputType};
export {MDCTextFieldAdapter, NativeInputType, FoundationMapType};
2 changes: 1 addition & 1 deletion packages/mdc-textfield/bottom-line/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class MDCTextFieldBottomLine extends MDCComponent {
}

/**
* @return {MDCTextFieldBottomLineFoundation}
* @return {!MDCTextFieldBottomLineFoundation}
*/
get foundation() {
return this.foundation_;
Expand Down
45 changes: 23 additions & 22 deletions packages/mdc-textfield/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
*/

import MDCFoundation from '@material/base/foundation';
import {MDCTextFieldAdapter, NativeInputType} from './adapter';
import {MDCTextFieldAdapter, NativeInputType, FoundationMapType} from './adapter';
import MDCTextFieldBottomLineFoundation from './bottom-line/foundation';
// eslint-disable-next-line no-unused-vars
import MDCTextFieldHelperTextFoundation from './helper-text/foundation';
import {cssClasses, strings} from './constants';


Expand Down Expand Up @@ -57,17 +59,22 @@ class MDCTextFieldFoundation extends MDCFoundation {
registerBottomLineEventHandler: () => {},
deregisterBottomLineEventHandler: () => {},
getNativeInput: () => {},
getBottomLineFoundation: () => {},
getHelperTextFoundation: () => {},
});
}

/**
* @param {!MDCTextFieldAdapter=} adapter
* @param {!FoundationMapType=} foundationMap Map from subcomponent names to their subfoundations.
*/
constructor(adapter = /** @type {!MDCTextFieldAdapter} */ ({})) {
constructor(adapter = /** @type {!MDCTextFieldAdapter} */ ({}),
foundationMap = /** @type {!FoundationMapType} */ ({})) {
super(Object.assign(MDCTextFieldFoundation.defaultAdapter, adapter));

/** @type {!MDCTextFieldBottomLineFoundation|undefined} */
this.bottomLine_ = foundationMap.bottomLine;
/** @type {!MDCTextFieldHelperTextFoundation|undefined} */
this.helperText_ = foundationMap.helperText;

/** @private {boolean} */
this.isFocused_ = false;
/** @private {boolean} */
Expand Down Expand Up @@ -150,15 +157,13 @@ class MDCTextFieldFoundation extends MDCFoundation {
activateFocus() {
const {FOCUSED, LABEL_FLOAT_ABOVE, LABEL_SHAKE} = MDCTextFieldFoundation.cssClasses;
this.adapter_.addClass(FOCUSED);
const bottomLine = this.adapter_.getBottomLineFoundation();
if (bottomLine) {
bottomLine.activate();
if (this.bottomLine_) {
this.bottomLine_.activate();
}
this.adapter_.addClassToLabel(LABEL_FLOAT_ABOVE);
this.adapter_.removeClassFromLabel(LABEL_SHAKE);
const helperText = this.adapter_.getHelperTextFoundation();
if (helperText) {
helperText.showToScreenReader();
if (this.helperText_) {
this.helperText_.showToScreenReader();
}
this.isFocused_ = true;
}
Expand All @@ -169,9 +174,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
* @param {!Event} evt
*/
setBottomLineTransformOrigin(evt) {
const bottomLine = this.adapter_.getBottomLineFoundation();
if (bottomLine) {
bottomLine.setTransformOrigin(evt);
if (this.bottomLine_) {
this.bottomLine_.setTransformOrigin(evt);
}
}

Expand All @@ -190,12 +194,11 @@ class MDCTextFieldFoundation extends MDCFoundation {
* for animations to finish.
*/
handleBottomLineAnimationEnd() {
const bottomLine = this.adapter_.getBottomLineFoundation();
// We need to wait for the bottom line to be entirely transparent
// before removing the class. If we do not, we see the line start to
// scale down before disappearing
if (!this.isFocused_ && bottomLine) {
bottomLine.deactivate();
if (!this.isFocused_ && this.bottomLine_) {
this.bottomLine_.deactivate();
}
}

Expand Down Expand Up @@ -233,9 +236,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.adapter_.addClassToLabel(LABEL_SHAKE);
this.adapter_.addClass(INVALID);
}
const helperText = this.adapter_.getHelperTextFoundation();
if (helperText) {
helperText.setValidity(isValid);
if (this.helperText_) {
this.helperText_.setValidity(isValid);
}
}

Expand Down Expand Up @@ -275,9 +277,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
* @param {string} content Sets the content of the helper text.
*/
setHelperTextContent(content) {
const helperText = this.adapter_.getHelperTextFoundation();
if (helperText) {
helperText.setContent(content);
if (this.helperText_) {
this.helperText_.setContent(content);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/mdc-textfield/helper-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class MDCTextFieldHelperText extends MDCComponent {
}

/**
* @return {MDCTextFieldHelperTextFoundation}
* @return {!MDCTextFieldHelperTextFoundation}
*/
get foundation() {
return this.foundation_;
Expand Down
95 changes: 49 additions & 46 deletions packages/mdc-textfield/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import {getMatchesProperty} from '@material/ripple/util';


import {cssClasses, strings} from './constants';
import {MDCTextFieldAdapter} from './adapter';
import {MDCTextFieldAdapter, FoundationMapType} from './adapter';
import MDCTextFieldFoundation from './foundation';
import {MDCTextFieldBottomLine} from './bottom-line';
import {MDCTextFieldHelperText} from './helper-text';
/* eslint-disable no-unused-vars */
import {MDCTextFieldBottomLine, MDCTextFieldBottomLineFoundation} from './bottom-line';
import {MDCTextFieldHelperText, MDCTextFieldHelperTextFoundation} from './helper-text';
/* eslint-enable no-unused-vars */

/**
* @extends {MDCComponent<!MDCTextFieldFoundation>}
Expand Down Expand Up @@ -151,50 +153,40 @@ class MDCTextField extends MDCComponent {
* @return {!MDCTextFieldFoundation}
*/
getDefaultFoundation() {
return new MDCTextFieldFoundation(/** @type {!MDCTextFieldAdapter} */ (Object.assign({
addClass: (className) => this.root_.classList.add(className),
removeClass: (className) => this.root_.classList.remove(className),
addClassToLabel: (className) => {
const label = this.label_;
if (label) {
label.classList.add(className);
}
},
removeClassFromLabel: (className) => {
const label = this.label_;
if (label) {
label.classList.remove(className);
}
},
eventTargetHasClass: (target, className) => target.classList.contains(className),
registerTextFieldInteractionHandler: (evtType, handler) => this.root_.addEventListener(evtType, handler),
deregisterTextFieldInteractionHandler: (evtType, handler) => this.root_.removeEventListener(evtType, handler),
notifyIconAction: () => this.emit(MDCTextFieldFoundation.strings.ICON_EVENT, {}),
registerBottomLineEventHandler: (evtType, handler) => {
if (this.bottomLine_) {
this.bottomLine_.listen(evtType, handler);
}
},
deregisterBottomLineEventHandler: (evtType, handler) => {
if (this.bottomLine_) {
this.bottomLine_.unlisten(evtType, handler);
}
},
getBottomLineFoundation: () => {
if (this.bottomLine_) {
return this.bottomLine_.foundation;
}
return undefined;
return new MDCTextFieldFoundation(
/** @type {!MDCTextFieldAdapter} */ (Object.assign({
addClass: (className) => this.root_.classList.add(className),
removeClass: (className) => this.root_.classList.remove(className),
addClassToLabel: (className) => {
const label = this.label_;
if (label) {
label.classList.add(className);
}
},
removeClassFromLabel: (className) => {
const label = this.label_;
if (label) {
label.classList.remove(className);
}
},
eventTargetHasClass: (target, className) => target.classList.contains(className),
registerTextFieldInteractionHandler: (evtType, handler) => this.root_.addEventListener(evtType, handler),
deregisterTextFieldInteractionHandler: (evtType, handler) => this.root_.removeEventListener(evtType, handler),
notifyIconAction: () => this.emit(MDCTextFieldFoundation.strings.ICON_EVENT, {}),
registerBottomLineEventHandler: (evtType, handler) => {
if (this.bottomLine_) {
this.bottomLine_.listen(evtType, handler);
}
},
deregisterBottomLineEventHandler: (evtType, handler) => {
if (this.bottomLine_) {
this.bottomLine_.unlisten(evtType, handler);
}
},
},
getHelperTextFoundation: () => {
if (this.helperText_) {
return this.helperText_.foundation;
}
return undefined;
},
},
this.getInputAdapterMethods_(),
this.getIconAdapterMethods_())));
this.getInputAdapterMethods_(),
this.getIconAdapterMethods_())),
this.getFoundationMap_());
}

/**
Expand Down Expand Up @@ -226,6 +218,17 @@ class MDCTextField extends MDCComponent {
getNativeInput: () => this.input_,
};
}

/**
* Returns a map of all subcomponents to subfoundations.
* @return {!FoundationMapType}
*/
getFoundationMap_() {
return {
bottomLine: this.bottomLine_ ? this.bottomLine_.foundation : undefined,
helperText: this.helperText_ ? this.helperText_.foundation : undefined,
};
}
}

export {MDCTextField, MDCTextFieldFoundation};
Loading

0 comments on commit 80223f2

Please sign in to comment.