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 {
|
export function elementStylingApply(index: number): void {
|
||||||
const viewData = getViewData();
|
const viewData = getViewData();
|
||||||
const totalPlayersQueued =
|
const isFirstRender = (viewData[FLAGS] & LViewFlags.CreationMode) !== 0;
|
||||||
renderStyleAndClassBindings(getStylingContext(index, viewData), getRenderer(), viewData);
|
const totalPlayersQueued = renderStyleAndClassBindings(
|
||||||
|
getStylingContext(index, viewData), getRenderer(), viewData, isFirstRender);
|
||||||
if (totalPlayersQueued > 0) {
|
if (totalPlayersQueued > 0) {
|
||||||
const rootContext = getRootContext(viewData);
|
const rootContext = getRootContext(viewData);
|
||||||
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
||||||
|
@ -21,8 +21,8 @@ export interface Player {
|
|||||||
|
|
||||||
export const enum BindingType {
|
export const enum BindingType {
|
||||||
Unset = 0,
|
Unset = 0,
|
||||||
Class = 2,
|
Class = 1,
|
||||||
Style = 3,
|
Style = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BindingStore { setValue(prop: string, value: any): void; }
|
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`.
|
* to be used with `PlayerFactory`.
|
||||||
*/
|
*/
|
||||||
export interface PlayerFactoryBuildFn {
|
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;
|
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 PlayerFactory { '__brand__': 'Brand for PlayerFactory that nothing will match'; }
|
||||||
|
|
||||||
export interface PlayerBuilder extends BindingStore {
|
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
|
// The max amount of bits used to represent these configuration values
|
||||||
BitCountSize = 5,
|
BitCountSize = 5,
|
||||||
// There are only five bits here
|
// There are only five bits here
|
||||||
BitMask = 0b1111
|
BitMask = 0b11111
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
|
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
|
||||||
|
@ -480,8 +480,10 @@ export function updateClassProp(
|
|||||||
*/
|
*/
|
||||||
export function renderStyleAndClassBindings(
|
export function renderStyleAndClassBindings(
|
||||||
context: StylingContext, renderer: Renderer3, rootOrView: RootContext | LViewData,
|
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;
|
let totalPlayersQueued = 0;
|
||||||
|
|
||||||
if (isContextDirty(context)) {
|
if (isContextDirty(context)) {
|
||||||
const flushPlayerBuilders: any =
|
const flushPlayerBuilders: any =
|
||||||
context[StylingIndex.MasterFlagPosition] & StylingFlags.PlayerBuildersDirty;
|
context[StylingIndex.MasterFlagPosition] & StylingFlags.PlayerBuildersDirty;
|
||||||
@ -523,15 +525,23 @@ export function renderStyleAndClassBindings(
|
|||||||
valueToApply = getInitialValue(context, flag);
|
valueToApply = getInitialValue(context, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClassBased) {
|
// if the first render is true then we do not want to start applying falsy
|
||||||
setClass(
|
// values to the DOM element's styling. Otherwise then we know there has
|
||||||
native, prop, valueToApply ? true : false, renderer, classesStore, playerBuilder);
|
// been a change and even if it's falsy then it's removing something that
|
||||||
} else {
|
// was truthy before.
|
||||||
const sanitizer = (flag & StylingFlags.Sanitize) ? styleSanitizer : null;
|
const doApplyValue = isFirstRender ? valueToApply : true;
|
||||||
setStyle(
|
if (doApplyValue) {
|
||||||
native, prop, valueToApply as string | null, renderer, sanitizer, stylesStore,
|
if (isClassBased) {
|
||||||
playerBuilder);
|
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);
|
setDirty(context, i, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,7 +557,7 @@ export function renderStyleAndClassBindings(
|
|||||||
const playerInsertionIndex = i + PlayerIndex.PlayerOffsetPosition;
|
const playerInsertionIndex = i + PlayerIndex.PlayerOffsetPosition;
|
||||||
const oldPlayer = playerContext[playerInsertionIndex] as Player | null;
|
const oldPlayer = playerContext[playerInsertionIndex] as Player | null;
|
||||||
if (builder) {
|
if (builder) {
|
||||||
const player = builder.buildPlayer(oldPlayer);
|
const player = builder.buildPlayer(oldPlayer, isFirstRender);
|
||||||
if (player !== undefined) {
|
if (player !== undefined) {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
const wasQueued = addPlayerInternal(
|
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
|
// if no values have been set here then this means the binding didn't
|
||||||
// change and therefore the binding values were not updated through
|
// change and therefore the binding values were not updated through
|
||||||
// `setValue` which means no new player will be provided.
|
// `setValue` which means no new player will be provided.
|
||||||
if (this._dirty) {
|
if (this._dirty) {
|
||||||
const player =
|
const player = this._factory.fn(
|
||||||
this._factory.fn(this._element, this._type, this._values !, currentPlayer || null);
|
this._element, this._type, this._values !, isFirstRender, currentPlayer || null);
|
||||||
this._values = {};
|
this._values = {};
|
||||||
this._dirty = false;
|
this._dirty = false;
|
||||||
return player;
|
return player;
|
||||||
|
@ -147,11 +147,14 @@ renderComponent(AnimationWorldComponent, {playerHandler});
|
|||||||
function animateStyleFactory(keyframes: any[], duration: number, easing: string) {
|
function animateStyleFactory(keyframes: any[], duration: number, easing: string) {
|
||||||
const limit = keyframes.length - 1;
|
const limit = keyframes.length - 1;
|
||||||
const finalKeyframe = keyframes[limit];
|
const finalKeyframe = keyframes[limit];
|
||||||
return bindPlayerFactory((element: HTMLElement, type: number, values: {[key: string]: any}) => {
|
return bindPlayerFactory(
|
||||||
const kf = keyframes.slice(0, limit);
|
(element: HTMLElement, type: number, values: {[key: string]: any},
|
||||||
kf.push(values);
|
isFirstRender: boolean) => {
|
||||||
return new WebAnimationsPlayer(element, keyframes, duration, easing);
|
const kf = keyframes.slice(0, limit);
|
||||||
}, finalKeyframe);
|
kf.push(values);
|
||||||
|
return new WebAnimationsPlayer(element, keyframes, duration, easing);
|
||||||
|
},
|
||||||
|
finalKeyframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebAnimationsPlayer implements Player {
|
class WebAnimationsPlayer implements Player {
|
||||||
|
@ -48,48 +48,55 @@ describe('style and class based bindings', () => {
|
|||||||
return lViewData[CONTEXT] as RootContext;
|
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 store = new MockStylingStore(element as HTMLElement, BindingType.Style);
|
||||||
const handler = new CorePlayerHandler();
|
const handler = new CorePlayerHandler();
|
||||||
_renderStyling(
|
_renderStyling(
|
||||||
context, (renderer || {}) as Renderer3,
|
context, (renderer || {}) as Renderer3,
|
||||||
getRootContextInternal(lViewData || createMockViewData(handler, context)), null, store);
|
getRootContextInternal(lViewData || createMockViewData(handler, context)), !!firstRender,
|
||||||
|
null, store);
|
||||||
return store.getValues();
|
return store.getValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
function trackStylesFactory() {
|
function trackStylesFactory(store?: MockStylingStore) {
|
||||||
const store = new MockStylingStore(element as HTMLElement, BindingType.Style);
|
store = store || new MockStylingStore(element as HTMLElement, BindingType.Style);
|
||||||
const handler = new CorePlayerHandler();
|
const handler = new CorePlayerHandler();
|
||||||
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
return function(context: StylingContext, firstRender?: boolean, renderer?: Renderer3):
|
||||||
const lViewData = createMockViewData(handler, context);
|
{[key: string]: any} {
|
||||||
_renderStyling(
|
const lViewData = createMockViewData(handler, context);
|
||||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData), null, store);
|
_renderStyling(
|
||||||
return store.getValues();
|
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData),
|
||||||
};
|
!!firstRender, null, store);
|
||||||
|
return store !.getValues();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function trackClassesFactory() {
|
function trackClassesFactory(store?: MockStylingStore) {
|
||||||
const store = new MockStylingStore(element as HTMLElement, BindingType.Class);
|
store = store || new MockStylingStore(element as HTMLElement, BindingType.Class);
|
||||||
const handler = new CorePlayerHandler();
|
const handler = new CorePlayerHandler();
|
||||||
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
return function(context: StylingContext, firstRender?: boolean, renderer?: Renderer3):
|
||||||
const lViewData = createMockViewData(handler, context);
|
{[key: string]: any} {
|
||||||
_renderStyling(
|
const lViewData = createMockViewData(handler, context);
|
||||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData), store);
|
_renderStyling(
|
||||||
return store.getValues();
|
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData),
|
||||||
};
|
!!firstRender, store);
|
||||||
|
return store !.getValues();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function trackStylesAndClasses() {
|
function trackStylesAndClasses() {
|
||||||
const classStore = new MockStylingStore(element as HTMLElement, BindingType.Class);
|
const classStore = new MockStylingStore(element as HTMLElement, BindingType.Class);
|
||||||
const styleStore = new MockStylingStore(element as HTMLElement, BindingType.Style);
|
const styleStore = new MockStylingStore(element as HTMLElement, BindingType.Style);
|
||||||
const handler = new CorePlayerHandler();
|
const handler = new CorePlayerHandler();
|
||||||
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
return function(context: StylingContext, firstRender?: boolean, renderer?: Renderer3):
|
||||||
const lViewData = createMockViewData(handler, context);
|
{[key: string]: any} {
|
||||||
_renderStyling(
|
const lViewData = createMockViewData(handler, context);
|
||||||
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData), classStore,
|
_renderStyling(
|
||||||
styleStore);
|
context, (renderer || {}) as Renderer3, getRootContextInternal(lViewData),
|
||||||
return [classStore.getValues(), styleStore.getValues()];
|
!!firstRender, classStore, styleStore);
|
||||||
};
|
return [classStore.getValues(), styleStore.getValues()];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateClasses(context: StylingContext, classes: string | {[key: string]: any} | null) {
|
function updateClasses(context: StylingContext, classes: string | {[key: string]: any} | null) {
|
||||||
@ -234,7 +241,7 @@ describe('style and class based bindings', () => {
|
|||||||
height: '100px',
|
height: '100px',
|
||||||
});
|
});
|
||||||
updateStyles(stylingContext, {height: '200px'});
|
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', () => {
|
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', () => {
|
describe('classes', () => {
|
||||||
@ -1447,6 +1480,34 @@ describe('style and class based bindings', () => {
|
|||||||
// apply the styles
|
// apply the styles
|
||||||
expect(getClasses(stylingContext)).toEqual({apple: true, orange: true, banana: true});
|
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', () => {
|
describe('players', () => {
|
||||||
@ -1457,20 +1518,22 @@ describe('style and class based bindings', () => {
|
|||||||
const classes = 'foo bar';
|
const classes = 'foo bar';
|
||||||
|
|
||||||
let classResult: any;
|
let classResult: any;
|
||||||
const classFactory =
|
const classFactory = bindPlayerFactory(
|
||||||
bindPlayerFactory((element: HTMLElement, type: BindingType, value: any) => {
|
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean) => {
|
||||||
const player = new MockPlayer();
|
const player = new MockPlayer();
|
||||||
classResult = {player, element, type, value};
|
classResult = {player, element, type, value};
|
||||||
return player;
|
return player;
|
||||||
}, classes);
|
},
|
||||||
|
classes);
|
||||||
|
|
||||||
let styleResult: any;
|
let styleResult: any;
|
||||||
const styleFactory =
|
const styleFactory = bindPlayerFactory(
|
||||||
bindPlayerFactory((element: HTMLElement, type: BindingType, value: any) => {
|
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean) => {
|
||||||
const player = new MockPlayer();
|
const player = new MockPlayer();
|
||||||
styleResult = {player, element, type, value};
|
styleResult = {player, element, type, value};
|
||||||
return player;
|
return player;
|
||||||
}, styles);
|
},
|
||||||
|
styles);
|
||||||
|
|
||||||
updateStylingMap(context, classFactory, styleFactory);
|
updateStylingMap(context, classFactory, styleFactory);
|
||||||
expect(classResult).toBeFalsy();
|
expect(classResult).toBeFalsy();
|
||||||
@ -1562,7 +1625,7 @@ describe('style and class based bindings', () => {
|
|||||||
5, classPlayerBuilder, null, stylePlayerBuilder, null
|
5, classPlayerBuilder, null, stylePlayerBuilder, null
|
||||||
]);
|
]);
|
||||||
|
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
expect(context[StylingIndex.PlayerContext]).toEqual([
|
expect(context[StylingIndex.PlayerContext]).toEqual([
|
||||||
5, classPlayerBuilder, currentClassPlayer !, stylePlayerBuilder, currentStylePlayer !
|
5, classPlayerBuilder, currentClassPlayer !, stylePlayerBuilder, currentStylePlayer !
|
||||||
]);
|
]);
|
||||||
@ -1638,7 +1701,7 @@ describe('style and class based bindings', () => {
|
|||||||
barPlayerBuilder, null
|
barPlayerBuilder, null
|
||||||
]);
|
]);
|
||||||
|
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
const classMapPlayer = capturedClassPlayers.shift() !;
|
const classMapPlayer = capturedClassPlayers.shift() !;
|
||||||
const barPlayer = capturedClassPlayers.shift() !;
|
const barPlayer = capturedClassPlayers.shift() !;
|
||||||
const styleMapPlayer = capturedStylePlayers.shift() !;
|
const styleMapPlayer = capturedStylePlayers.shift() !;
|
||||||
@ -1671,7 +1734,7 @@ describe('style and class based bindings', () => {
|
|||||||
bazPlayerBuilder, null
|
bazPlayerBuilder, null
|
||||||
]);
|
]);
|
||||||
|
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
const heightPlayer = capturedStylePlayers.shift() !;
|
const heightPlayer = capturedStylePlayers.shift() !;
|
||||||
const bazPlayer = capturedClassPlayers.shift() !;
|
const bazPlayer = capturedClassPlayers.shift() !;
|
||||||
|
|
||||||
@ -1698,7 +1761,7 @@ describe('style and class based bindings', () => {
|
|||||||
|
|
||||||
const players: MockPlayer[] = [];
|
const players: MockPlayer[] = [];
|
||||||
const buildFn =
|
const buildFn =
|
||||||
(element: HTMLElement, type: BindingType, value: any,
|
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean,
|
||||||
oldPlayer: MockPlayer | null) => {
|
oldPlayer: MockPlayer | null) => {
|
||||||
const player = new MockPlayer(value);
|
const player = new MockPlayer(value);
|
||||||
players.push(player);
|
players.push(player);
|
||||||
@ -1709,7 +1772,7 @@ describe('style and class based bindings', () => {
|
|||||||
|
|
||||||
let mapFactory = bindPlayerFactory(buildFn, {width: '200px'});
|
let mapFactory = bindPlayerFactory(buildFn, {width: '200px'});
|
||||||
updateStylingMap(context, null, mapFactory);
|
updateStylingMap(context, null, mapFactory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(players.length).toEqual(1);
|
expect(players.length).toEqual(1);
|
||||||
const p1 = players.pop() !;
|
const p1 = players.pop() !;
|
||||||
@ -1717,7 +1780,7 @@ describe('style and class based bindings', () => {
|
|||||||
|
|
||||||
mapFactory = bindPlayerFactory(buildFn, {width: '100px'});
|
mapFactory = bindPlayerFactory(buildFn, {width: '100px'});
|
||||||
updateStylingMap(context, null, mapFactory);
|
updateStylingMap(context, null, mapFactory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(players.length).toEqual(1);
|
expect(players.length).toEqual(1);
|
||||||
const p2 = players.pop() !;
|
const p2 = players.pop() !;
|
||||||
@ -1792,7 +1855,7 @@ describe('style and class based bindings', () => {
|
|||||||
const fooPlayerBuilder = makePlayerBuilder(fooWithPlayerFactory, true);
|
const fooPlayerBuilder = makePlayerBuilder(fooWithPlayerFactory, true);
|
||||||
updateStyleProp(context, 0, colorWithPlayerFactory as any);
|
updateStyleProp(context, 0, colorWithPlayerFactory as any);
|
||||||
updateClassProp(context, 0, fooWithPlayerFactory as any);
|
updateClassProp(context, 0, fooWithPlayerFactory as any);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
const p1 = classPlayers.shift();
|
const p1 = classPlayers.shift();
|
||||||
const p2 = stylePlayers.shift();
|
const p2 = stylePlayers.shift();
|
||||||
@ -1856,7 +1919,7 @@ describe('style and class based bindings', () => {
|
|||||||
const fooWithoutPlayerFactory = false;
|
const fooWithoutPlayerFactory = false;
|
||||||
updateStyleProp(context, 0, colorWithoutPlayerFactory);
|
updateStyleProp(context, 0, colorWithoutPlayerFactory);
|
||||||
updateClassProp(context, 0, fooWithoutPlayerFactory);
|
updateClassProp(context, 0, fooWithoutPlayerFactory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(context).toEqual([
|
expect(context).toEqual([
|
||||||
([9, null, null, null, null, null, null, null, null] as any),
|
([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(styleCalls).toEqual(0);
|
||||||
expect(classCalls).toEqual(0);
|
expect(classCalls).toEqual(0);
|
||||||
|
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
expect(styleCalls).toEqual(1);
|
expect(styleCalls).toEqual(1);
|
||||||
expect(classCalls).toEqual(1);
|
expect(classCalls).toEqual(1);
|
||||||
|
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
expect(styleCalls).toEqual(1);
|
expect(styleCalls).toEqual(1);
|
||||||
expect(classCalls).toEqual(1);
|
expect(classCalls).toEqual(1);
|
||||||
|
|
||||||
styleFactory.value = {opacity: '0.5'};
|
styleFactory.value = {opacity: '0.5'};
|
||||||
updateStylingMap(context, classFactory, styleFactory);
|
updateStylingMap(context, classFactory, styleFactory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
expect(styleCalls).toEqual(2);
|
expect(styleCalls).toEqual(2);
|
||||||
expect(classCalls).toEqual(1);
|
expect(classCalls).toEqual(1);
|
||||||
|
|
||||||
classFactory.value = 'foo';
|
classFactory.value = 'foo';
|
||||||
updateStylingMap(context, classFactory, styleFactory);
|
updateStylingMap(context, classFactory, styleFactory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
expect(styleCalls).toEqual(2);
|
expect(styleCalls).toEqual(2);
|
||||||
expect(classCalls).toEqual(2);
|
expect(classCalls).toEqual(2);
|
||||||
|
|
||||||
updateStylingMap(context, 'foo', {opacity: '0.5'});
|
updateStylingMap(context, 'foo', {opacity: '0.5'});
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
expect(styleCalls).toEqual(2);
|
expect(styleCalls).toEqual(2);
|
||||||
expect(classCalls).toEqual(2);
|
expect(classCalls).toEqual(2);
|
||||||
});
|
});
|
||||||
@ -1975,14 +2038,14 @@ describe('style and class based bindings', () => {
|
|||||||
const mapFactory = bindPlayerFactory(mapBuildFn, {color: 'black'});
|
const mapFactory = bindPlayerFactory(mapBuildFn, {color: 'black'});
|
||||||
updateStylingMap(context, null, mapFactory);
|
updateStylingMap(context, null, mapFactory);
|
||||||
updateStyleProp(context, 0, 'green');
|
updateStyleProp(context, 0, 'green');
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(propPlayer).toBeFalsy();
|
expect(propPlayer).toBeFalsy();
|
||||||
expect(styleMapPlayer).toBeFalsy();
|
expect(styleMapPlayer).toBeFalsy();
|
||||||
|
|
||||||
const propFactory = bindPlayerFactory(propBuildFn, 'orange');
|
const propFactory = bindPlayerFactory(propBuildFn, 'orange');
|
||||||
updateStyleProp(context, 0, propFactory as any);
|
updateStyleProp(context, 0, propFactory as any);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(propPlayer).toBeTruthy();
|
expect(propPlayer).toBeTruthy();
|
||||||
expect(styleMapPlayer).toBeFalsy();
|
expect(styleMapPlayer).toBeFalsy();
|
||||||
@ -1990,7 +2053,7 @@ describe('style and class based bindings', () => {
|
|||||||
propPlayer = styleMapPlayer = null;
|
propPlayer = styleMapPlayer = null;
|
||||||
|
|
||||||
updateStyleProp(context, 0, null);
|
updateStyleProp(context, 0, null);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(propPlayer).toBeFalsy();
|
expect(propPlayer).toBeFalsy();
|
||||||
expect(styleMapPlayer).toBeTruthy();
|
expect(styleMapPlayer).toBeTruthy();
|
||||||
@ -1998,7 +2061,7 @@ describe('style and class based bindings', () => {
|
|||||||
propPlayer = styleMapPlayer = null;
|
propPlayer = styleMapPlayer = null;
|
||||||
|
|
||||||
updateStylingMap(context, null, null);
|
updateStylingMap(context, null, null);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(propPlayer).toBeFalsy();
|
expect(propPlayer).toBeFalsy();
|
||||||
expect(styleMapPlayer).toBeFalsy();
|
expect(styleMapPlayer).toBeFalsy();
|
||||||
@ -2012,14 +2075,15 @@ describe('style and class based bindings', () => {
|
|||||||
let previousPlayer: MockPlayer|null = null;
|
let previousPlayer: MockPlayer|null = null;
|
||||||
let currentPlayer: MockPlayer|null = null;
|
let currentPlayer: MockPlayer|null = null;
|
||||||
const buildFn =
|
const buildFn =
|
||||||
(element: HTMLElement, type: BindingType, value: any, existingPlayer: MockPlayer) => {
|
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean,
|
||||||
|
existingPlayer: MockPlayer) => {
|
||||||
previousPlayer = existingPlayer;
|
previousPlayer = existingPlayer;
|
||||||
return currentPlayer = new MockPlayer(value);
|
return currentPlayer = new MockPlayer(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
let factory = bindPlayerFactory<{[key: string]: any}>(buildFn, {width: '200px'});
|
let factory = bindPlayerFactory<{[key: string]: any}>(buildFn, {width: '200px'});
|
||||||
updateStylingMap(context, null, factory);
|
updateStylingMap(context, null, factory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(previousPlayer).toEqual(null);
|
expect(previousPlayer).toEqual(null);
|
||||||
expect(currentPlayer !.value).toEqual({width: '200px'});
|
expect(currentPlayer !.value).toEqual({width: '200px'});
|
||||||
@ -2027,7 +2091,7 @@ describe('style and class based bindings', () => {
|
|||||||
factory = bindPlayerFactory(buildFn, {height: '200px'});
|
factory = bindPlayerFactory(buildFn, {height: '200px'});
|
||||||
|
|
||||||
updateStylingMap(context, null, factory);
|
updateStylingMap(context, null, factory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(previousPlayer !.value).toEqual({width: '200px'});
|
expect(previousPlayer !.value).toEqual({width: '200px'});
|
||||||
expect(currentPlayer !.value).toEqual({width: null, height: '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 currentPlayer: MockPlayer|null = null;
|
||||||
let previousPlayer: MockPlayer|null = null;
|
let previousPlayer: MockPlayer|null = null;
|
||||||
const buildFn =
|
const buildFn =
|
||||||
(element: HTMLElement, type: BindingType, value: any,
|
(element: HTMLElement, type: BindingType, value: any, firstRender: boolean,
|
||||||
existingPlayer: MockPlayer | null) => {
|
existingPlayer: MockPlayer | null) => {
|
||||||
previousPlayer = existingPlayer;
|
previousPlayer = existingPlayer;
|
||||||
return currentPlayer = new MockPlayer(value);
|
return currentPlayer = new MockPlayer(value);
|
||||||
@ -2049,7 +2113,7 @@ describe('style and class based bindings', () => {
|
|||||||
|
|
||||||
let factory = bindPlayerFactory<any>(buildFn, {foo: true});
|
let factory = bindPlayerFactory<any>(buildFn, {foo: true});
|
||||||
updateStylingMap(context, null, factory);
|
updateStylingMap(context, null, factory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(currentPlayer).toBeTruthy();
|
expect(currentPlayer).toBeTruthy();
|
||||||
expect(previousPlayer).toBeFalsy();
|
expect(previousPlayer).toBeFalsy();
|
||||||
@ -2059,7 +2123,7 @@ describe('style and class based bindings', () => {
|
|||||||
|
|
||||||
factory = bindPlayerFactory(buildFn, {bar: true});
|
factory = bindPlayerFactory(buildFn, {bar: true});
|
||||||
updateStylingMap(context, null, factory);
|
updateStylingMap(context, null, factory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(currentPlayer).toBeTruthy();
|
expect(currentPlayer).toBeTruthy();
|
||||||
expect(previousPlayer).toBeTruthy();
|
expect(previousPlayer).toBeTruthy();
|
||||||
@ -2081,21 +2145,22 @@ describe('style and class based bindings', () => {
|
|||||||
const lViewData = createMockViewData(handler, context);
|
const lViewData = createMockViewData(handler, context);
|
||||||
|
|
||||||
let values: {[key: string]: any}|null = null;
|
let values: {[key: string]: any}|null = null;
|
||||||
const buildFn = (element: HTMLElement, type: BindingType, value: any) => {
|
const buildFn =
|
||||||
values = value;
|
(element: HTMLElement, type: BindingType, value: any, isFirstRender: boolean) => {
|
||||||
return new MockPlayer();
|
values = value;
|
||||||
};
|
return new MockPlayer();
|
||||||
|
};
|
||||||
|
|
||||||
let factory = bindPlayerFactory<{[key: string]: any}>(
|
let factory = bindPlayerFactory<{[key: string]: any}>(
|
||||||
buildFn, {width: '200px', height: '100px', opacity: '1'});
|
buildFn, {width: '200px', height: '100px', opacity: '1'});
|
||||||
updateStylingMap(context, null, factory);
|
updateStylingMap(context, null, factory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(values !).toEqual({width: '200px-safe!', height: '100px-safe!', opacity: '1'});
|
expect(values !).toEqual({width: '200px-safe!', height: '100px-safe!', opacity: '1'});
|
||||||
|
|
||||||
factory = bindPlayerFactory(buildFn, {width: 'auto'});
|
factory = bindPlayerFactory(buildFn, {width: 'auto'});
|
||||||
updateStylingMap(context, null, factory);
|
updateStylingMap(context, null, factory);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
|
|
||||||
expect(values !).toEqual({width: 'auto-safe!', height: null, opacity: null});
|
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);
|
updateStyleProp(context, 0, bindPlayerFactory(styleBuildFn, '100px') as any);
|
||||||
updateClassProp(context, 0, bindPlayerFactory(classBuildFn, true) as any);
|
updateClassProp(context, 0, bindPlayerFactory(classBuildFn, true) as any);
|
||||||
updateClassProp(context, 1, bindPlayerFactory(classBuildFn, true) as any);
|
updateClassProp(context, 1, bindPlayerFactory(classBuildFn, true) as any);
|
||||||
renderStyles(context, undefined, lViewData);
|
renderStyles(context, false, undefined, lViewData);
|
||||||
handler.flushPlayers();
|
handler.flushPlayers();
|
||||||
|
|
||||||
const [p1, p2, p3, p4, p5] = players;
|
const [p1, p2, p3, p4, p5] = players;
|
||||||
@ -2145,7 +2210,7 @@ describe('style and class based bindings', () => {
|
|||||||
expect(p4.state).toEqual(PlayState.Running);
|
expect(p4.state).toEqual(PlayState.Running);
|
||||||
expect(p5.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(p1.state).toEqual(PlayState.Destroyed);
|
||||||
expect(p2.state).toEqual(PlayState.Destroyed);
|
expect(p2.state).toEqual(PlayState.Destroyed);
|
||||||
expect(p3.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]);
|
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