diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 5e34be0df7..a09a3d4d58 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -30,9 +30,9 @@ import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from './interfaces/injector'; import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from './interfaces/node'; import {PlayerFactory} from './interfaces/player'; -import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; +import {CssSelectorList} from './interfaces/projection'; import {LQueries} from './interfaces/query'; -import {GlobalTargetResolver, ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer'; +import {GlobalTargetResolver, RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer'; import {SanitizerFn} from './interfaces/sanitization'; import {StylingContext} from './interfaces/styling'; import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from './interfaces/view'; @@ -43,8 +43,9 @@ import {applyOnCreateInstructions} from './node_util'; import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode} from './state'; import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialClasses, renderInitialStyles, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings'; import {BoundPlayerFactory} from './styling/player_factory'; -import {ANIMATION_PROP_PREFIX, allocateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContext, hasClassInput, hasStyleInput, hasStyling, isAnimationProp} from './styling/util'; +import {ANIMATION_PROP_PREFIX, allocateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContext, hasClassInput, hasStyleInput, isAnimationProp} from './styling/util'; import {NO_CHANGE} from './tokens'; +import {attrsStylingIndexOf, setUpAttributes} from './util/attrs_utils'; import {INTERPOLATION_DELIMITER, renderStringify} from './util/misc_utils'; import {findComponentView, getLViewParent, getRootContext, getRootView} from './util/view_traversal_utils'; import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readPatchedLView, unwrapRNode, viewAttachedToChangeDetector} from './util/view_utils'; @@ -620,15 +621,21 @@ export function elementStart( const tNode = createNodeAtIndex(index, TNodeType.Element, native !, name, attrs || null); if (attrs) { + const lastAttrIndex = setUpAttributes(native, attrs); + // it's important to only prepare styling-related datastructures once for a given // tNode and not each time an element is created. Also, the styling code is designed - // to be patched and constructed at various points, but only up until the first element - // is created. Then the styling context is locked and can only be instantiated for each - // successive element that is created. - if (tView.firstTemplatePass && !tNode.stylingTemplate && hasStyling(attrs)) { - tNode.stylingTemplate = initializeStaticStylingContext(attrs); + // to be patched and constructed at various points, but only up until the styling + // template is first allocated (which happens when the very first style/class binding + // value is evaluated). When the template is allocated (when it turns into a context) + // then the styling template is locked and cannot be further extended (it can only be + // instantiated into a context per element) + if (tView.firstTemplatePass && !tNode.stylingTemplate) { + const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, lastAttrIndex); + if (stylingAttrsStartIndex >= 0) { + tNode.stylingTemplate = initializeStaticStylingContext(attrs, stylingAttrsStartIndex); + } } - setUpAttributes(native, attrs); } appendChild(native, tNode, lView); @@ -825,87 +832,6 @@ function createViewBlueprint(bindingStartIndex: number, initialViewLength: numbe return blueprint; } -/** - * Assigns all attribute values to the provided element via the inferred renderer. - * - * This function accepts two forms of attribute entries: - * - * default: (key, value): - * attrs = [key1, value1, key2, value2] - * - * namespaced: (NAMESPACE_MARKER, uri, name, value) - * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value] - * - * The `attrs` array can contain a mix of both the default and namespaced entries. - * The "default" values are set without a marker, but if the function comes across - * a marker value then it will attempt to set a namespaced value. If the marker is - * not of a namespaced value then the function will quit and return the index value - * where it stopped during the iteration of the attrs array. - * - * See [AttributeMarker] to understand what the namespace marker value is. - * - * Note that this instruction does not support assigning style and class values to - * an element. See `elementStart` and `elementHostAttrs` to learn how styling values - * are applied to an element. - * - * @param native The element that the attributes will be assigned to - * @param attrs The attribute array of values that will be assigned to the element - * @returns the index value that was last accessed in the attributes array - */ -function setUpAttributes(native: RElement, attrs: TAttributes): number { - const renderer = getLView()[RENDERER]; - const isProc = isProceduralRenderer(renderer); - - let i = 0; - while (i < attrs.length) { - const value = attrs[i]; - if (typeof value === 'number') { - // only namespaces are supported. Other value types (such as style/class - // entries) are not supported in this function. - if (value !== AttributeMarker.NamespaceURI) { - break; - } - - // we just landed on the marker value ... therefore - // we should skip to the next entry - i++; - - const namespaceURI = attrs[i++] as string; - const attrName = attrs[i++] as string; - const attrVal = attrs[i++] as string; - ngDevMode && ngDevMode.rendererSetAttribute++; - isProc ? - (renderer as ProceduralRenderer3).setAttribute(native, attrName, attrVal, namespaceURI) : - native.setAttributeNS(namespaceURI, attrName, attrVal); - } else { - /// attrName is string; - const attrName = value as string; - const attrVal = attrs[++i]; - if (attrName !== NG_PROJECT_AS_ATTR_NAME) { - // Standard attributes - ngDevMode && ngDevMode.rendererSetAttribute++; - if (isAnimationProp(attrName)) { - if (isProc) { - (renderer as ProceduralRenderer3).setProperty(native, attrName, attrVal); - } - } else { - isProc ? - (renderer as ProceduralRenderer3) - .setAttribute(native, attrName as string, attrVal as string) : - native.setAttribute(attrName as string, attrVal as string); - } - } - i++; - } - } - - // another piece of code may iterate over the same attributes array. Therefore - // it may be helpful to return the exact spot where the attributes array exited - // whether by running into an unsupported marker or if all the static values were - // iterated over. - return i; -} - export function createError(text: string, token: any) { return new Error(`Renderer: ${text} [${renderStringify(token)}]`); } @@ -1556,13 +1482,18 @@ function initElementStyling( */ export function elementHostAttrs(directive: any, attrs: TAttributes) { const tNode = getPreviousOrParentTNode(); - if (!tNode.stylingTemplate) { - tNode.stylingTemplate = initializeStaticStylingContext(attrs); - } const lView = getLView(); const native = getNativeByTNode(tNode, lView) as RElement; - const i = setUpAttributes(native, attrs); - patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, i, directive); + const lastAttrIndex = setUpAttributes(native, attrs); + const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, lastAttrIndex); + if (stylingAttrsStartIndex >= 0) { + if (tNode.stylingTemplate) { + patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, stylingAttrsStartIndex, directive); + } else { + tNode.stylingTemplate = + initializeStaticStylingContext(attrs, stylingAttrsStartIndex, directive); + } + } } /** diff --git a/packages/core/src/render3/interfaces/styling.ts b/packages/core/src/render3/interfaces/styling.ts index 1ac69ee063..90ca68100f 100644 --- a/packages/core/src/render3/interfaces/styling.ts +++ b/packages/core/src/render3/interfaces/styling.ts @@ -323,9 +323,9 @@ export interface StylingContext extends * * See [InitialStylingValuesIndex] for a breakdown of how all this works. */ -export interface InitialStylingValues extends Array { +export interface InitialStylingValues extends Array { [InitialStylingValuesIndex.DefaultNullValuePosition]: null; - [InitialStylingValuesIndex.InitialClassesStringPosition]: string|null; + [InitialStylingValuesIndex.CachedStringValuePosition]: string|null; } /** @@ -432,12 +432,48 @@ export interface InitialStylingValues extends Array { * ``` */ export const enum InitialStylingValuesIndex { + /** + * The first value is always `null` so that `styles[0] == null` for unassigned values + */ DefaultNullValuePosition = 0, - InitialClassesStringPosition = 1, + + /** + * Used for non-styling code to examine what the style or className string is: + * styles: ['width', '100px', 0, 'opacity', null, 0, 'height', '200px', 0] + * => initialStyles[CachedStringValuePosition] = 'width:100px;height:200px'; + * classes: ['foo', true, 0, 'bar', false, 0, 'baz', true, 0] + * => initialClasses[CachedStringValuePosition] = 'foo bar'; + * + * Note that this value is `null` by default and it will only be populated + * once `getInitialStyleStringValue` or `getInitialClassNameValue` is executed. + */ + CachedStringValuePosition = 1, + + /** + * Where the style or class values start in the tuple + */ KeyValueStartPosition = 2, + + /** + * The offset value (index + offset) for the property value for each style/class entry + */ PropOffset = 0, + + /** + * The offset value (index + offset) for the style/class value for each style/class entry + */ ValueOffset = 1, - Size = 2 + + /** + * The offset value (index + offset) for the style/class directive owner for each style/class + entry + */ + DirectiveOwnerOffset = 2, + + /** + * The total size for each style/class entry (prop + value + directiveOwner) + */ + Size = 3 } /** diff --git a/packages/core/src/render3/styling/class_and_style_bindings.ts b/packages/core/src/render3/styling/class_and_style_bindings.ts index d8f43405d0..1fec18b5d5 100644 --- a/packages/core/src/render3/styling/class_and_style_bindings.ts +++ b/packages/core/src/render3/styling/class_and_style_bindings.ts @@ -41,29 +41,10 @@ import {addPlayerInternal, allocPlayerContext, allocateDirectiveIntoContext, cre /** * Creates a new StylingContext an fills it with the provided static styling attribute values. */ -export function initializeStaticContext(attrs: TAttributes): StylingContext { +export function initializeStaticContext( + attrs: TAttributes, stylingStartIndex: number, directiveRef?: any | null): StylingContext { const context = createEmptyStylingContext(); - const initialClasses: InitialStylingValues = context[StylingIndex.InitialClassValuesPosition] = - [null, null]; - const initialStyles: InitialStylingValues = context[StylingIndex.InitialStyleValuesPosition] = - [null, null]; - - // The attributes array has marker values (numbers) indicating what the subsequent - // values represent. When we encounter a number, we set the mode to that type of attribute. - let mode = -1; - for (let i = 0; i < attrs.length; i++) { - const attr = attrs[i]; - if (typeof attr == 'number') { - mode = attr; - } else if (mode === AttributeMarker.Styles) { - initialStyles.push(attr as string, attrs[++i] as string); - } else if (mode === AttributeMarker.Classes) { - initialClasses.push(attr as string, true); - } else if (mode === AttributeMarker.SelectOnly) { - break; - } - } - + patchContextWithStaticAttrs(context, attrs, stylingStartIndex, directiveRef); return context; } @@ -74,34 +55,41 @@ export function initializeStaticContext(attrs: TAttributes): StylingContext { * @param context the existing styling context * @param attrs an array of new static styling attributes that will be * assigned to the context + * @param attrsStylingStartIndex what index to start iterating within the + * provided `attrs` array to start reading style and class values * @param directiveRef the directive instance with which static data is associated with. */ export function patchContextWithStaticAttrs( - context: StylingContext, attrs: TAttributes, startingIndex: number, directiveRef: any): void { + context: StylingContext, attrs: TAttributes, attrsStylingStartIndex: number, + directiveRef?: any | null): void { + // this means the context has already been set and instantiated + if (context[StylingIndex.MasterFlagPosition] & StylingFlags.BindingAllocationLocked) return; + // If the styling context has already been patched with the given directive's bindings, // then there is no point in doing it again. The reason why this may happen (the directive // styling being patched twice) is because the `stylingBinding` function is called each time // an element is created (both within a template function and within directive host bindings). const directives = context[StylingIndex.DirectiveRegistryPosition]; - if (getDirectiveRegistryValuesIndexOf(directives, directiveRef) == -1) { + let detectedIndex = getDirectiveRegistryValuesIndexOf(directives, directiveRef || null); + if (detectedIndex === -1) { // this is a new directive which we have not seen yet. - allocateDirectiveIntoContext(context, directiveRef); + detectedIndex = allocateDirectiveIntoContext(context, directiveRef); + } + const directiveIndex = detectedIndex / DirectiveRegistryValuesIndex.Size; - let initialClasses: InitialStylingValues|null = null; - let initialStyles: InitialStylingValues|null = null; - - let mode = -1; - for (let i = startingIndex; i < attrs.length; i++) { - const attr = attrs[i]; - if (typeof attr == 'number') { - mode = attr; - } else if (mode == AttributeMarker.Classes) { - initialClasses = initialClasses || context[StylingIndex.InitialClassValuesPosition]; - patchInitialStylingValue(initialClasses, attr, true); - } else if (mode == AttributeMarker.Styles) { - initialStyles = initialStyles || context[StylingIndex.InitialStyleValuesPosition]; - patchInitialStylingValue(initialStyles, attr, attrs[++i]); - } + let initialClasses: InitialStylingValues|null = null; + let initialStyles: InitialStylingValues|null = null; + let mode = -1; + for (let i = attrsStylingStartIndex; i < attrs.length; i++) { + const attr = attrs[i]; + if (typeof attr == 'number') { + mode = attr; + } else if (mode == AttributeMarker.Classes) { + initialClasses = initialClasses || context[StylingIndex.InitialClassValuesPosition]; + patchInitialStylingValue(initialClasses, attr, true, directiveIndex); + } else if (mode == AttributeMarker.Styles) { + initialStyles = initialStyles || context[StylingIndex.InitialStyleValuesPosition]; + patchInitialStylingValue(initialStyles, attr, attrs[++i], directiveIndex); } } } @@ -110,29 +98,44 @@ export function patchContextWithStaticAttrs( * Designed to add a style or class value into the existing set of initial styles. * * The function will search and figure out if a style/class value is already present - * within the provided initial styling array. If and when a style/class value is not - * present (or if it's value is falsy) then it will be inserted/updated in the list - * of initial styling values. + * within the provided initial styling array. If and when a style/class value is + * present (allocated) then the code below will set the new value depending on the + * following cases: + * + * 1) if the existing value is falsy (this happens because a `[class.prop]` or + * `[style.prop]` binding was set, but there wasn't a matching static style + * or class present on the context) + * 2) if the value was set already by the template, component or directive, but the + * new value is set on a higher level (i.e. a sub component which extends a parent + * component sets its value after the parent has already set the same one) + * 3) if the same directive provides a new set of styling values to set + * + * @param initialStyling the initial styling array where the new styling entry will be added to + * @param prop the property value of the new entry (e.g. `width` (styles) or `foo` (classes)) + * @param value the styling value of the new entry (e.g. `absolute` (styles) or `true` (classes)) + * @param directiveOwnerIndex the directive owner index value of the styling source responsible + * for these styles (see `interfaces/styling.ts#directives` for more info) */ function patchInitialStylingValue( - initialStyling: InitialStylingValues, prop: string, value: any): void { - // Even values are keys; Odd numbers are values; Search keys only - for (let i = InitialStylingValuesIndex.KeyValueStartPosition; i < initialStyling.length;) { - const key = initialStyling[i]; + initialStyling: InitialStylingValues, prop: string, value: any, + directiveOwnerIndex: number): void { + for (let i = InitialStylingValuesIndex.KeyValueStartPosition; i < initialStyling.length; + i += InitialStylingValuesIndex.Size) { + const key = initialStyling[i + InitialStylingValuesIndex.PropOffset]; if (key === prop) { - const existingValue = initialStyling[i + InitialStylingValuesIndex.ValueOffset]; - - // If there is no previous style value (when `null`) or no previous class - // applied (when `false`) then we update the the newly given value. - if (existingValue == null || existingValue == false) { - initialStyling[i + InitialStylingValuesIndex.ValueOffset] = value; + const existingValue = + initialStyling[i + InitialStylingValuesIndex.ValueOffset] as string | null | boolean; + const existingOwner = + initialStyling[i + InitialStylingValuesIndex.DirectiveOwnerOffset] as number; + if (allowValueChange(existingValue, value, existingOwner, directiveOwnerIndex)) { + addOrUpdateStaticStyle(i, initialStyling, prop, value, directiveOwnerIndex); } return; } - i = i + InitialStylingValuesIndex.Size; } + // We did not find existing key, add a new one. - initialStyling.push(prop, value); + addOrUpdateStaticStyle(null, initialStyling, prop, value, directiveOwnerIndex); } /** @@ -377,8 +380,10 @@ export function updateContextWithBindings( let initialValuesToLookup = entryIsClassBased ? initialClasses : initialStyles; let indexForInitial = getInitialStylingValuesIndexOf(initialValuesToLookup, propName); if (indexForInitial === -1) { - indexForInitial = initialValuesToLookup.length + InitialStylingValuesIndex.ValueOffset; - initialValuesToLookup.push(propName, entryIsClassBased ? false : null); + indexForInitial = addOrUpdateStaticStyle( + null, initialValuesToLookup, propName, entryIsClassBased ? false : null, + directiveIndex) + + InitialStylingValuesIndex.ValueOffset; } else { indexForInitial += InitialStylingValuesIndex.ValueOffset; } @@ -1262,7 +1267,7 @@ function getInitialValue(context: StylingContext, flag: number): string|boolean| const entryIsClassBased = flag & StylingFlags.Class; const initialValues = entryIsClassBased ? context[StylingIndex.InitialClassValuesPosition] : context[StylingIndex.InitialStyleValuesPosition]; - return initialValues[index]; + return initialValues[index] as string | boolean | null; } function getInitialIndex(flag: number): number { @@ -1769,7 +1774,7 @@ function allowValueChange( */ export function getInitialClassNameValue(context: StylingContext): string { const initialClassValues = context[StylingIndex.InitialClassValuesPosition]; - let className = initialClassValues[InitialStylingValuesIndex.InitialClassesStringPosition]; + let className = initialClassValues[InitialStylingValuesIndex.CachedStringValuePosition]; if (className === null) { className = ''; for (let i = InitialStylingValuesIndex.KeyValueStartPosition; i < initialClassValues.length; @@ -1779,7 +1784,7 @@ export function getInitialClassNameValue(context: StylingContext): string { className += (className.length ? ' ' : '') + initialClassValues[i]; } } - initialClassValues[InitialStylingValuesIndex.InitialClassesStringPosition] = className; + initialClassValues[InitialStylingValuesIndex.CachedStringValuePosition] = className; } return className; } @@ -1797,7 +1802,7 @@ export function getInitialClassNameValue(context: StylingContext): string { */ export function getInitialStyleStringValue(context: StylingContext): string { const initialStyleValues = context[StylingIndex.InitialStyleValuesPosition]; - let styleString = initialStyleValues[InitialStylingValuesIndex.InitialClassesStringPosition]; + let styleString = initialStyleValues[InitialStylingValuesIndex.CachedStringValuePosition]; if (styleString === null) { styleString = ''; for (let i = InitialStylingValuesIndex.KeyValueStartPosition; i < initialStyleValues.length; @@ -1807,7 +1812,7 @@ export function getInitialStyleStringValue(context: StylingContext): string { styleString += (styleString.length ? ';' : '') + `${initialStyleValues[i]}:${value}`; } } - initialStyleValues[InitialStylingValuesIndex.InitialClassesStringPosition] = styleString; + initialStyleValues[InitialStylingValuesIndex.CachedStringValuePosition] = styleString; } return styleString; } @@ -1956,3 +1961,30 @@ function registerMultiMapEntry( } cachedValues.push(0, startPosition, null, count); } + +/** + * Inserts or updates an existing entry in the provided `staticStyles` collection. + * + * @param index the index representing an existing styling entry in the collection: + * if provided (numeric): then it will update the existing entry at the given position + * if null: then it will insert a new entry within the collection + * @param staticStyles a collection of style or class entries where the value will + * be inserted or patched + * @param prop the property value of the entry (e.g. `width` (styles) or `foo` (classes)) + * @param value the styling value of the entry (e.g. `absolute` (styles) or `true` (classes)) + * @param directiveOwnerIndex the directive owner index value of the styling source responsible + * for these styles (see `interfaces/styling.ts#directives` for more info) + * @returns the index of the updated or new entry within the collection + */ +function addOrUpdateStaticStyle( + index: number | null, staticStyles: InitialStylingValues, prop: string, + value: string | boolean | null, directiveOwnerIndex: number) { + if (index === null) { + index = staticStyles.length; + staticStyles.push(null, null, null); + staticStyles[index + InitialStylingValuesIndex.PropOffset] = prop; + } + staticStyles[index + InitialStylingValuesIndex.ValueOffset] = value; + staticStyles[index + InitialStylingValuesIndex.DirectiveOwnerOffset] = directiveOwnerIndex; + return index; +} diff --git a/packages/core/src/render3/styling/util.ts b/packages/core/src/render3/styling/util.ts index 2984841b72..0331d39397 100644 --- a/packages/core/src/render3/styling/util.ts +++ b/packages/core/src/render3/styling/util.ts @@ -14,7 +14,7 @@ import {LContext} from '../interfaces/context'; import {AttributeMarker, TAttributes, TNode, TNodeFlags} from '../interfaces/node'; import {PlayState, Player, PlayerContext, PlayerIndex} from '../interfaces/player'; import {RElement} from '../interfaces/renderer'; -import {InitialStylingValues, StylingContext, StylingFlags, StylingIndex} from '../interfaces/styling'; +import {DirectiveRegistryValuesIndex, InitialStylingValues, StylingContext, StylingFlags, StylingIndex} from '../interfaces/styling'; import {HEADER_OFFSET, HOST, LView, RootContext} from '../interfaces/view'; import {getTNode, isStylingContext} from '../util/view_utils'; @@ -37,13 +37,48 @@ export function createEmptyStylingContext( [0], // CachedMultiStyleValue null, // PlayerContext ]; + + // whenever a context is created there is always a `null` directive + // that is registered (which is a placeholder for the "template"). allocateDirectiveIntoContext(context, null); return context; } -export function allocateDirectiveIntoContext(context: StylingContext, directiveRef: any | null) { +/** + * Allocates (registers) a directive into the directive registry within the provided styling + * context. + * + * For each and every `[style]`, `[style.prop]`, `[class]`, `[class.name]` binding + * (as well as static style and class attributes) a directive, component or template + * is marked as the owner. When an owner is determined (this happens when the template + * is first passed over) the directive owner is allocated into the styling context. When + * this happens, each owner gets its own index value. This then ensures that once any + * style and/or class binding are assigned into the context then they are marked to + * that directive's index value. + * + * @param context the target StylingContext + * @param directiveRef the directive that will be allocated into the context + * @returns the index where the directive was inserted into + */ +export function allocateDirectiveIntoContext( + context: StylingContext, directiveRef: any | null): number { // this is a new directive which we have not seen yet. - context[StylingIndex.DirectiveRegistryPosition].push(directiveRef, -1, false, null); + const dirs = context[StylingIndex.DirectiveRegistryPosition]; + const i = dirs.length; + + // we preemptively make space into the directives array and then + // assign values slot-by-slot to ensure that if the directive ordering + // changes then it will still function + dirs.push(null, null, null, null); + dirs[i + DirectiveRegistryValuesIndex.DirectiveValueOffset] = directiveRef; + dirs[i + DirectiveRegistryValuesIndex.DirtyFlagOffset] = false; + dirs[i + DirectiveRegistryValuesIndex.StyleSanitizerOffset] = null; + + // -1 is used to signal that the directive has been allocated, but + // no actual style or class bindings have been registered yet... + dirs[i + DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset] = -1; + + return i; } /** @@ -228,11 +263,3 @@ export function allocPlayerContext(data: StylingContext): PlayerContext { export function throwInvalidRefError() { throw new Error('Only elements that exist in an Angular application can be used for animations'); } - -export function hasStyling(attrs: TAttributes): boolean { - for (let i = 0; i < attrs.length; i++) { - const attr = attrs[i]; - if (attr == AttributeMarker.Classes || attr == AttributeMarker.Styles) return true; - } - return false; -} diff --git a/packages/core/src/render3/util/attrs_utils.ts b/packages/core/src/render3/util/attrs_utils.ts new file mode 100644 index 0000000000..f810cd6b0d --- /dev/null +++ b/packages/core/src/render3/util/attrs_utils.ts @@ -0,0 +1,107 @@ +/** + * @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 {AttributeMarker, TAttributes} from '../interfaces/node'; +import {NG_PROJECT_AS_ATTR_NAME} from '../interfaces/projection'; +import {ProceduralRenderer3, RElement, isProceduralRenderer} from '../interfaces/renderer'; +import {RENDERER} from '../interfaces/view'; +import {getLView} from '../state'; +import {isAnimationProp} from '../styling/util'; + + + +/** + * Assigns all attribute values to the provided element via the inferred renderer. + * + * This function accepts two forms of attribute entries: + * + * default: (key, value): + * attrs = [key1, value1, key2, value2] + * + * namespaced: (NAMESPACE_MARKER, uri, name, value) + * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value] + * + * The `attrs` array can contain a mix of both the default and namespaced entries. + * The "default" values are set without a marker, but if the function comes across + * a marker value then it will attempt to set a namespaced value. If the marker is + * not of a namespaced value then the function will quit and return the index value + * where it stopped during the iteration of the attrs array. + * + * See [AttributeMarker] to understand what the namespace marker value is. + * + * Note that this instruction does not support assigning style and class values to + * an element. See `elementStart` and `elementHostAttrs` to learn how styling values + * are applied to an element. + * + * @param native The element that the attributes will be assigned to + * @param attrs The attribute array of values that will be assigned to the element + * @returns the index value that was last accessed in the attributes array + */ +export function setUpAttributes(native: RElement, attrs: TAttributes): number { + const renderer = getLView()[RENDERER]; + const isProc = isProceduralRenderer(renderer); + + let i = 0; + while (i < attrs.length) { + const value = attrs[i]; + if (typeof value === 'number') { + // only namespaces are supported. Other value types (such as style/class + // entries) are not supported in this function. + if (value !== AttributeMarker.NamespaceURI) { + break; + } + + // we just landed on the marker value ... therefore + // we should skip to the next entry + i++; + + const namespaceURI = attrs[i++] as string; + const attrName = attrs[i++] as string; + const attrVal = attrs[i++] as string; + ngDevMode && ngDevMode.rendererSetAttribute++; + isProc ? + (renderer as ProceduralRenderer3).setAttribute(native, attrName, attrVal, namespaceURI) : + native.setAttributeNS(namespaceURI, attrName, attrVal); + } else { + /// attrName is string; + const attrName = value as string; + const attrVal = attrs[++i]; + if (attrName !== NG_PROJECT_AS_ATTR_NAME) { + // Standard attributes + ngDevMode && ngDevMode.rendererSetAttribute++; + if (isAnimationProp(attrName)) { + if (isProc) { + (renderer as ProceduralRenderer3).setProperty(native, attrName, attrVal); + } + } else { + isProc ? + (renderer as ProceduralRenderer3) + .setAttribute(native, attrName as string, attrVal as string) : + native.setAttribute(attrName as string, attrVal as string); + } + } + i++; + } + } + + // another piece of code may iterate over the same attributes array. Therefore + // it may be helpful to return the exact spot where the attributes array exited + // whether by running into an unsupported marker or if all the static values were + // iterated over. + return i; +} + + +export function attrsStylingIndexOf(attrs: TAttributes, startIndex: number): number { + for (let i = startIndex; i < attrs.length; i++) { + const val = attrs[i]; + if (val === AttributeMarker.Classes || val === AttributeMarker.Styles) { + return i; + } + } + return -1; +} diff --git a/packages/core/test/acceptance/integration_spec.ts b/packages/core/test/acceptance/integration_spec.ts index 0360ecb21d..7612e8b22e 100644 --- a/packages/core/test/acceptance/integration_spec.ts +++ b/packages/core/test/acceptance/integration_spec.ts @@ -9,39 +9,9 @@ import {Component, Directive, HostBinding, HostListener, Input, QueryList, ViewC import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; -import {onlyInIvy} from '@angular/private/testing'; +import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; describe('acceptance integration tests', () => { - onlyInIvy('[style] and [class] bindings are a new feature') - .it('should render host bindings on the root component', () => { - @Component({template: '...'}) - class MyApp { - @HostBinding('style') public myStylesExp = {}; - @HostBinding('class') public myClassesExp = {}; - } - - TestBed.configureTestingModule({declarations: [MyApp]}); - const fixture = TestBed.createComponent(MyApp); - const element = fixture.nativeElement; - fixture.detectChanges(); - - const component = fixture.componentInstance; - component.myStylesExp = {width: '100px'}; - component.myClassesExp = 'foo'; - fixture.detectChanges(); - - expect(element.style['width']).toEqual('100px'); - expect(element.classList.contains('foo')).toBeTruthy(); - - component.myStylesExp = {width: '200px'}; - component.myClassesExp = 'bar'; - fixture.detectChanges(); - - expect(element.style['width']).toEqual('200px'); - expect(element.classList.contains('foo')).toBeFalsy(); - expect(element.classList.contains('bar')).toBeTruthy(); - }); - it('should only call inherited host listeners once', () => { let clicks = 0; @@ -97,20 +67,6 @@ describe('acceptance integration tests', () => { expect(subInstance.dirs.first).toBeAnInstanceOf(SomeDir); }); - it('should render host class and style on the root component', () => { - @Component({template: '...', host: {class: 'foo', style: 'color: red'}}) - class MyApp { - } - - TestBed.configureTestingModule({declarations: [MyApp]}); - const fixture = TestBed.createComponent(MyApp); - const element = fixture.nativeElement; - fixture.detectChanges(); - - expect(element.style['color']).toEqual('red'); - expect(element.classList.contains('foo')).toBeTruthy(); - }); - it('should not set inputs after destroy', () => { @Directive({ selector: '[no-assign-after-destroy]', @@ -146,5 +102,4 @@ describe('acceptance integration tests', () => { fixture.detectChanges(); }).not.toThrow(); }); - }); diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts new file mode 100644 index 0000000000..4eb477fef4 --- /dev/null +++ b/packages/core/test/acceptance/styling_spec.ts @@ -0,0 +1,99 @@ +/** + * @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 {Component, HostBinding} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; +import {expect} from '@angular/platform-browser/testing/src/matchers'; +import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; + +describe('acceptance integration tests', () => { + onlyInIvy('[style] and [class] bindings are a new feature') + .it('should render host bindings on the root component', () => { + @Component({template: '...'}) + class MyApp { + @HostBinding('style') public myStylesExp = {}; + @HostBinding('class') public myClassesExp = {}; + } + + TestBed.configureTestingModule({declarations: [MyApp]}); + const fixture = TestBed.createComponent(MyApp); + const element = fixture.nativeElement; + fixture.detectChanges(); + + const component = fixture.componentInstance; + component.myStylesExp = {width: '100px'}; + component.myClassesExp = 'foo'; + fixture.detectChanges(); + + expect(element.style['width']).toEqual('100px'); + expect(element.classList.contains('foo')).toBeTruthy(); + + component.myStylesExp = {width: '200px'}; + component.myClassesExp = 'bar'; + fixture.detectChanges(); + + expect(element.style['width']).toEqual('200px'); + expect(element.classList.contains('foo')).toBeFalsy(); + expect(element.classList.contains('bar')).toBeTruthy(); + }); + + it('should render host class and style on the root component', () => { + @Component({template: '...', host: {class: 'foo', style: 'color: red'}}) + class MyApp { + } + + TestBed.configureTestingModule({declarations: [MyApp]}); + const fixture = TestBed.createComponent(MyApp); + const element = fixture.nativeElement; + fixture.detectChanges(); + + expect(element.style['color']).toEqual('red'); + expect(element.classList.contains('foo')).toBeTruthy(); + }); + + it('should combine the inherited static styles of a parent and child component', () => { + @Component({template: '...', host: {'style': 'width:100px; height:100px;'}}) + class ParentCmp { + } + + @Component({template: '...', host: {'style': 'width:200px; color:red'}}) + class ChildCmp extends ParentCmp { + } + + TestBed.configureTestingModule({declarations: [ChildCmp]}); + const fixture = TestBed.createComponent(ChildCmp); + fixture.detectChanges(); + + const element = fixture.nativeElement; + if (ivyEnabled) { + expect(element.style['height']).toEqual('100px'); + } + expect(element.style['width']).toEqual('200px'); + expect(element.style['color']).toEqual('red'); + }); + + it('should combine the inherited static classes of a parent and child component', () => { + @Component({template: '...', host: {'class': 'foo bar'}}) + class ParentCmp { + } + + @Component({template: '...', host: {'class': 'foo baz'}}) + class ChildCmp extends ParentCmp { + } + + TestBed.configureTestingModule({declarations: [ChildCmp]}); + const fixture = TestBed.createComponent(ChildCmp); + fixture.detectChanges(); + + const element = fixture.nativeElement; + if (ivyEnabled) { + expect(element.classList.contains('bar')).toBeTruthy(); + } + expect(element.classList.contains('foo')).toBeTruthy(); + expect(element.classList.contains('baz')).toBeTruthy(); + }); +}); diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 10ee5080db..d0d82ba7e6 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -158,6 +158,9 @@ { "name": "addComponentLogic" }, + { + "name": "addOrUpdateStaticStyle" + }, { "name": "addToViewTree" }, @@ -167,6 +170,9 @@ { "name": "allocateDirectiveIntoContext" }, + { + "name": "allowValueChange" + }, { "name": "appendChild" }, @@ -176,6 +182,9 @@ { "name": "attachPatchData" }, + { + "name": "attrsStylingIndexOf" + }, { "name": "baseResolveDirective" }, @@ -323,6 +332,9 @@ { "name": "getDirectiveDef" }, + { + "name": "getDirectiveRegistryValuesIndexOf" + }, { "name": "getElementDepthCount" }, @@ -416,9 +428,6 @@ { "name": "hasStyleInput" }, - { - "name": "hasStyling" - }, { "name": "hasTagAndTypeMatch" }, @@ -524,6 +533,12 @@ { "name": "noSideEffects" }, + { + "name": "patchContextWithStaticAttrs" + }, + { + "name": "patchInitialStylingValue" + }, { "name": "postProcessBaseDirective" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index c8b4beddd7..a279b0e311 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -362,6 +362,9 @@ { "name": "addComponentLogic" }, + { + "name": "addOrUpdateStaticStyle" + }, { "name": "addPlayerInternal" }, @@ -401,6 +404,9 @@ { "name": "attachPatchData" }, + { + "name": "attrsStylingIndexOf" + }, { "name": "baseResolveDirective" }, @@ -854,9 +860,6 @@ { "name": "hasStyleInput" }, - { - "name": "hasStyling" - }, { "name": "hasTagAndTypeMatch" }, @@ -1064,6 +1067,12 @@ { "name": "noSideEffects" }, + { + "name": "patchContextWithStaticAttrs" + }, + { + "name": "patchInitialStylingValue" + }, { "name": "pointers" }, diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 21371db81e..5e396bee48 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -1678,7 +1678,7 @@ describe('render3 integration test', () => { if (rf & RenderFlags.Create) { elementStart( 0, 'div', - ['DirWithClass', AttributeMarker.Classes, 'apple', 'orange', 'banana']); + ['DirWithClass', '', AttributeMarker.Classes, 'apple', 'orange', 'banana']); elementStyling(); elementEnd(); } @@ -1698,9 +1698,9 @@ describe('render3 integration test', () => { */ const App = createComponent('app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - elementStart( - 0, 'div', - ['DirWithStyle', AttributeMarker.Styles, 'width', '100px', 'height', '200px']); + elementStart(0, 'div', [ + 'DirWithStyle', '', AttributeMarker.Styles, 'width', '100px', 'height', '200px' + ]); elementStyling(); elementEnd(); } diff --git a/packages/core/test/render3/node_selector_matcher_spec.ts b/packages/core/test/render3/node_selector_matcher_spec.ts index 9a62268fd1..0b55dd306d 100644 --- a/packages/core/test/render3/node_selector_matcher_spec.ts +++ b/packages/core/test/render3/node_selector_matcher_spec.ts @@ -317,7 +317,7 @@ describe('css selector matching', () => { expect(isMatching('div', tNode, selector)).toBeTruthy(); //
(with styling context but without attrs) - tNode.stylingTemplate = initializeStaticContext([AttributeMarker.Classes, 'abc']); + tNode.stylingTemplate = initializeStaticContext([AttributeMarker.Classes, 'abc'], 0); tNode.attrs = null; expect(isMatching('div', tNode, selector)).toBeTruthy(); }); diff --git a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts index 2ea84900b3..7eda24e508 100644 --- a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts +++ b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts @@ -39,7 +39,7 @@ describe('style and class based bindings', () => { return lView; } - function initContext( + function createStylingTemplate( initialStyles?: (number | string)[] | null, styleBindings?: string[] | null, initialClasses?: (string | number | boolean)[] | null, classBindings?: string[] | null, sanitizer?: StyleSanitizeFn | null): StylingContext { @@ -53,8 +53,17 @@ describe('style and class based bindings', () => { attrsWithStyling.push(...initialStyles as any); } - const tpl = initializeStaticContext(attrsWithStyling) !; + const tpl = initializeStaticContext(attrsWithStyling, 0) !; updateContextWithBindings(tpl, null, classBindings || null, styleBindings || null, sanitizer); + return tpl; + } + + function createStylingContext( + initialStyles?: (number | string)[] | null, styleBindings?: string[] | null, + initialClasses?: (string | number | boolean)[] | null, classBindings?: string[] | null, + sanitizer?: StyleSanitizeFn | null): StylingContext { + const tpl = createStylingTemplate( + initialStyles, styleBindings, initialClasses, classBindings, sanitizer); return allocStylingContext(element, tpl); } @@ -198,7 +207,7 @@ describe('style and class based bindings', () => { describe('styles', () => { describe('static styling properties within a context', () => { it('should initialize empty template', () => { - const template = initContext(); + const template = createStylingContext(); assertContext(template, [ element, masterConfig(9), @@ -213,13 +222,14 @@ describe('style and class based bindings', () => { }); it('should initialize static styles and classes', () => { - const template = initContext(['color', 'red', 'width', '10px'], null, ['foo', 'bar']); + const template = + createStylingContext(['color', 'red', 'width', '10px'], null, ['foo', 'bar']); assertContext(template, [ element, masterConfig(9), [null, 2, false, null], - [null, null, 'color', 'red', 'width', '10px'], - [null, null, 'foo', true, 'bar', true], + [null, null, 'color', 'red', 0, 'width', '10px', 0], + [null, null, 'foo', true, 0, 'bar', true, 0], [0, 0, 0, 0], [0, 0, 9, null, 0], [0, 0, 9, null, 0], @@ -227,91 +237,133 @@ describe('style and class based bindings', () => { ]); }); - it('should initialize and then patch static styling inline with existing static styling', + it('should initialize and then patch static styling inline with existing static styling and also replace values if the same directive runs twice', () => { - const template = initContext(['color', 'red'], null, ['foo']); + const template = createStylingTemplate(['color', 'red'], null, ['foo']); expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ null, null, 'color', 'red', + 0, ]); expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ null, null, 'foo', true, + 0, ]); patchContext(template, ['color', 'black', 'height', '200px'], ['bar', 'foo'], '1'); expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ - null, null, 'color', 'red', 'height', '200px' + null, + null, + 'color', + 'red', + 0, + 'height', + '200px', + 1, ]); expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ - null, null, 'foo', true, 'bar', true + null, + null, + 'foo', + true, + 0, + 'bar', + true, + 1, + ]); + + patchContext(template, ['color', 'orange'], []); + expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ + null, + null, + 'color', + 'orange', + 0, + 'height', + '200px', + 1, + ]); + expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ + null, + null, + 'foo', + true, + 0, + 'bar', + true, + 1, ]); }); - it('should only populate static styles for a given directive once', () => { - const template = initContext(['color', 'red'], null, ['foo']); - expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ - null, - null, - 'color', - 'red', - ]); - expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ - null, - null, - 'foo', - true, - ]); + it('should only populate static styles for a given directive once only when the context is allocated', + () => { + const template = createStylingTemplate(['color', 'red'], null, ['foo']); + expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ + null, + null, + 'color', + 'red', + 0, + ]); + expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ + null, + null, + 'foo', + true, + 0, + ]); - patchContext(template, ['color', 'black', 'height', '200px'], ['bar', 'foo']); - expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ - null, - null, - 'color', - 'red', - ]); - expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ - null, - null, - 'foo', - true, - ]); + patchContext(template, ['color', 'black', 'height', '200px'], ['bar', 'foo']); + expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ + null, + null, + 'color', + 'black', + 0, + 'height', + '200px', + 0, + ]); + expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ + null, + null, + 'foo', + true, + 0, + 'bar', + true, + 0, + ]); - patchContext(template, ['color', 'black', 'height', '200px'], ['bar', 'foo'], '1'); - expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ - null, - null, - 'color', - 'red', - 'height', - '200px', - ]); - expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ - null, null, 'foo', true, 'bar', true - ]); + allocStylingContext(element, template); - patchContext(template, ['color', 'black', 'height', '200px'], ['bar', 'foo'], '1'); - expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ - null, - null, - 'color', - 'red', - 'height', - '200px', - ]); - expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ - null, - null, - 'foo', - true, - 'bar', - true, - ]); - }); + patchContext(template, ['color', 'black', 'height', '200px'], ['bar', 'foo'], '1'); + expect(template[StylingIndex.InitialStyleValuesPosition]).toEqual([ + null, + null, + 'color', + 'black', + 0, + 'height', + '200px', + 0, + ]); + expect(template[StylingIndex.InitialClassValuesPosition]).toEqual([ + null, + null, + 'foo', + true, + 0, + 'bar', + true, + 0, + ]); + }); }); describe('instructions', () => { @@ -440,8 +492,8 @@ describe('style and class based bindings', () => { null, masterConfig(17, false, false), // [null, 2, false, null], - [null, null, 'width', null], - [null, null, 'foo', false], + [null, null, 'width', null, 0], + [null, null, 'foo', false, 0], [1, 1, 1, 1, 9, 13], [1, 0, 21, null, 1], [1, 0, 17, null, 1], @@ -478,8 +530,8 @@ describe('style and class based bindings', () => { null, masterConfig(25, false, false), // [null, 2, false, null, 'SOME DIRECTIVE', 6, false, null], - [null, null, 'width', null, 'height', null], - [null, null, 'foo', false, 'bar', false], + [null, null, 'width', null, 0, 'height', null, 1], + [null, null, 'foo', false, 0, 'bar', false, 1], [2, 2, 1, 1, 9, 17, 2, 1, 9, 13, 21], [2, 0, 33, null, 1, 0, 37, null, 1], [2, 0, 25, null, 1, 0, 29, null, 1], @@ -492,7 +544,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 29), + cleanStyle(6, 29), 'height', null, 1, @@ -504,7 +556,7 @@ describe('style and class based bindings', () => { 0, // #21 - cleanClass(5, 37), + cleanClass(6, 37), 'bar', null, 1, @@ -516,7 +568,7 @@ describe('style and class based bindings', () => { 0, // #29 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 1, @@ -528,7 +580,7 @@ describe('style and class based bindings', () => { 0, // #37 - cleanClass(5, 21), + cleanClass(6, 21), 'bar', null, 1, @@ -544,8 +596,8 @@ describe('style and class based bindings', () => { null, 2, false, null, 'SOME DIRECTIVE', 6, false, null, 'SOME DIRECTIVE 2', 11, false, null ], - [null, null, 'width', null, 'height', null, 'opacity', null], - [null, null, 'foo', false, 'bar', false, 'baz', false], + [null, null, 'width', null, 0, 'height', null, 1, 'opacity', null, 2], + [null, null, 'foo', false, 0, 'bar', false, 1, 'baz', false, 2], [3, 3, 1, 1, 9, 21, 2, 1, 9, 13, 25, 3, 3, 17, 9, 13, 29, 25, 21], [3, 0, 45, null, 1, 0, 49, null, 1, 0, 53, null, 1], [3, 0, 33, null, 1, 0, 37, null, 1, 0, 41, null, 1], @@ -558,13 +610,13 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 37), + cleanStyle(6, 37), 'height', null, 1, // #17 - cleanStyle(7, 41), + cleanStyle(9, 41), 'opacity', null, 2, @@ -576,13 +628,13 @@ describe('style and class based bindings', () => { 0, // #25 - cleanClass(5, 49), + cleanClass(6, 49), 'bar', null, 1, // #29 - cleanClass(7, 53), + cleanClass(9, 53), 'baz', null, 2, @@ -594,13 +646,13 @@ describe('style and class based bindings', () => { 0, // #37 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 1, // #41 - cleanStyle(7, 17), + cleanStyle(9, 17), 'opacity', null, 2, @@ -612,13 +664,13 @@ describe('style and class based bindings', () => { 0, // #49 - cleanClass(5, 25), + cleanClass(6, 25), 'bar', null, 1, // #53 - cleanClass(7, 29), + cleanClass(9, 29), 'baz', null, 2, @@ -642,7 +694,7 @@ describe('style and class based bindings', () => { it('should build a list of multiple styling values', () => { const getStyles = trackStylesFactory(); - const stylingContext = initContext(); + const stylingContext = createStylingContext(); updateStyles(stylingContext, { width: '100px', height: '100px', @@ -652,7 +704,7 @@ describe('style and class based bindings', () => { }); it('should evaluate the delta between style changes when rendering occurs', () => { - const stylingContext = initContext(['width', '100px'], ['width', 'height']); + const stylingContext = createStylingContext(['width', '100px'], ['width', 'height']); updateStyles(stylingContext, { height: '200px', }); @@ -674,7 +726,7 @@ describe('style and class based bindings', () => { it('should update individual values on a set of styles', () => { const getStyles = trackStylesFactory(); - const stylingContext = initContext(null, ['width', 'height']); + const stylingContext = createStylingContext(null, ['width', 'height']); updateStyles(stylingContext, { width: '100px', height: '100px', @@ -684,7 +736,7 @@ describe('style and class based bindings', () => { }); it('should only mark itself as updated when one or more properties have been applied', () => { - const stylingContext = initContext(); + const stylingContext = createStylingContext(); expect(isContextDirty(stylingContext)).toBeFalsy(); updateStyles(stylingContext, { @@ -709,7 +761,7 @@ describe('style and class based bindings', () => { }); it('should only mark itself as updated when any single properties have been applied', () => { - const stylingContext = initContext(null, ['height']); + const stylingContext = createStylingContext(null, ['height']); updateStyles(stylingContext, { width: '100px', height: '100px', @@ -729,7 +781,7 @@ describe('style and class based bindings', () => { it('should prioritize multi and single styles over initial styles', () => { const getStyles = trackStylesFactory(); - const stylingContext = initContext( + const stylingContext = createStylingContext( ['width', '100px', 'height', '100px', 'opacity', '0'], ['width', 'height', 'opacity']); expect(getStyles(stylingContext)).toEqual({}); @@ -764,7 +816,7 @@ describe('style and class based bindings', () => { }); it('should cleanup removed styles from the context once the styles are built', () => { - const stylingContext = initContext(null, ['width', 'height']); + const stylingContext = createStylingContext(null, ['width', 'height']); const getStyles = trackStylesFactory(); updateStyles(stylingContext, {width: '100px', height: '100px'}); @@ -776,7 +828,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 21), + cleanStyle(6, 21), 'height', null, 0, @@ -788,7 +840,7 @@ describe('style and class based bindings', () => { 0, // #21 - dirtyStyle(5, 13), + dirtyStyle(6, 13), 'height', '100px', 0, @@ -805,7 +857,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 25), + cleanStyle(6, 25), 'height', null, 0, @@ -823,7 +875,7 @@ describe('style and class based bindings', () => { 0, // #25 - dirtyStyle(5, 13), + dirtyStyle(6, 13), 'height', null, 0, @@ -838,7 +890,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 25), + cleanStyle(6, 25), 'height', null, 0, @@ -856,7 +908,7 @@ describe('style and class based bindings', () => { 0, // #23 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 0, @@ -873,7 +925,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 25), + cleanStyle(6, 25), 'height', null, 0, @@ -891,7 +943,7 @@ describe('style and class based bindings', () => { 0, // #23 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 0, @@ -908,7 +960,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 25), + cleanStyle(6, 25), 'height', null, 0, @@ -926,7 +978,7 @@ describe('style and class based bindings', () => { 0, // #23 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 0, @@ -935,7 +987,7 @@ describe('style and class based bindings', () => { it('should find the next available space in the context when data is added after being removed before', () => { - const stylingContext = initContext(null, ['line-height']); + const stylingContext = createStylingContext(null, ['line-height']); const getStyles = trackStylesFactory(); updateStyles(stylingContext, {width: '100px', height: '100px', opacity: '0.5'}); @@ -1137,7 +1189,7 @@ describe('style and class based bindings', () => { it('should render all data as not being dirty after the styles are built', () => { const getStyles = trackStylesFactory(); - const stylingContext = initContext(null, ['height']); + const stylingContext = createStylingContext(null, ['height']); const cachedStyleValue = {width: '100px'}; @@ -1148,7 +1200,7 @@ describe('style and class based bindings', () => { element, masterConfig(13, true), // [null, 2, true, null], - [null, null, 'height', null], + [null, null, 'height', null, 0], [null, null], [1, 0, 1, 0, 9], [0, 0, 21, null, 0], @@ -1180,7 +1232,7 @@ describe('style and class based bindings', () => { element, masterConfig(13, false), // [null, 2, false, null], - [null, null, 'height', null], + [null, null, 'height', null, 0], [null, null], [1, 0, 1, 0, 9], [0, 0, 21, null, 0], @@ -1212,7 +1264,8 @@ describe('style and class based bindings', () => { const getStyles = trackStylesFactory(); const styleBindings = ['border-image', 'border-width']; const styleSanitizer = defaultStyleSanitizer; - const stylingContext = initContext(null, styleBindings, null, null, styleSanitizer); + const stylingContext = + createStylingContext(null, styleBindings, null, null, styleSanitizer); updateStyleProp(stylingContext, 0, 'url(foo.jpg)'); updateStyleProp(stylingContext, 1, '100px'); @@ -1225,7 +1278,7 @@ describe('style and class based bindings', () => { 0, // #13 - dirtyStyle(5, 21), + dirtyStyle(6, 21), 'border-width', '100px', 0, @@ -1237,7 +1290,7 @@ describe('style and class based bindings', () => { 0, // #21 - cleanStyle(5, 13), + cleanStyle(6, 13), 'border-width', null, 0, @@ -1253,7 +1306,7 @@ describe('style and class based bindings', () => { 0, // #13 - dirtyStyle(5, 25), + dirtyStyle(6, 25), 'border-width', '100px', 0, @@ -1271,7 +1324,7 @@ describe('style and class based bindings', () => { 0, // #23 - cleanStyle(5, 13), + cleanStyle(6, 13), 'border-width', null, 0, @@ -1287,7 +1340,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 25), + cleanStyle(6, 25), 'border-width', '100px', 0, @@ -1305,7 +1358,7 @@ describe('style and class based bindings', () => { 0, // #23 - cleanStyle(5, 13), + cleanStyle(6, 13), 'border-width', null, 0, @@ -1427,7 +1480,7 @@ describe('style and class based bindings', () => { 1, // #13 - cleanStyle(5, 21), + cleanStyle(6, 21), 'height', null, 1, @@ -1439,7 +1492,7 @@ describe('style and class based bindings', () => { 1, // #21 - dirtyStyle(5, 13), + dirtyStyle(6, 13), 'height', '99px', 1, @@ -1463,7 +1516,7 @@ describe('style and class based bindings', () => { 1, // #13 - cleanStyle(5, 25), + cleanStyle(6, 25), 'height', null, 1, @@ -1481,7 +1534,7 @@ describe('style and class based bindings', () => { 2, // #25 - dirtyStyle(5, 13), + dirtyStyle(6, 13), 'height', '999px', 3, @@ -1499,7 +1552,7 @@ describe('style and class based bindings', () => { 1, // #13 - cleanStyle(5, 25), + cleanStyle(6, 25), 'height', null, 1, @@ -1517,7 +1570,7 @@ describe('style and class based bindings', () => { 2, // #25 - dirtyStyle(5, 13), + dirtyStyle(6, 13), 'height', '999px', 3, @@ -1560,7 +1613,7 @@ describe('style and class based bindings', () => { 1, // #13 - cleanClass(5, 33), + cleanClass(6, 33), 'green', null, 1, @@ -1590,7 +1643,7 @@ describe('style and class based bindings', () => { 3, // #33 - dirtyClass(5, 13), + dirtyClass(6, 13), 'green', true, 3, @@ -1608,7 +1661,7 @@ describe('style and class based bindings', () => { 1, // #13 - cleanClass(5, 29), + cleanClass(6, 29), 'green', null, 1, @@ -1632,7 +1685,7 @@ describe('style and class based bindings', () => { 2, // #29 - dirtyClass(5, 13), + dirtyClass(6, 13), 'green', true, 3, @@ -1656,13 +1709,13 @@ describe('style and class based bindings', () => { 1, // #13 - cleanClass(5, 17), + cleanClass(6, 17), 'green', null, 1, // #17 - dirtyClass(5, 13), + dirtyClass(6, 13), 'green', true, 1, @@ -1813,7 +1866,7 @@ describe('style and class based bindings', () => { }); it('should skip issuing style updates if there is nothing to update upon first render', () => { - const stylingContext = initContext(null, ['color']); + const stylingContext = createStylingContext(null, ['color']); const store = new MockStylingStore(element as HTMLElement, BindingType.Class); const getStyles = trackStylesFactory(store); const otherDirective = {}; @@ -1844,13 +1897,13 @@ describe('style and class based bindings', () => { describe('classes', () => { it('should initialize with the provided class bindings', () => { - const template = initContext(null, null, null, ['one', 'two']); + const template = createStylingContext(null, null, null, ['one', 'two']); assertContext(template, [ element, masterConfig(17, false), // [null, 2, false, null], [null, null], - [null, null, 'one', false, 'two', false], + [null, null, 'one', false, 0, 'two', false, 0], [0, 2, 0, 2, 9, 13], [2, 0, 17, null, 2], [0, 0, 17, null, 0], @@ -1863,7 +1916,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanClass(5, 21), + cleanClass(6, 21), 'two', null, 0, @@ -1875,7 +1928,7 @@ describe('style and class based bindings', () => { 0, // #21 - cleanClass(5, 13), + cleanClass(6, 13), 'two', null, 0, @@ -1884,7 +1937,7 @@ describe('style and class based bindings', () => { it('should update multi class properties against the static classes', () => { const getClasses = trackClassesFactory(); - const stylingContext = initContext(null, null, ['bar'], ['bar', 'foo']); + const stylingContext = createStylingContext(null, null, ['bar'], ['bar', 'foo']); expect(getClasses(stylingContext)).toEqual({}); updateClasses(stylingContext, {foo: true, bar: false}); expect(getClasses(stylingContext)).toEqual({'foo': true, 'bar': false}); @@ -1894,7 +1947,7 @@ describe('style and class based bindings', () => { it('should update single class properties despite static classes being present', () => { const getClasses = trackClassesFactory(); - const stylingContext = initContext(null, null, ['bar'], ['bar', 'foo']); + const stylingContext = createStylingContext(null, null, ['bar'], ['bar', 'foo']); expect(getClasses(stylingContext)).toEqual({}); updateClassProp(stylingContext, 0, true); @@ -1909,7 +1962,7 @@ describe('style and class based bindings', () => { it('should understand updating multi-classes using a string-based value while respecting single class-based props', () => { const getClasses = trackClassesFactory(); - const stylingContext = initContext(null, null, null, ['baz']); + const stylingContext = createStylingContext(null, null, null, ['baz']); expect(getClasses(stylingContext)).toEqual({}); updateStylingMap(stylingContext, 'foo bar baz'); @@ -1924,14 +1977,14 @@ describe('style and class based bindings', () => { it('should place styles within the context and work alongside style-based values in harmony', () => { const getStylesAndClasses = trackStylesAndClasses(); - const stylingContext = - initContext(['width', '100px'], ['width', 'height'], ['wide'], ['wide', 'tall']); + const stylingContext = createStylingContext( + ['width', '100px'], ['width', 'height'], ['wide'], ['wide', 'tall']); assertContext(stylingContext, [ element, masterConfig(25, false), // [null, 2, false, null], - [null, null, 'width', '100px', 'height', null], - [null, null, 'wide', true, 'tall', false], + [null, null, 'width', '100px', 0, 'height', null, 0], + [null, null, 'wide', true, 0, 'tall', false, 0], [2, 2, 2, 2, 9, 13, 17, 21], [2, 0, 33, null, 2], [2, 0, 25, null, 2], @@ -1944,7 +1997,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 29), + cleanStyle(6, 29), 'height', null, 0, @@ -1956,7 +2009,7 @@ describe('style and class based bindings', () => { 0, // #21 - cleanClass(5, 37), + cleanClass(6, 37), 'tall', null, 0, @@ -1968,7 +2021,7 @@ describe('style and class based bindings', () => { 0, // #29 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 0, @@ -1980,7 +2033,7 @@ describe('style and class based bindings', () => { 0, // #37 - cleanClass(5, 21), + cleanClass(6, 21), 'tall', null, 0, @@ -1994,8 +2047,8 @@ describe('style and class based bindings', () => { element, masterConfig(25, true), // [null, 2, true, null], - [null, null, 'width', '100px', 'height', null], - [null, null, 'wide', true, 'tall', false], + [null, null, 'width', '100px', 0, 'height', null, 0], + [null, null, 'wide', true, 0, 'tall', false, 0], [2, 2, 2, 2, 9, 13, 17, 21], [2, 0, 37, 'tall round', 2], [2, 0, 25, cachedStyleMap, 2], @@ -2008,7 +2061,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 33), + cleanStyle(6, 33), 'height', null, 0, @@ -2020,7 +2073,7 @@ describe('style and class based bindings', () => { 0, // #21 - cleanClass(5, 37), + cleanClass(6, 37), 'tall', null, 0, @@ -2038,13 +2091,13 @@ describe('style and class based bindings', () => { 0, // #33 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 0, // #37 - dirtyClass(5, 21), + dirtyClass(6, 21), 'tall', true, 0, @@ -2076,8 +2129,8 @@ describe('style and class based bindings', () => { element, masterConfig(25, true), // [null, 2, true, null], - [null, null, 'width', '100px', 'height', null], - [null, null, 'wide', true, 'tall', false], + [null, null, 'width', '100px', 0, 'height', null, 0], + [null, null, 'wide', true, 0, 'tall', false, 0], [2, 2, 2, 2, 9, 13, 17, 21], [2, 0, 37, cachedClassMap, 2], [1, 0, 25, cachedStyleMap, 1], @@ -2090,7 +2143,7 @@ describe('style and class based bindings', () => { 0, // #13 - cleanStyle(5, 33), + cleanStyle(6, 33), 'height', null, 0, @@ -2102,7 +2155,7 @@ describe('style and class based bindings', () => { 0, // #21 - cleanClass(5, 37), + cleanClass(6, 37), 'tall', null, 0, @@ -2120,13 +2173,13 @@ describe('style and class based bindings', () => { 0, // #33 - cleanStyle(5, 13), + cleanStyle(6, 13), 'height', null, 0, // #37 - cleanClass(5, 21), + cleanClass(6, 21), 'tall', true, 0, @@ -2158,7 +2211,7 @@ describe('style and class based bindings', () => { it('should skip updating multi classes and styles if the input identity has not changed', () => { - const stylingContext = initContext(); + const stylingContext = createStylingContext(); const getStylesAndClasses = trackStylesAndClasses(); const stylesMap = {width: '200px'}; @@ -2214,7 +2267,7 @@ describe('style and class based bindings', () => { }); it('should skip updating multi classes if the string-based identity has not changed', () => { - const stylingContext = initContext(); + const stylingContext = createStylingContext(); const getClasses = trackClassesFactory(); const classes = 'apple orange banana'; @@ -2262,7 +2315,7 @@ describe('style and class based bindings', () => { }); it('should skip issuing class updates if there is nothing to update upon first render', () => { - const stylingContext = initContext(null, null, ['blue'], ['blue']); + const stylingContext = createStylingContext(null, null, ['blue'], ['blue']); const store = new MockStylingStore(element as HTMLElement, BindingType.Class); const getClasses = trackClassesFactory(store); @@ -2292,7 +2345,7 @@ describe('style and class based bindings', () => { describe('players', () => { it('should build a player with the computed styles and classes', () => { - const context = initContext(); + const context = createStylingContext(); const styles = {width: '100px', height: '200px'}; const classes = 'foo bar'; @@ -2332,7 +2385,7 @@ describe('style and class based bindings', () => { }); it('should only build one player for a given style map', () => { - const context = initContext(null, []); + const context = createStylingContext(null, []); let count = 0; const buildFn = (element: HTMLElement, type: BindingType, value: any) => { @@ -2355,7 +2408,7 @@ describe('style and class based bindings', () => { }); it('should only build one player for a given class map', () => { - const context = initContext(null, []); + const context = createStylingContext(null, []); let count = 0; const buildFn = (element: HTMLElement, type: BindingType, value: any) => { @@ -2377,7 +2430,7 @@ describe('style and class based bindings', () => { }); it('should store active players in the player context and remove them once destroyed', () => { - const context = initContext(null, []); + const context = createStylingContext(null, []); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2439,7 +2492,7 @@ describe('style and class based bindings', () => { it('should kick off single property change players alongside map-based ones and remove the players', () => { - const context = initContext(null, ['width', 'height'], null, ['foo', 'bar']); + const context = createStylingContext(null, ['width', 'height'], null, ['foo', 'bar']); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2536,7 +2589,7 @@ describe('style and class based bindings', () => { it('should destroy an existing player that was queued before it is flushed once the binding updates', () => { - const context = initContext(null, ['width']); + const context = createStylingContext(null, ['width']); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2571,7 +2624,7 @@ describe('style and class based bindings', () => { it('should nullify style map and style property factories if any follow up expressions not use them', () => { - const context = initContext(null, ['color'], null, ['foo']); + const context = createStylingContext(null, ['color'], null, ['foo']); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2590,15 +2643,15 @@ describe('style and class based bindings', () => { }; assertContext(context, [ - element, // - masterConfig(17, false), // - [null, 2, false, null], // - [null, null, 'color', null], // - [null, null, 'foo', false], // - [1, 1, 1, 1, 9, 13], // - [1, 0, 21, null, 1], // - [1, 0, 17, null, 1], // - null, // + element, // + masterConfig(17, false), // + [null, 2, false, null], // + [null, null, 'color', null, 0], // + [null, null, 'foo', false, 0], // + [1, 1, 1, 1, 9, 13], // + [1, 0, 21, null, 1], // + [1, 0, 17, null, 1], // + null, // // #9 cleanStyle(3, 17), @@ -2656,8 +2709,8 @@ describe('style and class based bindings', () => { element, // masterConfig(17, false), // [null, 2, false, null], // - [null, null, 'color', null], // - [null, null, 'foo', false], // + [null, null, 'color', null, 0], // + [null, null, 'foo', false, 0], // [1, 1, 1, 1, 9, 13], // [1, 0, 25, classMapWithPlayerFactory, 1], // [1, 0, 17, styleMapWithPlayerFactory, 1], // @@ -2714,14 +2767,14 @@ describe('style and class based bindings', () => { ] as PlayerContext); assertContext(context, [ - element, // - masterConfig(17, false), // - [null, 2, false, null], // - [null, null, 'color', null], // - [null, null, 'foo', false], // - [1, 1, 1, 1, 9, 13], // - [1, 0, 25, cachedClassMap, 1], // - [1, 0, 17, cachedStyleMap, 1], // + element, // + masterConfig(17, false), // + [null, 2, false, null], // + [null, null, 'color', null, 0], // + [null, null, 'foo', false, 0], // + [1, 1, 1, 1, 9, 13], // + [1, 0, 25, cachedClassMap, 1], // + [1, 0, 17, cachedStyleMap, 1], // playerContext, // #9 @@ -2763,7 +2816,7 @@ describe('style and class based bindings', () => { }); it('should not call a factory if no style and/or class values have been updated', () => { - const context = initContext([]); + const context = createStylingContext([]); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2813,7 +2866,7 @@ describe('style and class based bindings', () => { it('should invoke a single prop player over a multi style player when present and delegate back if not', () => { - const context = initContext(null, ['color']); + const context = createStylingContext(null, ['color']); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2860,7 +2913,7 @@ describe('style and class based bindings', () => { }); it('should return the old player for styles when a follow-up player is instantiated', () => { - const context = initContext([]); + const context = createStylingContext([]); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2890,7 +2943,7 @@ describe('style and class based bindings', () => { }); it('should return the old player for classes when a follow-up player is instantiated', () => { - const context = initContext(); + const context = createStylingContext(); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2932,7 +2985,7 @@ describe('style and class based bindings', () => { } }) as StyleSanitizeFn; - const context = initContext(null, null, null, null, sanitizer); + const context = createStylingContext(null, null, null, null, sanitizer); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); @@ -2959,7 +3012,7 @@ describe('style and class based bindings', () => { it('should automatically destroy existing players when the follow-up binding is not apart of a factory', () => { - const context = initContext(null, ['width'], null, ['foo', 'bar']); + const context = createStylingContext(null, ['width'], null, ['foo', 'bar']); const handler = new CorePlayerHandler(); const lView = createMockViewData(handler, context); diff --git a/tools/material-ci/angular_material_test_blocklist.js b/tools/material-ci/angular_material_test_blocklist.js index d2fda4e4b3..5f1ce83d21 100644 --- a/tools/material-ci/angular_material_test_blocklist.js +++ b/tools/material-ci/angular_material_test_blocklist.js @@ -17,37 +17,33 @@ // tslint:disable window.testBlocklist = { - "Portals CdkPortalOutlet should not clear programmatically-attached portals on init": { - "error": "ObjectUnsubscribedError: object unsubscribed", - "notes": "Unknown" - }, "Portals DomPortalOutlet should attach and detach a component portal without a ViewContainerRef": { "error": "Error: Expected '

Pizza

Chocolate

' to be '', 'Expected the DomPortalOutlet to be empty after detach'.", "notes": "Unknown" }, "CdkDrag in a drop container should be able to customize the preview element": { "error": "Error: Expected cdk-drag cdk-drag-preview to contain 'custom-preview'.", - "notes": "FW-1134: Queries don't match structural directives with ng-template in selector" + "notes": "Unknown" }, "CdkDrag in a drop container should position custom previews next to the pointer": { "error": "Error: Expected 'translate3d(8px, 33px, 0px)' to be 'translate3d(50px, 50px, 0px)'.", - "notes": "FW-1134: Queries don't match structural directives with ng-template in selector" + "notes": "Unknown" }, "CdkDrag in a drop container should lock position inside a drop container along the x axis": { "error": "Error: Expected 'translate3d(58px, 33px, 0px)' to be 'translate3d(100px, 50px, 0px)'.", - "notes": "FW-1134: Queries don't match structural directives with ng-template in selector" + "notes": "Unknown" }, "CdkDrag in a drop container should lock position inside a drop container along the y axis": { "error": "Error: Expected 'translate3d(8px, 83px, 0px)' to be 'translate3d(50px, 100px, 0px)'.", - "notes": "FW-1134: Queries don't match structural directives with ng-template in selector" + "notes": "Unknown" }, "CdkDrag in a drop container should inherit the position locking from the drop container": { "error": "Error: Expected 'translate3d(58px, 33px, 0px)' to be 'translate3d(100px, 50px, 0px)'.", - "notes": "FW-1134: Queries don't match structural directives with ng-template in selector" + "notes": "Unknown" }, "CdkDrag in a drop container should be able to customize the placeholder": { "error": "Error: Expected cdk-drag cdk-drag-placeholder to contain 'custom-placeholder'.", - "notes": "FW-1134: Queries don't match structural directives with ng-template in selector" + "notes": "Unknown" }, "CdkTable should be able to render multiple header and footer rows": { "error": "Error: Missing definitions for header, footer, and row; cannot determine which columns should be rendered.", @@ -129,21 +125,13 @@ window.testBlocklist = { "error": "Error: Failed: Expected node descendant num to be 2 but was 0", "notes": "Unknown" }, - "MatInput without forms validates the type": { - "error": "Error: Input type \"file\" isn't supported by matInput.", - "notes": "Unknown" - }, - "MatInput with textarea autosize should work in a step": { - "error": "TypeError: Cannot read property 'getBoundingClientRect' of null", - "notes": "Unknown" - }, "MatChipList StandardChipList basic behaviors should toggle the chips disabled state based on whether it is disabled": { "error": "Error: Expected true to be false.", - "notes": "MatChipList does not find MatChip content children because descendants is not true anymore. TODO: Fix spec so that it does not have the wrapping div" + "notes": "Unknown" }, "MatChipList StandardChipList focus behaviors should focus the first chip on focus": { "error": "Error: Expected -1 to be 0.", - "notes": "MatChipList does not find MatChip content children because descendants is not true anymore. TODO: Fix spec so that it does not have the wrapping div" + "notes": "Unknown" }, "MatChipList StandardChipList focus behaviors should watch for chip focus": { "error": "TypeError: Cannot read property 'focus' of undefined", @@ -187,23 +175,23 @@ window.testBlocklist = { }, "MatChipList FormFieldChipList keyboard behavior should maintain focus if the active chip is deleted": { "error": "TypeError: Cannot read property 'nativeElement' of null", - "notes": "FW-1064: debugElement.query does not find directive when that directive precedes another" + "notes": "Unknown" }, "MatChipList FormFieldChipList keyboard behavior when the input has focus should not focus the last chip when press DELETE": { "error": "TypeError: Cannot read property 'nativeElement' of null", - "notes": "FW-1064: debugElement.query does not find directive when that directive precedes another" + "notes": "Unknown" }, "MatChipList FormFieldChipList keyboard behavior when the input has focus should focus the last chip when press BACKSPACE": { "error": "TypeError: Cannot read property 'nativeElement' of null", - "notes": "FW-1064: debugElement.query does not find directive when that directive precedes another" + "notes": "Unknown" }, "MatChipList FormFieldChipList should complete the stateChanges stream on destroy": { "error": "TypeError: Cannot read property 'nativeElement' of null", - "notes": "FW-1064: debugElement.query does not find directive when that directive precedes another" + "notes": "Unknown" }, "MatChipList FormFieldChipList should point the label id to the chip input": { "error": "TypeError: Cannot read property 'nativeElement' of null", - "notes": "FW-1064: debugElement.query does not find directive when that directive precedes another" + "notes": "Unknown" }, "MatChipList with chip remove should properly focus next item if chip is removed through click": { "error": "TypeError: Cannot read property 'focus' of undefined", @@ -229,30 +217,10 @@ window.testBlocklist = { "error": "TypeError: Cannot read property 'nativeElement' of undefined", "notes": "Unknown" }, - "MatStepper basic stepper should set the correct aria-posinset and aria-setsize": { - "error": "Error: Expected $.length = 0 to equal 3.", - "notes": "Unknown" - }, "MatStepper linear stepper should not move to next step if current step is pending": { "error": "TypeError: Cannot read property 'nativeElement' of undefined", "notes": "Unknown" }, - "MatStepper aria labelling should not set aria-label or aria-labelledby attributes if they are not passed in": { - "error": "TypeError: Cannot read property 'hasAttribute' of null", - "notes": "Unknown" - }, - "MatStepper aria labelling should set the aria-label attribute": { - "error": "TypeError: Cannot read property 'getAttribute' of null", - "notes": "Unknown" - }, - "MatStepper aria labelling should set the aria-labelledby attribute": { - "error": "TypeError: Cannot read property 'getAttribute' of null", - "notes": "Unknown" - }, - "MatStepper aria labelling should not be able to set both an aria-label and aria-labelledby": { - "error": "TypeError: Cannot read property 'getAttribute' of null", - "notes": "Unknown" - }, "MatStepper stepper with error state should show error state": { "error": "TypeError: Cannot read property 'nativeElement' of undefined", "notes": "Unknown" @@ -266,44 +234,36 @@ window.testBlocklist = { "notes": "Unknown" }, "MatSidenav should be fixed position when in fixed mode": { - "error": "Error: Expected ng-tns-c28435-0 ng-trigger ng-trigger-transform mat-drawer mat-drawer-over ng-star-inserted to contain 'mat-sidenav-fixed'.", - "notes": "FW-1132: Host class bindings don't work if super class has host class bindings" + "error": "Error: Expected ng-tns-c21962-0 ng-trigger ng-trigger-transform mat-drawer mat-sidenav mat-drawer-over ng-star-inserted to contain 'mat-sidenav-fixed'.", + "notes": "Unknown" }, "MatSidenav should set fixed bottom and top when in fixed mode": { "error": "Error: Expected '' to be '20px'.", - "notes": "FW-1132: Host class bindings don't work if super class has host class bindings" - }, - "MatTree flat tree should initialize with rendered dataNodes": { - "error": "TypeError: Cannot read property 'classList' of undefined", "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" }, "MatTree flat tree with toggle should expand/collapse the node": { - "error": "TypeError: Cannot read property 'click' of undefined", - "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" + "error": "Error: Expected 0 to be 1, 'Expect node expanded one level'.", + "notes": "Unknown" }, "MatTree flat tree with toggle should expand/collapse the node recursively": { - "error": "TypeError: Cannot read property 'click' of undefined", - "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" - }, - "MatTree flat tree with undefined or null children should initialize with rendered dataNodes": { - "error": "TypeError: Cannot read property 'classList' of undefined", - "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" - }, - "MatTree nested tree with undefined or null children should initialize with rendered dataNodes": { - "error": "TypeError: Cannot read property 'classList' of undefined", - "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" - }, - "MatTree nested tree should initialize with rendered dataNodes": { - "error": "TypeError: Cannot read property 'classList' of undefined", - "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" + "error": "Error: Expected 0 to be 3, 'Expect nodes expanded'.", + "notes": "Unknown" }, "MatTree nested tree with toggle should expand/collapse the node": { - "error": "TypeError: Cannot read property 'click' of undefined", - "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" + "error": "Error: Expected 0 to be 1, 'Expect node expanded'.", + "notes": "Unknown" }, "MatTree nested tree with toggle should expand/collapse the node recursively": { - "error": "TypeError: Cannot read property 'click' of undefined", - "notes": "FW-1081: Static host classes don't work if component has superclass with host classes" + "error": "Error: Expected 0 to be 3, 'Expect node expanded'.", + "notes": "Unknown" + }, + "MatInput without forms validates the type": { + "error": "Error: Input type \"file\" isn't supported by matInput.", + "notes": "Unknown" + }, + "MatInput with textarea autosize should work in a step": { + "error": "TypeError: Cannot read property 'getBoundingClientRect' of null", + "notes": "Unknown" }, "Dialog should set the proper animation states": { "error": "TypeError: Cannot read property 'componentInstance' of null", @@ -355,79 +315,47 @@ window.testBlocklist = { }, "MatTooltip special cases should clear the `user-select` when a tooltip is set on a text field": { "error": "Error: Expected 'none' to be falsy.", - "notes": "FW-1133: Inline styles are not applied before constructor is run" + "notes": "Unknown" }, "MatTooltip special cases should clear the `-webkit-user-drag` on draggable elements": { "error": "Error: Expected 'none' to be falsy.", - "notes": "FW-1133: Inline styles are not applied before constructor is run" - }, - "MatTable with basic data source should be able to create a table with the right content and without when row": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", - "notes": "Unknown" - }, - "MatTable with basic data source should create a table with special when row": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", - "notes": "Unknown" - }, - "MatTable with basic data source should create a table with multiTemplateDataRows true": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", "notes": "Unknown" }, "MatTable should be able to render a table correctly with native elements": { "error": "Error: Missing definitions for header, footer, and row; cannot determine which columns should be rendered.", "notes": "Unknown" }, - "MatTable should render with MatTableDataSource and sort": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", - "notes": "Unknown" - }, - "MatTable should render with MatTableDataSource and pagination": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", - "notes": "Unknown" - }, "MatTable should apply custom sticky CSS class to sticky cells": { "error": "Error: Missing definitions for header, footer, and row; cannot determine which columns should be rendered.", "notes": "Unknown" }, - "MatTable with MatTableDataSource and sort/pagination/filter should create table and display data source contents": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", - "notes": "Unknown" - }, - "MatTable with MatTableDataSource and sort/pagination/filter changing data should update the table contents": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", - "notes": "Unknown" - }, "MatTable with MatTableDataSource and sort/pagination/filter should be able to filter the table contents": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", - "notes": "Unknown" - }, - "MatTable with MatTableDataSource and sort/pagination/filter should not match concatenated words": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", + "error": "TypeError: Cannot read property 'length' of undefined", "notes": "Unknown" }, "MatTable with MatTableDataSource and sort/pagination/filter should be able to sort the table contents": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", + "error": "Error: Failed: Expected cell contents to be a_3 but was a_1", "notes": "Unknown" }, "MatTable with MatTableDataSource and sort/pagination/filter should by default correctly sort an empty string": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", + "error": "Error: Failed: Expected cell contents to be but was a_1", "notes": "Unknown" }, "MatTable with MatTableDataSource and sort/pagination/filter should by default correctly sort undefined values": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", + "error": "Error: Failed: Expected cell contents to be but was a_1", "notes": "Unknown" }, "MatTable with MatTableDataSource and sort/pagination/filter should sort zero correctly": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", + "error": "Error: Failed: Expected cell contents to be -1 but was a_1", "notes": "Unknown" }, "MatTable with MatTableDataSource and sort/pagination/filter should be able to page the table contents": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", + "error": "Error: Failed: Expected 7 total rows but got 105", "notes": "Unknown" }, "MatTable with MatTableDataSource and sort/pagination/filter should sort strings with numbers larger than MAX_SAFE_INTEGER correctly": { - "error": "TypeError: Cannot read property 'querySelectorAll' of null", + "error": "Error: Failed: Expected cell contents to be 9563256840123535 but was a_1", "notes": "Unknown" } }; -// clang-format on +// clang-format on \ No newline at end of file