ngx-print 21.2.0 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintrc.json +50 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  5. package/.github/dependabot.yml +17 -0
  6. package/.github/workflows/PullRequest.yml +32 -0
  7. package/.github/workflows/stale.yml +25 -0
  8. package/.prettierignore +42 -0
  9. package/.prettierrc.json +14 -0
  10. package/CHANGELOG.md +126 -0
  11. package/README.md +90 -52
  12. package/_config.yml +1 -0
  13. package/angular.json +132 -0
  14. package/eslint.config.js +32 -0
  15. package/ng-package.json +7 -0
  16. package/package.json +60 -43
  17. package/projects/demo/public/favicon.ico +0 -0
  18. package/projects/demo/src/app/app.config.ts +11 -0
  19. package/projects/demo/src/app/app.routes.ts +3 -0
  20. package/projects/demo/src/app/demo.component.html +83 -0
  21. package/projects/demo/src/app/demo.component.scss +54 -0
  22. package/projects/demo/src/app/demo.component.ts +73 -0
  23. package/projects/demo/src/index.html +13 -0
  24. package/projects/demo/src/main.ts +5 -0
  25. package/projects/demo/src/styles.scss +1 -0
  26. package/projects/demo/tsconfig.app.json +19 -0
  27. package/projects/demo/tsconfig.spec.json +15 -0
  28. package/src/lib/ngx-print.base.ts +381 -0
  29. package/src/lib/ngx-print.directive.spec.ts +232 -0
  30. package/src/lib/ngx-print.directive.ts +72 -0
  31. package/src/lib/ngx-print.module.ts +8 -0
  32. package/src/lib/ngx-print.service.spec.ts +276 -0
  33. package/src/lib/ngx-print.service.ts +50 -0
  34. package/src/lib/print-options.ts +16 -0
  35. package/src/public_api.ts +8 -0
  36. package/tsconfig.json +38 -0
  37. package/tsconfig.lib.json +19 -0
  38. package/tsconfig.spec.json +10 -0
  39. package/tslint.json +17 -0
  40. package/fesm2022/ngx-print.mjs +0 -543
  41. package/fesm2022/ngx-print.mjs.map +0 -1
  42. package/types/ngx-print.d.ts +0 -228
  43. package/types/ngx-print.d.ts.map +0 -1
@@ -0,0 +1,276 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
3
+
4
+ import { NgxPrintService } from './ngx-print.service';
5
+ import { Component, CSP_NONCE, inject, provideZonelessChangeDetection } from '@angular/core';
6
+ import { PrintOptions } from './print-options';
7
+ import { PrintStyle } from './ngx-print.base';
8
+
9
+ const testNonce = 'dummy-nonce-value';
10
+
11
+ @Component({
12
+ template: `
13
+ <div id="print-section" style="border: 2px solid red;">
14
+ <h1>Welcome to ngx-print</h1>
15
+ <img
16
+ width="300"
17
+ alt="Angular Logo"
18
+ src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==" />
19
+ <h2>Here are some links to help you start:</h2>
20
+ <ul>
21
+ <li>
22
+ <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
23
+ </li>
24
+ <li>
25
+ <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
26
+ </li>
27
+ <li>
28
+ <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
29
+ </li>
30
+ </ul>
31
+ <table border="1">
32
+ <tr>
33
+ <td>Row 1, Column 1</td>
34
+ <td>Row 1, Column 2</td>
35
+ </tr>
36
+ <tr>
37
+ <td>Row 2, Column 1</td>
38
+ <td>Row 2, Column 2</td>
39
+ </tr>
40
+ </table>
41
+ </div>
42
+ `,
43
+ })
44
+ class TestNgxPrintServiceComponent {
45
+ private printService = inject(NgxPrintService);
46
+
47
+ printMe(printOptions: PrintOptions) {
48
+ this.printService.print(printOptions);
49
+ }
50
+ }
51
+
52
+ describe('NgxPrintService', () => {
53
+ let service: NgxPrintService;
54
+ let component: TestNgxPrintServiceComponent;
55
+ let fixture: ComponentFixture<TestNgxPrintServiceComponent>;
56
+
57
+ const styleSheet: PrintStyle = {
58
+ 'h2': { 'border': 'solid 1px' },
59
+ 'h1': { 'color': 'red', 'border': '1px solid' },
60
+ };
61
+
62
+ beforeEach(() => {
63
+ TestBed.configureTestingModule({
64
+ imports: [TestNgxPrintServiceComponent],
65
+ providers: [{ provide: CSP_NONCE, useValue: testNonce }, NgxPrintService, provideZonelessChangeDetection()],
66
+ });
67
+ service = TestBed.inject(NgxPrintService);
68
+ // Create a fixture object (that is going to allows us to create an instance of that component)
69
+ fixture = TestBed.createComponent(TestNgxPrintServiceComponent);
70
+
71
+ // Create a component instance ( ~ new Component)
72
+ component = fixture.componentInstance;
73
+ });
74
+
75
+ it('should be created', () => {
76
+ expect(service).toBeTruthy();
77
+ });
78
+
79
+ it('should print', () => {
80
+ vi.spyOn(service, 'print');
81
+
82
+ const customPrintOptions: PrintOptions = new PrintOptions({
83
+ printSectionId: 'print-section',
84
+ });
85
+
86
+ component.printMe(customPrintOptions);
87
+
88
+ expect(service.print).toHaveBeenCalledWith(customPrintOptions);
89
+ });
90
+
91
+ it('should print with title', () => {
92
+ vi.spyOn(service, 'print');
93
+
94
+ const customPrintOptions: PrintOptions = new PrintOptions({
95
+ printSectionId: 'print-section',
96
+ printTitle: 'Test Title',
97
+ });
98
+
99
+ component.printMe(customPrintOptions);
100
+
101
+ expect(service.print).toHaveBeenCalledWith(customPrintOptions);
102
+ });
103
+
104
+ it('should print with existing css', () => {
105
+ vi.spyOn(service, 'print');
106
+
107
+ const customPrintOptions: PrintOptions = new PrintOptions({
108
+ printSectionId: 'print-section',
109
+ useExistingCss: true,
110
+ });
111
+
112
+ component.printMe(customPrintOptions);
113
+
114
+ expect(service.print).toHaveBeenCalledWith(customPrintOptions);
115
+ });
116
+
117
+ it('should print with delay', () => {
118
+ vi.spyOn(service, 'print');
119
+
120
+ const customPrintOptions: PrintOptions = new PrintOptions({
121
+ printSectionId: 'print-section',
122
+ printDelay: 2000,
123
+ });
124
+
125
+ component.printMe(customPrintOptions);
126
+
127
+ expect(service.print).toHaveBeenCalledWith(customPrintOptions);
128
+ });
129
+
130
+ it('should print with previewOnly', () => {
131
+ vi.spyOn(service, 'print');
132
+
133
+ const customPrintOptions: PrintOptions = new PrintOptions({
134
+ printSectionId: 'print-section',
135
+ previewOnly: true,
136
+ });
137
+
138
+ component.printMe(customPrintOptions);
139
+
140
+ expect(service.print).toHaveBeenCalledWith(customPrintOptions);
141
+ });
142
+
143
+ it('should not close', () => {
144
+ vi.spyOn(service, 'print');
145
+
146
+ const customPrintOptions: PrintOptions = new PrintOptions({
147
+ printSectionId: 'print-section',
148
+ closeWindow: false,
149
+ });
150
+
151
+ component.printMe(customPrintOptions);
152
+
153
+ expect(service.print).toHaveBeenCalledWith(customPrintOptions);
154
+ });
155
+
156
+ it('should open new tab', () => {
157
+ vi.spyOn(service, 'print');
158
+
159
+ const customPrintOptions: PrintOptions = new PrintOptions({
160
+ printSectionId: 'print-section',
161
+ printMethod: 'tab',
162
+ });
163
+
164
+ component.printMe(customPrintOptions);
165
+
166
+ expect(service.print).toHaveBeenCalledWith(customPrintOptions);
167
+ });
168
+
169
+ it('should test the printStyle', () => {
170
+ // Create a spy on the instance's method
171
+ vi.spyOn(service, 'returnStyleValues');
172
+
173
+ // Call the function before checking if it has been called
174
+ service.returnStyleValues();
175
+
176
+ // Check if returnStyleValues has been called
177
+ expect(service.returnStyleValues).toHaveBeenCalled();
178
+ });
179
+
180
+ it('should return a string from array of objects', () => {
181
+ service.printStyle = styleSheet;
182
+
183
+ // Ensure the print styles are correctly formatted in the document
184
+ expect(service.returnStyleValues()).toEqual('<style nonce="' + testNonce + '"> h2{border:solid 1px} h1{color:red;border:1px solid} </style>');
185
+ });
186
+
187
+ it('should preserve quotes and commas within style values', () => {
188
+ service.printStyle = {
189
+ 'li::before': { content: '"→"' },
190
+ 'p': { 'font-family': '"Helvetica Neue", Arial, sans-serif', color: 'red' },
191
+ };
192
+
193
+ expect(service.returnStyleValues()).toEqual(
194
+ '<style nonce="' + testNonce + '"> li::before{content:"→"} p{font-family:"Helvetica Neue", Arial, sans-serif;color:red} </style>',
195
+ );
196
+ });
197
+
198
+ it('should accept a raw CSS string for printStyle', () => {
199
+ service.printStyle = 'h1, h2 { color: red; }';
200
+
201
+ expect(service.returnStyleValues()).toEqual('<style nonce="' + testNonce + '"> h1, h2 { color: red; } </style>');
202
+ });
203
+
204
+ it('should clear the style when printStyle is set to an empty string', () => {
205
+ service.printStyle = 'h1 { color: red; }';
206
+ service.printStyle = '';
207
+
208
+ expect(service.returnStyleValues()).toEqual('<style nonce="' + testNonce + '"> </style>');
209
+ });
210
+
211
+ it('should preserve the print section root element attributes (e.g. style)', () => {
212
+ const body = {
213
+ className: '',
214
+ classList: { contains: () => false },
215
+ innerHTML: '',
216
+ appendChild: vi.fn(),
217
+ };
218
+ const mockDocument = {
219
+ body,
220
+ open: vi.fn(),
221
+ close: vi.fn(),
222
+ head: { appendChild: vi.fn(), innerHTML: '' },
223
+ appendChild: vi.fn(),
224
+ createElement: (tag: string) => (tag === 'body' ? body : { innerHTML: '', appendChild: vi.fn(), textContent: '' }),
225
+ };
226
+ const mockWindow = {
227
+ document: mockDocument,
228
+ closed: true,
229
+ addEventListener: vi.fn(),
230
+ };
231
+ vi.spyOn(window, 'open').mockReturnValue(mockWindow as unknown as Window);
232
+
233
+ component.printMe(new PrintOptions({ printSectionId: 'print-section' }));
234
+
235
+ // The print section's own id and inline style must survive (not just its children's).
236
+ expect(mockDocument.body.innerHTML).toContain('id="print-section"');
237
+ expect(mockDocument.body.innerHTML).toContain('border: 2px solid red');
238
+ });
239
+
240
+ it('should emit on print completion (void)', () => {
241
+ vi.useFakeTimers();
242
+ const body = {
243
+ className: '',
244
+ classList: { contains: () => false },
245
+ innerHTML: '',
246
+ appendChild: vi.fn(),
247
+ };
248
+ const mockDocument = {
249
+ body,
250
+ open: vi.fn(),
251
+ close: vi.fn(),
252
+ head: { appendChild: vi.fn(), innerHTML: '' },
253
+ appendChild: vi.fn(),
254
+ createElement: (tag: string) => (tag === 'body' ? body : { innerHTML: '', appendChild: vi.fn(), textContent: '' }),
255
+ };
256
+ const mockWindow = {
257
+ document: mockDocument,
258
+ closed: true,
259
+ addEventListener: vi.fn(),
260
+ };
261
+ vi.spyOn(window, 'open').mockReturnValue(mockWindow as unknown as Window);
262
+
263
+ return new Promise<void>(resolve => {
264
+ service.printComplete$.subscribe(() => {
265
+ expect(true).toBe(true);
266
+ vi.useRealTimers();
267
+ resolve();
268
+ });
269
+ const customPrintOptions: PrintOptions = new PrintOptions({
270
+ printSectionId: 'print-section',
271
+ });
272
+ component.printMe(customPrintOptions);
273
+ vi.advanceTimersByTime(600);
274
+ });
275
+ });
276
+ });
@@ -0,0 +1,50 @@
1
+ import { Service } from '@angular/core';
2
+ import { PrintBase, PrintStyleInput } from './ngx-print.base';
3
+ import { PrintOptions } from './print-options';
4
+
5
+ /**
6
+ * Service for handling printing functionality in Angular applications.
7
+ * Extends the base printing class (PrintBase).
8
+ *
9
+ * @export
10
+ * @class NgxPrintService
11
+ * @extends {PrintBase}
12
+ */
13
+ @Service()
14
+ export class NgxPrintService extends PrintBase {
15
+ printComplete$ = this.printComplete.asObservable();
16
+
17
+ /**
18
+ * Initiates the printing process using the provided print options.
19
+ *
20
+ * @param {PrintOptions} printOptions - Options for configuring the printing process.
21
+ * @memberof NgxPrintService
22
+ * @returns {void}
23
+ */
24
+ public override print(printOptions?: Partial<PrintOptions>): void {
25
+ // Call the print method in the parent class
26
+ super.print(printOptions);
27
+ }
28
+
29
+ /**
30
+ * Sets the print style for the printing process.
31
+ *
32
+ * @param values - Either a dictionary representing the print styles, or a raw CSS string.
33
+ * @memberof NgxPrintService
34
+ * @setter
35
+ */
36
+ set printStyle(values: PrintStyleInput) {
37
+ super.setPrintStyle(values);
38
+ }
39
+
40
+ /**
41
+ * Sets the stylesheet file for the printing process.
42
+ *
43
+ * @param {string} cssList - A string representing the path to the stylesheet file.
44
+ * @memberof NgxPrintService
45
+ * @setter
46
+ */
47
+ set styleSheetFile(cssList: string) {
48
+ super.setStyleSheetFile(cssList);
49
+ }
50
+ }
@@ -0,0 +1,16 @@
1
+ export class PrintOptions {
2
+ printSectionId = '';
3
+ printTitle = '';
4
+ useExistingCss = false;
5
+ bodyClass = '';
6
+ printMethod: 'window' | 'tab' | 'iframe' = 'window';
7
+ previewOnly = false;
8
+ closeWindow = true;
9
+ printDelay = 0;
10
+
11
+ constructor(options?: Partial<PrintOptions>) {
12
+ if (options) {
13
+ Object.assign(this, options);
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,8 @@
1
+ /*
2
+ * Public API Surface of ngx-print
3
+ */
4
+ export { NgxPrintDirective } from './lib/ngx-print.directive';
5
+ export { NgxPrintModule } from './lib/ngx-print.module';
6
+ export { NgxPrintService } from './lib/ngx-print.service';
7
+ export { PrintOptions } from './lib/print-options';
8
+ export { PrintStyle, PrintStyleInput } from './lib/ngx-print.base';
package/tsconfig.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "compileOnSave": false,
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "sourceMap": true,
6
+ "esModuleInterop": true,
7
+ "declaration": false,
8
+ "module": "ES2022",
9
+ "moduleResolution": "bundler",
10
+ "emitDecoratorMetadata": true,
11
+ "experimentalDecorators": true,
12
+ "target": "ES2022",
13
+ "strict": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "typeRoots": [
16
+ "node_modules/@types"
17
+ ],
18
+ "paths": {
19
+ "ngx-print": [
20
+ "./dist/ngx-print"
21
+ ],
22
+ "ngx-print/*": [
23
+ "./dist/ngx-print/*"
24
+ ]
25
+ }
26
+ },
27
+ "angularCompilerOptions": {
28
+ "compilationMode": "partial"
29
+ },
30
+ "references": [
31
+ {
32
+ "path": "./projects/demo/tsconfig.app.json"
33
+ },
34
+ {
35
+ "path": "./projects/demo/tsconfig.spec.json"
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../out-tsc/lib",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "inlineSources": true,
8
+ "types": []
9
+ },
10
+ "exclude": ["src/test.ts", "**/*.spec.ts"],
11
+ "angularCompilerOptions": {
12
+ "extendedDiagnostics": {
13
+ "checks": {
14
+ "nullishCoalescingNotNullable": "suppress",
15
+ "optionalChainNotNullable": "suppress"
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./out-tsc/spec",
5
+ "types": ["node"],
6
+ "skipLibCheck": true
7
+ },
8
+ "files": [],
9
+ "include": ["**/*.spec.ts", "**/*.d.ts", "**/*.ts"]
10
+ }
package/tslint.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "../../tslint.json",
3
+ "rules": {
4
+ "directive-selector": [
5
+ false,
6
+ "attribute",
7
+ "lib",
8
+ "camelCase"
9
+ ],
10
+ "component-selector": [
11
+ true,
12
+ "element",
13
+ "lib",
14
+ "kebab-case"
15
+ ]
16
+ }
17
+ }