Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

chore(text-field): Split out label into subelement #1693

Merged
merged 20 commits into from
Dec 11, 2017
Merged
Show file tree
Hide file tree
Changes from 17 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: 0 additions & 2 deletions packages/mdc-textfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,6 @@ complicated.
| --- | --- |
| addClass(className: string) => void | Adds a class to the root element |
| removeClass(className: string) => void | Removes a class from the root element |
| addClassToLabel(className: string) => void | Adds a class to the label element. We recommend you add a conditional check here, and in `removeClassFromLabel` for whether or not the label is present so that the JS component could be used with text fields that don't require a label, such as the full-width text field. |
| removeClassFromLabel(className: string) => void | Removes a class from the label element |
| eventTargetHasClass(target: HTMLElement, className: string) => boolean | Returns true if classname exists for a given target element |
| registerTextFieldInteractionHandler(evtType: string, handler: EventListener) => void | Registers an event handler on the root element for a given event |
| deregisterTextFieldInteractionHandler(evtType: string, handler: EventListener) => void | Deregisters an event handler on the root element for a given event |
Expand Down
19 changes: 3 additions & 16 deletions packages/mdc-textfield/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
/* eslint-disable no-unused-vars */
import MDCTextFieldBottomLineFoundation from './bottom-line/foundation';
import MDCTextFieldHelperTextFoundation from './helper-text/foundation';
import MDCTextFieldLabelFoundation from './label/foundation';

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

Expand All @@ -34,7 +35,8 @@ let NativeInputType;
/**
* @typedef {{
* bottomLine: (!MDCTextFieldBottomLineFoundation|undefined),
* helperText: (!MDCTextFieldHelperTextFoundation|undefined)
* helperText: (!MDCTextFieldHelperTextFoundation|undefined),
* label: (!MDCTextFieldLabelFoundation|undefined)
* }}
*/
let FoundationMapType;
Expand Down Expand Up @@ -62,21 +64,6 @@ class MDCTextFieldAdapter {
*/
removeClass(className) {}

/**
* Adds a class to the label Element. We recommend you add a conditional
* check here, and in removeClassFromLabel for whether or not the label is
* present so that the JS component could be used with text fields that don't
* require a label, such as the full-width text field.
* @param {string} className
*/
addClassToLabel(className) {}

/**
* Removes a class from the label Element.
* @param {string} className
*/
removeClassFromLabel(className) {}

/**
* Sets an attribute on the icon Element.
* @param {string} name
Expand Down
2 changes: 0 additions & 2 deletions packages/mdc-textfield/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ const cssClasses = {
DISABLED: 'mdc-text-field--disabled',
FOCUSED: 'mdc-text-field--focused',
INVALID: 'mdc-text-field--invalid',
LABEL_FLOAT_ABOVE: 'mdc-text-field__label--float-above',
LABEL_SHAKE: 'mdc-text-field__label--shake',
BOX: 'mdc-text-field--box',
TEXT_FIELD_ICON: 'mdc-text-field__icon',
TEXTAREA: 'mdc-text-field--textarea',
Expand Down
36 changes: 21 additions & 15 deletions packages/mdc-textfield/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import MDCFoundation from '@material/base/foundation';
import {MDCTextFieldAdapter, NativeInputType, FoundationMapType} from './adapter';
import MDCTextFieldBottomLineFoundation from './bottom-line/foundation';
// eslint-disable-next-line no-unused-vars
/* eslint-disable no-unused-vars */
import MDCTextFieldHelperTextFoundation from './helper-text/foundation';
import MDCTextFieldLabelFoundation from './label/foundation';
/* eslint-enable no-unused-vars */
import {cssClasses, strings} from './constants';


Expand Down Expand Up @@ -47,8 +49,6 @@ class MDCTextFieldFoundation extends MDCFoundation {
return /** @type {!MDCTextFieldAdapter} */ ({
addClass: () => {},
removeClass: () => {},
addClassToLabel: () => {},
removeClassFromLabel: () => {},
setIconAttr: () => {},
eventTargetHasClass: () => {},
registerTextFieldInteractionHandler: () => {},
Expand All @@ -74,6 +74,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.bottomLine_ = foundationMap.bottomLine;
/** @type {!MDCTextFieldHelperTextFoundation|undefined} */
this.helperText_ = foundationMap.helperText;
/** @type {!MDCTextFieldLabelFoundation|undefined} */
this.label_ = foundationMap.label;

/** @private {boolean} */
this.isFocused_ = false;
Expand All @@ -98,8 +100,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
init() {
this.adapter_.addClass(MDCTextFieldFoundation.cssClasses.UPGRADED);
// Ensure label does not collide with any pre-filled value.
if (this.getNativeInput_().value) {
this.adapter_.addClassToLabel(MDCTextFieldFoundation.cssClasses.LABEL_FLOAT_ABOVE);
if (this.getNativeInput_().value && this.label_) {
this.label_.floatAbove();
}

this.adapter_.registerInputInteractionHandler('focus', this.inputFocusHandler_);
Expand Down Expand Up @@ -155,13 +157,14 @@ class MDCTextFieldFoundation extends MDCFoundation {
* Activates the text field focus state.
*/
activateFocus() {
const {FOCUSED, LABEL_FLOAT_ABOVE, LABEL_SHAKE} = MDCTextFieldFoundation.cssClasses;
const {FOCUSED} = MDCTextFieldFoundation.cssClasses;
this.adapter_.addClass(FOCUSED);
if (this.bottomLine_) {
this.bottomLine_.activate();
}
this.adapter_.addClassToLabel(LABEL_FLOAT_ABOVE);
this.adapter_.removeClassFromLabel(LABEL_SHAKE);
if (this.label_) {
this.label_.floatAbove();
}
if (this.helperText_) {
this.helperText_.showToScreenReader();
}
Expand Down Expand Up @@ -206,15 +209,16 @@ class MDCTextFieldFoundation extends MDCFoundation {
* Deactives the Text Field's focus state.
*/
deactivateFocus() {
const {FOCUSED, LABEL_FLOAT_ABOVE, LABEL_SHAKE} = MDCTextFieldFoundation.cssClasses;
const {FOCUSED} = MDCTextFieldFoundation.cssClasses;
const input = this.getNativeInput_();
const shouldRemoveLabelFloat = !input.value && !this.isBadInput_();

this.isFocused_ = false;
this.adapter_.removeClass(FOCUSED);
this.adapter_.removeClassFromLabel(LABEL_SHAKE);

if (!input.value && !this.isBadInput_()) {
this.adapter_.removeClassFromLabel(LABEL_FLOAT_ABOVE);
if (this.label_) {
this.label_.deactivateFocus(shouldRemoveLabelFloat);
}
if (shouldRemoveLabelFloat) {
this.receivedUserInput_ = false;
}

Expand All @@ -229,16 +233,18 @@ class MDCTextFieldFoundation extends MDCFoundation {
* @private
*/
changeValidity_(isValid) {
const {INVALID, LABEL_SHAKE} = MDCTextFieldFoundation.cssClasses;
const {INVALID} = MDCTextFieldFoundation.cssClasses;
if (isValid) {
this.adapter_.removeClass(INVALID);
} else {
this.adapter_.addClassToLabel(LABEL_SHAKE);
this.adapter_.addClass(INVALID);
}
if (this.helperText_) {
this.helperText_.setValidity(isValid);
}
if (this.label_) {
this.label_.setValidity(isValid);
}
}

/**
Expand Down
24 changes: 10 additions & 14 deletions packages/mdc-textfield/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import MDCTextFieldFoundation from './foundation';
/* eslint-disable no-unused-vars */
import {MDCTextFieldBottomLine, MDCTextFieldBottomLineFoundation} from './bottom-line';
import {MDCTextFieldHelperText, MDCTextFieldHelperTextFoundation} from './helper-text';
import {MDCTextFieldLabel, MDCTextFieldLabelFoundation} from './label';
/* eslint-enable no-unused-vars */

/**
Expand All @@ -40,7 +41,7 @@ class MDCTextField extends MDCComponent {
super(...args);
/** @private {?Element} */
this.input_;
/** @private {?Element} */
/** @private {?MDCTextFieldLabel} */
this.label_;
/** @type {?MDCRipple} */
this.ripple;
Expand Down Expand Up @@ -70,7 +71,10 @@ class MDCTextField extends MDCComponent {
rippleFactory = (el, foundation) => new MDCRipple(el, foundation),
bottomLineFactory = (el) => new MDCTextFieldBottomLine(el)) {
this.input_ = this.root_.querySelector(strings.INPUT_SELECTOR);
this.label_ = this.root_.querySelector(strings.LABEL_SELECTOR);
const labelElement = this.root_.querySelector(strings.LABEL_SELECTOR);
if (labelElement) {
this.label_ = new MDCTextFieldLabel(labelElement);
}
this.ripple = null;
if (this.root_.classList.contains(cssClasses.BOX)) {
const MATCHES = getMatchesProperty(HTMLElement.prototype);
Expand Down Expand Up @@ -109,6 +113,9 @@ class MDCTextField extends MDCComponent {
if (this.helperText_) {
this.helperText_.destroy();
}
if (this.label_) {
this.label_.destroy();
}
super.destroy();
}

Expand Down Expand Up @@ -157,18 +164,6 @@ class MDCTextField extends MDCComponent {
/** @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),
Expand Down Expand Up @@ -227,6 +222,7 @@ class MDCTextField extends MDCComponent {
return {
bottomLine: this.bottomLine_ ? this.bottomLine_.foundation : undefined,
helperText: this.helperText_ ? this.helperText_.foundation : undefined,
label: this.label_ ? this.label_.foundation : undefined,
};
}
}
Expand Down
50 changes: 50 additions & 0 deletions packages/mdc-textfield/label/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!--docs:
title: "Text Field Label"
layout: detail
section: components
excerpt: "The label is a text caption or description for the text field."
iconId: text_field
path: /catalog/input-controls/text-fields/label/
-->

# Text Field Label

The label is a text caption or description for the text field.

## Design & API Documentation

<ul class="icon-list">
Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't catch this when I looked at earlier PRs, but I'm wondering whether it really makes sense to have this heading in the subcomponents' readmes.

<li class="icon-list-item icon-list-item--spec">
<a href="https://material.io/guidelines/components/text-fields.html#text-fields-layout">Material Design guidelines: Text Fields Layout</a>
</li>
</ul>


## Usage

#### MDCTextFieldLabel API
Copy link
Contributor

Choose a reason for hiding this comment

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

These heading levels don't make sense to me... Usage is h2, this is h4, foundation is h5, but then "using the foundation class" is h3?

Now that I look, the same applies to #1697. Maybe we should have a task to iron out the subcomponents' readme formats once they all exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Filed #1707.


##### MDCTextFieldLabel.foundation

MDCTextFieldLabelFoundation. This allows the parent MDCTextField component to access the public methods on the MDCTextFieldLabelFoundation class.

### Using the foundation class
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as on #1697 - this is documenting the adapter, so we should re-word the heading or add a sentence.


Method Signature | Description
--- | ---
addClass(className: string) => void | Adds a class to the label element
removeClass(className: string) => void | Removes a class from the label element

#### The full foundation API

##### MDCTextFieldLabelFoundation.floatAbove()

Makes the label float above the text field.

##### MDCTextFieldLabelFoundation.deactivateFocus(shouldRemoveLabelFloat: boolean)

Deactivates the label's focus state.
Copy link
Contributor

Choose a reason for hiding this comment

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

We should still explain what the parameter does here, e.g. "shouldRemoveLabelFloat indicates whether to also reset the label's position and size.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!


##### MDCTextFieldLabelFoundation.setValidity(isValid: boolean)

Updates the label's valid state based on the supplied validity.
44 changes: 44 additions & 0 deletions packages/mdc-textfield/label/adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

/**
* Adapter for MDC Text Field Label.
*
* Defines the shape of the adapter expected by the foundation. Implement this
* adapter to integrate the Text Field label into your framework. See
* https://github.com/material-components/material-components-web/blob/master/docs/authoring-components.md
* for more information.
*
* @record
*/
class MDCTextFieldLabelAdapter {
/**
* Adds a class to the label element.
* @param {string} className
*/
addClass(className) {}

/**
* Removes a class from the label element.
* @param {string} className
*/
removeClass(className) {}
}

export default MDCTextFieldLabelAdapter;
24 changes: 24 additions & 0 deletions packages/mdc-textfield/label/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @license
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/** @enum {string} */
const cssClasses = {
LABEL_FLOAT_ABOVE: 'mdc-text-field__label--float-above',
LABEL_SHAKE: 'mdc-text-field__label--shake',
};

export {cssClasses};
Loading