Skip to content

Commit

Permalink
fix(text-field): Add role="button" to icon (#2584)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Adds removeAttr(attr) adapter API
  • Loading branch information
kfranqueiro committed Apr 19, 2018
1 parent 9639689 commit 4c52589
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 14 deletions.
8 changes: 4 additions & 4 deletions demos/text-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ <h2>Text Field - Leading/Trailing icons</h2>
<div id="demo-tf-box-leading-wrapper">
<div id="tf-box-leading-example"
class="mdc-text-field mdc-text-field--box mdc-text-field--with-leading-icon" data-demo-no-auto-js>
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
<input type="text" id="tf-box-leading" class="mdc-text-field__input">
<label for="tf-box-leading" class="mdc-floating-label">Your name</label>
<div class="mdc-line-ripple"></div>
Expand All @@ -258,14 +258,14 @@ <h2>Text Field - Leading/Trailing icons</h2>
class="mdc-text-field mdc-text-field--box mdc-text-field--with-trailing-icon" data-demo-no-auto-js>
<input type="text" id="tf-box-trailing" class="mdc-text-field__input">
<label for="tf-box-trailing" class="mdc-floating-label">Your other name</label>
<i class="material-icons mdc-text-field__icon" tabindex="0">delete</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">delete</i>
<div class="mdc-line-ripple"></div>
</div>
</div>
<div id="demo-tf-outlined-leading-wrapper">
<div id="tf-outlined-leading-example"
class="mdc-text-field mdc-text-field--outlined mdc-text-field--with-leading-icon" data-demo-no-auto-js>
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
<input type="text" id="tf-outlined-leading" class="mdc-text-field__input">
<label for="tf-outlined-leading" class="mdc-floating-label">Your other name</label>
<div class="mdc-notched-outline">
Expand All @@ -281,7 +281,7 @@ <h2>Text Field - Leading/Trailing icons</h2>
class="mdc-text-field mdc-text-field--outlined mdc-text-field--with-trailing-icon" data-demo-no-auto-js>
<input type="text" id="tf-outlined-trailing" class="mdc-text-field__input">
<label for="tf-outlined-trailing" class="mdc-floating-label">Your other name</label>
<i class="material-icons mdc-text-field__icon" tabindex="0">delete</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">delete</i>
<div class="mdc-notched-outline">
<svg>
<path class="mdc-notched-outline__path"/>
Expand Down
15 changes: 8 additions & 7 deletions packages/mdc-textfield/icon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Icons describe the type of input a text field requires. They can also be interac
### HTML Structure

```html
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
```

### Usage within `mdc-text-field`
Expand All @@ -36,7 +36,7 @@ Leading and trailing icons can be applied to text fields styled as `mdc-text-fie
In text field box:
```html
<div class="mdc-text-field mdc-text-field--box mdc-text-field--with-leading-icon">
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
<input type="text" id="my-input" class="mdc-text-field__input">
<label for="my-input" class="mdc-floating-label">Your Name</label>
<div class="mdc-text-field__bottom-line"></div>
Expand All @@ -46,7 +46,7 @@ In text field box:
In outlined text field:
```html
<div class="mdc-text-field mdc-text-field--outlined mdc-text-field--with-leading-icon">
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
<input type="text" id="my-input" class="mdc-text-field__input">
<label for="my-input" class="mdc-floating-label">Your Name</label>
<div class="mdc-notched-outline">
Expand All @@ -65,7 +65,7 @@ In text field box:
<div class="mdc-text-field mdc-text-field--box mdc-text-field--with-trailing-icon">
<input type="text" id="my-input" class="mdc-text-field__input">
<label for="my-input" class="mdc-floating-label">Your Name</label>
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
<div class="mdc-text-field__bottom-line"></div>
</div>
```
Expand All @@ -75,7 +75,7 @@ In outlined text field:
<div class="mdc-text-field mdc-text-field--outlined mdc-text-field--with-trailing-icon">
<input type="text" id="my-input" class="mdc-text-field__input">
<label for="my-input" class="mdc-floating-label">Your Name</label>
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
<div class="mdc-notched-outline">
<svg>
<path class="mdc-notched-outline__path"/>
Expand All @@ -85,8 +85,8 @@ In outlined text field:
</div>
```

>**NOTE:** if you would like to display un-clickable icons, simply remove `tabindex="0"`,
and the css will ensure the cursor is set to default, and that actioning on an icon doesn't
>**NOTE:** if you would like to display un-clickable icons, simply remove `tabindex="0"` and `role="button"`,
and the CSS will ensure the cursor is set to default, and that interacting with an icon doesn't
do anything unexpected.

### Sass Mixins
Expand All @@ -106,6 +106,7 @@ This allows the parent `MDCTextField` component to access the public methods on
Method Signature | Description
--- | ---
`setAttr(attr: string, value: string) => void` | Sets an attribute with a given value on the icon element
`removeAttr(attr: string) => void` | Removes an attribute from the icon element
`registerInteractionHandler(evtType: string, handler: EventListener) => void` | Registers an event listener for a given event
`deregisterInteractionHandler(evtType: string, handler: EventListener) => void` | Deregisters an event listener for a given event
`notifyIconAction() => void` | Emits a custom event "MDCTextField:icon" denoting a user has clicked the icon, which bubbles to the top-level text field element
Expand Down
6 changes: 6 additions & 0 deletions packages/mdc-textfield/icon/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class MDCTextFieldIconAdapter {
*/
setAttr(attr, value) {}

/**
* Removes an attribute from the icon element.
* @param {string} attr
*/
removeAttr(attr) {}

/**
* Registers an event listener on the icon element for a given event.
* @param {string} evtType
Expand Down
1 change: 1 addition & 0 deletions packages/mdc-textfield/icon/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
/** @enum {string} */
const strings = {
ICON_EVENT: 'MDCTextField:icon',
ICON_ROLE: 'button',
};

export {strings};
3 changes: 3 additions & 0 deletions packages/mdc-textfield/icon/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class MDCTextFieldIconFoundation extends MDCFoundation {
static get defaultAdapter() {
return /** @type {!MDCTextFieldIconAdapter} */ ({
setAttr: () => {},
removeAttr: () => {},
registerInteractionHandler: () => {},
deregisterInteractionHandler: () => {},
notifyIconAction: () => {},
Expand Down Expand Up @@ -73,8 +74,10 @@ class MDCTextFieldIconFoundation extends MDCFoundation {
setDisabled(disabled) {
if (disabled) {
this.adapter_.setAttr('tabindex', '-1');
this.adapter_.removeAttr('role');
} else {
this.adapter_.setAttr('tabindex', '0');
this.adapter_.setAttr('role', strings.ICON_ROLE);
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/mdc-textfield/icon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class MDCTextFieldIcon extends MDCComponent {
getDefaultFoundation() {
return new MDCTextFieldIconFoundation(/** @type {!MDCTextFieldIconAdapter} */ (Object.assign({
setAttr: (attr, value) => this.root_.setAttribute(attr, value),
removeAttr: (attr) => this.root_.removeAttribute(attr),
registerInteractionHandler: (evtType, handler) => this.root_.addEventListener(evtType, handler),
deregisterInteractionHandler: (evtType, handler) => this.root_.removeEventListener(evtType, handler),
notifyIconAction: () => this.emit(
Expand Down
17 changes: 15 additions & 2 deletions test/unit/mdc-textfield/mdc-text-field-icon-foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ import td from 'testdouble';
import {verifyDefaultAdapter} from '../helpers/foundation';
import {setupFoundationTest} from '../helpers/setup';
import MDCTextFieldIconFoundation from '../../../packages/mdc-textfield/icon/foundation';
import {strings} from '../../../packages/mdc-textfield/icon/constants';

suite('MDCTextFieldIconFoundation');

test('exports strings', () => {
assert.isOk('strings' in MDCTextFieldIconFoundation);
assert.deepEqual(MDCTextFieldIconFoundation.strings, strings);
});

test('defaultAdapter returns a complete adapter implementation', () => {
verifyDefaultAdapter(MDCTextFieldIconFoundation, [
'setAttr', 'registerInteractionHandler', 'deregisterInteractionHandler',
'setAttr', 'removeAttr', 'registerInteractionHandler', 'deregisterInteractionHandler',
'notifyIconAction',
]);
});
Expand Down Expand Up @@ -58,12 +59,24 @@ test('#setDisabled sets icon tabindex to -1 when set to true', () => {
td.verify(mockAdapter.setAttr('tabindex', '-1'));
});

test('#setDisabled removes icon role when set to true', () => {
const {foundation, mockAdapter} = setupTest();
foundation.setDisabled(true);
td.verify(mockAdapter.removeAttr('role'));
});

test('#setDisabled sets icon tabindex to 0 when set to false', () => {
const {foundation, mockAdapter} = setupTest();
foundation.setDisabled(false);
td.verify(mockAdapter.setAttr('tabindex', '0'));
});

test(`#setDisabled sets icon role to ${strings.ICON_ROLE} when set to false`, () => {
const {foundation, mockAdapter} = setupTest();
foundation.setDisabled(false);
td.verify(mockAdapter.setAttr('role', strings.ICON_ROLE));
});

test('on click notifies custom icon event', () => {
const {foundation, mockAdapter} = setupTest();
const evt = {
Expand Down
7 changes: 7 additions & 0 deletions test/unit/mdc-textfield/mdc-text-field-icon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ test('#adapter.setAttr adds a given attribute to the element', () => {
assert.equal(root.getAttribute('aria-label'), 'foo');
});

test('#adapter.removeAttr removes a given attribute from the element', () => {
const {root, component} = setupTest();
root.setAttribute('role', 'button');
component.getDefaultFoundation().adapter_.removeAttr('role');
assert.isFalse(root.hasAttribute('role'));
});

test('#adapter.registerInteractionHandler adds event listener for a given event to the element', () => {
const {root, component} = setupTest();
const handler = td.func('keydown handler');
Expand Down
2 changes: 1 addition & 1 deletion test/unit/mdc-textfield/mdc-text-field.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const {cssClasses} = MDCTextFieldFoundation;

const getFixture = () => bel`
<div class="mdc-text-field">
<i class="material-icons mdc-text-field__icon" tabindex="0">event</i>
<i class="material-icons mdc-text-field__icon" tabindex="0" role="button">event</i>
<input type="text" class="mdc-text-field__input" id="my-text-field">
<label class="mdc-floating-label" for="my-text-field">My Label</label>
<div class="mdc-line-ripple"></div>
Expand Down

0 comments on commit 4c52589

Please sign in to comment.