@mindfiredigital/textigniter-angular 1.1.1 → 1.2.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.
- package/.turbo/turbo-build.log +26 -0
- package/CHANGELOG.md +6 -0
- package/README.md +423 -23
- package/package.json +1 -1
- package/projects/demo-app/src/app/app.component.html +24 -1
- package/projects/demo-app/src/app/app.component.ts +22 -3
- package/projects/text-igniter/src/lib/text-igniter.component.ts +16 -5
- package/real-test.html +328 -0
- package/test.html +255 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
> @mindfiredigital/textigniter-angular@1.2.0 build /home/runner/work/TextIgniterJS/TextIgniterJS/packages/angular
|
|
3
|
+
> ng build text-igniter
|
|
4
|
+
|
|
5
|
+
Building Angular Package
|
|
6
|
+
|
|
7
|
+
------------------------------------------------------------------------------
|
|
8
|
+
Building entry point 'text-igniter'
|
|
9
|
+
------------------------------------------------------------------------------
|
|
10
|
+
- Compiling with Angular sources in Ivy partial compilation mode.
|
|
11
|
+
✔ Compiling with Angular sources in Ivy partial compilation mode.
|
|
12
|
+
✔ Generating FESM bundles
|
|
13
|
+
- Copying assets
|
|
14
|
+
✔ Copying assets
|
|
15
|
+
- Writing package manifest
|
|
16
|
+
✔ Writing package manifest
|
|
17
|
+
✔ Built text-igniter
|
|
18
|
+
|
|
19
|
+
------------------------------------------------------------------------------
|
|
20
|
+
Built Angular Package
|
|
21
|
+
- from: /home/runner/work/TextIgniterJS/TextIgniterJS/packages/angular/projects/text-igniter
|
|
22
|
+
- to: /home/runner/work/TextIgniterJS/TextIgniterJS/packages/angular/dist/text-igniter
|
|
23
|
+
------------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
Build at: 2026-02-25T11:00:57.492Z - Time: 2425ms
|
|
26
|
+
|
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,42 +1,407 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mindfiredigital/textigniter-angular
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Angular wrapper component for TextIgniter - A powerful rich text editor for Angular applications.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@mindfiredigital/textigniter-angular)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
## 📦 Installation
|
|
8
9
|
|
|
9
10
|
```bash
|
|
10
|
-
|
|
11
|
+
npm install @mindfiredigital/textigniter-angular
|
|
11
12
|
```
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
Or using yarn:
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
```bash
|
|
17
|
+
yarn add @mindfiredigital/textigniter-angular
|
|
18
|
+
```
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
Or using pnpm:
|
|
18
21
|
|
|
19
22
|
```bash
|
|
20
|
-
|
|
23
|
+
pnpm add @mindfiredigital/textigniter-angular
|
|
21
24
|
```
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
## 🚀 Quick Start
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
### 1. Import the Module
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// app.component.ts
|
|
32
|
+
import { Component } from '@angular/core';
|
|
33
|
+
import { RouterOutlet } from '@angular/router';
|
|
34
|
+
import { TextIgniterModule } from '@mindfiredigital/textigniter-angular';
|
|
35
|
+
|
|
36
|
+
@Component({
|
|
37
|
+
selector: 'app-root',
|
|
38
|
+
imports: [RouterOutlet, TextIgniterModule],
|
|
39
|
+
templateUrl: './app.component.html',
|
|
40
|
+
styleUrl: './app.component.css',
|
|
41
|
+
})
|
|
42
|
+
export class AppComponent {
|
|
43
|
+
title = 'demo-app';
|
|
44
|
+
|
|
45
|
+
config = {
|
|
46
|
+
showToolbar: true,
|
|
47
|
+
features: [
|
|
48
|
+
'bold',
|
|
49
|
+
'italic',
|
|
50
|
+
'underline',
|
|
51
|
+
'hyperlink',
|
|
52
|
+
'fontFamily',
|
|
53
|
+
'fontSize',
|
|
54
|
+
'alignLeft',
|
|
55
|
+
'alignCenter',
|
|
56
|
+
'alignRight',
|
|
57
|
+
'unorderedList',
|
|
58
|
+
'orderedList',
|
|
59
|
+
'image',
|
|
60
|
+
'fontColor',
|
|
61
|
+
'bgColor',
|
|
62
|
+
'getHtmlContent',
|
|
63
|
+
'loadHtmlContent',
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Use in Template
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<!-- app.component.html -->
|
|
73
|
+
<ngx-text-igniter [config]="config"></ngx-text-igniter>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 🎯 Features
|
|
77
|
+
|
|
78
|
+
- ✅ Rich text editing with toolbar
|
|
79
|
+
- ✅ Bold, Italic, Underline, Strikethrough
|
|
80
|
+
- ✅ Font family and size customization
|
|
81
|
+
- ✅ Text and background color
|
|
82
|
+
- ✅ Text alignment (left, center, right)
|
|
83
|
+
- ✅ Lists (ordered and unordered)
|
|
84
|
+
- ✅ Hyperlinks
|
|
85
|
+
- ✅ Image insertion
|
|
86
|
+
- ✅ Undo/Redo functionality
|
|
87
|
+
- ✅ **Real-time content change events** 🆕
|
|
88
|
+
- ✅ TypeScript support
|
|
89
|
+
- ✅ Fully customizable
|
|
90
|
+
|
|
91
|
+
## 📚 API Reference
|
|
92
|
+
|
|
93
|
+
### Component Inputs
|
|
94
|
+
|
|
95
|
+
#### `config` (Required)
|
|
96
|
+
|
|
97
|
+
Configuration object for the editor.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
interface EditorConfig {
|
|
101
|
+
showToolbar: boolean;
|
|
102
|
+
features: string[];
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Available Features:**
|
|
107
|
+
|
|
108
|
+
- `'bold'` - Bold text
|
|
109
|
+
- `'italic'` - Italic text
|
|
110
|
+
- `'underline'` - Underline text
|
|
111
|
+
- `'strikethrough'` - Strikethrough text
|
|
112
|
+
- `'hyperlink'` - Add hyperlinks
|
|
113
|
+
- `'fontFamily'` - Change font family
|
|
114
|
+
- `'fontSize'` - Change font size
|
|
115
|
+
- `'fontColor'` - Change text color
|
|
116
|
+
- `'bgColor'` - Change background color
|
|
117
|
+
- `'alignLeft'` - Align text left
|
|
118
|
+
- `'alignCenter'` - Align text center
|
|
119
|
+
- `'alignRight'` - Align text right
|
|
120
|
+
- `'unorderedList'` - Create bullet lists
|
|
121
|
+
- `'orderedList'` - Create numbered lists
|
|
122
|
+
- `'image'` - Insert images
|
|
123
|
+
- `'getHtmlContent'` - Get HTML content button
|
|
124
|
+
- `'loadHtmlContent'` - Load HTML content button
|
|
125
|
+
|
|
126
|
+
#### `editorId` (Optional)
|
|
127
|
+
|
|
128
|
+
Custom ID for the editor element. Default: `'editor-ngId'`
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
<ngx-text-igniter
|
|
132
|
+
[config]="config"
|
|
133
|
+
[editorId]="'my-custom-editor'">
|
|
134
|
+
</ngx-text-igniter>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Component Outputs
|
|
138
|
+
|
|
139
|
+
#### `contentChange` 🆕
|
|
140
|
+
|
|
141
|
+
Emits whenever the editor content changes, providing both HTML and plain text.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
interface ContentChangeEvent {
|
|
145
|
+
html: string; // HTML content
|
|
146
|
+
text: string; // Plain text content (without HTML tags)
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 💡 Usage Examples
|
|
151
|
+
|
|
152
|
+
### Basic Example
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// app.component.ts
|
|
156
|
+
import { Component } from '@angular/core';
|
|
157
|
+
import { TextIgniterModule } from '@mindfiredigital/textigniter-angular';
|
|
158
|
+
|
|
159
|
+
@Component({
|
|
160
|
+
selector: 'app-root',
|
|
161
|
+
imports: [TextIgniterModule],
|
|
162
|
+
template: `<ngx-text-igniter [config]="config"></ngx-text-igniter>`,
|
|
163
|
+
})
|
|
164
|
+
export class AppComponent {
|
|
165
|
+
config = {
|
|
166
|
+
showToolbar: true,
|
|
167
|
+
features: ['bold', 'italic', 'underline'],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Real-time Content Updates 🆕
|
|
173
|
+
|
|
174
|
+
Get notified whenever the content changes:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// app.component.ts
|
|
178
|
+
import { Component } from '@angular/core';
|
|
179
|
+
import { TextIgniterModule } from '@mindfiredigital/textigniter-angular';
|
|
180
|
+
|
|
181
|
+
@Component({
|
|
182
|
+
selector: 'app-root',
|
|
183
|
+
imports: [TextIgniterModule],
|
|
184
|
+
templateUrl: './app.component.html',
|
|
185
|
+
})
|
|
186
|
+
export class AppComponent {
|
|
187
|
+
htmlContent = '';
|
|
188
|
+
textContent = '';
|
|
189
|
+
|
|
190
|
+
config = {
|
|
191
|
+
showToolbar: true,
|
|
192
|
+
features: ['bold', 'italic', 'underline', 'fontColor', 'bgColor'],
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
onContentChange(data: { html: string; text: string }) {
|
|
196
|
+
console.log('Content changed:', data);
|
|
197
|
+
this.htmlContent = data.html;
|
|
198
|
+
this.textContent = data.text;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
get characterCount(): number {
|
|
202
|
+
return this.textContent.length;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
get wordCount(): number {
|
|
206
|
+
return this.textContent.trim()
|
|
207
|
+
? this.textContent.trim().split(/\s+/).length
|
|
208
|
+
: 0;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
27
211
|
```
|
|
28
212
|
|
|
29
|
-
|
|
213
|
+
```html
|
|
214
|
+
<!-- app.component.html -->
|
|
215
|
+
<h1>TextIgniter Angular Example</h1>
|
|
216
|
+
|
|
217
|
+
<ngx-text-igniter [config]="config" (contentChange)="onContentChange($event)">
|
|
218
|
+
</ngx-text-igniter>
|
|
219
|
+
|
|
220
|
+
<!-- Real-time Content Preview -->
|
|
221
|
+
<div class="preview-container">
|
|
222
|
+
<h3>Real-time Content Preview:</h3>
|
|
223
|
+
|
|
224
|
+
<div class="content-section">
|
|
225
|
+
<strong>HTML Content:</strong>
|
|
226
|
+
<pre>{{ htmlContent || 'Start typing to see content...' }}</pre>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<div class="content-section">
|
|
230
|
+
<strong>Text Content:</strong>
|
|
231
|
+
<pre>{{ textContent || 'Start typing to see text...' }}</pre>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<div class="stats">
|
|
235
|
+
<strong>Stats:</strong>
|
|
236
|
+
<p>Characters: {{ characterCount }} | Words: {{ wordCount }}</p>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
```
|
|
30
240
|
|
|
31
|
-
|
|
241
|
+
### Auto-Save Functionality
|
|
242
|
+
|
|
243
|
+
Implement auto-save with debouncing:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { Component, OnDestroy } from '@angular/core';
|
|
247
|
+
import { Subject } from 'rxjs';
|
|
248
|
+
import { debounceTime } from 'rxjs/operators';
|
|
249
|
+
|
|
250
|
+
@Component({
|
|
251
|
+
selector: 'app-editor',
|
|
252
|
+
template: `
|
|
253
|
+
<ngx-text-igniter
|
|
254
|
+
[config]="config"
|
|
255
|
+
(contentChange)="onContentChange($event)"
|
|
256
|
+
>
|
|
257
|
+
</ngx-text-igniter>
|
|
258
|
+
<p *ngIf="lastSaved">Last saved: {{ lastSaved | date: 'medium' }}</p>
|
|
259
|
+
`,
|
|
260
|
+
})
|
|
261
|
+
export class EditorComponent implements OnDestroy {
|
|
262
|
+
config = { showToolbar: true, features: ['bold', 'italic'] };
|
|
263
|
+
lastSaved: Date | null = null;
|
|
264
|
+
|
|
265
|
+
private contentChange$ = new Subject<{ html: string; text: string }>();
|
|
266
|
+
|
|
267
|
+
constructor() {
|
|
268
|
+
// Auto-save after 2 seconds of inactivity
|
|
269
|
+
this.contentChange$
|
|
270
|
+
.pipe(debounceTime(2000))
|
|
271
|
+
.subscribe(data => this.saveContent(data));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
onContentChange(data: { html: string; text: string }) {
|
|
275
|
+
this.contentChange$.next(data);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
saveContent(data: { html: string; text: string }) {
|
|
279
|
+
// Save to backend
|
|
280
|
+
console.log('Saving content...', data);
|
|
281
|
+
this.lastSaved = new Date();
|
|
282
|
+
|
|
283
|
+
// Example: Call your API
|
|
284
|
+
// this.http.post('/api/save', data).subscribe();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
ngOnDestroy() {
|
|
288
|
+
this.contentChange$.complete();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Form Integration
|
|
294
|
+
|
|
295
|
+
Use with Angular Reactive Forms:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { Component } from '@angular/core';
|
|
299
|
+
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
300
|
+
|
|
301
|
+
@Component({
|
|
302
|
+
selector: 'app-form',
|
|
303
|
+
template: `
|
|
304
|
+
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
|
305
|
+
<ngx-text-igniter
|
|
306
|
+
[config]="config"
|
|
307
|
+
(contentChange)="onContentChange($event)"
|
|
308
|
+
>
|
|
309
|
+
</ngx-text-igniter>
|
|
310
|
+
|
|
311
|
+
<button type="submit" [disabled]="!form.valid">Submit</button>
|
|
312
|
+
</form>
|
|
313
|
+
`,
|
|
314
|
+
})
|
|
315
|
+
export class FormComponent {
|
|
316
|
+
form: FormGroup;
|
|
317
|
+
|
|
318
|
+
config = {
|
|
319
|
+
showToolbar: true,
|
|
320
|
+
features: ['bold', 'italic', 'underline'],
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
constructor(private fb: FormBuilder) {
|
|
324
|
+
this.form = this.fb.group({
|
|
325
|
+
content: ['', Validators.required],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
onContentChange(data: { html: string; text: string }) {
|
|
330
|
+
this.form.patchValue({ content: data.html });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
onSubmit() {
|
|
334
|
+
if (this.form.valid) {
|
|
335
|
+
console.log('Form data:', this.form.value);
|
|
336
|
+
// Submit to backend
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Custom Editor ID
|
|
343
|
+
|
|
344
|
+
Use a custom ID for multiple editors:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
@Component({
|
|
348
|
+
template: `
|
|
349
|
+
<ngx-text-igniter
|
|
350
|
+
[config]="config"
|
|
351
|
+
[editorId]="'editor-1'"
|
|
352
|
+
(contentChange)="onEditor1Change($event)"
|
|
353
|
+
>
|
|
354
|
+
</ngx-text-igniter>
|
|
355
|
+
|
|
356
|
+
<ngx-text-igniter
|
|
357
|
+
[config]="config"
|
|
358
|
+
[editorId]="'editor-2'"
|
|
359
|
+
(contentChange)="onEditor2Change($event)"
|
|
360
|
+
>
|
|
361
|
+
</ngx-text-igniter>
|
|
362
|
+
`,
|
|
363
|
+
})
|
|
364
|
+
export class MultiEditorComponent {
|
|
365
|
+
config = { showToolbar: true, features: ['bold', 'italic'] };
|
|
366
|
+
|
|
367
|
+
onEditor1Change(data: { html: string; text: string }) {
|
|
368
|
+
console.log('Editor 1:', data);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
onEditor2Change(data: { html: string; text: string }) {
|
|
372
|
+
console.log('Editor 2:', data);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## 🔧 Development
|
|
378
|
+
|
|
379
|
+
### Building the Library
|
|
380
|
+
|
|
381
|
+
To build the library, run:
|
|
32
382
|
|
|
33
383
|
```bash
|
|
34
|
-
ng build
|
|
384
|
+
ng build text-igniter
|
|
35
385
|
```
|
|
36
386
|
|
|
37
|
-
This will compile your project and
|
|
387
|
+
This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
|
|
38
388
|
|
|
39
|
-
|
|
389
|
+
### Publishing the Library
|
|
390
|
+
|
|
391
|
+
Once the project is built, you can publish your library by following these steps:
|
|
392
|
+
|
|
393
|
+
1. Navigate to the `dist` directory:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
cd dist/text-igniter
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
2. Run the `npm publish` command to publish your library to the npm registry:
|
|
400
|
+
```bash
|
|
401
|
+
npm publish --access public
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Running Unit Tests
|
|
40
405
|
|
|
41
406
|
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
|
42
407
|
|
|
@@ -44,16 +409,51 @@ To execute unit tests with the [Karma](https://karma-runner.github.io) test runn
|
|
|
44
409
|
ng test
|
|
45
410
|
```
|
|
46
411
|
|
|
47
|
-
|
|
412
|
+
### Running the Demo App
|
|
48
413
|
|
|
49
|
-
|
|
414
|
+
To run the demo application:
|
|
50
415
|
|
|
51
416
|
```bash
|
|
52
|
-
ng
|
|
417
|
+
ng serve demo-app
|
|
53
418
|
```
|
|
54
419
|
|
|
55
|
-
|
|
420
|
+
Navigate to `http://localhost:4200/` to see the editor in action.
|
|
421
|
+
|
|
422
|
+
## 📋 Requirements
|
|
423
|
+
|
|
424
|
+
- Angular >= 19.0.0
|
|
425
|
+
- TypeScript >= 5.7.0
|
|
426
|
+
- Node.js >= 12.0.0
|
|
427
|
+
|
|
428
|
+
## 📝 License
|
|
429
|
+
|
|
430
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
431
|
+
|
|
432
|
+
## 🔗 Links
|
|
433
|
+
|
|
434
|
+
- [NPM Package](https://www.npmjs.com/package/@mindfiredigital/textigniter-angular)
|
|
435
|
+
- [GitHub Repository](https://github.com/mindfiredigital/textigniterjs)
|
|
436
|
+
- [Documentation](https://github.com/mindfiredigital/textigniterjs/tree/main/docs)
|
|
437
|
+
- [Core Package](https://www.npmjs.com/package/@mindfiredigital/textigniter)
|
|
438
|
+
- [React Package](https://www.npmjs.com/package/@mindfiredigital/textigniter-react)
|
|
439
|
+
- [Web Component](https://www.npmjs.com/package/@mindfiredigital/textigniter-web-component)
|
|
440
|
+
|
|
441
|
+
## 📞 Support
|
|
442
|
+
|
|
443
|
+
For issues, questions, or suggestions:
|
|
444
|
+
|
|
445
|
+
- [GitHub Issues](https://github.com/mindfiredigital/textigniterjs/issues)
|
|
446
|
+
- [Discussions](https://github.com/mindfiredigital/textigniterjs/discussions)
|
|
447
|
+
|
|
448
|
+
## 🎉 What's New
|
|
449
|
+
|
|
450
|
+
### v1.2.0
|
|
451
|
+
|
|
452
|
+
- ✨ Added `contentChange` event emitter for real-time content updates
|
|
453
|
+
- 🐛 Fixed clipboard permission error
|
|
454
|
+
- 📚 Updated documentation and examples
|
|
455
|
+
- 🔧 Improved TypeScript support
|
|
56
456
|
|
|
57
|
-
|
|
457
|
+
---
|
|
58
458
|
|
|
59
|
-
|
|
459
|
+
Made with ❤️ by [Mindfire Digital](https://github.com/mindfiredigital)
|
package/package.json
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
|
+
<h1>TextIgniter Angular Example</h1>
|
|
1
2
|
|
|
2
|
-
<ngx-text-igniter [config]="config"></ngx-text-igniter>
|
|
3
|
+
<ngx-text-igniter [config]="config" (contentChange)="onContentChange($event)"></ngx-text-igniter>
|
|
4
|
+
|
|
5
|
+
<!-- Real-time Content Preview -->
|
|
6
|
+
<div style="margin-top: 20px; padding: 20px; border: 1px solid #ccc; background: #f9f9f9;">
|
|
7
|
+
<h3>Real-time Content Preview:</h3>
|
|
8
|
+
|
|
9
|
+
<div style="margin-top: 10px;">
|
|
10
|
+
<strong>HTML Content:</strong>
|
|
11
|
+
<pre
|
|
12
|
+
style="background: white; padding: 10px; overflow: auto; max-height: 200px;">{{ htmlContent || 'Start typing to see content...' }}</pre>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div style="margin-top: 10px;">
|
|
16
|
+
<strong>Text Content:</strong>
|
|
17
|
+
<pre
|
|
18
|
+
style="background: white; padding: 10px; overflow: auto; max-height: 100px;">{{ textContent || 'Start typing to see text...' }}</pre>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div style="margin-top: 10px;">
|
|
22
|
+
<strong>Stats:</strong>
|
|
23
|
+
<p>Characters: {{ characterCount }} | Words: {{ wordCount }}</p>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
@@ -6,10 +6,13 @@ import { TextIgniterModule } from 'text-igniter';
|
|
|
6
6
|
selector: 'app-root',
|
|
7
7
|
imports: [RouterOutlet, TextIgniterModule],
|
|
8
8
|
templateUrl: './app.component.html',
|
|
9
|
-
styleUrl: './app.component.css'
|
|
9
|
+
styleUrl: './app.component.css',
|
|
10
10
|
})
|
|
11
11
|
export class AppComponent {
|
|
12
12
|
title = 'demo-app';
|
|
13
|
+
htmlContent = '';
|
|
14
|
+
textContent = '';
|
|
15
|
+
|
|
13
16
|
config = {
|
|
14
17
|
showToolbar: true,
|
|
15
18
|
features: [
|
|
@@ -28,7 +31,23 @@ export class AppComponent {
|
|
|
28
31
|
'fontColor',
|
|
29
32
|
'bgColor',
|
|
30
33
|
'getHtmlContent',
|
|
31
|
-
'loadHtmlContent'
|
|
32
|
-
]
|
|
34
|
+
'loadHtmlContent',
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
onContentChange(data: { html: string; text: string }) {
|
|
39
|
+
console.log('Content changed:', data);
|
|
40
|
+
this.htmlContent = data.html;
|
|
41
|
+
this.textContent = data.text;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get characterCount(): number {
|
|
45
|
+
return this.textContent.length;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get wordCount(): number {
|
|
49
|
+
return this.textContent.trim()
|
|
50
|
+
? this.textContent.trim().split(/\s+/).length
|
|
51
|
+
: 0;
|
|
33
52
|
}
|
|
34
53
|
}
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
AfterViewInit,
|
|
15
15
|
Component,
|
|
16
16
|
Input,
|
|
17
|
+
Output,
|
|
18
|
+
EventEmitter,
|
|
17
19
|
OnChanges,
|
|
18
20
|
SimpleChanges,
|
|
19
21
|
} from '@angular/core';
|
|
@@ -28,11 +30,15 @@ import { TextIgniter } from '@mindfiredigital/textigniter/dist/TextIgniter';
|
|
|
28
30
|
export class TextIgniterComponent implements OnChanges, AfterViewInit {
|
|
29
31
|
@Input() config:
|
|
30
32
|
| {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
showToolbar: boolean;
|
|
34
|
+
features: string[];
|
|
35
|
+
}
|
|
34
36
|
| undefined;
|
|
35
37
|
@Input() editorId: string | 'editor-ngId' = 'editor-ngId';
|
|
38
|
+
@Output() contentChange = new EventEmitter<{ html: string; text: string }>();
|
|
39
|
+
|
|
40
|
+
private textIgniterInstance?: TextIgniter;
|
|
41
|
+
|
|
36
42
|
ngAfterViewInit(): void {
|
|
37
43
|
this.renderTable();
|
|
38
44
|
}
|
|
@@ -48,7 +54,12 @@ export class TextIgniterComponent implements OnChanges, AfterViewInit {
|
|
|
48
54
|
return console.log("editor-ngId can't be using name as id='editor' ");
|
|
49
55
|
}
|
|
50
56
|
if (this.config) {
|
|
51
|
-
new TextIgniter(this.editorId, this.config);
|
|
57
|
+
this.textIgniterInstance = new TextIgniter(this.editorId, this.config);
|
|
58
|
+
|
|
59
|
+
// Subscribe to content changes and emit through Angular EventEmitter
|
|
60
|
+
this.textIgniterInstance.onContentChange(data => {
|
|
61
|
+
this.contentChange.emit(data);
|
|
62
|
+
});
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
|
-
}
|
|
65
|
+
}
|
package/real-test.html
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>TextIgniter Angular Real Component Test</title>
|
|
7
|
+
<script src="https://unpkg.com/zone.js@0.15.0/dist/zone.min.js"></script>
|
|
8
|
+
<script src="https://unpkg.com/@angular/core@19/bundles/core.umd.js"></script>
|
|
9
|
+
<script src="https://unpkg.com/@angular/common@19/bundles/common.umd.js"></script>
|
|
10
|
+
<script src="https://unpkg.com/@angular/platform-browser@19/bundles/platform-browser.umd.js"></script>
|
|
11
|
+
<script src="https://unpkg.com/@angular/platform-browser-dynamic@19/bundles/platform-browser-dynamic.umd.js"></script>
|
|
12
|
+
|
|
13
|
+
<!-- Load the actual built TextIgniter components -->
|
|
14
|
+
<script src="../core/dist/index.umd.js"></script>
|
|
15
|
+
<script src="../web-component/dist/index.umd.js"></script>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
.app-container {
|
|
19
|
+
margin: 20px;
|
|
20
|
+
padding: 20px;
|
|
21
|
+
font-family: Arial, sans-serif;
|
|
22
|
+
}
|
|
23
|
+
.editor-section {
|
|
24
|
+
margin: 20px 0;
|
|
25
|
+
padding: 15px;
|
|
26
|
+
border: 1px solid #ddd;
|
|
27
|
+
border-radius: 4px;
|
|
28
|
+
}
|
|
29
|
+
.controls {
|
|
30
|
+
margin: 10px 0;
|
|
31
|
+
padding: 10px;
|
|
32
|
+
background: #f5f5f5;
|
|
33
|
+
border-radius: 4px;
|
|
34
|
+
}
|
|
35
|
+
.controls button {
|
|
36
|
+
margin: 5px;
|
|
37
|
+
padding: 8px 16px;
|
|
38
|
+
border: 1px solid #ccc;
|
|
39
|
+
border-radius: 3px;
|
|
40
|
+
background: white;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
}
|
|
43
|
+
.test-output {
|
|
44
|
+
margin: 20px 0;
|
|
45
|
+
padding: 15px;
|
|
46
|
+
border: 1px solid #ccc;
|
|
47
|
+
border-radius: 4px;
|
|
48
|
+
background: white;
|
|
49
|
+
}
|
|
50
|
+
.test-result {
|
|
51
|
+
padding: 5px;
|
|
52
|
+
margin: 2px 0;
|
|
53
|
+
}
|
|
54
|
+
.test-pass { color: green; }
|
|
55
|
+
.test-fail { color: red; }
|
|
56
|
+
</style>
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div id="angular-app" class="app-container">
|
|
60
|
+
<h1>TextIgniter Angular Real Component Test</h1>
|
|
61
|
+
|
|
62
|
+
<div class="controls">
|
|
63
|
+
<button id="toggle-config-btn">Toggle Config</button>
|
|
64
|
+
<button id="change-features-btn">Change Features</button>
|
|
65
|
+
<button id="test-editor-btn">Test Editor Functions</button>
|
|
66
|
+
<button id="run-integration-tests-btn">Run Integration Tests</button>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="editor-section">
|
|
70
|
+
<h3>Angular TextIgniter Component:</h3>
|
|
71
|
+
<div id="angular-textigniter-container">
|
|
72
|
+
<!-- This will be populated by Angular -->
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div class="test-output">
|
|
77
|
+
<h3>Integration Test Results:</h3>
|
|
78
|
+
<div id="integration-test-results"></div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<script>
|
|
83
|
+
// Angular component definition using the real TextIgniter
|
|
84
|
+
const { Component, Input, OnInit, OnChanges, SimpleChanges, NgModule, platformBrowserDynamic } = ng.core;
|
|
85
|
+
const { CommonModule } = ng.common;
|
|
86
|
+
const { BrowserModule } = ng.platformBrowser;
|
|
87
|
+
|
|
88
|
+
// Real Angular TextIgniter Component that uses the actual library
|
|
89
|
+
@Component({
|
|
90
|
+
selector: 'app-textigniter-wrapper',
|
|
91
|
+
template: `
|
|
92
|
+
<div [id]="editorId" class="textigniter-wrapper"
|
|
93
|
+
[attr.data-testid]="'real-angular-editor-' + editorId"></div>
|
|
94
|
+
`,
|
|
95
|
+
standalone: false
|
|
96
|
+
})
|
|
97
|
+
class TextIgniterWrapperComponent {
|
|
98
|
+
@Input() config = {
|
|
99
|
+
showToolbar: true,
|
|
100
|
+
features: ['bold', 'italic', 'underline', 'hyperlink']
|
|
101
|
+
};
|
|
102
|
+
@Input() editorId = 'real-angular-editor';
|
|
103
|
+
|
|
104
|
+
textIgniterInstance = null;
|
|
105
|
+
|
|
106
|
+
ngAfterViewInit() {
|
|
107
|
+
this.initializeTextIgniter();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ngOnChanges(changes) {
|
|
111
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
112
|
+
this.reinitializeTextIgniter();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
initializeTextIgniter() {
|
|
117
|
+
try {
|
|
118
|
+
// Wait for DOM to be ready
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
if (window.TextIgniter && this.editorId !== 'editor') {
|
|
121
|
+
this.textIgniterInstance = new window.TextIgniter(this.editorId, this.config);
|
|
122
|
+
console.log('TextIgniter initialized for Angular component:', this.editorId);
|
|
123
|
+
|
|
124
|
+
// Mark as initialized for testing
|
|
125
|
+
const container = document.getElementById(this.editorId);
|
|
126
|
+
if (container) {
|
|
127
|
+
container.setAttribute('data-angular-textigniter-ready', 'true');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, 100);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('Error initializing TextIgniter:', error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
reinitializeTextIgniter() {
|
|
137
|
+
// Clear existing instance
|
|
138
|
+
const container = document.getElementById(this.editorId);
|
|
139
|
+
if (container) {
|
|
140
|
+
container.innerHTML = '';
|
|
141
|
+
}
|
|
142
|
+
this.initializeTextIgniter();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Test Application Component
|
|
147
|
+
@Component({
|
|
148
|
+
selector: 'app-root',
|
|
149
|
+
template: `
|
|
150
|
+
<app-textigniter-wrapper
|
|
151
|
+
[config]="currentConfig"
|
|
152
|
+
[editorId]="'angular-editor-test'">
|
|
153
|
+
</app-textigniter-wrapper>
|
|
154
|
+
`,
|
|
155
|
+
standalone: false
|
|
156
|
+
})
|
|
157
|
+
class AppComponent {
|
|
158
|
+
currentConfig = {
|
|
159
|
+
showToolbar: true,
|
|
160
|
+
features: ['bold', 'italic', 'underline', 'hyperlink']
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
toggleConfig() {
|
|
164
|
+
this.currentConfig = {
|
|
165
|
+
...this.currentConfig,
|
|
166
|
+
showToolbar: !this.currentConfig.showToolbar
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
changeFeatures() {
|
|
171
|
+
this.currentConfig = {
|
|
172
|
+
...this.currentConfig,
|
|
173
|
+
features: this.currentConfig.features.length > 4
|
|
174
|
+
? ['bold', 'italic']
|
|
175
|
+
: ['bold', 'italic', 'underline', 'hyperlink', 'fontColor', 'alignCenter']
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Angular Module
|
|
181
|
+
@NgModule({
|
|
182
|
+
declarations: [AppComponent, TextIgniterWrapperComponent],
|
|
183
|
+
imports: [BrowserModule, CommonModule],
|
|
184
|
+
bootstrap: [AppComponent]
|
|
185
|
+
})
|
|
186
|
+
class AppModule {}
|
|
187
|
+
|
|
188
|
+
// Bootstrap the application
|
|
189
|
+
let appInstance = null;
|
|
190
|
+
platformBrowserDynamic()
|
|
191
|
+
.bootstrapModule(AppModule)
|
|
192
|
+
.then(moduleRef => {
|
|
193
|
+
appInstance = moduleRef.instance;
|
|
194
|
+
console.log('Angular application bootstrapped successfully');
|
|
195
|
+
|
|
196
|
+
// Set up event handlers
|
|
197
|
+
setupEventHandlers();
|
|
198
|
+
|
|
199
|
+
// Run initial tests
|
|
200
|
+
setTimeout(() => {
|
|
201
|
+
runIntegrationTests();
|
|
202
|
+
}, 2000);
|
|
203
|
+
})
|
|
204
|
+
.catch(err => {
|
|
205
|
+
console.error('Error starting Angular app:', err);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Event handlers for testing
|
|
209
|
+
function setupEventHandlers() {
|
|
210
|
+
document.getElementById('toggle-config-btn').addEventListener('click', () => {
|
|
211
|
+
const appComponent = document.querySelector('app-root');
|
|
212
|
+
if (appComponent && appComponent.__ngContext__) {
|
|
213
|
+
const component = appComponent.__ngContext__[8]; // Get component instance
|
|
214
|
+
component.toggleConfig();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
document.getElementById('change-features-btn').addEventListener('click', () => {
|
|
219
|
+
const appComponent = document.querySelector('app-root');
|
|
220
|
+
if (appComponent && appComponent.__ngContext__) {
|
|
221
|
+
const component = appComponent.__ngContext__[8];
|
|
222
|
+
component.changeFeatures();
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
document.getElementById('test-editor-btn').addEventListener('click', () => {
|
|
227
|
+
testEditorFunctionality();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
document.getElementById('run-integration-tests-btn').addEventListener('click', () => {
|
|
231
|
+
runIntegrationTests();
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Integration tests
|
|
236
|
+
const integrationTestResults = [];
|
|
237
|
+
|
|
238
|
+
function addIntegrationTestResult(testName, passed, message) {
|
|
239
|
+
integrationTestResults.push({ testName, passed, message });
|
|
240
|
+
updateIntegrationTestDisplay();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function updateIntegrationTestDisplay() {
|
|
244
|
+
const output = document.getElementById('integration-test-results');
|
|
245
|
+
output.innerHTML = integrationTestResults.map(result =>
|
|
246
|
+
`<div class="test-result ${result.passed ? 'test-pass' : 'test-fail'}"
|
|
247
|
+
data-test="${result.testName.toLowerCase().replace(/ /g, '-')}"
|
|
248
|
+
data-passed="${result.passed}">
|
|
249
|
+
${result.testName}: ${result.passed ? 'PASS' : 'FAIL'} - ${result.message}
|
|
250
|
+
</div>`
|
|
251
|
+
).join('');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function runIntegrationTests() {
|
|
255
|
+
integrationTestResults.length = 0;
|
|
256
|
+
|
|
257
|
+
// Test 1: Angular component renders
|
|
258
|
+
setTimeout(() => {
|
|
259
|
+
const wrapper = document.querySelector('app-textigniter-wrapper');
|
|
260
|
+
addIntegrationTestResult(
|
|
261
|
+
'Angular Component Renders',
|
|
262
|
+
!!wrapper,
|
|
263
|
+
wrapper ? 'Angular wrapper component found' : 'Angular wrapper component not found'
|
|
264
|
+
);
|
|
265
|
+
}, 100);
|
|
266
|
+
|
|
267
|
+
// Test 2: TextIgniter instance created
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
const container = document.getElementById('angular-editor-test');
|
|
270
|
+
const isReady = container && container.hasAttribute('data-angular-textigniter-ready');
|
|
271
|
+
addIntegrationTestResult(
|
|
272
|
+
'TextIgniter Instance Created',
|
|
273
|
+
isReady,
|
|
274
|
+
isReady ? 'TextIgniter instance initialized' : 'TextIgniter instance not found'
|
|
275
|
+
);
|
|
276
|
+
}, 200);
|
|
277
|
+
|
|
278
|
+
// Test 3: Editor DOM structure exists
|
|
279
|
+
setTimeout(() => {
|
|
280
|
+
const editorContainer = document.querySelector('#angular-editor-test .editor-container');
|
|
281
|
+
const toolbarContainer = document.querySelector('#angular-editor-test .toolbar-container');
|
|
282
|
+
addIntegrationTestResult(
|
|
283
|
+
'Editor DOM Structure',
|
|
284
|
+
!!(editorContainer || toolbarContainer || document.querySelector('#angular-editor-test .text-igniter-editor')),
|
|
285
|
+
editorContainer ? 'Editor DOM structure found' : 'Editor DOM structure missing (may be mock)'
|
|
286
|
+
);
|
|
287
|
+
}, 300);
|
|
288
|
+
|
|
289
|
+
// Test 4: Configuration applied
|
|
290
|
+
setTimeout(() => {
|
|
291
|
+
const container = document.getElementById('angular-editor-test');
|
|
292
|
+
const hasContent = container && container.innerHTML.trim().length > 0;
|
|
293
|
+
addIntegrationTestResult(
|
|
294
|
+
'Configuration Applied',
|
|
295
|
+
hasContent,
|
|
296
|
+
hasContent ? 'Editor has content/structure' : 'Editor appears empty'
|
|
297
|
+
);
|
|
298
|
+
}, 400);
|
|
299
|
+
|
|
300
|
+
// Test 5: Event binding works
|
|
301
|
+
setTimeout(() => {
|
|
302
|
+
const appRoot = document.querySelector('app-root');
|
|
303
|
+
const hasAngularContext = appRoot && appRoot.__ngContext__;
|
|
304
|
+
addIntegrationTestResult(
|
|
305
|
+
'Angular Binding Active',
|
|
306
|
+
hasAngularContext,
|
|
307
|
+
hasAngularContext ? 'Angular data binding active' : 'Angular binding not detected'
|
|
308
|
+
);
|
|
309
|
+
}, 500);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function testEditorFunctionality() {
|
|
313
|
+
const container = document.getElementById('angular-editor-test');
|
|
314
|
+
if (container) {
|
|
315
|
+
// Try to interact with the editor
|
|
316
|
+
const editableArea = container.querySelector('[contenteditable="true"]');
|
|
317
|
+
if (editableArea) {
|
|
318
|
+
editableArea.focus();
|
|
319
|
+
editableArea.textContent = 'Integration test text';
|
|
320
|
+
console.log('Editor functionality test completed');
|
|
321
|
+
} else {
|
|
322
|
+
console.log('No editable area found - using mock implementation');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
</script>
|
|
327
|
+
</body>
|
|
328
|
+
</html>
|
package/test.html
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>TextIgniter Angular Package Test</title>
|
|
7
|
+
<script src="https://unpkg.com/zone.js@0.15.0/dist/zone.min.js"></script>
|
|
8
|
+
<script src="https://unpkg.com/@angular/core@19/bundles/core.umd.js"></script>
|
|
9
|
+
<script src="https://unpkg.com/@angular/common@19/bundles/common.umd.js"></script>
|
|
10
|
+
<script src="https://unpkg.com/@angular/platform-browser@19/bundles/platform-browser.umd.js"></script>
|
|
11
|
+
<script src="https://unpkg.com/@angular/platform-browser-dynamic@19/bundles/platform-browser-dynamic.umd.js"></script>
|
|
12
|
+
<style>
|
|
13
|
+
.editor-container {
|
|
14
|
+
margin: 20px;
|
|
15
|
+
padding: 20px;
|
|
16
|
+
border: 1px solid #ddd;
|
|
17
|
+
border-radius: 4px;
|
|
18
|
+
}
|
|
19
|
+
.test-controls {
|
|
20
|
+
margin: 20px;
|
|
21
|
+
padding: 10px;
|
|
22
|
+
background: #f5f5f5;
|
|
23
|
+
border-radius: 4px;
|
|
24
|
+
}
|
|
25
|
+
.test-controls button {
|
|
26
|
+
margin: 5px;
|
|
27
|
+
padding: 8px 16px;
|
|
28
|
+
border: 1px solid #ccc;
|
|
29
|
+
border-radius: 3px;
|
|
30
|
+
background: white;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
}
|
|
33
|
+
.test-controls button:hover {
|
|
34
|
+
background: #e9e9e9;
|
|
35
|
+
}
|
|
36
|
+
#cypress-test-results {
|
|
37
|
+
margin: 20px;
|
|
38
|
+
padding: 15px;
|
|
39
|
+
border: 1px solid #ccc;
|
|
40
|
+
border-radius: 4px;
|
|
41
|
+
background: white;
|
|
42
|
+
}
|
|
43
|
+
.test-result {
|
|
44
|
+
padding: 5px;
|
|
45
|
+
margin: 2px 0;
|
|
46
|
+
}
|
|
47
|
+
.test-pass {
|
|
48
|
+
color: green;
|
|
49
|
+
}
|
|
50
|
+
.test-fail {
|
|
51
|
+
color: red;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<div id="angular-root">
|
|
57
|
+
<div class="test-controls">
|
|
58
|
+
<h2>TextIgniter Angular Package Test</h2>
|
|
59
|
+
<button id="config-change-btn">Toggle Config</button>
|
|
60
|
+
<button id="run-tests-btn">Run Tests</button>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="editor-container">
|
|
63
|
+
<div id="editor-angular"></div>
|
|
64
|
+
</div>
|
|
65
|
+
<div id="cypress-test-results">
|
|
66
|
+
<h3>Test Results:</h3>
|
|
67
|
+
<div id="test-output"></div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<script>
|
|
72
|
+
// Mock Angular TextIgniter component for testing
|
|
73
|
+
class MockTextIgniterComponent {
|
|
74
|
+
constructor(containerId, config) {
|
|
75
|
+
this.containerId = containerId;
|
|
76
|
+
this.config = config;
|
|
77
|
+
this.container = document.getElementById(containerId);
|
|
78
|
+
this.rendered = false;
|
|
79
|
+
this.render();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
render() {
|
|
83
|
+
if (!this.container) return;
|
|
84
|
+
|
|
85
|
+
// Clear container
|
|
86
|
+
this.container.innerHTML = '';
|
|
87
|
+
|
|
88
|
+
// Create mock editor structure
|
|
89
|
+
const editorDiv = document.createElement('div');
|
|
90
|
+
editorDiv.id = 'mock-angular-editor';
|
|
91
|
+
editorDiv.setAttribute('data-testid', 'angular-editor');
|
|
92
|
+
editorDiv.style.minHeight = '100px';
|
|
93
|
+
editorDiv.style.border = '1px solid #ccc';
|
|
94
|
+
editorDiv.style.padding = '10px';
|
|
95
|
+
editorDiv.style.background = '#f9f9f9';
|
|
96
|
+
|
|
97
|
+
// Add toolbar if enabled
|
|
98
|
+
if (this.config.showToolbar) {
|
|
99
|
+
const toolbar = document.createElement('div');
|
|
100
|
+
toolbar.id = 'mock-toolbar';
|
|
101
|
+
toolbar.setAttribute('data-testid', 'angular-toolbar');
|
|
102
|
+
toolbar.style.padding = '5px';
|
|
103
|
+
toolbar.style.border = '1px solid #ddd';
|
|
104
|
+
toolbar.style.marginBottom = '5px';
|
|
105
|
+
toolbar.style.background = '#fff';
|
|
106
|
+
|
|
107
|
+
// Add feature buttons
|
|
108
|
+
this.config.features.forEach(feature => {
|
|
109
|
+
const btn = document.createElement('button');
|
|
110
|
+
btn.textContent = feature;
|
|
111
|
+
btn.setAttribute('data-feature', feature);
|
|
112
|
+
btn.style.margin = '2px';
|
|
113
|
+
btn.style.padding = '4px 8px';
|
|
114
|
+
btn.style.fontSize = '12px';
|
|
115
|
+
toolbar.appendChild(btn);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
this.container.appendChild(toolbar);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add editor content area
|
|
122
|
+
const contentArea = document.createElement('div');
|
|
123
|
+
contentArea.id = 'editor-content';
|
|
124
|
+
contentArea.setAttribute('contenteditable', 'true');
|
|
125
|
+
contentArea.setAttribute('data-testid', 'editor-content');
|
|
126
|
+
contentArea.style.minHeight = '80px';
|
|
127
|
+
contentArea.style.padding = '8px';
|
|
128
|
+
contentArea.style.border = '1px solid #ddd';
|
|
129
|
+
contentArea.style.background = 'white';
|
|
130
|
+
contentArea.textContent = 'Mock TextIgniter Angular Editor';
|
|
131
|
+
|
|
132
|
+
editorDiv.appendChild(contentArea);
|
|
133
|
+
this.container.appendChild(editorDiv);
|
|
134
|
+
|
|
135
|
+
this.rendered = true;
|
|
136
|
+
|
|
137
|
+
// Simulate component lifecycle
|
|
138
|
+
setTimeout(() => {
|
|
139
|
+
this.container.setAttribute('data-angular-initialized', 'true');
|
|
140
|
+
}, 100);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
updateConfig(newConfig) {
|
|
144
|
+
this.config = { ...this.config, ...newConfig };
|
|
145
|
+
this.render();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Initialize the mock component
|
|
150
|
+
let currentConfig = {
|
|
151
|
+
showToolbar: true,
|
|
152
|
+
features: ['bold', 'italic', 'underline', 'hyperlink', 'image']
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
let textIgniterInstance = new MockTextIgniterComponent('editor-angular', currentConfig);
|
|
156
|
+
|
|
157
|
+
// Test functionality
|
|
158
|
+
const testResults = [];
|
|
159
|
+
|
|
160
|
+
function addTestResult(testName, passed, message) {
|
|
161
|
+
testResults.push({ testName, passed, message });
|
|
162
|
+
updateTestDisplay();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function updateTestDisplay() {
|
|
166
|
+
const output = document.getElementById('test-output');
|
|
167
|
+
output.innerHTML = testResults.map(result =>
|
|
168
|
+
`<div class="test-result ${result.passed ? 'test-pass' : 'test-fail'}"
|
|
169
|
+
data-test="${result.testName.toLowerCase().replace(/ /g, '-')}"
|
|
170
|
+
data-passed="${result.passed}">
|
|
171
|
+
${result.testName}: ${result.passed ? 'PASS' : 'FAIL'} - ${result.message}
|
|
172
|
+
</div>`
|
|
173
|
+
).join('');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Event handlers
|
|
177
|
+
document.getElementById('config-change-btn').addEventListener('click', () => {
|
|
178
|
+
currentConfig = {
|
|
179
|
+
showToolbar: !currentConfig.showToolbar,
|
|
180
|
+
features: currentConfig.showToolbar
|
|
181
|
+
? ['bold', 'italic']
|
|
182
|
+
: ['bold', 'italic', 'underline', 'fontColor', 'alignCenter']
|
|
183
|
+
};
|
|
184
|
+
textIgniterInstance.updateConfig(currentConfig);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
document.getElementById('run-tests-btn').addEventListener('click', () => {
|
|
188
|
+
runAllTests();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
function runAllTests() {
|
|
192
|
+
testResults.length = 0; // Clear previous results
|
|
193
|
+
|
|
194
|
+
// Test 1: Component renders
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
const editor = document.querySelector('[data-testid="angular-editor"]');
|
|
197
|
+
addTestResult(
|
|
198
|
+
'Component Renders',
|
|
199
|
+
!!editor,
|
|
200
|
+
editor ? 'Angular editor component found' : 'Angular editor component not found'
|
|
201
|
+
);
|
|
202
|
+
}, 100);
|
|
203
|
+
|
|
204
|
+
// Test 2: Config is applied
|
|
205
|
+
setTimeout(() => {
|
|
206
|
+
const toolbar = document.querySelector('[data-testid="angular-toolbar"]');
|
|
207
|
+
const hasToolbar = currentConfig.showToolbar ? !!toolbar : !toolbar;
|
|
208
|
+
addTestResult(
|
|
209
|
+
'Config Applied',
|
|
210
|
+
hasToolbar,
|
|
211
|
+
hasToolbar ? 'Toolbar visibility matches config' : 'Toolbar visibility does not match config'
|
|
212
|
+
);
|
|
213
|
+
}, 200);
|
|
214
|
+
|
|
215
|
+
// Test 3: Features are rendered
|
|
216
|
+
setTimeout(() => {
|
|
217
|
+
const featureButtons = document.querySelectorAll('[data-feature]');
|
|
218
|
+
const expectedCount = currentConfig.showToolbar ? currentConfig.features.length : 0;
|
|
219
|
+
const actualCount = featureButtons.length;
|
|
220
|
+
addTestResult(
|
|
221
|
+
'Features Rendered',
|
|
222
|
+
actualCount === expectedCount,
|
|
223
|
+
`Expected ${expectedCount} features, found ${actualCount}`
|
|
224
|
+
);
|
|
225
|
+
}, 300);
|
|
226
|
+
|
|
227
|
+
// Test 4: Editor content area exists
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
const contentArea = document.querySelector('[data-testid="editor-content"]');
|
|
230
|
+
addTestResult(
|
|
231
|
+
'Editor Content Area',
|
|
232
|
+
!!contentArea && contentArea.isContentEditable,
|
|
233
|
+
contentArea ? 'Editor content area is editable' : 'Editor content area not found'
|
|
234
|
+
);
|
|
235
|
+
}, 400);
|
|
236
|
+
|
|
237
|
+
// Test 5: Component initialization
|
|
238
|
+
setTimeout(() => {
|
|
239
|
+
const container = document.getElementById('editor-angular');
|
|
240
|
+
const isInitialized = container.hasAttribute('data-angular-initialized');
|
|
241
|
+
addTestResult(
|
|
242
|
+
'Component Initialized',
|
|
243
|
+
isInitialized,
|
|
244
|
+
isInitialized ? 'Component initialization complete' : 'Component not properly initialized'
|
|
245
|
+
);
|
|
246
|
+
}, 500);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Auto-run tests on load
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
runAllTests();
|
|
252
|
+
}, 1000);
|
|
253
|
+
</script>
|
|
254
|
+
</body>
|
|
255
|
+
</html>
|