Skip to content

Fix #75: md-button attaches class "md-undefined" to host element when on color provided #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all 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
30 changes: 30 additions & 0 deletions src/components/button/button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ export function main() {
done();
});
});
it('should append class based on color attribute to an existing class list', (done: () => void) => {
return builder.createAsync(TestApp).then((fixture) => {
let testComponent = fixture.debugElement.componentInstance;
let buttonDebugElement = fixture.debugElement.query(By.css('button'));
let aDebugElement = fixture.debugElement.query(By.css('a'));

//button color should append to an existing class list
testComponent.buttonColor = 'warn';
buttonDebugElement.nativeElement.classList.add('foo');
aDebugElement.nativeElement.classList.add('foo');
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.classList.contains('md-warn')).toBe(true);
expect(buttonDebugElement.nativeElement.classList.contains('foo')).toBe(true);
expect(aDebugElement.nativeElement.classList.contains('md-warn')).toBe(true);
expect(aDebugElement.nativeElement.classList.contains('foo')).toBe(true);
done();
});
});
it('should not render a class attribute when no color attribute is provided', (done: () => void) => {
//covering fix #75
return builder.createAsync(TestApp).then((fixture) => {
let buttonDebugElement = fixture.debugElement.query(By.css('button'));
let aDebugElement = fixture.debugElement.query(By.css('a'));

fixture.detectChanges();
expect(buttonDebugElement.nativeElement.hasAttribute('class')).toBe(false);
expect(aDebugElement.nativeElement.hasAttribute('class')).toBe(false);
done();
});
});

// Regular button tests
describe('button[md-button]', () => {
Expand Down
47 changes: 39 additions & 8 deletions src/components/button/button.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {
Component,
ElementRef,
ViewEncapsulation,
Input,
HostBinding,
HostListener,
ChangeDetectionStrategy,
Renderer,
} from 'angular2/core';

import {TimerWrapper} from 'angular2/src/facade/async';

// TODO(jelbourn): Ink ripples.
Expand All @@ -18,7 +21,6 @@ import {TimerWrapper} from 'angular2/src/facade/async';
inputs: ['color'],
host: {
'[class.md-button-focus]': 'isKeyboardFocused',
'[class]': 'setClassList()',
'(mousedown)': 'setMousedown()',
'(focus)': 'setKeyboardFocus()',
'(blur)': 'removeKeyboardFocus()'
Expand All @@ -29,23 +31,47 @@ import {TimerWrapper} from 'angular2/src/facade/async';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdButton {
color: string;
private color_: string;

// Setting the class when a color property is provided
// cannot be done through [ngClass] on host or [class] = 'getClassList()'
// @see http://stackoverflow.com/questions/35506395
@Input()
set color(value: string) {
Copy link
Member

Choose a reason for hiding this comment

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

Why can this fix not be something like:

setClassList() {
  return color != null ? `md-${this.color}` : '';
}

?

Copy link
Author

Choose a reason for hiding this comment

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

  1. This results in an empty class attribute which is not valid XHTML
  2. It overrides any other specified class in the component

<button md-button class="myClass" color="warn">Submit</button>
results in
<button md-button class="md-warn">Submit</button>

It should be
<button md-button class="myClass md-warn">Submit</button>

Copy link
Member

Choose a reason for hiding this comment

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

I chatted with Misko and he agreed that this is an issue in Angular: angular/angular#7289

In the meantime I would be okay with the renderer approach.

// Remove the previous color if one provided
if (this.color_ != null) {
this.renderer.setElementClass(this.elementRef, 'md-' + this.color_, false);
}

this.color_ = value;

if (value != null) {
this.renderer.setElementClass(this.elementRef, 'md-' + this.color_, true);
}
}

get color(): string {
return this.color_;
}

constructor(public elementRef: ElementRef, public renderer: Renderer) {
}

/** Whether the button has focus from the keyboard (not the mouse). Used for class binding. */
isKeyboardFocused: boolean = false;

/** Whether a mousedown has occurred on this element in the last 100ms. */
isMouseDown: boolean = false;

setClassList() {return `md-${this.color}`;}

setMousedown() {
// We only *show* the focus style when focus has come to the button via the keyboard.
// The Material Design spec is silent on this topic, and without doing this, the
// button continues to look :active after clicking.
// @see http://marcysutton.com/button-focus-hell/
this.isMouseDown = true;
TimerWrapper.setTimeout(() => { this.isMouseDown = false; }, 100);
TimerWrapper.setTimeout(() => {
Copy link
Member

Choose a reason for hiding this comment

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

Why aren't you using the lambda expression instead?

TimeWrapper.setTimeout(() => this.isMouseDown = false, 100);

Copy link
Contributor

Choose a reason for hiding this comment

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

You need brackets for ts2dart to work as expected.

Copy link
Member

Choose a reason for hiding this comment

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

@hansl Ah well, thanks for the information :)

Copy link
Contributor

Choose a reason for hiding this comment

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

This is because TimerWrapper.setTimeout() expects the function to be void. Dart is stricter than TypeScript in that it strictly enforce return values of callbacks. In TypeScript the return value is simply ignored. Please note that there are proposals to enforce stricter rules in TypeScript as well.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks great!, indeed - just saw, lambda will always return the expression.

this.isMouseDown = false;
}, 100);
}

setKeyboardFocus($event: any) {
Expand All @@ -62,7 +88,6 @@ export class MdButton {
inputs: ['color'],
host: {
'[class.md-button-focus]': 'isKeyboardFocused',
'[class]': 'setClassList()',
'(mousedown)': 'setMousedown()',
'(focus)': 'setKeyboardFocus()',
'(blur)': 'removeKeyboardFocus()'
Expand All @@ -74,8 +99,12 @@ export class MdButton {
export class MdAnchor extends MdButton {
disabled_: boolean = null;

constructor(elementRef_: ElementRef, renderer_: Renderer) {
super(elementRef_, renderer_);
}

@HostBinding('tabIndex')
get tabIndex():number {
get tabIndex(): number {
return this.disabled ? -1 : 0;
}

Expand All @@ -87,7 +116,9 @@ export class MdAnchor extends MdButton {

@HostBinding('attr.disabled')
@Input('disabled')
get disabled() { return this.disabled_; }
get disabled() {
return this.disabled_;
}

set disabled(value: boolean) {
// The presence of *any* disabled value makes the component disabled, *except* for false.
Expand Down
2 changes: 1 addition & 1 deletion src/demo-app/button/button-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</section>

<section>
<button md-button color="primary">primary</button>
<button md-button color="primary" class="test">primary</button>
<button md-button color="accent">accent</button>
<button md-button color="warn">warn</button>
</section>
Expand Down