Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit 7f2b07b

Browse files
fix(api): restore orig display mode and more...
* fxShow and fxHide should restore original display modes when deactivating * combination uses of fxHide + fxShow should work properly * static uses of `fxHide=""` should hide the hosting elmement fixes #140, fixes #141.
1 parent 6e46561 commit 7f2b07b

File tree

8 files changed

+134
-37
lines changed

8 files changed

+134
-37
lines changed

src/lib/flexbox/api/base.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {ElementRef, Renderer, OnDestroy} from '@angular/core';
9+
10+
import {__platform_browser_private__} from '@angular/platform-browser';
11+
const getDOM = __platform_browser_private__.getDOM;
12+
913
import {applyCssPrefixes} from '../../utils/auto-prefixer';
1014

1115
import {ResponsiveActivation, KeyOptions} from '../responsive/responsive-activation';
@@ -16,7 +20,7 @@ import {MediaQuerySubscriber} from '../../media-query/media-change';
1620
* Definition of a css style. Either a property name (e.g. "flex-basis") or an object
1721
* map of property name and value (e.g. {display: 'none', flex-order: 5}).
1822
*/
19-
export type StyleDefinition = string|{[property: string]: string|number};
23+
export type StyleDefinition = string|{ [property: string]: string|number };
2024

2125
/** Abstract base class for the Layout API styling directives. */
2226
export abstract class BaseFxDirective implements OnDestroy {
@@ -88,7 +92,8 @@ export abstract class BaseFxDirective implements OnDestroy {
8892
*/
8993
protected _getDisplayStyle(): string {
9094
let element: HTMLElement = this._elementRef.nativeElement;
91-
return (element.style as any)['display'] || "flex";
95+
let value = (element.style as any)['display'] || getDOM().getComputedStyle(element)['display'];
96+
return value.trim();
9297
}
9398

9499
/**
@@ -109,7 +114,7 @@ export abstract class BaseFxDirective implements OnDestroy {
109114

110115
// Iterate all properties in hashMap and set styles
111116
for (let key in styles) {
112-
this._renderer.setElementStyle(element, key, styles[key]);
117+
this._renderer.setElementStyle(element, key, styles[key] as string);
113118
}
114119
}
115120

@@ -122,7 +127,7 @@ export abstract class BaseFxDirective implements OnDestroy {
122127
elements.forEach(el => {
123128
// Iterate all properties in hashMap and set styles
124129
for (let key in styles) {
125-
this._renderer.setElementStyle(el, key, styles[key]);
130+
this._renderer.setElementStyle(el, key, styles[key] as string);
126131
}
127132
});
128133

@@ -172,4 +177,11 @@ export abstract class BaseFxDirective implements OnDestroy {
172177
return buffer;
173178
}
174179

180+
/**
181+
* Fast validator for presence of attribute on the host element
182+
*/
183+
protected hasKeyValue(key) {
184+
return this._mqActivation.hasKeyValue(key);
185+
}
186+
175187
}

src/lib/flexbox/api/hide.spec.ts

+46-15
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {ObservableMediaService} from '../../media-query/observable-media-service
1717
import {BreakPointsProvider} from '../../media-query/providers/break-points-provider';
1818
import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-registry';
1919

20-
import {customMatchers, expect} from '../../utils/testing/custom-matchers';
20+
import {customMatchers, expect, NgMatchers} from '../../utils/testing/custom-matchers';
2121
import {
2222
makeCreateTestComponent, expectNativeEl, queryFor
2323
} from '../../utils/testing/helpers';
@@ -27,9 +27,22 @@ import {MediaQueriesModule} from '../../media-query/_module';
2727
describe('hide directive', () => {
2828
let fixture: ComponentFixture<any>;
2929
let createTestComponent = makeCreateTestComponent(() => TestHideComponent);
30-
let activateMediaQuery = (alias) => {
30+
let activateMediaQuery: Function = (alias, useOverlaps = false): void => {
3131
let matchMedia: MockMatchMedia = fixture.debugElement.injector.get(MatchMedia);
32-
matchMedia.activate(alias);
32+
matchMedia.activate(alias, useOverlaps);
33+
};
34+
let makeExpectWithActivation = (_fixture_: ComponentFixture<any>, selector: string) => {
35+
fixture = _fixture_;
36+
return (alias?: string): NgMatchers => {
37+
if (alias) {
38+
activateMediaQuery(alias);
39+
}
40+
fixture.detectChanges();
41+
42+
let nodes = queryFor(fixture, selector);
43+
expect(nodes.length).toEqual(1);
44+
return expect(nodes[0].nativeElement);
45+
};
3346
};
3447

3548
beforeEach(() => {
@@ -102,7 +115,7 @@ describe('hide directive', () => {
102115
</button>
103116
`);
104117
expectNativeEl(fixture, {isHidden: true}).toHaveCssStyle({'display': 'none'});
105-
expectNativeEl(fixture, {isHidden: false}).toHaveCssStyle({'display': 'block'});
118+
expectNativeEl(fixture, {isHidden: false}).toHaveCssStyle({'display': 'inline-block'});
106119
});
107120

108121
it('should use "flex" display style when the element also has an fxLayout', () => {
@@ -226,23 +239,41 @@ describe('hide directive', () => {
226239
</div>
227240
</div>
228241
`;
242+
let expectActivation = makeExpectWithActivation(createTestComponent(template), '.hideOnMd');
229243

230-
fixture = createTestComponent(template);
231-
fixture.detectChanges();
232-
233-
let nodes = queryFor(fixture, ".hideOnMd");
234-
expect(nodes.length).toEqual(1);
235-
expect(nodes[0].nativeElement).toHaveCssStyle({'display': 'block'});
244+
expectActivation().toHaveCssStyle({'display': 'block'});
245+
expectActivation('md').toHaveCssStyle({'display': 'none'});
246+
});
236247

237-
activateMediaQuery('md');
238-
fixture.detectChanges();
248+
it('should restore proper display mode when not hiding', () => {
249+
let template = `
250+
<div>
251+
<span fxHide.xs class="hideOnXs">Label</span>
252+
</div>
253+
`;
254+
let expectActivation = makeExpectWithActivation(createTestComponent(template), '.hideOnXs');
239255

240-
nodes = queryFor(fixture, ".hideOnMd");
241-
expect(nodes.length).toEqual(1);
242-
expect(nodes[0].nativeElement).toHaveCssStyle({'display': 'none'});
256+
expectActivation().toHaveCssStyle({'display': 'inline'});
257+
expectActivation('xs').toHaveCssStyle({'display': 'none'});
258+
expectActivation('md').toHaveCssStyle({'display': 'inline'});
243259
});
244260
});
245261

262+
it('should support hide and show', () => {
263+
fixture = createTestComponent(`
264+
<div fxShow fxHide.gt-sm>
265+
This content to be shown ONLY when gt-sm
266+
</div>
267+
`);
268+
expectNativeEl(fixture).toHaveCssStyle({'display': 'block'});
269+
270+
activateMediaQuery('md', true);
271+
expectNativeEl(fixture).toHaveCssStyle({'display': 'none'});
272+
273+
activateMediaQuery('xs', true);
274+
expectNativeEl(fixture).toHaveCssStyle({'display': 'block'});
275+
});
276+
246277
});
247278

248279

src/lib/flexbox/api/hide.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,14 @@ export class HideDirective extends BaseFxDirective implements OnInit, OnChanges,
101101
protected renderer: Renderer) {
102102
super(monitor, elRef, renderer);
103103

104-
this._display = this._getDisplayStyle(); // re-invoke override to use `this._layout`
104+
/**
105+
* The Layout can set the display:flex (and incorrectly affect the Hide/Show directives.
106+
* Whenever Layout [on the same element] resets its CSS, then update the Hide/Show CSS
107+
*/
105108
if (_layout) {
106-
/**
107-
* The Layout can set the display:flex (and incorrectly affect the Hide/Show directives.
108-
* Whenever Layout [on the same element] resets its CSS, then update the Hide/Show CSS
109-
*/
110109
this._layoutWatcher = _layout.layout$.subscribe(() => this._updateWithValue());
111110
}
111+
this._display = this._getDisplayStyle(); // re-invoke override to use `this._layout`
112112
}
113113

114114

@@ -122,8 +122,7 @@ export class HideDirective extends BaseFxDirective implements OnInit, OnChanges,
122122
* unless it was already explicitly defined.
123123
*/
124124
protected _getDisplayStyle(): string {
125-
let element: HTMLElement = this._elementRef.nativeElement;
126-
return (element.style as any)['display'] || (this._layout ? "flex" : "block");
125+
return this._layout ? "flex" : super._getDisplayStyle();
127126
}
128127

129128
/**
@@ -140,9 +139,15 @@ export class HideDirective extends BaseFxDirective implements OnInit, OnChanges,
140139
/**
141140
* After the initial onChanges, build an mqActivation object that bridges
142141
* mql change events to onMediaQueryChange handlers
142+
* NOTE: fxHide has special fallback defaults.
143+
* - If the non-responsive fxHide="" is specified we default to hide==true
144+
* - If the non-responsive fxHide is NOT specified, use default hide == false
145+
* This logic supports mixed usages with fxShow; e.g. `<div fxHide fxShow.gt-sm>`
143146
*/
144147
ngOnInit() {
145-
let value = this._getDefaultVal("hide", false);
148+
// If the attribute 'fxHide' is specified we default to hide==true, otherwise do nothing..
149+
let value = (this._queryInput('hide') == "") ? true : this._getDefaultVal("hide", false);
150+
146151
this._listenForMediaQueryChanges('hide', value, (changes: MediaChange) => {
147152
this._updateWithValue(changes.value);
148153
});

src/lib/flexbox/api/show.spec.ts

+19
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,25 @@ describe('show directive', () => {
199199

200200

201201
});
202+
203+
describe('with fxHide features', () => {
204+
205+
it('should support hide and show', () => {
206+
fixture = createTestComponent(`
207+
<div fxHide fxShow.gt-sm>
208+
This content to be shown ONLY when gt-sm
209+
</div>
210+
`);
211+
expectNativeEl(fixture).toHaveCssStyle({'display': 'none'});
212+
213+
activateMediaQuery('md', true);
214+
expectNativeEl(fixture).toHaveCssStyle({'display': 'block'});
215+
216+
activateMediaQuery('xs', true);
217+
expectNativeEl(fixture).toHaveCssStyle({'display': 'none'});
218+
});
219+
});
220+
202221
});
203222

204223

src/lib/flexbox/api/show.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {MediaChange} from '../../media-query/media-change';
2525
import {MediaMonitor} from '../../media-query/media-monitor';
2626

2727
import {LayoutDirective} from './layout';
28+
import {HideDirective} from './hide';
2829

2930

3031
const FALSY = ['false', false, 0];
@@ -100,6 +101,7 @@ export class ShowDirective extends BaseFxDirective implements OnInit, OnChanges,
100101
*/
101102
constructor(monitor: MediaMonitor,
102103
@Optional() @Self() private _layout: LayoutDirective,
104+
@Optional() @Self() private _hide: HideDirective,
103105
protected elRef: ElementRef,
104106
protected renderer: Renderer) {
105107

@@ -148,10 +150,16 @@ export class ShowDirective extends BaseFxDirective implements OnInit, OnChanges,
148150
ngOnInit() {
149151
let value = this._getDefaultVal("show", true);
150152

153+
// Build _mqActivation controller
151154
this._listenForMediaQueryChanges('show', value, (changes: MediaChange) => {
152-
this._updateWithValue(changes.value);
155+
if (!this._delegateToHide(changes)) {
156+
this._updateWithValue(changes.value);
157+
}
153158
});
154-
this._updateWithValue();
159+
160+
if (!this._delegateToHide()) {
161+
this._updateWithValue();
162+
}
155163
}
156164

157165
ngOnDestroy() {
@@ -165,6 +173,21 @@ export class ShowDirective extends BaseFxDirective implements OnInit, OnChanges,
165173
// Protected methods
166174
// *********************************************
167175

176+
/**
177+
* If deactiving Show, then delegate action to the Hide directive if it is
178+
* specified on same element.
179+
*/
180+
protected _delegateToHide(changes?: MediaChange) {
181+
if (this._hide) {
182+
let delegate = (changes && !changes.matches) || (!changes && !this.hasKeyValue('show'));
183+
if (delegate) {
184+
this._hide.ngOnChanges({});
185+
return true;
186+
}
187+
}
188+
return false;
189+
}
190+
168191
/** Validate the visibility value and then update the host's inline display style */
169192
private _updateWithValue(value?: string|number|boolean) {
170193
value = value || this._getDefaultVal("show", true);

src/lib/flexbox/responsive/responsive-activation.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
import {Subscription} from 'rxjs/Subscription';
99
import 'rxjs/add/operator/map';
10+
import 'rxjs/add/operator/filter';
1011
import {extendObject} from '../../utils/object-extend';
1112

1213
import {MediaChange, MediaQuerySubscriber} from '../../media-query/media-change';
@@ -23,7 +24,7 @@ export interface BreakPointX extends BreakPoint {
2324
export class KeyOptions {
2425
constructor(public baseKey: string,
2526
public defaultValue: string|number|boolean,
26-
public inputKeys: {[key: string]: any}) {
27+
public inputKeys: { [key: string]: any }) {
2728
}
2829
}
2930

@@ -78,7 +79,15 @@ export class ResponsiveActivation {
7879
*/
7980
get activatedInput(): any {
8081
let key = this.activatedInputKey;
81-
return this._hasKeyValue(key) ? this._lookupKeyValue(key) : this._options.defaultValue;
82+
return this.hasKeyValue(key) ? this._lookupKeyValue(key) : this._options.defaultValue;
83+
}
84+
85+
/**
86+
* Fast validator for presence of attribute on the host element
87+
*/
88+
public hasKeyValue(key) {
89+
let value = this._options.inputKeys[key];
90+
return typeof value !== 'undefined';
8291
}
8392

8493
/**
@@ -203,8 +212,4 @@ export class ResponsiveActivation {
203212
return this._options.inputKeys[key];
204213
}
205214

206-
private _hasKeyValue(key) {
207-
let value = this._options.inputKeys[key];
208-
return typeof value !== 'undefined';
209-
}
210215
}

src/lib/media-query/mock/mock-match-media.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ export class MockMediaQueryList implements MediaQueryList {
217217
if (this._listeners.indexOf(listener) === -1) {
218218
this._listeners.push(listener);
219219
}
220-
if (this._isActive) { listener(this); }
220+
if (this._isActive) {
221+
listener(this);
222+
}
221223
}
222224

223225
removeListener(listener: MediaQueryListListener) {

src/lib/utils/testing/custom-matchers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export const customMatchers: jasmine.CustomMatcherFactories = {
148148
* (Safari, IE, etc) will use prefixed style instead of defaults.
149149
*/
150150
function hasPrefixedStyles(actual, key, value) {
151-
let hasStyle = getDOM().hasStyle(actual, key, value);
151+
let hasStyle = getDOM().hasStyle(actual, key, value.trim());
152152
if (!hasStyle) {
153153
let prefixedStyles = applyCssPrefixes({[key]: value});
154154
Object.keys(prefixedStyles).forEach(prop => {

0 commit comments

Comments
 (0)