1131 lines
28 KiB
TypeScript
1131 lines
28 KiB
TypeScript
/**
|
|
* @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 {elementEnd, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap} from '../../src/render3/instructions';
|
|
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
|
import {LElementNode} from '../../src/render3/interfaces/node';
|
|
import {Renderer3} from '../../src/render3/interfaces/renderer';
|
|
import {StylingContext, StylingFlags, StylingIndex, allocStylingContext, createStylingContextTemplate, isContextDirty, renderStyling as _renderStyling, setContextDirty, updateClassProp, updateStyleProp, updateStylingMap} from '../../src/render3/styling';
|
|
import {defaultStyleSanitizer} from '../../src/sanitization/sanitization';
|
|
import {StyleSanitizeFn} from '../../src/sanitization/style_sanitizer';
|
|
|
|
import {renderToHtml} from './render_util';
|
|
|
|
describe('styling', () => {
|
|
let element: LElementNode|null = null;
|
|
beforeEach(() => { element = {} as any; });
|
|
|
|
function initContext(
|
|
styles?: (number | string)[] | null, classes?: (string | number | boolean)[] | null,
|
|
sanitizer?: StyleSanitizeFn | null): StylingContext {
|
|
return allocStylingContext(element, createStylingContextTemplate(classes, styles, sanitizer));
|
|
}
|
|
|
|
function renderStyles(context: StylingContext, renderer?: Renderer3) {
|
|
const styles: {[key: string]: any} = {};
|
|
_renderStyling(context, (renderer || {}) as Renderer3, styles);
|
|
return styles;
|
|
}
|
|
|
|
function trackStylesFactory() {
|
|
const styles: {[key: string]: any} = {};
|
|
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
|
_renderStyling(context, (renderer || {}) as Renderer3, styles);
|
|
return styles;
|
|
};
|
|
}
|
|
|
|
function trackClassesFactory() {
|
|
const classes: {[className: string]: boolean} = {};
|
|
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
|
_renderStyling(context, (renderer || {}) as Renderer3, {}, classes);
|
|
return classes;
|
|
};
|
|
}
|
|
|
|
function trackStylesAndClasses() {
|
|
const classes: {[className: string]: boolean} = {};
|
|
const styles: {[prop: string]: any} = {};
|
|
return function(context: StylingContext, renderer?: Renderer3): {[key: string]: any} {
|
|
_renderStyling(context, (renderer || {}) as Renderer3, styles, classes);
|
|
return [styles, classes];
|
|
};
|
|
}
|
|
|
|
function updateClasses(context: StylingContext, classes: string | {[key: string]: any} | null) {
|
|
updateStylingMap(context, classes, null);
|
|
}
|
|
|
|
function updateStyles(context: StylingContext, styles: {[key: string]: any} | null) {
|
|
updateStylingMap(context, null, styles);
|
|
}
|
|
|
|
function cleanStyle(a: number = 0, b: number = 0): number { return _clean(a, b, false, false); }
|
|
|
|
function cleanStyleWithSanitization(a: number = 0, b: number = 0): number {
|
|
return _clean(a, b, false, true);
|
|
}
|
|
|
|
function cleanClass(a: number, b: number) { return _clean(a, b, true); }
|
|
|
|
function _clean(
|
|
a: number = 0, b: number = 0, isClassBased: boolean, sanitizable?: boolean): number {
|
|
let num = 0;
|
|
if (a) {
|
|
num |= a << StylingFlags.BitCountSize;
|
|
}
|
|
if (b) {
|
|
num |= b << (StylingFlags.BitCountSize + StylingIndex.BitCountSize);
|
|
}
|
|
if (isClassBased) {
|
|
num |= StylingFlags.Class;
|
|
}
|
|
if (sanitizable) {
|
|
num |= StylingFlags.Sanitize;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
function _dirty(
|
|
a: number = 0, b: number = 0, isClassBased: boolean, sanitizable?: boolean): number {
|
|
return _clean(a, b, isClassBased, sanitizable) | StylingFlags.Dirty;
|
|
}
|
|
|
|
function dirtyStyle(a: number = 0, b: number = 0): number {
|
|
return _dirty(a, b, false) | StylingFlags.Dirty;
|
|
}
|
|
|
|
function dirtyStyleWithSanitization(a: number = 0, b: number = 0): number {
|
|
return _dirty(a, b, false, true);
|
|
}
|
|
|
|
function dirtyClass(a: number, b: number) { return _dirty(a, b, true); }
|
|
|
|
describe('styles', () => {
|
|
describe('createStylingContextTemplate', () => {
|
|
it('should initialize empty template', () => {
|
|
const template = initContext();
|
|
expect(template).toEqual([element, null, [null], cleanStyle(0, 6), 0, null]);
|
|
});
|
|
|
|
it('should initialize static styles', () => {
|
|
const template =
|
|
initContext([InitialStylingFlags.VALUES_MODE, 'color', 'red', 'width', '10px']);
|
|
expect(template).toEqual([
|
|
element,
|
|
null,
|
|
[null, 'red', '10px'],
|
|
dirtyStyle(0, 12), //
|
|
0,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(1, 12),
|
|
'color',
|
|
null,
|
|
|
|
// #9
|
|
cleanStyle(2, 15),
|
|
'width',
|
|
null,
|
|
|
|
// #12
|
|
dirtyStyle(1, 6),
|
|
'color',
|
|
null,
|
|
|
|
// #15
|
|
dirtyStyle(2, 9),
|
|
'width',
|
|
null,
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('instructions', () => {
|
|
it('should handle a combination of initial, multi and singular style values (in that order)',
|
|
() => {
|
|
function Template(rf: RenderFlags, ctx: any) {
|
|
if (rf & RenderFlags.Create) {
|
|
elementStart(0, 'span');
|
|
elementStyling([], [
|
|
'width', 'height', 'opacity', //
|
|
InitialStylingFlags.VALUES_MODE, 'width', '100px', 'height', '100px', 'opacity',
|
|
'0.5'
|
|
]);
|
|
elementEnd();
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
elementStylingMap(0, ctx.myStyles);
|
|
elementStyleProp(0, 0, ctx.myWidth);
|
|
elementStylingApply(0);
|
|
}
|
|
}
|
|
|
|
expect(renderToHtml(
|
|
Template, {myStyles: {width: '200px', height: '200px'}, myWidth: '300px'}, 1))
|
|
.toEqual('<span style="width: 300px; height: 200px; opacity: 0.5;"></span>');
|
|
|
|
expect(
|
|
renderToHtml(Template, {myStyles: {width: '200px', height: null}, myWidth: null}, 1))
|
|
.toEqual('<span style="width: 200px; height: 100px; opacity: 0.5;"></span>');
|
|
});
|
|
});
|
|
|
|
describe('helper functions', () => {
|
|
it('should build a list of multiple styling values', () => {
|
|
const getStyles = trackStylesFactory();
|
|
const stylingContext = initContext();
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
height: '100px',
|
|
});
|
|
updateStyles(stylingContext, {height: '200px'});
|
|
expect(getStyles(stylingContext)).toEqual({width: null, height: '200px'});
|
|
});
|
|
|
|
it('should evaluate the delta between style changes when rendering occurs', () => {
|
|
const stylingContext =
|
|
initContext(['width', 'height', InitialStylingFlags.VALUES_MODE, 'width', '100px']);
|
|
updateStyles(stylingContext, {
|
|
height: '200px',
|
|
});
|
|
expect(renderStyles(stylingContext)).toEqual({width: '100px', height: '200px'});
|
|
expect(renderStyles(stylingContext)).toEqual({});
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
height: '100px',
|
|
});
|
|
expect(renderStyles(stylingContext)).toEqual({height: '100px'});
|
|
updateStyleProp(stylingContext, 1, '100px');
|
|
expect(renderStyles(stylingContext)).toEqual({});
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
height: '100px',
|
|
});
|
|
expect(renderStyles(stylingContext)).toEqual({});
|
|
});
|
|
|
|
it('should update individual values on a set of styles', () => {
|
|
const getStyles = trackStylesFactory();
|
|
const stylingContext = initContext(['width', 'height']);
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
height: '100px',
|
|
});
|
|
updateStyleProp(stylingContext, 1, '200px');
|
|
expect(getStyles(stylingContext)).toEqual({width: '100px', height: '200px'});
|
|
});
|
|
|
|
it('should only mark itself as updated when one or more properties have been applied', () => {
|
|
const stylingContext = initContext();
|
|
expect(isContextDirty(stylingContext)).toBeFalsy();
|
|
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
height: '100px',
|
|
});
|
|
expect(isContextDirty(stylingContext)).toBeTruthy();
|
|
|
|
setContextDirty(stylingContext, false);
|
|
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
height: '100px',
|
|
});
|
|
expect(isContextDirty(stylingContext)).toBeFalsy();
|
|
|
|
updateStyles(stylingContext, {
|
|
width: '200px',
|
|
height: '100px',
|
|
});
|
|
expect(isContextDirty(stylingContext)).toBeTruthy();
|
|
});
|
|
|
|
it('should only mark itself as updated when any single properties have been applied', () => {
|
|
const stylingContext = initContext(['height']);
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
height: '100px',
|
|
});
|
|
|
|
setContextDirty(stylingContext, false);
|
|
|
|
updateStyleProp(stylingContext, 0, '100px');
|
|
expect(isContextDirty(stylingContext)).toBeFalsy();
|
|
|
|
setContextDirty(stylingContext, false);
|
|
|
|
updateStyleProp(stylingContext, 0, '200px');
|
|
expect(isContextDirty(stylingContext)).toBeTruthy();
|
|
});
|
|
|
|
it('should prioritize multi and single styles over initial styles', () => {
|
|
const getStyles = trackStylesFactory();
|
|
|
|
const stylingContext = initContext([
|
|
'width', 'height', 'opacity', InitialStylingFlags.VALUES_MODE, 'width', '100px', 'height',
|
|
'100px', 'opacity', '0'
|
|
]);
|
|
|
|
expect(getStyles(stylingContext)).toEqual({
|
|
width: '100px',
|
|
height: '100px',
|
|
opacity: '0',
|
|
});
|
|
|
|
updateStyles(stylingContext, {width: '200px', height: '200px'});
|
|
|
|
expect(getStyles(stylingContext)).toEqual({
|
|
width: '200px',
|
|
height: '200px',
|
|
opacity: '0',
|
|
});
|
|
|
|
updateStyleProp(stylingContext, 0, '300px');
|
|
|
|
expect(getStyles(stylingContext)).toEqual({
|
|
width: '300px',
|
|
height: '200px',
|
|
opacity: '0',
|
|
});
|
|
|
|
updateStyleProp(stylingContext, 0, null);
|
|
|
|
expect(getStyles(stylingContext)).toEqual({
|
|
width: '200px',
|
|
height: '200px',
|
|
opacity: '0',
|
|
});
|
|
|
|
updateStyles(stylingContext, {});
|
|
|
|
expect(getStyles(stylingContext)).toEqual({
|
|
width: '100px',
|
|
height: '100px',
|
|
opacity: '0',
|
|
});
|
|
});
|
|
|
|
it('should cleanup removed styles from the context once the styles are built', () => {
|
|
const stylingContext = initContext(['width', 'height']);
|
|
const getStyles = trackStylesFactory();
|
|
|
|
updateStyles(stylingContext, {width: '100px', height: '100px'});
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(0, 12),
|
|
'width',
|
|
null,
|
|
|
|
// #9
|
|
cleanStyle(0, 15),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
dirtyStyle(0, 6),
|
|
'width',
|
|
'100px',
|
|
|
|
// #15
|
|
dirtyStyle(0, 9),
|
|
'height',
|
|
'100px',
|
|
]);
|
|
|
|
getStyles(stylingContext);
|
|
updateStyles(stylingContext, {width: '200px', opacity: '0'});
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(0, 12),
|
|
'width',
|
|
null,
|
|
|
|
// #9
|
|
cleanStyle(0, 18),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
dirtyStyle(0, 6),
|
|
'width',
|
|
'200px',
|
|
|
|
// #15
|
|
dirtyStyle(),
|
|
'opacity',
|
|
'0',
|
|
|
|
// #18
|
|
dirtyStyle(0, 9),
|
|
'height',
|
|
null,
|
|
]);
|
|
|
|
getStyles(stylingContext);
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
cleanStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(0, 12),
|
|
'width',
|
|
null,
|
|
|
|
// #9
|
|
cleanStyle(0, 18),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
cleanStyle(0, 6),
|
|
'width',
|
|
'200px',
|
|
|
|
// #15
|
|
cleanStyle(),
|
|
'opacity',
|
|
'0',
|
|
|
|
// #18
|
|
cleanStyle(0, 9),
|
|
'height',
|
|
null,
|
|
]);
|
|
|
|
updateStyles(stylingContext, {width: null});
|
|
updateStyleProp(stylingContext, 0, '300px');
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyle(0, 12),
|
|
'width',
|
|
'300px',
|
|
|
|
// #9
|
|
cleanStyle(0, 18),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
cleanStyle(0, 6),
|
|
'width',
|
|
null,
|
|
|
|
// #15
|
|
dirtyStyle(),
|
|
'opacity',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(0, 9),
|
|
'height',
|
|
null,
|
|
]);
|
|
|
|
getStyles(stylingContext);
|
|
|
|
updateStyleProp(stylingContext, 0, null);
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyle(0, 12),
|
|
'width',
|
|
null,
|
|
|
|
// #9
|
|
cleanStyle(0, 18),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
cleanStyle(0, 6),
|
|
'width',
|
|
null,
|
|
|
|
// #15
|
|
cleanStyle(),
|
|
'opacity',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(0, 9),
|
|
'height',
|
|
null,
|
|
]);
|
|
});
|
|
|
|
it('should find the next available space in the context when data is added after being removed before',
|
|
() => {
|
|
const stylingContext = initContext(['lineHeight']);
|
|
const getStyles = trackStylesFactory();
|
|
|
|
updateStyles(stylingContext, {width: '100px', height: '100px', opacity: '0.5'});
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 9), //
|
|
1,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(0, 18),
|
|
'lineHeight',
|
|
null,
|
|
|
|
// #9
|
|
dirtyStyle(),
|
|
'width',
|
|
'100px',
|
|
|
|
// #12
|
|
dirtyStyle(),
|
|
'height',
|
|
'100px',
|
|
|
|
// #15
|
|
dirtyStyle(),
|
|
'opacity',
|
|
'0.5',
|
|
|
|
// #18
|
|
cleanStyle(0, 6),
|
|
'lineHeight',
|
|
null,
|
|
]);
|
|
|
|
getStyles(stylingContext);
|
|
|
|
updateStyles(stylingContext, {});
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 9), //
|
|
1,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(0, 18),
|
|
'lineHeight',
|
|
null,
|
|
|
|
// #9
|
|
dirtyStyle(),
|
|
'width',
|
|
null,
|
|
|
|
// #12
|
|
dirtyStyle(),
|
|
'height',
|
|
null,
|
|
|
|
// #15
|
|
dirtyStyle(),
|
|
'opacity',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(0, 6),
|
|
'lineHeight',
|
|
null,
|
|
]);
|
|
|
|
getStyles(stylingContext);
|
|
updateStyles(stylingContext, {
|
|
borderWidth: '5px',
|
|
});
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 9), //
|
|
1,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(0, 21),
|
|
'lineHeight',
|
|
null,
|
|
|
|
// #9
|
|
dirtyStyle(),
|
|
'borderWidth',
|
|
'5px',
|
|
|
|
// #12
|
|
cleanStyle(),
|
|
'width',
|
|
null,
|
|
|
|
// #15
|
|
cleanStyle(),
|
|
'height',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(),
|
|
'opacity',
|
|
null,
|
|
|
|
// #21
|
|
cleanStyle(0, 6),
|
|
'lineHeight',
|
|
null,
|
|
]);
|
|
|
|
updateStyleProp(stylingContext, 0, '200px');
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 9), //
|
|
1,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyle(0, 21),
|
|
'lineHeight',
|
|
'200px',
|
|
|
|
// #9
|
|
dirtyStyle(),
|
|
'borderWidth',
|
|
'5px',
|
|
|
|
// #12
|
|
cleanStyle(),
|
|
'width',
|
|
null,
|
|
|
|
// #15
|
|
cleanStyle(),
|
|
'height',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(),
|
|
'opacity',
|
|
null,
|
|
|
|
// #21
|
|
cleanStyle(0, 6),
|
|
'lineHeight',
|
|
null,
|
|
]);
|
|
|
|
updateStyles(stylingContext, {borderWidth: '15px', borderColor: 'red'});
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 9), //
|
|
1,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyle(0, 24),
|
|
'lineHeight',
|
|
'200px',
|
|
|
|
// #9
|
|
dirtyStyle(),
|
|
'borderWidth',
|
|
'15px',
|
|
|
|
// #12
|
|
dirtyStyle(),
|
|
'borderColor',
|
|
'red',
|
|
|
|
// #15
|
|
cleanStyle(),
|
|
'width',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(),
|
|
'height',
|
|
null,
|
|
|
|
// #21
|
|
cleanStyle(),
|
|
'opacity',
|
|
null,
|
|
|
|
// #24
|
|
cleanStyle(0, 6),
|
|
'lineHeight',
|
|
null,
|
|
]);
|
|
});
|
|
|
|
it('should render all data as not being dirty after the styles are built', () => {
|
|
const getStyles = trackStylesFactory();
|
|
const stylingContext = initContext(['height']);
|
|
|
|
updateStyles(stylingContext, {
|
|
width: '100px',
|
|
});
|
|
|
|
updateStyleProp(stylingContext, 0, '200px');
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
dirtyStyle(0, 9), //
|
|
1,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyle(0, 12),
|
|
'height',
|
|
'200px',
|
|
|
|
// #6
|
|
dirtyStyle(),
|
|
'width',
|
|
'100px',
|
|
|
|
// #12
|
|
cleanStyle(0, 6),
|
|
'height',
|
|
null,
|
|
]);
|
|
|
|
getStyles(stylingContext);
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null],
|
|
cleanStyle(0, 9), //
|
|
1,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(0, 12),
|
|
'height',
|
|
'200px',
|
|
|
|
// #6
|
|
cleanStyle(),
|
|
'width',
|
|
'100px',
|
|
|
|
// #12
|
|
cleanStyle(0, 6),
|
|
'height',
|
|
null,
|
|
]);
|
|
});
|
|
|
|
it('should mark styles that may contain url values as being sanitizable (when a sanitizer is passed in)',
|
|
() => {
|
|
const getStyles = trackStylesFactory();
|
|
const initialStyles = ['border-image', 'border-width'];
|
|
const styleSanitizer = defaultStyleSanitizer;
|
|
const stylingContext = initContext(initialStyles, null, styleSanitizer);
|
|
|
|
updateStyleProp(stylingContext, 0, 'url(foo.jpg)');
|
|
updateStyleProp(stylingContext, 1, '100px');
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
styleSanitizer,
|
|
[null],
|
|
dirtyStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyleWithSanitization(0, 12),
|
|
'border-image',
|
|
'url(foo.jpg)',
|
|
|
|
// #9
|
|
dirtyStyle(0, 15),
|
|
'border-width',
|
|
'100px',
|
|
|
|
// #12
|
|
cleanStyleWithSanitization(0, 6),
|
|
'border-image',
|
|
null,
|
|
|
|
// #15
|
|
cleanStyle(0, 9),
|
|
'border-width',
|
|
null,
|
|
]);
|
|
|
|
updateStyles(stylingContext, {'background-image': 'unsafe'});
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
styleSanitizer,
|
|
[null],
|
|
dirtyStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyleWithSanitization(0, 15),
|
|
'border-image',
|
|
'url(foo.jpg)',
|
|
|
|
// #9
|
|
dirtyStyle(0, 18),
|
|
'border-width',
|
|
'100px',
|
|
|
|
// #12
|
|
dirtyStyleWithSanitization(0, 0),
|
|
'background-image',
|
|
'unsafe',
|
|
|
|
// #15
|
|
cleanStyleWithSanitization(0, 6),
|
|
'border-image',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(0, 9),
|
|
'border-width',
|
|
null,
|
|
]);
|
|
|
|
getStyles(stylingContext);
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
styleSanitizer,
|
|
[null],
|
|
cleanStyle(0, 12), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyleWithSanitization(0, 15),
|
|
'border-image',
|
|
'url(foo.jpg)',
|
|
|
|
// #9
|
|
cleanStyle(0, 18),
|
|
'border-width',
|
|
'100px',
|
|
|
|
// #12
|
|
cleanStyleWithSanitization(0, 0),
|
|
'background-image',
|
|
'unsafe',
|
|
|
|
// #15
|
|
cleanStyleWithSanitization(0, 6),
|
|
'border-image',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(0, 9),
|
|
'border-width',
|
|
null,
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('classes', () => {
|
|
it('should initialize with the provided classes', () => {
|
|
const template =
|
|
initContext(null, [InitialStylingFlags.VALUES_MODE, 'one', true, 'two', true]);
|
|
expect(template).toEqual([
|
|
element, null, [null, true, true], dirtyStyle(0, 12), //
|
|
0, null,
|
|
|
|
// #6
|
|
cleanClass(1, 12), 'one', null,
|
|
|
|
// #9
|
|
cleanClass(2, 15), 'two', null,
|
|
|
|
// #12
|
|
dirtyClass(1, 6), 'one', null,
|
|
|
|
// #15
|
|
dirtyClass(2, 9), 'two', null
|
|
]);
|
|
});
|
|
|
|
it('should update multi class properties against the static classes', () => {
|
|
const getClasses = trackClassesFactory();
|
|
const stylingContext = initContext(null, ['bar']);
|
|
expect(getClasses(stylingContext)).toEqual({});
|
|
updateClasses(stylingContext, {foo: true, bar: false});
|
|
expect(getClasses(stylingContext)).toEqual({'foo': true, 'bar': false});
|
|
updateClasses(stylingContext, 'bar');
|
|
expect(getClasses(stylingContext)).toEqual({'foo': false, 'bar': true});
|
|
});
|
|
|
|
it('should update single class properties against the static classes', () => {
|
|
const getClasses = trackClassesFactory();
|
|
const stylingContext =
|
|
initContext(null, ['bar', 'foo', InitialStylingFlags.VALUES_MODE, 'bar', true]);
|
|
expect(getClasses(stylingContext)).toEqual({'bar': true});
|
|
|
|
updateClassProp(stylingContext, 0, true);
|
|
updateClassProp(stylingContext, 1, true);
|
|
expect(getClasses(stylingContext)).toEqual({'bar': true, 'foo': true});
|
|
|
|
updateClassProp(stylingContext, 0, false);
|
|
updateClassProp(stylingContext, 1, false);
|
|
expect(getClasses(stylingContext)).toEqual({'bar': true, 'foo': false});
|
|
});
|
|
|
|
it('should understand updating multi-classes using a string-based value while respecting single class-based props',
|
|
() => {
|
|
const getClasses = trackClassesFactory();
|
|
const stylingContext = initContext(null, ['guy']);
|
|
expect(getClasses(stylingContext)).toEqual({});
|
|
|
|
updateStylingMap(stylingContext, 'foo bar guy');
|
|
expect(getClasses(stylingContext)).toEqual({'foo': true, 'bar': true, 'guy': true});
|
|
|
|
updateStylingMap(stylingContext, 'foo man');
|
|
updateClassProp(stylingContext, 0, true);
|
|
expect(getClasses(stylingContext))
|
|
.toEqual({'foo': true, 'man': true, 'bar': false, 'guy': true});
|
|
});
|
|
|
|
it('should house itself inside the context alongside styling in harmony', () => {
|
|
const getStylesAndClasses = trackStylesAndClasses();
|
|
const initialStyles = ['width', 'height', InitialStylingFlags.VALUES_MODE, 'width', '100px'];
|
|
const initialClasses = ['wide', 'tall', InitialStylingFlags.VALUES_MODE, 'wide', true];
|
|
const stylingContext = initContext(initialStyles, initialClasses);
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null, '100px', true],
|
|
dirtyStyle(0, 18), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
cleanStyle(1, 18),
|
|
'width',
|
|
null,
|
|
|
|
// #9
|
|
cleanStyle(0, 21),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
cleanClass(2, 24),
|
|
'wide',
|
|
null,
|
|
|
|
// #15
|
|
cleanClass(0, 27),
|
|
'tall',
|
|
null,
|
|
|
|
// #18
|
|
dirtyStyle(1, 6),
|
|
'width',
|
|
null,
|
|
|
|
// #21
|
|
cleanStyle(0, 9),
|
|
'height',
|
|
null,
|
|
|
|
// #24
|
|
dirtyClass(2, 12),
|
|
'wide',
|
|
null,
|
|
|
|
// #27
|
|
cleanClass(0, 15),
|
|
'tall',
|
|
null,
|
|
]);
|
|
|
|
expect(getStylesAndClasses(stylingContext)).toEqual([{width: '100px'}, {wide: true}]);
|
|
|
|
updateStylingMap(stylingContext, 'tall round', {width: '200px', opacity: '0.5'});
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null, '100px', true],
|
|
dirtyStyle(0, 18), //
|
|
2,
|
|
'tall round',
|
|
|
|
// #6
|
|
cleanStyle(1, 18),
|
|
'width',
|
|
null,
|
|
|
|
// #9
|
|
cleanStyle(0, 33),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
cleanClass(2, 30),
|
|
'wide',
|
|
null,
|
|
|
|
// #15
|
|
cleanClass(0, 24),
|
|
'tall',
|
|
null,
|
|
|
|
// #18
|
|
dirtyStyle(1, 6),
|
|
'width',
|
|
'200px',
|
|
|
|
// #21
|
|
dirtyStyle(0, 0),
|
|
'opacity',
|
|
'0.5',
|
|
|
|
// #24
|
|
dirtyClass(0, 15),
|
|
'tall',
|
|
true,
|
|
|
|
// #27
|
|
dirtyClass(0, 0),
|
|
'round',
|
|
true,
|
|
|
|
// #30
|
|
cleanClass(2, 12),
|
|
'wide',
|
|
null,
|
|
|
|
// #33
|
|
cleanStyle(0, 9),
|
|
'height',
|
|
null,
|
|
]);
|
|
|
|
expect(getStylesAndClasses(stylingContext)).toEqual([
|
|
{width: '200px', opacity: '0.5'}, {tall: true, round: true, wide: true}
|
|
]);
|
|
|
|
updateStylingMap(stylingContext, {tall: true, wide: true}, {width: '500px'});
|
|
updateStyleProp(stylingContext, 0, '300px');
|
|
|
|
expect(stylingContext).toEqual([
|
|
element,
|
|
null,
|
|
[null, '100px', true],
|
|
dirtyStyle(0, 18), //
|
|
2,
|
|
null,
|
|
|
|
// #6
|
|
dirtyStyle(1, 18),
|
|
'width',
|
|
'300px',
|
|
|
|
// #9
|
|
cleanStyle(0, 33),
|
|
'height',
|
|
null,
|
|
|
|
// #12
|
|
cleanClass(2, 24),
|
|
'wide',
|
|
null,
|
|
|
|
// #15
|
|
cleanClass(0, 21),
|
|
'tall',
|
|
null,
|
|
|
|
// #18
|
|
cleanStyle(1, 6),
|
|
'width',
|
|
'500px',
|
|
|
|
// #21
|
|
cleanClass(0, 15),
|
|
'tall',
|
|
true,
|
|
|
|
// #24
|
|
cleanClass(2, 12),
|
|
'wide',
|
|
true,
|
|
|
|
// #27
|
|
dirtyClass(0, 0),
|
|
'round',
|
|
null,
|
|
|
|
// #30
|
|
dirtyStyle(0, 0),
|
|
'opacity',
|
|
null,
|
|
|
|
// #33
|
|
cleanStyle(0, 9),
|
|
'height',
|
|
null,
|
|
]);
|
|
|
|
expect(getStylesAndClasses(stylingContext)).toEqual([
|
|
{width: '300px', opacity: null}, {tall: true, round: false, wide: true}
|
|
]);
|
|
});
|
|
});
|
|
});
|