|
|
|
@ -1,16 +1,16 @@
|
|
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
|
*/
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import {DirectiveDef} from '@angular/core/src/render3';
|
|
|
|
|
import {ɵɵdefineDirective} from '@angular/core/src/render3/definition';
|
|
|
|
|
import {classStringParser, styleStringParser, toStylingKeyValueArray, ɵɵclassProp, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer} from '@angular/core/src/render3/instructions/styling';
|
|
|
|
|
import {AttributeMarker, TAttributes} from '@angular/core/src/render3/interfaces/node';
|
|
|
|
|
import {StylingRange, TStylingKey, TStylingRange, getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, setTStylingRangeNext, setTStylingRangePrev, toTStylingRange} from '@angular/core/src/render3/interfaces/styling';
|
|
|
|
|
import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, setTStylingRangeNext, setTStylingRangePrev, StylingRange, toTStylingRange, TStylingKey, TStylingRange} from '@angular/core/src/render3/interfaces/styling';
|
|
|
|
|
import {HEADER_OFFSET, TVIEW} from '@angular/core/src/render3/interfaces/view';
|
|
|
|
|
import {getLView, leaveView, setBindingRootForHostBindings} from '@angular/core/src/render3/state';
|
|
|
|
|
import {getNativeByIndex} from '@angular/core/src/render3/util/view_utils';
|
|
|
|
@ -27,7 +27,7 @@ describe('styling', () => {
|
|
|
|
|
beforeEach(enterViewWithOneDiv);
|
|
|
|
|
afterEach(leaveView);
|
|
|
|
|
|
|
|
|
|
let div !: HTMLElement;
|
|
|
|
|
let div!: HTMLElement;
|
|
|
|
|
beforeEach(() => div = getNativeByIndex(0, getLView()) as HTMLElement);
|
|
|
|
|
|
|
|
|
|
it('should do set basic style', () => {
|
|
|
|
@ -68,7 +68,7 @@ describe('styling', () => {
|
|
|
|
|
ɵɵstyleProp('color', 'blue'); // Higher priority, should win.
|
|
|
|
|
expectStyle(div).toEqual({color: 'blue'});
|
|
|
|
|
// The intermediate value has to set since it does not know if it is last one.
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(2);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(2);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
clearFirstUpdatePass();
|
|
|
|
@ -76,34 +76,34 @@ describe('styling', () => {
|
|
|
|
|
ɵɵstyleProp('color', 'red'); // no change
|
|
|
|
|
ɵɵstyleProp('color', 'green'); // change to green
|
|
|
|
|
expectStyle(div).toEqual({color: 'green'});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleProp('color', 'black'); // First binding update
|
|
|
|
|
expectStyle(div).toEqual({color: 'green'}); // Green still has priority.
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(0);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleProp('color', 'black'); // no change
|
|
|
|
|
ɵɵstyleProp('color', undefined); // Clear second binding
|
|
|
|
|
expectStyle(div).toEqual({color: 'black'}); // default to first binding
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleProp('color', null);
|
|
|
|
|
expectStyle(div).toEqual({}); // default to first binding
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set class based on priority', () => {
|
|
|
|
|
ɵɵclassProp('foo', false);
|
|
|
|
|
ɵɵclassProp('foo', true); // Higher priority, should win.
|
|
|
|
|
expectClass(div).toEqual({foo: true});
|
|
|
|
|
expect(ngDevMode !.rendererAddClass).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererAddClass).toEqual(1);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
clearFirstUpdatePass();
|
|
|
|
@ -111,8 +111,8 @@ describe('styling', () => {
|
|
|
|
|
ɵɵclassProp('foo', false); // no change
|
|
|
|
|
ɵɵclassProp('foo', undefined); // change (have no opinion)
|
|
|
|
|
expectClass(div).toEqual({});
|
|
|
|
|
expect(ngDevMode !.rendererAddClass).toEqual(0);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveClass).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererAddClass).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveClass).toEqual(1);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
@ -130,8 +130,8 @@ describe('styling', () => {
|
|
|
|
|
it('should work with maps', () => {
|
|
|
|
|
ɵɵstyleMap({});
|
|
|
|
|
expectStyle(div).toEqual({});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
clearFirstUpdatePass();
|
|
|
|
@ -139,30 +139,30 @@ describe('styling', () => {
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleMap({color: 'blue'});
|
|
|
|
|
expectStyle(div).toEqual({color: 'blue'});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleMap({color: 'red'});
|
|
|
|
|
expectStyle(div).toEqual({color: 'red'});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleMap({color: null, width: '100px'});
|
|
|
|
|
expectStyle(div).toEqual({width: '100px'});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(1);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should work with object literal and strings', () => {
|
|
|
|
|
ɵɵstyleMap('');
|
|
|
|
|
expectStyle(div).toEqual({});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
clearFirstUpdatePass();
|
|
|
|
@ -170,22 +170,22 @@ describe('styling', () => {
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleMap('color: blue');
|
|
|
|
|
expectStyle(div).toEqual({color: 'blue'});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleMap('color: red');
|
|
|
|
|
expectStyle(div).toEqual({color: 'red'});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(0);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
|
|
|
|
|
rewindBindingIndex();
|
|
|
|
|
ɵɵstyleMap('width: 100px');
|
|
|
|
|
expectStyle(div).toEqual({width: '100px'});
|
|
|
|
|
expect(ngDevMode !.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode !.rendererRemoveStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererSetStyle).toEqual(1);
|
|
|
|
|
expect(ngDevMode!.rendererRemoveStyle).toEqual(1);
|
|
|
|
|
ngDevModeResetPerfCounters();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@ -395,7 +395,6 @@ describe('styling', () => {
|
|
|
|
|
['color', 'red', 'content', '"TEMPLATE"']
|
|
|
|
|
]);
|
|
|
|
|
expectStyle(div).toEqual({content: '"TEMPLATE"', color: 'red', width: '100px'});
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
@ -404,23 +403,23 @@ describe('styling', () => {
|
|
|
|
|
describe('toStylingArray', () => {
|
|
|
|
|
describe('falsy', () => {
|
|
|
|
|
it('should return empty KeyValueArray', () => {
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null !, '')).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null !, null)).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null !, undefined)).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null !, [])).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null !, {})).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null!, '')).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null!, null)).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null!, undefined)).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null!, [])).toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null!, {})).toEqual([] as any);
|
|
|
|
|
});
|
|
|
|
|
describe('string', () => {
|
|
|
|
|
it('should parse classes', () => {
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' ')).toEqual([
|
|
|
|
|
] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' '))
|
|
|
|
|
.toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' X A ')).toEqual([
|
|
|
|
|
'A', true, 'X', true
|
|
|
|
|
] as any);
|
|
|
|
|
});
|
|
|
|
|
it('should parse styles', () => {
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, ' ')).toEqual([
|
|
|
|
|
] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, ' '))
|
|
|
|
|
.toEqual([] as any);
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, 'B:b;A:a')).toEqual([
|
|
|
|
|
'A', 'a', 'B', 'b'
|
|
|
|
|
] as any);
|
|
|
|
@ -428,14 +427,14 @@ describe('styling', () => {
|
|
|
|
|
});
|
|
|
|
|
describe('array', () => {
|
|
|
|
|
it('should parse', () => {
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null !, ['X', 'A'])).toEqual([
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null!, ['X', 'A'])).toEqual([
|
|
|
|
|
'A', true, 'X', true
|
|
|
|
|
] as any);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
describe('object', () => {
|
|
|
|
|
it('should parse', () => {
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null !, {X: 'x', A: 'a'})).toEqual([
|
|
|
|
|
expect(toStylingKeyValueArray(keyValueArraySet, null!, {X: 'x', A: 'a'})).toEqual([
|
|
|
|
|
'A', 'a', 'X', 'x'
|
|
|
|
|
] as any);
|
|
|
|
|
});
|
|
|
|
@ -495,7 +494,7 @@ function givenTemplateAttrs(tAttrs: TAttributes) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTNode() {
|
|
|
|
|
return getLView()[TVIEW].firstChild !;
|
|
|
|
|
return getLView()[TVIEW].firstChild!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTData() {
|
|
|
|
@ -517,7 +516,7 @@ function givenDirectiveAttrs(tAttrs: TAttributes[]) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function applyTAttributes(attrs: TAttributes | null) {
|
|
|
|
|
function applyTAttributes(attrs: TAttributes|null) {
|
|
|
|
|
if (attrs === null) return;
|
|
|
|
|
const div: HTMLElement = getLView()[HEADER_OFFSET];
|
|
|
|
|
let mode: AttributeMarker = AttributeMarker.ImplicitAttributes;
|
|
|
|
@ -553,12 +552,12 @@ function getTDataIndexFromDirectiveIndex(index: number) {
|
|
|
|
|
return HEADER_OFFSET + index + 10; // offset to give template bindings space.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function expectTStylingKeys(styling: 'style' | 'class') {
|
|
|
|
|
function expectTStylingKeys(styling: 'style'|'class') {
|
|
|
|
|
const tNode = getTNode();
|
|
|
|
|
const tData = getTData();
|
|
|
|
|
const isClassBased = styling === 'class';
|
|
|
|
|
const headIndex = getTStylingRangePrev(isClassBased ? tNode.classBindings : tNode.styleBindings);
|
|
|
|
|
const tStylingKeys: (string | (null | string)[] | null)[] = [];
|
|
|
|
|
const tStylingKeys: (string|(null | string)[]|null)[] = [];
|
|
|
|
|
let index = headIndex;
|
|
|
|
|
let prevIndex = index;
|
|
|
|
|
// rewind to beginning of list.
|
|
|
|
|