mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(breakpoint-observer): Emit matching state of each query provided (#12506)
This commit is contained in:
@@ -34,7 +34,7 @@ describe('BreakpointObserver', () => {
|
||||
});
|
||||
|
||||
it('retrieves the whether a query is currently matched', () => {
|
||||
let query = 'everything starts as true in the FakeMediaMatcher';
|
||||
const query = 'everything starts as true in the FakeMediaMatcher';
|
||||
expect(breakpointManager.isMatched(query)).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -61,15 +61,15 @@ describe('BreakpointObserver', () => {
|
||||
});
|
||||
|
||||
it('accepts an array of queries', () => {
|
||||
let queries = ['1 query', '2 query', 'red query', 'blue query'];
|
||||
const queries = ['1 query', '2 query', 'red query', 'blue query'];
|
||||
breakpointManager.observe(queries);
|
||||
expect(mediaMatcher.queryCount).toBe(queries.length);
|
||||
});
|
||||
|
||||
it('completes all events when the breakpoint manager is destroyed', () => {
|
||||
let firstTest = jasmine.createSpy('test1');
|
||||
const firstTest = jasmine.createSpy('test1');
|
||||
breakpointManager.observe('test1').subscribe(undefined, undefined, firstTest);
|
||||
let secondTest = jasmine.createSpy('test2');
|
||||
const secondTest = jasmine.createSpy('test2');
|
||||
breakpointManager.observe('test2').subscribe(undefined, undefined, secondTest);
|
||||
|
||||
expect(firstTest).not.toHaveBeenCalled();
|
||||
@@ -82,8 +82,8 @@ describe('BreakpointObserver', () => {
|
||||
});
|
||||
|
||||
it('emits an event on the observable when values change', () => {
|
||||
let query = '(width: 999px)';
|
||||
let queryMatchState: boolean = false;
|
||||
const query = '(width: 999px)';
|
||||
let queryMatchState = false;
|
||||
breakpointManager.observe(query).subscribe((state: BreakpointState) => {
|
||||
queryMatchState = state.matches;
|
||||
});
|
||||
@@ -93,14 +93,31 @@ describe('BreakpointObserver', () => {
|
||||
expect(queryMatchState).toBeFalsy();
|
||||
});
|
||||
|
||||
it('emits an event on the observable with the matching state of all queries provided', () => {
|
||||
const queryOne = '(width: 999px)';
|
||||
const queryTwo = '(width: 700px)';
|
||||
let state: BreakpointState = {matches: false, breakpoints: {}};
|
||||
breakpointManager.observe([queryOne, queryTwo]).subscribe((breakpoint: BreakpointState) => {
|
||||
state = breakpoint;
|
||||
});
|
||||
|
||||
mediaMatcher.setMatchesQuery(queryOne, false);
|
||||
mediaMatcher.setMatchesQuery(queryTwo, false);
|
||||
expect(state.breakpoints).toEqual({[queryOne]: false, [queryTwo]: false});
|
||||
|
||||
mediaMatcher.setMatchesQuery(queryOne, true);
|
||||
mediaMatcher.setMatchesQuery(queryTwo, false);
|
||||
expect(state.breakpoints).toEqual({[queryOne]: true, [queryTwo]: false});
|
||||
});
|
||||
|
||||
it('emits a true matches state when the query is matched', () => {
|
||||
let query = '(width: 999px)';
|
||||
const query = '(width: 999px)';
|
||||
mediaMatcher.setMatchesQuery(query, true);
|
||||
expect(breakpointManager.isMatched(query)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('emits a false matches state when the query is not matched', () => {
|
||||
let query = '(width: 999px)';
|
||||
const query = '(width: 999px)';
|
||||
mediaMatcher.setMatchesQuery(query, false);
|
||||
expect(breakpointManager.isMatched(query)).toBeTruthy();
|
||||
});
|
||||
@@ -130,7 +147,7 @@ export class FakeMediaQueryList implements MediaQueryList {
|
||||
@Injectable()
|
||||
export class FakeMediaMatcher {
|
||||
/** A map of match media queries. */
|
||||
private queries: Map<string, FakeMediaQueryList> = new Map();
|
||||
private queries = new Map<string, FakeMediaQueryList>();
|
||||
|
||||
/** The number of distinct queries created in the media matcher during a test. */
|
||||
get queryCount(): number {
|
||||
@@ -139,7 +156,7 @@ export class FakeMediaMatcher {
|
||||
|
||||
/** Fakes the match media response to be controlled in tests. */
|
||||
matchMedia(query: string): FakeMediaQueryList {
|
||||
let mql = new FakeMediaQueryList(true, query);
|
||||
const mql = new FakeMediaQueryList(true, query);
|
||||
this.queries.set(query, mql);
|
||||
return mql;
|
||||
}
|
||||
|
||||
@@ -16,10 +16,25 @@ import {coerceArray} from '@angular/cdk/coercion';
|
||||
export interface BreakpointState {
|
||||
/** Whether the breakpoint is currently matching. */
|
||||
matches: boolean;
|
||||
/**
|
||||
* A key boolean pair for each query provided to the observe method,
|
||||
* with its current matched state.
|
||||
*/
|
||||
breakpoints: {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/** The current state of a layout breakpoint. */
|
||||
interface InternalBreakpointState {
|
||||
/** Whether the breakpoint is currently matching. */
|
||||
matches: boolean;
|
||||
/** The media query being to be matched */
|
||||
query: string;
|
||||
}
|
||||
|
||||
interface Query {
|
||||
observable: Observable<BreakpointState>;
|
||||
observable: Observable<InternalBreakpointState>;
|
||||
mql: MediaQueryList;
|
||||
}
|
||||
|
||||
@@ -27,9 +42,9 @@ interface Query {
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class BreakpointObserver implements OnDestroy {
|
||||
/** A map of all media queries currently being listened for. */
|
||||
private _queries: Map<string, Query> = new Map();
|
||||
private _queries = new Map<string, Query>();
|
||||
/** A subject for all other observables to takeUntil based on. */
|
||||
private _destroySubject: Subject<{}> = new Subject();
|
||||
private _destroySubject = new Subject<void>();
|
||||
|
||||
constructor(private mediaMatcher: MediaMatcher, private zone: NgZone) {}
|
||||
|
||||
@@ -59,10 +74,16 @@ export class BreakpointObserver implements OnDestroy {
|
||||
const queries = splitQueries(coerceArray(value));
|
||||
const observables = queries.map(query => this._registerQuery(query).observable);
|
||||
|
||||
return combineLatest(observables).pipe(map((breakpointStates: BreakpointState[]) => {
|
||||
return {
|
||||
matches: breakpointStates.some(state => state && state.matches)
|
||||
return combineLatest(observables).pipe(map((breakpointStates: InternalBreakpointState[]) => {
|
||||
const response: BreakpointState = {
|
||||
matches: false,
|
||||
breakpoints: {},
|
||||
};
|
||||
breakpointStates.forEach((state: InternalBreakpointState) => {
|
||||
response.matches = response.matches || state.matches;
|
||||
response.breakpoints[state.query] = state.matches;
|
||||
});
|
||||
return response;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -90,11 +111,11 @@ export class BreakpointObserver implements OnDestroy {
|
||||
.pipe(
|
||||
takeUntil(this._destroySubject),
|
||||
startWith(mql),
|
||||
map((nextMql: MediaQueryList) => ({matches: nextMql.matches}))
|
||||
map((nextMql: MediaQueryList) => ({query, matches: nextMql.matches}))
|
||||
);
|
||||
|
||||
// Add the MediaQueryList to the set of queries.
|
||||
const output = {observable: queryObservable, mql: mql};
|
||||
const output = {observable: queryObservable, mql};
|
||||
this._queries.set(query, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user