Skip to content

Commit 5f1ee5d

Browse files
committed
Merge branch 'master' into 2601/dialog-pop-state
2 parents 40e5b75 + fe113eb commit 5f1ee5d

File tree

15 files changed

+260
-48
lines changed

15 files changed

+260
-48
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ High level items planned for January 2017:
7575
| dialog | Available | [README][22] | [#114][0114] |
7676
| snackbar / toast | Available | [README][21] | [#115][0115] |
7777
| select | Available | [README][23] | [#118][0118] |
78-
| textarea | Available | - | - |
78+
| textarea | Available | [README][5] | - |
7979
| autocomplete | In-progress | - | [#117][0117] |
8080
| chips | Initial version, features evolving | - | [#120][0120] |
8181
| theming | Available, need guidance overlays | [Guide][20] | - |

guides/theming.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The actual path will depend on your server setup.
4141
You can also concatenate the file with the rest of your application's css.
4242

4343
Finally, if your app's content **is not** placed inside of a `md-sidenav-container` element, you
44-
need to add the `md-app-background` class to your wrapper element (for example the `body`). This
44+
need to add the `mat-app-background` class to your wrapper element (for example the `body`). This
4545
ensures that the proper theme background is applied to your page.
4646

4747
### Defining a custom theme
@@ -55,19 +55,19 @@ the corresponding styles. A typical theme file will look something like this:
5555

5656
// Include the base styles for Angular Material core. We include this here so that you only
5757
// have to load a single css file for Angular Material in your app.
58-
@include md-core();
58+
@include mat-core();
5959

6060
// Define the palettes for your theme using the Material Design palettes available in palette.scss
6161
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
6262
// hue.
63-
$candy-app-primary: md-palette($md-indigo);
64-
$candy-app-accent: md-palette($md-pink, A200, A100, A400);
63+
$candy-app-primary: mat-palette($mat-indigo);
64+
$candy-app-accent: mat-palette($mat-pink, A200, A100, A400);
6565

6666
// The warn palette is optional (defaults to red).
67-
$candy-app-warn: md-palette($md-red);
67+
$candy-app-warn: mat-palette($mat-red);
6868

6969
// Create the theme object (a Sass map containing all of the palettes).
70-
$candy-app-theme: md-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);
70+
$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);
7171

7272
// Include theme styles for core and each component used in your app.
7373
// Alternatively, you can import and @include the theme mixins for each component
@@ -96,11 +96,11 @@ some selector. For example, we could append the following to the example above t
9696
secondary dark theme:
9797
```scss
9898
.unicorn-dark-theme {
99-
$dark-primary: md-palette($md-blue-grey);
100-
$dark-accent: md-palette($md-amber, A200, A100, A400);
101-
$dark-warn: md-palette($md-deep-orange);
99+
$dark-primary: mat-palette($mat-blue-grey);
100+
$dark-accent: mat-palette($mat-amber, A200, A100, A400);
101+
$dark-warn: mat-palette($mat-deep-orange);
102102

103-
$dark-theme: md-dark-theme($dark-primary, $dark-accent, $dark-warn);
103+
$dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
104104

105105
@include angular-material-theme($dark-theme);
106106
}

src/demo-app/tooltip/tooltip-demo.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="demo-tooltip">
22
<h1>Tooltip Demo</h1>
33

4-
<p class="centered">
4+
<div class="centered" cdk-scrollable>
55
<button #tooltip="mdTooltip"
66
md-raised-button
77
color="primary"
@@ -11,7 +11,9 @@ <h1>Tooltip Demo</h1>
1111
[mdTooltipHideDelay]="hideDelay">
1212
Mouse over to see the tooltip
1313
</button>
14-
</p>
14+
<div>Scroll down while tooltip is open to see it hide automatically</div>
15+
<div style="height: 400px;"></div>
16+
</div>
1517

1618
<p>
1719
<md-radio-group [(ngModel)]="position">

src/demo-app/tooltip/tooltip-demo.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
.demo-tooltip {
22
.centered {
33
text-align: center;
4+
height: 200px;
5+
overflow: auto;
6+
7+
button {
8+
margin: 16px;
9+
}
410
}
511
.mat-radio-button {
612
display: block;

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
8484
private _blurStream = new Subject<any>();
8585

8686
/** View -> model callback called when value changes */
87-
_onChange: (value: any) => {};
87+
_onChange = (value: any) => {};
8888

8989
/** View -> model callback called when autocomplete has been touched */
9090
_onTouched = () => {};

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('MdAutocomplete', () => {
2424
imports: [
2525
MdAutocompleteModule.forRoot(), MdInputModule.forRoot(), ReactiveFormsModule
2626
],
27-
declarations: [SimpleAutocomplete],
27+
declarations: [SimpleAutocomplete, AutocompleteWithoutForms],
2828
providers: [
2929
{provide: OverlayContainer, useFactory: () => {
3030
overlayContainerElement = document.createElement('div');
@@ -790,6 +790,25 @@ describe('MdAutocomplete', () => {
790790

791791
});
792792

793+
describe('misc', () => {
794+
795+
it('should allow basic use without any forms directives', () => {
796+
expect(() => {
797+
const fixture = TestBed.createComponent(AutocompleteWithoutForms);
798+
fixture.detectChanges();
799+
800+
const input = fixture.debugElement.query(By.css('input')).nativeElement;
801+
input.value = 'd';
802+
dispatchEvent('input', input);
803+
fixture.detectChanges();
804+
805+
const options =
806+
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
807+
expect(options.length).toBe(1);
808+
}).not.toThrowError();
809+
});
810+
811+
});
793812
});
794813

795814
@Component({
@@ -849,6 +868,33 @@ class SimpleAutocomplete implements OnDestroy {
849868
}
850869

851870

871+
@Component({
872+
template: `
873+
<md-input-container>
874+
<input mdInput placeholder="State" [mdAutocomplete]="auto"
875+
(input)="onInput($event.target?.value)">
876+
</md-input-container>
877+
878+
<md-autocomplete #auto="mdAutocomplete">
879+
<md-option *ngFor="let state of filteredStates" [value]="state">
880+
<span> {{ state }} </span>
881+
</md-option>
882+
</md-autocomplete>
883+
`
884+
})
885+
class AutocompleteWithoutForms {
886+
filteredStates: any[];
887+
states = ['Alabama', 'California', 'Florida'];
888+
889+
constructor() {
890+
this.filteredStates = this.states.slice();
891+
}
892+
893+
onInput(value: any) {
894+
this.filteredStates = this.states.filter(s => new RegExp(value, 'gi').test(s));
895+
}
896+
897+
}
852898

853899
/**
854900
* TODO: Move this to core testing utility until Angular has event faking

src/lib/core/overlay/scroll/scroll-dispatcher.spec.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {inject, TestBed, async, ComponentFixture} from '@angular/core/testing';
2-
import {NgModule, Component, ViewChild, ElementRef, QueryList, ViewChildren} from '@angular/core';
1+
import {inject, TestBed, async, fakeAsync, ComponentFixture, tick} from '@angular/core/testing';
2+
import {NgModule, Component, ViewChild, ElementRef} from '@angular/core';
33
import {ScrollDispatcher} from './scroll-dispatcher';
44
import {OverlayModule} from '../overlay-directives';
55
import {Scrollable} from './scrollable';
@@ -38,15 +38,17 @@ describe('Scroll Dispatcher', () => {
3838
expect(scroll.scrollableReferences.has(componentScrollable)).toBe(false);
3939
});
4040

41-
it('should notify through the directive and service that a scroll event occurred', () => {
41+
it('should notify through the directive and service that a scroll event occurred',
42+
fakeAsync(() => {
4243
let hasDirectiveScrollNotified = false;
4344
// Listen for notifications from scroll directive
4445
let scrollable = fixture.componentInstance.scrollable;
4546
scrollable.elementScrolled().subscribe(() => { hasDirectiveScrollNotified = true; });
4647

47-
// Listen for notifications from scroll service
48+
// Listen for notifications from scroll service with a throttle of 100ms
49+
const throttleTime = 100;
4850
let hasServiceScrollNotified = false;
49-
scroll.scrolled().subscribe(() => { hasServiceScrollNotified = true; });
51+
scroll.scrolled(throttleTime).subscribe(() => { hasServiceScrollNotified = true; });
5052

5153
// Emit a scroll event from the scrolling element in our component.
5254
// This event should be picked up by the scrollable directive and notify.
@@ -55,9 +57,17 @@ describe('Scroll Dispatcher', () => {
5557
scrollEvent.initUIEvent('scroll', true, true, window, 0);
5658
fixture.componentInstance.scrollingElement.nativeElement.dispatchEvent(scrollEvent);
5759

60+
// The scrollable directive should have notified the service immediately.
5861
expect(hasDirectiveScrollNotified).toBe(true);
62+
63+
// Verify that the throttle is used, the service should wait for the throttle time until
64+
// sending the notification.
65+
expect(hasServiceScrollNotified).toBe(false);
66+
67+
// After the throttle time, the notification should be sent.
68+
tick(throttleTime);
5969
expect(hasServiceScrollNotified).toBe(true);
60-
});
70+
}));
6171
});
6272

6373
describe('Nested scrollables', () => {
@@ -107,7 +117,6 @@ class ScrollingComponent {
107117
})
108118
class NestedScrollingComponent {
109119
@ViewChild('interestingElement') interestingElement: ElementRef;
110-
@ViewChildren(Scrollable) scrollables: QueryList<Scrollable>;
111120
}
112121

113122
const TEST_COMPONENTS = [ScrollingComponent, NestedScrollingComponent];

src/lib/core/overlay/scroll/scroll-dispatcher.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import {Subject} from 'rxjs/Subject';
44
import {Observable} from 'rxjs/Observable';
55
import {Subscription} from 'rxjs/Subscription';
66
import 'rxjs/add/observable/fromEvent';
7+
import 'rxjs/add/operator/auditTime';
78

89

10+
/** Time in ms to throttle the scrolling events by default. */
11+
export const DEFAULT_SCROLL_TIME = 20;
12+
913
/**
1014
* Service contained all registered Scrollable references and emits an event when any one of the
1115
* Scrollable references emit a scrolled event.
@@ -50,11 +54,17 @@ export class ScrollDispatcher {
5054

5155
/**
5256
* Returns an observable that emits an event whenever any of the registered Scrollable
53-
* references (or window, document, or body) fire a scrolled event.
57+
* references (or window, document, or body) fire a scrolled event. Can provide a time in ms
58+
* to override the default "throttle" time.
5459
*/
55-
scrolled(): Observable<void> {
56-
// TODO: Add an event limiter that includes throttle with the leading and trailing events.
57-
return this._scrolled.asObservable();
60+
scrolled(auditTimeInMs: number = DEFAULT_SCROLL_TIME): Observable<void> {
61+
// In the case of a 0ms delay, return the observable without auditTime since it does add
62+
// a perceptible delay in processing overhead.
63+
if (auditTimeInMs == 0) {
64+
return this._scrolled.asObservable();
65+
}
66+
67+
return this._scrolled.asObservable().auditTime(auditTimeInMs);
5868
}
5969

6070
/** Returns all registered Scrollables that contain the provided element. */
@@ -90,7 +100,7 @@ export class ScrollDispatcher {
90100

91101
export function SCROLL_DISPATCHER_PROVIDER_FACTORY(parentDispatcher: ScrollDispatcher) {
92102
return parentDispatcher || new ScrollDispatcher();
93-
};
103+
}
94104

95105
export const SCROLL_DISPATCHER_PROVIDER = {
96106
// If there is already a ScrollDispatcher available, use that. Otherwise, provide a new one.

src/lib/dialog/dialog.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ describe('MdDialog', () => {
408408

409409
expect(document.activeElement.id)
410410
.toBe('dialog-trigger', 'Expected that the trigger was refocused after dialog close');
411+
412+
document.body.removeChild(button);
411413
}));
412414
});
413415

src/lib/select/select.spec.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ViewChild,
88
ViewChildren,
99
ChangeDetectionStrategy,
10+
OnInit,
1011
} from '@angular/core';
1112
import {MdSelectModule} from './index';
1213
import {OverlayContainer} from '../core/overlay/overlay-container';
@@ -34,6 +35,8 @@ describe('MdSelect', () => {
3435
SelectWithChangeEvent,
3536
CustomSelectAccessor,
3637
CompWithCustomSelect,
38+
SelectWithErrorSibling,
39+
ThrowsErrorOnInit,
3740
BasicSelectOnPush
3841
],
3942
providers: [
@@ -1239,6 +1242,14 @@ describe('MdSelect', () => {
12391242
});
12401243
}));
12411244

1245+
it('should not crash the browser when a sibling throws an error on init', async(() => {
1246+
// Note that this test can be considered successful if the error being thrown didn't
1247+
// end up crashing the testing setup altogether.
1248+
expect(() => {
1249+
TestBed.createComponent(SelectWithErrorSibling).detectChanges();
1250+
}).toThrowError(new RegExp('Oh no!', 'g'));
1251+
}));
1252+
12421253
});
12431254

12441255
describe('change event', () => {
@@ -1281,7 +1292,7 @@ describe('MdSelect', () => {
12811292
beforeEach(() => {
12821293
fixture = TestBed.createComponent(BasicSelectOnPush);
12831294
fixture.detectChanges();
1284-
trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement;
1295+
trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
12851296
});
12861297

12871298
it('should update the trigger based on the value', () => {
@@ -1474,6 +1485,27 @@ class CompWithCustomSelect {
14741485
@ViewChild(CustomSelectAccessor) customAccessor: CustomSelectAccessor;
14751486
}
14761487

1488+
@Component({
1489+
selector: 'select-infinite-loop',
1490+
template: `
1491+
<md-select [(ngModel)]="value"></md-select>
1492+
<throws-error-on-init></throws-error-on-init>
1493+
`
1494+
})
1495+
class SelectWithErrorSibling {
1496+
value: string;
1497+
}
1498+
1499+
@Component({
1500+
selector: 'throws-error-on-init',
1501+
template: ''
1502+
})
1503+
export class ThrowsErrorOnInit implements OnInit {
1504+
ngOnInit() {
1505+
throw new Error('Oh no!');
1506+
}
1507+
}
1508+
14771509
@Component({
14781510
selector: 'basic-select-on-push',
14791511
changeDetection: ChangeDetectionStrategy.OnPush,

0 commit comments

Comments
 (0)