(function(global) {
'use strict';
var MDCCheckbox = global.mdc.checkbox.MDCCheckbox;
- var checkbox = new MDCCheckbox(document.getElementById('mdc-js-checkbox'));
+ var MDCFormField = global.mdc.formField.MDCFormField;
+
+ var checkbox = document.getElementById('mdc-js-checkbox');
+ var checkboxInstance = new MDCCheckbox(document.getElementById('mdc-js-checkbox'));
+
+ var formField = checkbox.parentElement;
+ var formFieldInstance = new MDCFormField(formField);
+
+ formFieldInstance.input = checkboxInstance;
+
document.getElementById('make-ind').addEventListener('click', function() {
checkbox.indeterminate = true;
});
diff --git a/demos/radio.html b/demos/radio.html
index 4434e167ad1..e8d26293bbe 100644
--- a/demos/radio.html
+++ b/demos/radio.html
@@ -172,9 +172,18 @@
Disabled
diff --git a/packages/material-components-web/index.js b/packages/material-components-web/index.js
index 7afcc75dcd2..883f1c20042 100644
--- a/packages/material-components-web/index.js
+++ b/packages/material-components-web/index.js
@@ -16,6 +16,7 @@
import * as base from '@material/base';
import * as checkbox from '@material/checkbox';
+import * as formField from '@material/form-field';
import * as iconToggle from '@material/icon-toggle';
import * as radio from '@material/radio';
import * as ripple from '@material/ripple';
@@ -41,6 +42,7 @@ autoInit.register('MDCSelect', select.MDCSelect);
export {
base,
checkbox,
+ formField,
iconToggle,
radio,
ripple,
diff --git a/packages/mdc-checkbox/index.js b/packages/mdc-checkbox/index.js
index 5aae426d8e4..b62e3b09c4f 100644
--- a/packages/mdc-checkbox/index.js
+++ b/packages/mdc-checkbox/index.js
@@ -78,6 +78,10 @@ export class MDCCheckbox extends MDCComponent {
});
}
+ get ripple() {
+ return this.ripple_;
+ }
+
get checked() {
return this.foundation_.isChecked();
}
diff --git a/packages/mdc-form-field/README.md b/packages/mdc-form-field/README.md
index 19082664559..584ed270cfa 100644
--- a/packages/mdc-form-field/README.md
+++ b/packages/mdc-form-field/README.md
@@ -1,7 +1,8 @@
# MDC Form Field
MDC Form Field provides an `mdc-form-field` helper class for easily making theme-aware, RTL-aware
-form field + label combos.
+form field + label combos. It also provides an `MDCFormField` class for easily making input ripples
+respond to label events.
## Installation
@@ -9,7 +10,7 @@ form field + label combos.
npm install --save @material/form-field
```
-## Usage
+## CSS Usage
The `mdc-form-field` class can be used as a wrapper element with any `input` + `label` combo:
@@ -71,3 +72,81 @@ checkbox:
`mdc-form-field` is dark theme aware, and will change the text color to the "primary on dark" text
color when used within a dark theme.
+
+
+## JS Usage
+
+### Including in code
+
+#### ES2015
+
+```javascript
+import {MDCFormField, MDCFormFieldFoundation} from 'mdc-form-field';
+```
+
+#### CommonJS
+
+```javascript
+const mdcFormField = require('mdc-form-field');
+const MDCFormField = mdcFormField.MDCFormField;
+const MDCFormFieldFoundation = mdcFormField.MDCFormFieldFoundation;
+```
+
+#### AMD
+
+```javascript
+require(['path/to/mdc-form-field'], mdcFormField => {
+ const MDCFormField = mdcFormField.MDCFormField;
+ const MDCFormFieldFoundation = mdcFormField.MDCFormFieldFoundation;
+});
+```
+
+#### Global
+
+```javascript
+const MDCFormField = mdc.radio.MDCFormField;
+const MDCFormFieldFoundation = mdc.radio.MDCFormFieldFoundation;
+```
+
+### Instantiation
+
+```javascript
+import {MDCFormField} from 'mdc-form-field';
+
+const formField = new MDCFormField(document.querySelector('.mdc-form-field'));
+```
+
+### MDCFormField API
+
+The `MDCFormField` functionality is exposed through a single accessor method.
+
+#### MDCFormField.input
+
+Read-write property that works with an instance of an MDC-Web input element.
+
+In order for the label ripple integration to work correctly, this property needs to be set to a
+valid instance of an MDC-Web input element which exposes a `ripple` getter.
+
+```javascript
+const formField = new MDCFormField(document.querySelector('.mdc-form-field'));
+const radio = new MDCRadio(document.querySelector('.mdc-radio'));
+
+formField.input = radio;
+```
+
+No action is taken if the `input` property is not set or the input instance doesn't expose a
+`ripple` getter.
+
+
+### Adapter
+
+The adapter for `MDCFormField` is extremely simple, providing only methods for adding and
+removing event listeners from the label, as well as methods for activating and deactivating
+the input ripple.
+
+| Method Signature | Description |
+| --- | --- |
+| `registerInteractionHandler(type: string, handler: EventListener) => void` | Adds an event listener `handler` for event type `type` to the label. |
+| `deregisterInteractionHandler(type: string, handler: EventListener) => void` | Removes an event listener `handler` for event type `type` to the label. |
+| `activateInputRipple() => void` | Activates the ripple on the input element. Should call `activate` on the input element's `ripple` property. |
+| `deactivateInputRipple() => void` | Deactivates the ripple on the input element. Should call `deactivate` on the input element's `ripple` property. |
diff --git a/packages/mdc-form-field/constants.js b/packages/mdc-form-field/constants.js
new file mode 100644
index 00000000000..5bb9ccdc014
--- /dev/null
+++ b/packages/mdc-form-field/constants.js
@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+export const cssClasses = {
+ ROOT: 'mdc-form-field',
+};
+
+export const strings = {
+ LABEL_SELECTOR: '.mdc-form-field > label',
+};
diff --git a/packages/mdc-form-field/foundation.js b/packages/mdc-form-field/foundation.js
new file mode 100644
index 00000000000..ad6d157647c
--- /dev/null
+++ b/packages/mdc-form-field/foundation.js
@@ -0,0 +1,55 @@
+/**
+ * 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.
+ */
+
+import {MDCFoundation} from '@material/base';
+import {cssClasses, strings} from './constants';
+
+export default class MDCFormFieldFoundation extends MDCFoundation {
+ static get cssClasses() {
+ return cssClasses;
+ }
+
+ static get strings() {
+ return strings;
+ }
+
+ static get defaultAdapter() {
+ return {
+ registerInteractionHandler: (/* type: string, handler: EventListener */) => {},
+ deregisterInteractionHandler: (/* type: string, handler: EventListener */) => {},
+ activateInputRipple: () => {},
+ deactivateInputRipple: () => {},
+ };
+ }
+
+ constructor(adapter) {
+ super(Object.assign(MDCFormFieldFoundation.defaultAdapter, adapter));
+ this.clickHandler_ = (evt) => this.handleClick_(evt);
+ }
+
+ init() {
+ this.adapter_.registerInteractionHandler('click', this.clickHandler_);
+ }
+
+ destroy() {
+ this.adapter_.deregisterInteractionHandler('click', this.clickHandler_);
+ }
+
+ handleClick_(evt) {
+ this.adapter_.activateInputRipple();
+ requestAnimationFrame(() => this.adapter_.deactivateInputRipple());
+ }
+}
diff --git a/packages/mdc-form-field/index.js b/packages/mdc-form-field/index.js
new file mode 100644
index 00000000000..c0f1c69a78c
--- /dev/null
+++ b/packages/mdc-form-field/index.js
@@ -0,0 +1,55 @@
+/**
+ * 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.
+ */
+
+import {MDCComponent} from '@material/base';
+import MDCFormFieldFoundation from './foundation';
+
+export {MDCFormFieldFoundation};
+
+export class MDCFormField extends MDCComponent {
+ static attachTo(root) {
+ return new MDCFormField(root);
+ }
+
+ set input(input) {
+ this.input_ = input;
+ }
+
+ get input() {
+ return this.input_;
+ }
+
+ get label_() {
+ return this.root_.querySelector(MDCFormFieldFoundation.strings.LABEL_SELECTOR);
+ }
+
+ getDefaultFoundation() {
+ return new MDCFormFieldFoundation({
+ registerInteractionHandler: (type, handler) => this.label_.addEventListener(type, handler),
+ deregisterInteractionHandler: (type, handler) => this.label_.removeEventListener(type, handler),
+ activateInputRipple: () => {
+ if (this.input_ && this.input_.ripple) {
+ this.input_.ripple.activate();
+ }
+ },
+ deactivateInputRipple: () => {
+ if (this.input_ && this.input_.ripple) {
+ this.input_.ripple.deactivate();
+ }
+ },
+ });
+ }
+}
diff --git a/packages/mdc-form-field/package.json b/packages/mdc-form-field/package.json
index 36edd8e052a..6965bc9aeb9 100644
--- a/packages/mdc-form-field/package.json
+++ b/packages/mdc-form-field/package.json
@@ -1,6 +1,6 @@
{
"name": "@material/form-field",
- "description": "Material Components for the web wrapper styles for laying out form fields and labels next to one another",
+ "description": "Material Components for the web wrapper for laying out form fields and labels next to one another",
"version": "0.1.1",
"license": "Apache-2.0",
"keywords": [
@@ -8,11 +8,13 @@
"material design",
"form"
],
+ "main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/material-components/material-components-web.git"
},
"dependencies": {
+ "@material/base": "^0.1.1",
"@material/rtl": "^0.1.1",
"@material/theme": "^0.1.1",
"@material/typography": "^0.1.1"
diff --git a/packages/mdc-radio/index.js b/packages/mdc-radio/index.js
index 0b5ebb969fc..7315ddab86f 100644
--- a/packages/mdc-radio/index.js
+++ b/packages/mdc-radio/index.js
@@ -42,6 +42,10 @@ export class MDCRadio extends MDCComponent {
this.foundation_.setDisabled(disabled);
}
+ get ripple() {
+ return this.ripple_;
+ }
+
constructor(...args) {
super(...args);
this.ripple_ = this.initRipple_();
diff --git a/test/unit/mdc-form-field/foundation.test.js b/test/unit/mdc-form-field/foundation.test.js
new file mode 100644
index 00000000000..627e7e3f288
--- /dev/null
+++ b/test/unit/mdc-form-field/foundation.test.js
@@ -0,0 +1,64 @@
+/**
+ * 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.
+ */
+
+import test from 'tape';
+import td from 'testdouble';
+
+import MDCFormFieldFoundation from '../../../packages/mdc-form-field/foundation';
+import {verifyDefaultAdapter} from '../helpers/foundation';
+
+test('exports cssClasses', (t) => {
+ t.true('cssClasses' in MDCFormFieldFoundation);
+ t.end();
+});
+
+test('exports strings', (t) => {
+ t.true('strings' in MDCFormFieldFoundation);
+ t.end();
+});
+
+test('defaultAdapter returns a complete adapter implementation', (t) => {
+ verifyDefaultAdapter(MDCFormFieldFoundation, [
+ 'registerInteractionHandler', 'deregisterInteractionHandler', 'activateInputRipple', 'deactivateInputRipple',
+ ], t);
+
+ t.end();
+});
+
+function setupTest() {
+ const mockAdapter = td.object(MDCFormFieldFoundation.defaultAdapter);
+ const foundation = new MDCFormFieldFoundation(mockAdapter);
+ return {foundation, mockAdapter};
+}
+
+test('#init calls event registrations', (t) => {
+ const {foundation, mockAdapter} = setupTest();
+ const {isA} = td.matchers;
+
+ foundation.init();
+ t.doesNotThrow(() => td.verify(mockAdapter.registerInteractionHandler('click', isA(Function))));
+ t.end();
+});
+
+test('#destroy calls event deregistrations', (t) => {
+ const {foundation, mockAdapter} = setupTest();
+ const {isA} = td.matchers;
+
+ foundation.init();
+ foundation.destroy();
+ t.doesNotThrow(() => td.verify(mockAdapter.deregisterInteractionHandler('click', isA(Function))));
+ t.end();
+});
diff --git a/test/unit/mdc-form-field/mdc-form-field.test.js b/test/unit/mdc-form-field/mdc-form-field.test.js
new file mode 100644
index 00000000000..abc8ea784b2
--- /dev/null
+++ b/test/unit/mdc-form-field/mdc-form-field.test.js
@@ -0,0 +1,135 @@
+/**
+ * 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.
+ */
+
+import test from 'tape';
+import bel from 'bel';
+import domEvents from 'dom-events';
+import td from 'testdouble';
+
+import {MDCFormField} from '../../../packages/mdc-form-field';
+
+function getFixture() {
+ return bel`
+