Skip to content

Commit

Permalink
feat(text-field): Add outline subelement and demo for outlined text f…
Browse files Browse the repository at this point in the history
…ield (#1749)

BREAKING CHANGE: Public method `layout()` and adapter methods `getIdleOutlineStyleValue()` and `isRtl()` were added to MDCTextField. Added a new subcomponent MDCTextFieldOutline, and adapter method `getWidth()` to MDCTextFieldLabel.
  • Loading branch information
bonniezhou committed Dec 19, 2017
1 parent abbcc69 commit 4ce3582
Show file tree
Hide file tree
Showing 25 changed files with 862 additions and 27 deletions.
61 changes: 55 additions & 6 deletions demos/text-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ <h2>Full Functionality JS Component (Floating Label, Validation)</h2>
</label>
</div>
</section>

<section class="example">
<h2>Password field with validation</h2>
<div class="mdc-text-field">
Expand All @@ -148,6 +149,54 @@ <h2>Password field with validation</h2>
Must be at least 8 characters long
</p>
</section>

<section class="example">
<h2>Outlined Text Field</h2>
<div id="demo-tf-outlined-wrapper">
<div id="tf-outlined-example" class="mdc-text-field mdc-text-field--outlined" data-demo-no-auto-js>
<input required pattern=".{8,}" type="text" id="tf-outlined-input" class="mdc-text-field__input"
aria-controls="name-validation-message">
<label for="tf-outlined-input" class="mdc-text-field__label">Your Name</label>
<div class="mdc-text-field__outline">
<svg>
<path class="mdc-text-field__outline-path"/>
</svg>
</div>
<div class="mdc-text-field__idle-outline"></div>
</div>
<p class="mdc-text-field-helper-text mdc-text-field-helper-text--validation-msg"
id="name-validation-msg">
Must be at least 8 characters
</p>
</div>
<div>
<input id="outlined-disable" type="checkbox">
<label for="outlined-disable">Disabled</label>
</div>
<div>
<input id="outlined-rtl" type="checkbox">
<label for="outlined-rtl">RTL</label>
</div>
<script>
setTimeout(function() {
var tfEl = document.getElementById('tf-outlined-example');
var tf = new mdc.textField.MDCTextField(tfEl);
var wrapper = document.getElementById('demo-tf-outlined-wrapper');
document.getElementById('outlined-disable').addEventListener('change', function(evt) {
tf.disabled = evt.target.checked;
});
document.getElementById('outlined-rtl').addEventListener('change', function(evt) {
if (evt.target.checked) {
wrapper.setAttribute('dir', 'rtl');
} else {
wrapper.removeAttribute('dir');
}
tf.layout();
});
}, 0);
</script>
</section>

<section class="example">
<h2>Text Field box</h2>
<div id="demo-tf-box-wrapper">
Expand Down Expand Up @@ -194,7 +243,7 @@ <h2>Text Field box</h2>
} else {
wrapper.removeAttribute('dir');
}
tf.ripple.layout();
tf.layout();
});

document.getElementById('box-dark-theme').addEventListener('change', function(evt) {
Expand All @@ -203,7 +252,7 @@ <h2>Text Field box</h2>

document.getElementById('box-dense').addEventListener('change', function(evt) {
tfEl.classList[evt.target.checked ? 'add' : 'remove']('mdc-text-field--dense');
tf.ripple.layout();
tf.layout();
});
}, 0);
</script>
Expand Down Expand Up @@ -397,8 +446,8 @@ <h2>Full-Width Text Field and Textarea</h2>
wrapperLeading.removeAttribute('dir');
wrapperTrailing.removeAttribute('dir');
}
tfLeading.ripple.layout();
tfTrailing.ripple.layout();
tfLeading.layout();
tfTrailing.layout();
});

document.getElementById('box-dark-theme-leading-trailing').addEventListener('change', function(evt) {
Expand All @@ -408,9 +457,9 @@ <h2>Full-Width Text Field and Textarea</h2>

document.getElementById('box-dense-leading-trailing').addEventListener('change', function(evt) {
tfLeadingEl.classList[evt.target.checked ? 'add' : 'remove']('mdc-text-field--dense');
tfLeading.ripple.layout();
tfLeading.layout();
tfTrailingEl.classList[evt.target.checked ? 'add' : 'remove']('mdc-text-field--dense');
tfTrailing.ripple.layout();
tfTrailing.layout();
});

document.getElementById('box-unclickable-leading-trailing').addEventListener('change', function(evt) {
Expand Down
32 changes: 29 additions & 3 deletions packages/mdc-textfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,23 @@ Note that **full-width text fields do not support floating labels**. Labels shou
included as part of the DOM structure for full-width text fields. Full-width textareas
behave normally.

### Outlined Text Fields

```html
<div class="mdc-text-field mdc-text-field--outlined">
<input type="text" id="tf-outlined" class="mdc-text-field__input">
<label for="tf-outlined" class="mdc-text-field__label">Your Name</label>
<div class="mdc-text-field__outline">
<svg>
<path class="mdc-text-field__outline-path"/>
</svg>
</div>
<div class="mdc-text-field__idle-outline"></div>
</div>
```

See [here](outline/) for more information on using the outline sub-component.

### Text Field Boxes

```html
Expand All @@ -179,8 +196,7 @@ behave normally.
</div>
```

Note that Text field boxes support all of the same features as normal text-fields, including helper
text, validation, and dense UI.
Note that both Text Field Boxes and Outlined Text Fields support all of the same features as normal Text Fields, including helper text, validation, and dense UI.

#### CSS-only text field boxes

Expand Down Expand Up @@ -296,6 +312,10 @@ String setter. Proxies to the foundation's `setHelperTextContent` method when se
`MDCRipple` instance. Set to the `MDCRipple` instance for the root element that `MDCTextField`
initializes when given an `mdc-text-field--box` root element. Otherwise, the field is set to `null`.

##### MDCTextField.layout()

Recomputes the outline SVG path for the outline element, and recomputes all dimensions and positions for the ripple element.

### Using the foundation class

Because MDC Text Field is a feature-rich and relatively complex component, its adapter is a bit more
Expand All @@ -312,8 +332,10 @@ 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. |
| getIdleOutlineStyleValue(propertyName: string) => string | Returns the idle outline element's computed style value of the given css property `propertyName`. We achieve this via `getComputedStyle(...).getPropertyValue(propertyName)`.|
| isRtl() => boolean | Returns whether the direction of the root element is set to RTL. |

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()`.
MDC Text Field has multiple optional sub-elements: bottom line, helper text, and outline. 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 @@ -350,6 +372,10 @@ finish. Expects a transition-end event.

Sets the content of the helper text, if it exists.

##### MDCTextFieldFoundation.updateOutline()

Updates the focus outline for outlined text fields.

### Theming

MDC Text Field components use the configured theme's primary color for its underline and label text
Expand Down
6 changes: 5 additions & 1 deletion packages/mdc-textfield/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
// limitations under the License.
//

@mixin mdc-text-field-outlined-corner-radius($radius) {
border-radius: $radius;
}

@mixin mdc-text-field-box-corner-radius($radius) {
border-radius: $radius $radius 0 0;
}
Expand Down Expand Up @@ -45,7 +49,7 @@

33% {
animation-timing-function: cubic-bezier(.5, 0, .701732, .495819);
transform: translateX(10px) translateY(-#{$positionY}) scale(.75, .75);
transform: translateX(5px) translateY(-#{$positionY}) scale(.75, .75);
}

66% {
Expand Down
4 changes: 4 additions & 0 deletions packages/mdc-textfield/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ $mdc-text-field-box-disabled-background: rgba(black, .02);
$mdc-text-field-box-disabled-background-dark: rgba(white, .05);
$mdc-text-field-box-secondary-text: rgba(black, .6);

$mdc-text-field-outlined-idle-border: rgba(black, .12);
$mdc-text-field-outlined-disabled-border: rgba(black, .06);
$mdc-text-field-outlined-hover-border: rgba(black, .87);

$mdc-textarea-border-on-light: rgba(black, .73);
$mdc-textarea-border-on-dark: rgba(white, 1);
$mdc-textarea-light-background: rgba(white, 1);
Expand Down
14 changes: 14 additions & 0 deletions packages/mdc-textfield/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ class MDCTextFieldAdapter {
* @return {?Element|?NativeInputType}
*/
getNativeInput() {}

/**
* Returns the idle outline element's computed style value of the given css property `propertyName`.
* We achieve this via `getComputedStyle(...).getPropertyValue(propertyName)`.
* @param {string} propertyName
* @return {string}
*/
getIdleOutlineStyleValue(propertyName) {}

/**
* Returns true if the direction of the root element is set to RTL.
* @return {boolean}
*/
isRtl() {}
}

export {MDCTextFieldAdapter, NativeInputType, FoundationMapType};
2 changes: 2 additions & 0 deletions packages/mdc-textfield/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const strings = {
INPUT_SELECTOR: '.mdc-text-field__input',
LABEL_SELECTOR: '.mdc-text-field__label',
ICON_SELECTOR: '.mdc-text-field__icon',
IDLE_OUTLINE_SELECTOR: '.mdc-text-field__idle-outline',
OUTLINE_SELECTOR: '.mdc-text-field__outline',
BOTTOM_LINE_SELECTOR: '.mdc-text-field__bottom-line',
};

Expand Down
24 changes: 24 additions & 0 deletions packages/mdc-textfield/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import MDCTextFieldBottomLineFoundation from './bottom-line/foundation';
import MDCTextFieldHelperTextFoundation from './helper-text/foundation';
import MDCTextFieldIconFoundation from './icon/foundation';
import MDCTextFieldLabelFoundation from './label/foundation';
import MDCTextFieldOutlineFoundation from './outline/foundation';
/* eslint-enable no-unused-vars */
import {cssClasses, strings} from './constants';

Expand Down Expand Up @@ -57,6 +58,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
registerBottomLineEventHandler: () => {},
deregisterBottomLineEventHandler: () => {},
getNativeInput: () => {},
getIdleOutlineStyleValue: () => {},
isRtl: () => {},
});
}

Expand All @@ -76,6 +79,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.icon_ = foundationMap.icon;
/** @type {!MDCTextFieldLabelFoundation|undefined} */
this.label_ = foundationMap.label;
/** @type {!MDCTextFieldOutlineFoundation|undefined} */
this.outline_ = foundationMap.outline;

/** @private {boolean} */
this.isFocused_ = false;
Expand Down Expand Up @@ -142,6 +147,22 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.receivedUserInput_ = true;
}

/**
* Updates the focus outline for outlined text fields.
*/
updateOutline() {
if (!this.outline_ || !this.label_) {
return;
}
const labelWidth = this.label_.getFloatingWidth();
// Fall back to reading a specific corner's style because Firefox doesn't report the style on border-radius.
const radiusStyleValue = this.adapter_.getIdleOutlineStyleValue('border-radius') ||
this.adapter_.getIdleOutlineStyleValue('border-top-left-radius');
const radius = parseFloat(radiusStyleValue);
const isRtl = this.adapter_.isRtl();
this.outline_.updateSvgPath(labelWidth, radius, isRtl);
}

/**
* Activates the text field focus state.
*/
Expand All @@ -151,6 +172,9 @@ class MDCTextFieldFoundation extends MDCFoundation {
if (this.bottomLine_) {
this.bottomLine_.activate();
}
if (this.outline_) {
this.updateOutline();
}
if (this.label_) {
this.label_.floatAbove();
}
Expand Down

0 comments on commit 4ce3582

Please sign in to comment.