fix(ivy): ensure component/directive class
selectors are properly understood (#27849)
Angular allows for `<ng-content>` elements to include a selector which filters which content-projected entries are inserted into the container depending on whether or not the selector is matched. With Ivy this feature has not fully worked due to the massive changes that took place inside of Ivy's styling algorithm code (which is responsible for assigning classes and styles to an element). This fix ensures that content-projection can correctly identify which slot an element should be placed into when class-based selectors are used. PR Close #27849
This commit is contained in:

committed by
Andrew Kushnir

parent
06e5bf1661
commit
e62eeed7d4
@ -10,6 +10,7 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/
|
||||
|
||||
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags,} from '../../src/render3/interfaces/projection';
|
||||
import {getProjectAsAttrValue, isNodeMatchingSelectorList, isNodeMatchingSelector} from '../../src/render3/node_selector_matcher';
|
||||
import {initializeStaticContext} from '../../src/render3/styling/class_and_style_bindings';
|
||||
import {createTNode} from '@angular/core/src/render3/instructions';
|
||||
import {getLView} from '@angular/core/src/render3/state';
|
||||
|
||||
@ -18,9 +19,12 @@ function testLStaticData(tagName: string, attrs: TAttributes | null): TNode {
|
||||
}
|
||||
|
||||
describe('css selector matching', () => {
|
||||
function isMatching(tagName: string, attrs: TAttributes | null, selector: CssSelector): boolean {
|
||||
return isNodeMatchingSelector(
|
||||
createTNode(getLView(), TNodeType.Element, 0, tagName, attrs, null), selector, false);
|
||||
function isMatching(
|
||||
tagName: string, attrsOrTNode: TAttributes | TNode | null, selector: CssSelector): boolean {
|
||||
const tNode = (!attrsOrTNode || Array.isArray(attrsOrTNode)) ?
|
||||
createTNode(getLView(), TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes, null) :
|
||||
(attrsOrTNode as TNode);
|
||||
return isNodeMatchingSelector(tNode, selector, false);
|
||||
}
|
||||
|
||||
describe('isNodeMatchingSimpleSelector', () => {
|
||||
@ -298,6 +302,26 @@ describe('css selector matching', () => {
|
||||
// <div class="foo">
|
||||
expect(isMatching('div', ['class', 'foo'], selector)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should match against a class value before and after the styling context is created',
|
||||
() => {
|
||||
// selector: 'div.abc'
|
||||
const selector = ['div', SelectorFlags.CLASS, 'abc'];
|
||||
const tNode = createTNode(getLView(), TNodeType.Element, 0, 'div', [], null);
|
||||
|
||||
// <div> (without attrs or styling context)
|
||||
expect(isMatching('div', tNode, selector)).toBeFalsy();
|
||||
|
||||
// <div class="abc"> (with attrs but without styling context)
|
||||
tNode.attrs = ['class', 'abc'];
|
||||
tNode.stylingTemplate = null;
|
||||
expect(isMatching('div', tNode, selector)).toBeTruthy();
|
||||
|
||||
// <div class="abc"> (with styling context but without attrs)
|
||||
tNode.stylingTemplate = initializeStaticContext([AttributeMarker.Classes, 'abc']);
|
||||
tNode.attrs = null;
|
||||
expect(isMatching('div', tNode, selector)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user