Skip to content

Commit

Permalink
feat(textfield): Implement text field boxes
Browse files Browse the repository at this point in the history
Resolves #673
  • Loading branch information
traviskaufman committed Jun 8, 2017
1 parent 38adbb2 commit 89be862
Show file tree
Hide file tree
Showing 7 changed files with 397 additions and 24 deletions.
81 changes: 79 additions & 2 deletions demos/textfield.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
<style>
.mdc-theme--dark {
--mdc-theme-primary: var(--mdc-theme-accent, #64dd17);
background-color: #303030;
}

Expand Down Expand Up @@ -66,7 +67,13 @@
</div>
</section>

<section class="example interactive-demo">
<section class="example"><em>
Note that in browsers that support custom properties, we alter theme's primary
color when using the dark theme toggles so that the text fields appears more
visible.
</em></section>

<section class="example">
<h2>Full Functionality JS Component (Floating Label, Validation, Autocomplete)</h2>
<section id="demo-textfield-wrapper">
<div class="mdc-textfield">
Expand Down Expand Up @@ -129,7 +136,67 @@ <h2>Password field with validation</h2>
</p>
</section>
<section class="example">
<h2>CSS Only</h2>
<h2>Textfield box</h2>
<div id="demo-tf-box-wrapper">
<div id="tf-box-example" class="mdc-textfield mdc-textfield--box" data-demo-no-auto-js>
<input type="text" id="tf-box" class="mdc-textfield__input" required
aria-controls="name-validation-message">
<label for="tf-box" class="mdc-textfield__label">Your Name</label>
<div class="mdc-textfield__bottom-line"></div>
</div>
<p class="mdc-textfield-helptext mdc-textfield-helptext--validation-msg"
id="name-validation-msg">
Must provide a name
</p>
</div>
<div>
<input id="box-disable" type="checkbox">
<label for="box-disable">Disabled</label>
</div>
<div>
<input id="box-rtl" type="checkbox">
<label for="box-rtl">RTL</label>
</div>
<div>
<input id="box-dark-theme" type="checkbox">
<label for="box-dark-theme">Dark Theme</label>
</div>
<div>
<input id="box-dense" type="checkbox">
<label for="box-dense">Dense</label>
</div>
<script>
setTimeout(function() {
var tfEl = document.getElementById('tf-box-example');
var tf = new mdc.textfield.MDCTextfield(tfEl);
var wrapper = document.getElementById('demo-tf-box-wrapper');

document.getElementById('box-disable').addEventListener('change', function(evt) {
tf.disabled = evt.target.checked;
});

document.getElementById('box-rtl').addEventListener('change', function(evt) {
if (evt.target.checked) {
wrapper.setAttribute('dir', 'rtl');
} else {
wrapper.removeAttribute('dir');
}
tf.ripple.layout();
});

document.getElementById('box-dark-theme').addEventListener('change', function(evt) {
wrapper.classList[evt.target.checked ? 'add' : 'remove']('mdc-theme--dark');
});

document.getElementById('box-dense').addEventListener('change', function(evt) {
tfEl.classList[evt.target.checked ? 'add' : 'remove']('mdc-textfield--dense');
tf.ripple.layout();
});
}, 80);
</script>
</section>
<section class="example">
<h2>CSS Only Textfield</h2>
<div class="mdc-form-field mdc-form-field--align-end">
<div class="mdc-textfield" data-demo-no-auto-js>
<input type="text" class="mdc-textfield__input" id="css-only-textfield"
Expand All @@ -138,6 +205,16 @@ <h2>CSS Only</h2>
<label for="css-only-textfield">Your name:</label>
</div>
</section>
<section class="example">
<h2>CSS Only Textfield box</h2>
<div>
<label for="css-only-textfield-box">Your name:</label>
<div class="mdc-textfield mdc-textfield--box" data-demo-no-auto-js>
<input type="text" class="mdc-textfield__input" id="css-only-textfield-box"
placeholder="Name">
</div>
</div>
</section>
<section class="example">
<h2>Preventing FOUC</h2>
<div class="mdc-textfield mdc-textfield--upgraded">
Expand Down
44 changes: 44 additions & 0 deletions packages/mdc-textfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,28 @@ UX for client-side form field validation.
</div>
```

### Text Field Boxes

```html
<div class="mdc-textfield mdc-textfield--box">
<input type="text" id="tf-box" class="mdc-textfield__input">
<label for="tf-box" class="mdc-textfield__label">Your Name</label>
<div class="mdc-textfield__bottom-line"></div>
</div>
```

Note that Text field boxes support all of the same features as normal textfields, including help
text, validation, and dense UI.

#### CSS-only text field boxes

```html
<label for="css-only-textfield-box">Your name:</label>
<div class="mdc-textfield mdc-textfield--box">
<input type="text" class="mdc-textfield__input" id="css-only-textfield-box" placeholder="Name">
</div>
```

### Using the JS component

MDC Textfield ships with Component / Foundation classes which are used to provide a full-fidelity
Expand Down Expand Up @@ -265,6 +287,23 @@ import {MDCTextfield} from 'mdc-textfield';
const textfield = new MDCTextfield(document.querySelector('.mdc-textfield'));
```

#### Controlling ripple instantiation

When `MDCTextfield` is instantiated with a root element containing the `mdc-textfield--box` class,
it instantiates an `MDCRipple` instance on the element in order to facilitate the correct
interaction UX for text field boxes as outlined in the spec. The way this ripple is instantiated
can be controlled by passing a ripple factory argument to the constructor.

```js
const textfieldBoxEl = document.querySelector('.mdc-textfield--box');
const textfield = new MDCTextfield(textfieldBoxEl, /* foundation */ undefined, (el) => {
// do something with el...
return new MDCRipple(el);
});
```

By default the ripple factory simply calls `new MDCRipple(el)` and returns the result.

#### MDCTextfield API

Similar to regular DOM elements, the `MDCTextfield` functionality is exposed through accessor
Expand All @@ -282,6 +321,11 @@ with the corresponding id within the document and automatically assign it to thi
Boolean. Proxies to the foundation's `isDisabled/setDisabled` methods when retrieved/set
respectively.

##### MDCTextfield.ripple

`MDCRipple` instance. Set to the `MDCRipple` instance for the root element that `MDCTextfield`
initializes when given an `mdc-textfield--box` root element. Otherwise, the field is set to `null`.

### Using the foundation class

Because MDC Textfield is a feature-rich and relatively complex component, it's adapter is a bit more
Expand Down
1 change: 1 addition & 0 deletions packages/mdc-textfield/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ export const cssClasses = {
HELPTEXT_PERSISTENT: 'mdc-textfield-helptext--persistent',
HELPTEXT_VALIDATION_MSG: 'mdc-textfield-helptext--validation-msg',
LABEL_FLOAT_ABOVE: 'mdc-textfield__label--float-above',
BOX: 'mdc-textfield--box',
};
33 changes: 20 additions & 13 deletions packages/mdc-textfield/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/

import {MDCComponent} from '@material/base';
import {MDCRipple} from '@material/ripple';

import {cssClasses, strings} from './constants';
import MDCTextfieldFoundation from './foundation';

export {MDCTextfieldFoundation};
Expand All @@ -25,11 +27,24 @@ export class MDCTextfield extends MDCComponent {
return new MDCTextfield(root);
}

constructor(...args) {
super(...args);
const input = this.input_;
this.helptextElement = input.hasAttribute('aria-controls') ?
document.getElementById(input.getAttribute('aria-controls')) : null;
initialize(rippleFactory = (el) => new MDCRipple(el)) {
this.input_ = this.root_.querySelector(strings.INPUT_SELECTOR);
this.label_ = this.root_.querySelector(strings.LABEL_SELECTOR);
this.helptextElement = null;
this.ripple = null;
if (this.input_.hasAttribute('aria-controls')) {
this.helptextElement = document.getElementById(this.input_.getAttribute('aria-controls'));
}
if (this.root_.classList.contains(cssClasses.BOX)) {
this.ripple = rippleFactory(this.root_);
};
}

destroy() {
if (this.ripple) {
this.ripple.destroy();
}
super.destroy();
}

initialSyncWithDom() {
Expand All @@ -44,14 +59,6 @@ export class MDCTextfield extends MDCComponent {
this.foundation_.setDisabled(disabled);
}

get input_() {
return this.root_.querySelector(MDCTextfieldFoundation.strings.INPUT_SELECTOR);
}

get label_() {
return this.root_.querySelector(MDCTextfieldFoundation.strings.LABEL_SELECTOR);
}

getDefaultFoundation() {
return new MDCTextfieldFoundation(Object.assign({
addClass: (className) => this.root_.classList.add(className),
Expand Down
Loading

0 comments on commit 89be862

Please sign in to comment.