fix(ivy): ensure falsy styling is not applied during creation mode (#26793)
PR Close #26793
This commit is contained in:
parent
332394d87c
commit
68b2211e64
@ -1161,8 +1161,9 @@ export function elementStyling(
|
||||
*/
|
||||
export function elementStylingApply(index: number): void {
|
||||
const viewData = getViewData();
|
||||
const totalPlayersQueued =
|
||||
renderStyleAndClassBindings(getStylingContext(index, viewData), getRenderer(), viewData);
|
||||
const isFirstRender = (viewData[FLAGS] & LViewFlags.CreationMode) !== 0;
|
||||
const totalPlayersQueued = renderStyleAndClassBindings(
|
||||
getStylingContext(index, viewData), getRenderer(), viewData, isFirstRender);
|
||||
if (totalPlayersQueued > 0) {
|
||||
const rootContext = getRootContext(viewData);
|
||||
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
||||
|
@ -21,8 +21,8 @@ export interface Player {
|
||||
|
||||
export const enum BindingType {
|
||||
Unset = 0,
|
||||
Class = 2,
|
||||
Style = 3,
|
||||
Class = 1,
|
||||
Style = 2,
|
||||
}
|
||||
|
||||
export interface BindingStore { setValue(prop: string, value: any): void; }
|
||||
@ -35,7 +35,7 @@ export interface BindingStore { setValue(prop: string, value: any): void; }
|
||||
* to be used with `PlayerFactory`.
|
||||
*/
|
||||
export interface PlayerFactoryBuildFn {
|
||||
(element: HTMLElement, type: BindingType, values: {[key: string]: any},
|
||||
(element: HTMLElement, type: BindingType, values: {[key: string]: any}, isFirstRender: boolean,
|
||||
currentPlayer: Player|null): Player|null;
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ export interface PlayerFactoryBuildFn {
|
||||
export interface PlayerFactory { '__brand__': 'Brand for PlayerFactory that nothing will match'; }
|
||||
|
||||
export interface PlayerBuilder extends BindingStore {
|
||||
buildPlayer(currentPlayer: Player|null): Player|undefined|null;
|
||||
buildPlayer(currentPlayer: Player|null, isFirstRender: boolean): Player|undefined|null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,7 +196,7 @@ export const enum StylingFlags {
|
||||
// The max amount of bits used to represent these configuration values
|
||||
BitCountSize = 5,
|
||||
// There are only five bits here
|
||||
BitMask = 0b1111
|
||||
BitMask = 0b11111
|
||||
}
|
||||
|
||||
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
|
||||
|
@ -480,8 +480,10 @@ export function updateClassProp(
|
||||
*/
|
||||
export function renderStyleAndClassBindings(
|
||||
context: StylingContext, renderer: Renderer3, rootOrView: RootContext | LViewData,
|
||||
classesStore?: BindingStore | null, stylesStore?: BindingStore | null): number {
|
||||
isFirstRender: boolean, classesStore?: BindingStore | null,
|
||||
stylesStore?: BindingStore | null): number {
|
||||
let totalPlayersQueued = 0;
|
||||
|
||||
if (isContextDirty(context)) {
|
||||
const flushPlayerBuilders: any =
|
||||
context[StylingIndex.MasterFlagPosition] & StylingFlags.PlayerBuildersDirty;
|
||||
@ -523,15 +525,23 @@ export function renderStyleAndClassBindings(
|
||||
valueToApply = getInitialValue(context, flag);
|
||||
}
|
||||
|
||||
if (isClassBased) {
|
||||
setClass(
|
||||
native, prop, valueToApply ? true : false, renderer, classesStore, playerBuilder);
|
||||
} else {
|
||||
const sanitizer = (flag & StylingFlags.Sanitize) ? styleSanitizer : null;
|
||||
setStyle(
|
||||
native, prop, valueToApply as string | null, renderer, sanitizer, stylesStore,
|
||||
playerBuilder);
|
||||
// if the first render is true then we do not want to start applying falsy
|
||||
// values to the DOM element's styling. Otherwise then we know there has
|
||||
// been a change and even if it's falsy then it's removing something that
|
||||
// was truthy before.
|
||||
const doApplyValue = isFirstRender ? valueToApply : true;
|
||||
if (doApplyValue) {
|
||||
if (isClassBased) {
|
||||
setClass(
|
||||
native, prop, valueToApply ? true : false, renderer, classesStore, playerBuilder);
|
||||
} else {
|
||||
const sanitizer = (flag & StylingFlags.Sanitize) ? styleSanitizer : null;
|
||||
setStyle(
|
||||
native, prop, valueToApply as string | null, renderer, sanitizer, stylesStore,
|
||||
playerBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
setDirty(context, i, false);
|
||||
}
|
||||
}
|
||||
@ -547,7 +557,7 @@ export function renderStyleAndClassBindings(
|
||||
const playerInsertionIndex = i + PlayerIndex.PlayerOffsetPosition;
|
||||
const oldPlayer = playerContext[playerInsertionIndex] as Player | null;
|
||||
if (builder) {
|
||||
const player = builder.buildPlayer(oldPlayer);
|
||||
const player = builder.buildPlayer(oldPlayer, isFirstRender);
|
||||
if (player !== undefined) {
|
||||
if (player != null) {
|
||||
const wasQueued = addPlayerInternal(
|
||||
@ -924,13 +934,13 @@ export class ClassAndStylePlayerBuilder<T> implements PlayerBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
buildPlayer(currentPlayer?: Player|null): Player|undefined|null {
|
||||
buildPlayer(currentPlayer: Player|null, isFirstRender: boolean): Player|undefined|null {
|
||||
// if no values have been set here then this means the binding didn't
|
||||
// change and therefore the binding values were not updated through
|
||||
// `setValue` which means no new player will be provided.
|
||||
if (this._dirty) {
|
||||
const player =
|
||||
this._factory.fn(this._element, this._type, this._values !, currentPlayer || null);
|
||||
const player = this._factory.fn(
|
||||
this._element, this._type, this._values !, isFirstRender, currentPlayer || null);
|
||||
this._values = {};
|
||||
this._dirty = false;
|
||||
return player;
|
||||
|
@ -147,11 +147,14 @@ renderComponent(AnimationWorldComponent, {playerHandler});
|
||||
function animateStyleFactory(keyframes: any[], duration: number, easing: string) {
|
||||
const limit = keyframes.length - 1;
|
||||
const finalKeyframe = keyframes[limit];
|
||||
return bindPlayerFactory((element: HTMLElement, type: number, values: {[key: string]: any}) => {
|
||||
const kf = keyframes.slice(0, limit);
|
||||
kf.push(values);
|
||||
return new WebAnimationsPlayer(element, keyframes, duration, easing);
|
||||
}, finalKeyframe);
|
||||
return bindPlayerFactory(
|
||||
(element: HTMLElement, type: number, values: {[key: string]: any},
|
||||
isFirstRender: boolean) => {
|
||||
const kf = keyframes.slice(0, limit);
|
||||
kf.push(values);
|
||||
return new WebAnimationsPlayer(element, keyframes, duration, easing);
|
||||
},
|
||||
finalKeyframe);
|
||||
}
|
||||
|
||||
class WebAnimationsPlayer implements Player {
|
||||
|
@ -48,48 +48,55 @@ describe('style and class based bindings', () => {
|
||||
return lViewData[CONTEXT] as RootContext;
|
||||
}
|
||||
|
||||
function renderStyles(context: StylingContext, renderer?: Renderer3, lViewData?: LViewData) {
|
||||
function renderStyles(
|
||||
context: StylingContext, firstRender?: boolean, renderer?: Renderer3, lViewData?: LViewData) {
|
||||
const store = new MockStylingStore(element as HTMLElement, BindingType.Style);
|
||||
const handler = new CorePlayerHandler();
|
||||
_renderStyling(
|
||||
context, (renderer || {}) as Renderer3,
|
||||
getRootContextInternal(lViewData || createMockViewData(handler, context)), null, store);
|
||||
getRootContextInternal(lViewData || createMockViewData(handler, context)), !!firstRender,
|
||||
null, store);
|
||||
return store.getValues();
|
||||
}
|
||||
|
||||
function trackStylesFactory() {
|
||||
const store = new MockStylingStore(element as HTMLElement, BindingType.Style);
|
||||
function trackStylesFactory(store?: MockStylingStore) {
|
||||
store = store || new MockStylingStore(element as HTMLElement, BindingType.Style);
|
||||
const handler = new CorePlayerHandler();
|
||||
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
||||
const lViewData = createMockViewData(handler, context);
|
||||
_renderStyling(
|
||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData), null, store);
|
||||
return store.getValues();
|
||||
};
|
||||
return function(context: StylingContext, firstRender?: boolean, renderer?: Renderer3):
|
||||
{[key: string]: any} {
|
||||
const lViewData = createMockViewData(handler, context);
|
||||
_renderStyling(
|
||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData),
|
||||
!!firstRender, null, store);
|
||||
return store !.getValues();
|
||||
};
|
||||
}
|
||||
|
||||
function trackClassesFactory() {
|
||||
const store = new MockStylingStore(element as HTMLElement, BindingType.Class);
|
||||
function trackClassesFactory(store?: MockStylingStore) {
|
||||
store = store || new MockStylingStore(element as HTMLElement, BindingType.Class);
|
||||
const handler = new CorePlayerHandler();
|
||||
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
||||
const lViewData = createMockViewData(handler, context);
|
||||
_renderStyling(
|
||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData), store);
|
||||
return store.getValues();
|
||||
};
|
||||
return function(context: StylingContext, firstRender?: boolean, renderer?: Renderer3):
|
||||
{[key: string]: any} {
|
||||
const lViewData = createMockViewData(handler, context);
|
||||
_renderStyling(
|
||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData),
|
||||
!!firstRender, store);
|
||||
return store !.getValues();
|
||||
};
|
||||
}
|
||||
|
||||
function trackStylesAndClasses() {
|
||||
const classStore = new MockStylingStore(element as HTMLElement, BindingType.Class);
|
||||
const styleStore = new MockStylingStore(element as HTMLElement, BindingType.Style);
|
||||
const handler = new CorePlayerHandler();
|
||||
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
||||
const lViewData = createMockViewData(handler, context);
|
||||
_renderStyling(
|
||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData), classStore,
|
||||
styleStore);
|
||||
return [classStore.getValues(), styleStore.getValues()];
|
||||
};
|
||||
return function(context: StylingContext, firstRender?: boolean, renderer?: Renderer3):
|
||||
{[key: string]: any} {
|
||||
const lViewData = createMockViewData(handler, context);
|
||||
_renderStyling(
|
||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData),
|
||||
!!firstRender, classStore, styleStore);
|
||||
return [classStore.getValues(), styleStore.getValues()];
|
||||
};
|
||||
}
|
||||
|
||||
function updateClasses(context: StylingContext, classes: string | {[key: string]: any} | null) {
|
||||
@ -234,7 +241,7 @@ describe('style and class based bindings', () => {
|
||||
height: '100px',
|
||||
});
|
||||
updateStyles(stylingContext, {height: '200px'});
|
||||
expect(getStyles(stylingContext)).toEqual({width: null, height: '200px'});
|
||||
expect(getStyles(stylingContext, true)).toEqual({height: '200px'});
|
||||
});
|
||||
|
||||
it('should evaluate the delta between style changes when rendering occurs', () => {
|
||||
@ -1029,6 +1036,32 @@ describe('style and class based bindings', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should skip issuing style updates if there is nothing to update upon first render', () => {
|
||||
const stylingContext = initContext([InitialStylingFlags.VALUES_MODE, 'color', '']);
|
||||
const store = new MockStylingStore(element as HTMLElement, BindingType.Class);
|
||||
const getStyles = trackStylesFactory(store);
|
||||
|
||||
let styles: any = {fontSize: ''};
|
||||
updateStyleProp(stylingContext, 0, '');
|
||||
updateStylingMap(stylingContext, null, styles);
|
||||
|
||||
getStyles(stylingContext, true);
|
||||
expect(store.getValues()).toEqual({});
|
||||
|
||||
styles = {fontSize: '20px'};
|
||||
updateStyleProp(stylingContext, 0, 'red');
|
||||
updateStylingMap(stylingContext, null, styles);
|
||||
|
||||
getStyles(stylingContext);
|
||||
expect(store.getValues()).toEqual({fontSize: '20px', color: 'red'});
|
||||
|
||||
styles = {};
|
||||
updateStyleProp(stylingContext, 0, '');
|
||||
updateStylingMap(stylingContext, null, styles);
|
||||
|
||||
getStyles(stylingContext);
|
||||
expect(store.getValues()).toEqual({fontSize: null, color: ''});
|
||||
});
|
||||
});
|
||||
|
||||
describe('classes', () => {
|
||||
@ -1447,6 +1480,34 @@ describe('style and class based bindings', () => {
|
||||
// apply the styles
|
||||
expect(getClasses(stylingContext)).toEqual({apple: true, orange: true, banana: true});
|
||||
});
|
||||
|
||||
it('should skip issuing class updates if there is nothing to update upon first render', () => {
|
||||
const stylingContext = initContext(null, [InitialStylingFlags.VALUES_MODE, 'blue', false]);
|
||||
const store = new MockStylingStore(element as HTMLElement, BindingType.Class);
|
||||
const getClasses = trackClassesFactory(store);
|
||||
|
||||
let classes: any = {red: false};
|
||||
updateClassProp(stylingContext, 0, false);
|
||||
updateStylingMap(stylingContext, classes);
|
||||
|
||||
// apply the styles
|
||||
getClasses(stylingContext, true);
|
||||
expect(store.getValues()).toEqual({});
|
||||
|
||||
classes = {red: true};
|
||||
updateClassProp(stylingContext, 0, true);
|
||||
updateStylingMap(stylingContext, classes);
|
||||
|
||||
getClasses(stylingContext);
|
||||
expect(store.getValues()).toEqual({red: true, blue: true});
|
||||
|
||||
classes = {red: false};
|
||||
updateClassProp(stylingContext, 0, false);
|
||||
updateStylingMap(stylingContext, classes);
|
||||
|
||||
getClasses(stylingContext);
|
||||
expect(store.getValues()).toEqual({red: false, blue: false});
|
||||
});
|
||||
});
|
||||
|
||||
describe('players', () => {
|
||||
@ -1457,20 +1518,22 @@ describe('style and class based bindings', () => {
|
||||
const classes = 'foo bar';
|
||||
|
||||
let classResult: any;
|
||||
const classFactory =
|
||||
bindPlayerFactory((element: HTMLElement, type: BindingType, value: any) => {
|
||||
const classFactory = bindPlayerFactory(
|
||||
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean) => {
|
||||
const player = new MockPlayer();
|
||||
classResult = {player, element, type, value};
|
||||
return player;
|
||||
}, classes);
|
||||
},
|
||||
classes);
|
||||
|
||||
let styleResult: any;
|
||||
const styleFactory =
|
||||
bindPlayerFactory((element: HTMLElement, type: BindingType, value: any) => {
|
||||
const styleFactory = bindPlayerFactory(
|
||||
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean) => {
|
||||
const player = new MockPlayer();
|
||||
styleResult = {player, element, type, value};
|
||||
return player;
|
||||
}, styles);
|
||||
},
|
||||
styles);
|
||||
|
||||
updateStylingMap(context, classFactory, styleFactory);
|
||||
expect(classResult).toBeFalsy();
|
||||
@ -1562,7 +1625,7 @@ describe('style and class based bindings', () => {
|
||||
5, classPlayerBuilder, null, stylePlayerBuilder, null
|
||||
]);
|
||||
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
expect(context[StylingIndex.PlayerContext]).toEqual([
|
||||
5, classPlayerBuilder, currentClassPlayer !, stylePlayerBuilder, currentStylePlayer !
|
||||
]);
|
||||
@ -1638,7 +1701,7 @@ describe('style and class based bindings', () => {
|
||||
barPlayerBuilder, null
|
||||
]);
|
||||
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
const classMapPlayer = capturedClassPlayers.shift() !;
|
||||
const barPlayer = capturedClassPlayers.shift() !;
|
||||
const styleMapPlayer = capturedStylePlayers.shift() !;
|
||||
@ -1671,7 +1734,7 @@ describe('style and class based bindings', () => {
|
||||
bazPlayerBuilder, null
|
||||
]);
|
||||
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
const heightPlayer = capturedStylePlayers.shift() !;
|
||||
const bazPlayer = capturedClassPlayers.shift() !;
|
||||
|
||||
@ -1698,7 +1761,7 @@ describe('style and class based bindings', () => {
|
||||
|
||||
const players: MockPlayer[] = [];
|
||||
const buildFn =
|
||||
(element: HTMLElement, type: BindingType, value: any,
|
||||
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean,
|
||||
oldPlayer: MockPlayer | null) => {
|
||||
const player = new MockPlayer(value);
|
||||
players.push(player);
|
||||
@ -1709,7 +1772,7 @@ describe('style and class based bindings', () => {
|
||||
|
||||
let mapFactory = bindPlayerFactory(buildFn, {width: '200px'});
|
||||
updateStylingMap(context, null, mapFactory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(players.length).toEqual(1);
|
||||
const p1 = players.pop() !;
|
||||
@ -1717,7 +1780,7 @@ describe('style and class based bindings', () => {
|
||||
|
||||
mapFactory = bindPlayerFactory(buildFn, {width: '100px'});
|
||||
updateStylingMap(context, null, mapFactory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(players.length).toEqual(1);
|
||||
const p2 = players.pop() !;
|
||||
@ -1792,7 +1855,7 @@ describe('style and class based bindings', () => {
|
||||
const fooPlayerBuilder = makePlayerBuilder(fooWithPlayerFactory, true);
|
||||
updateStyleProp(context, 0, colorWithPlayerFactory as any);
|
||||
updateClassProp(context, 0, fooWithPlayerFactory as any);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
const p1 = classPlayers.shift();
|
||||
const p2 = stylePlayers.shift();
|
||||
@ -1856,7 +1919,7 @@ describe('style and class based bindings', () => {
|
||||
const fooWithoutPlayerFactory = false;
|
||||
updateStyleProp(context, 0, colorWithoutPlayerFactory);
|
||||
updateClassProp(context, 0, fooWithoutPlayerFactory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(context).toEqual([
|
||||
([9, null, null, null, null, null, null, null, null] as any),
|
||||
@ -1930,28 +1993,28 @@ describe('style and class based bindings', () => {
|
||||
expect(styleCalls).toEqual(0);
|
||||
expect(classCalls).toEqual(0);
|
||||
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
expect(styleCalls).toEqual(1);
|
||||
expect(classCalls).toEqual(1);
|
||||
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
expect(styleCalls).toEqual(1);
|
||||
expect(classCalls).toEqual(1);
|
||||
|
||||
styleFactory.value = {opacity: '0.5'};
|
||||
updateStylingMap(context, classFactory, styleFactory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
expect(styleCalls).toEqual(2);
|
||||
expect(classCalls).toEqual(1);
|
||||
|
||||
classFactory.value = 'foo';
|
||||
updateStylingMap(context, classFactory, styleFactory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
expect(styleCalls).toEqual(2);
|
||||
expect(classCalls).toEqual(2);
|
||||
|
||||
updateStylingMap(context, 'foo', {opacity: '0.5'});
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
expect(styleCalls).toEqual(2);
|
||||
expect(classCalls).toEqual(2);
|
||||
});
|
||||
@ -1975,14 +2038,14 @@ describe('style and class based bindings', () => {
|
||||
const mapFactory = bindPlayerFactory(mapBuildFn, {color: 'black'});
|
||||
updateStylingMap(context, null, mapFactory);
|
||||
updateStyleProp(context, 0, 'green');
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(propPlayer).toBeFalsy();
|
||||
expect(styleMapPlayer).toBeFalsy();
|
||||
|
||||
const propFactory = bindPlayerFactory(propBuildFn, 'orange');
|
||||
updateStyleProp(context, 0, propFactory as any);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(propPlayer).toBeTruthy();
|
||||
expect(styleMapPlayer).toBeFalsy();
|
||||
@ -1990,7 +2053,7 @@ describe('style and class based bindings', () => {
|
||||
propPlayer = styleMapPlayer = null;
|
||||
|
||||
updateStyleProp(context, 0, null);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(propPlayer).toBeFalsy();
|
||||
expect(styleMapPlayer).toBeTruthy();
|
||||
@ -1998,7 +2061,7 @@ describe('style and class based bindings', () => {
|
||||
propPlayer = styleMapPlayer = null;
|
||||
|
||||
updateStylingMap(context, null, null);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(propPlayer).toBeFalsy();
|
||||
expect(styleMapPlayer).toBeFalsy();
|
||||
@ -2012,14 +2075,15 @@ describe('style and class based bindings', () => {
|
||||
let previousPlayer: MockPlayer|null = null;
|
||||
let currentPlayer: MockPlayer|null = null;
|
||||
const buildFn =
|
||||
(element: HTMLElement, type: BindingType, value: any, existingPlayer: MockPlayer) => {
|
||||
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean,
|
||||
existingPlayer: MockPlayer) => {
|
||||
previousPlayer = existingPlayer;
|
||||
return currentPlayer = new MockPlayer(value);
|
||||
};
|
||||
|
||||
let factory = bindPlayerFactory<{[key: string]: any}>(buildFn, {width: '200px'});
|
||||
updateStylingMap(context, null, factory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(previousPlayer).toEqual(null);
|
||||
expect(currentPlayer !.value).toEqual({width: '200px'});
|
||||
@ -2027,7 +2091,7 @@ describe('style and class based bindings', () => {
|
||||
factory = bindPlayerFactory(buildFn, {height: '200px'});
|
||||
|
||||
updateStylingMap(context, null, factory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(previousPlayer !.value).toEqual({width: '200px'});
|
||||
expect(currentPlayer !.value).toEqual({width: null, height: '200px'});
|
||||
@ -2041,7 +2105,7 @@ describe('style and class based bindings', () => {
|
||||
let currentPlayer: MockPlayer|null = null;
|
||||
let previousPlayer: MockPlayer|null = null;
|
||||
const buildFn =
|
||||
(element: HTMLElement, type: BindingType, value: any,
|
||||
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean,
|
||||
existingPlayer: MockPlayer | null) => {
|
||||
previousPlayer = existingPlayer;
|
||||
return currentPlayer = new MockPlayer(value);
|
||||
@ -2049,7 +2113,7 @@ describe('style and class based bindings', () => {
|
||||
|
||||
let factory = bindPlayerFactory<any>(buildFn, {foo: true});
|
||||
updateStylingMap(context, null, factory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(currentPlayer).toBeTruthy();
|
||||
expect(previousPlayer).toBeFalsy();
|
||||
@ -2059,7 +2123,7 @@ describe('style and class based bindings', () => {
|
||||
|
||||
factory = bindPlayerFactory(buildFn, {bar: true});
|
||||
updateStylingMap(context, null, factory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(currentPlayer).toBeTruthy();
|
||||
expect(previousPlayer).toBeTruthy();
|
||||
@ -2081,21 +2145,22 @@ describe('style and class based bindings', () => {
|
||||
const lViewData = createMockViewData(handler, context);
|
||||
|
||||
let values: {[key: string]: any}|null = null;
|
||||
const buildFn = (element: HTMLElement, type: BindingType, value: any) => {
|
||||
values = value;
|
||||
return new MockPlayer();
|
||||
};
|
||||
const buildFn =
|
||||
(element: HTMLElement, type: BindingType, value: any, isFirstRender: boolean) => {
|
||||
values = value;
|
||||
return new MockPlayer();
|
||||
};
|
||||
|
||||
let factory = bindPlayerFactory<{[key: string]: any}>(
|
||||
buildFn, {width: '200px', height: '100px', opacity: '1'});
|
||||
updateStylingMap(context, null, factory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(values !).toEqual({width: '200px-safe!', height: '100px-safe!', opacity: '1'});
|
||||
|
||||
factory = bindPlayerFactory(buildFn, {width: 'auto'});
|
||||
updateStylingMap(context, null, factory);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
|
||||
expect(values !).toEqual({width: 'auto-safe!', height: null, opacity: null});
|
||||
});
|
||||
@ -2127,7 +2192,7 @@ describe('style and class based bindings', () => {
|
||||
updateStyleProp(context, 0, bindPlayerFactory(styleBuildFn, '100px') as any);
|
||||
updateClassProp(context, 0, bindPlayerFactory(classBuildFn, true) as any);
|
||||
updateClassProp(context, 1, bindPlayerFactory(classBuildFn, true) as any);
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
handler.flushPlayers();
|
||||
|
||||
const [p1, p2, p3, p4, p5] = players;
|
||||
@ -2145,7 +2210,7 @@ describe('style and class based bindings', () => {
|
||||
expect(p4.state).toEqual(PlayState.Running);
|
||||
expect(p5.state).toEqual(PlayState.Running);
|
||||
|
||||
renderStyles(context, undefined, lViewData);
|
||||
renderStyles(context, false, undefined, lViewData);
|
||||
expect(p1.state).toEqual(PlayState.Destroyed);
|
||||
expect(p2.state).toEqual(PlayState.Destroyed);
|
||||
expect(p3.state).toEqual(PlayState.Destroyed);
|
||||
@ -2226,6 +2291,74 @@ describe('style and class based bindings', () => {
|
||||
|
||||
expect(getPlayers(target)).toEqual([p1, p2, p4, p6]);
|
||||
});
|
||||
|
||||
it('should build a player and signal that the first render is active', () => {
|
||||
const firstRenderCaptures: any[] = [];
|
||||
const otherRenderCaptures: any[] = [];
|
||||
const buildFn =
|
||||
(element: HTMLElement, type: BindingType, value: any, isFirstRender: boolean) => {
|
||||
if (isFirstRender) {
|
||||
firstRenderCaptures.push({type, value});
|
||||
} else {
|
||||
otherRenderCaptures.push({type, value});
|
||||
}
|
||||
return new MockPlayer();
|
||||
};
|
||||
|
||||
const styleMapFactory =
|
||||
bindPlayerFactory(buildFn, {height: '200px'}) as BoundPlayerFactory<any>;
|
||||
const classMapFactory = bindPlayerFactory(buildFn, {bar: true}) as BoundPlayerFactory<any>;
|
||||
const widthFactory = bindPlayerFactory(buildFn, '100px') as BoundPlayerFactory<any>;
|
||||
const fooFactory = bindPlayerFactory(buildFn, true) as BoundPlayerFactory<any>;
|
||||
|
||||
class Comp {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Comp,
|
||||
selectors: [['comp']],
|
||||
directives: [Comp],
|
||||
factory: () => new Comp(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: Comp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div');
|
||||
elementStyling(['foo'], ['width']);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementStylingMap(0, classMapFactory, styleMapFactory);
|
||||
elementStyleProp(0, 0, widthFactory);
|
||||
elementClassProp(0, 0, fooFactory);
|
||||
elementStylingApply(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(Comp);
|
||||
|
||||
expect(firstRenderCaptures.length).toEqual(4);
|
||||
expect(firstRenderCaptures[0]).toEqual({type: BindingType.Class, value: {bar: true}});
|
||||
expect(firstRenderCaptures[1]).toEqual({type: BindingType.Style, value: {height: '200px'}});
|
||||
expect(firstRenderCaptures[2]).toEqual({type: BindingType.Style, value: {width: '100px'}});
|
||||
expect(firstRenderCaptures[3]).toEqual({type: BindingType.Class, value: {foo: true}});
|
||||
expect(otherRenderCaptures.length).toEqual(0);
|
||||
|
||||
firstRenderCaptures.length = 0;
|
||||
styleMapFactory.value = {height: '100px'};
|
||||
classMapFactory.value = {bar: false};
|
||||
widthFactory.value = '50px';
|
||||
fooFactory.value = false;
|
||||
|
||||
fixture.update();
|
||||
|
||||
expect(firstRenderCaptures.length).toEqual(0);
|
||||
expect(otherRenderCaptures.length).toEqual(4);
|
||||
expect(otherRenderCaptures[0]).toEqual({type: BindingType.Class, value: {bar: false}});
|
||||
expect(otherRenderCaptures[1]).toEqual({type: BindingType.Style, value: {height: '100px'}});
|
||||
expect(otherRenderCaptures[2]).toEqual({type: BindingType.Style, value: {width: '50px'}});
|
||||
expect(otherRenderCaptures[3]).toEqual({type: BindingType.Class, value: {foo: false}});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user