Skip to content

Commit 843651f

Browse files
authoredMar 11, 2023
feat(duplicate-directive): adds duplicate directive (#24)
1 parent b5d0d29 commit 843651f

File tree

6 files changed

+127
-4
lines changed

6 files changed

+127
-4
lines changed
 

Diff for: ‎apps/www/.eslintrc.json

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,21 @@
99
"error",
1010
{
1111
"type": "attribute",
12-
"prefix": "app",
12+
"prefix": [
13+
"app",
14+
"ngt"
15+
],
1316
"style": "camelCase"
1417
}
1518
],
1619
"@angular-eslint/component-selector": [
1720
"error",
1821
{
1922
"type": "element",
20-
"prefix": "app",
23+
"prefix": [
24+
"app",
25+
"ngt"
26+
],
2127
"style": "kebab-case"
2228
}
2329
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {render, RenderComponentOptions, screen} from "@testing-library/angular";
2+
import {DuplicateDirective} from "./duplicate.directive";
3+
4+
const renderOptions: RenderComponentOptions<unknown> = {
5+
imports: [DuplicateDirective]
6+
};
7+
8+
describe('Duplicate Directive', () => {
9+
10+
it.each([1, 2, 3, 5, 10, 15])(`should render element %s times`, async (duplicateValue) => {
11+
await render(`<p *ngtDuplicate="${duplicateValue}" data-testid="test"></p>`, renderOptions)
12+
const elements = screen.getAllByTestId<HTMLParagraphElement>('test');
13+
expect(elements.length).toEqual(duplicateValue);
14+
})
15+
16+
it('should return correct context for index', async () => {
17+
await render(`<p *ngtDuplicate="5; let index = index" data-testid="test">{{ index }}</p>`, renderOptions);
18+
const elements = screen.getAllByTestId<HTMLParagraphElement>('test');
19+
20+
elements.forEach((element, index) => expect(Number(element.textContent)).toEqual(index));
21+
})
22+
23+
it('should return correct context for first', async () => {
24+
await render(`<p *ngtDuplicate="5; let first = first" data-testid="test">{{ first }}</p>`, renderOptions);
25+
const elements = screen.getAllByTestId<HTMLParagraphElement>('test');
26+
27+
elements.forEach((element, index) => {
28+
const booleanValue = element.textContent === 'true';
29+
const isFirst = index === 0;
30+
expect(booleanValue).toEqual(isFirst);
31+
});
32+
})
33+
34+
it('should return correct context for last', async () => {
35+
await render(`<p *ngtDuplicate="5; let last = last" data-testid="test">{{ last }}</p>`, renderOptions);
36+
const elements = screen.getAllByTestId<HTMLParagraphElement>('test');
37+
38+
elements.forEach((element, index) => {
39+
const booleanValue = element.textContent === 'true';
40+
const isLast = index === elements.length - 1;
41+
expect(booleanValue).toEqual(isLast);
42+
});
43+
})
44+
45+
it('should return correct context for even', async () => {
46+
await render(`<p *ngtDuplicate="5; let even = even" data-testid="test">{{ even }}</p>`, renderOptions);
47+
const elements = screen.getAllByTestId<HTMLParagraphElement>('test');
48+
49+
elements.forEach((element, index) => {
50+
const booleanValue = element.textContent === 'true';
51+
const isEven = index % 2 === 0;
52+
expect(booleanValue).toEqual(isEven);
53+
});
54+
})
55+
56+
it('should return correct context for odd', async () => {
57+
await render(`<p *ngtDuplicate="5; let odd = odd" data-testid="test">{{ odd }}</p>`, renderOptions);
58+
const elements = screen.getAllByTestId<HTMLParagraphElement>('test');
59+
60+
elements.forEach((element, index) => {
61+
const booleanValue = element.textContent === 'true';
62+
const isOdd = index % 2 !== 0;
63+
expect(booleanValue).toEqual(isOdd);
64+
});
65+
})
66+
67+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {Directive, Input, TemplateRef, ViewContainerRef} from "@angular/core";
2+
3+
class DuplicateContext {
4+
constructor(public index: number, public count: number) {}
5+
6+
get first() {
7+
return this.index === 0;
8+
}
9+
10+
get last() {
11+
return this.index === this.count - 1;
12+
}
13+
14+
get even() {
15+
return this.index % 2 === 0;
16+
}
17+
18+
get odd() {
19+
return !this.even;
20+
}
21+
}
22+
23+
@Directive({
24+
selector: '[ngtDuplicate]',
25+
standalone: true
26+
})
27+
export class DuplicateDirective {
28+
constructor(
29+
private templateRef: TemplateRef<DuplicateContext>,
30+
private viewContainerRef: ViewContainerRef
31+
) {
32+
}
33+
34+
@Input('ngtDuplicate') set count(value: number) {
35+
this.viewContainerRef.clear();
36+
for (let index = 0; index < value; index++) {
37+
const context = new DuplicateContext(index, value);
38+
this.viewContainerRef.createEmbeddedView(this.templateRef, context);
39+
}
40+
}
41+
42+
static ngTemplateContextGuard(
43+
directive: DuplicateDirective,
44+
context: DuplicateContext
45+
): context is DuplicateContext {
46+
return true
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './duplicate.directive';
+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './duplicate';
12
export * from './radio-control-accessor';

Diff for: ‎apps/www/src/app/library-components/directives/radio-control-accessor/radio-value-accessor.directive.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {Component, DebugElement, HostBinding, HostListener, Input} from "@angular/core";
1+
import {Component, HostBinding, HostListener, Input} from "@angular/core";
22
import {RadioValueAccessorDirective} from "./radio-value-accessor.directive";
3-
import {CheckboxControlValueAccessor, FormControl, ReactiveFormsModule} from "@angular/forms";
3+
import {FormControl, ReactiveFormsModule} from "@angular/forms";
44
import {fireEvent, render, RenderResult} from "@testing-library/angular";
55

66
const selectors = {

0 commit comments

Comments
 (0)
Please sign in to comment.