• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

IgniteUI / igniteui-angular / 16193550997

10 Jul 2025 11:12AM UTC coverage: 4.657% (-87.0%) from 91.64%
16193550997

Pull #16028

github

web-flow
Merge f7a9963b8 into 87246e3ce
Pull Request #16028: fix(radio-group): dynamically added radio buttons do not initialize

178 of 15764 branches covered (1.13%)

18 of 19 new or added lines in 2 files covered. (94.74%)

25721 existing lines in 324 files now uncovered.

1377 of 29570 relevant lines covered (4.66%)

0.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

2.76
/projects/igniteui-angular/src/lib/splitter/splitter.component.ts
1
import { DOCUMENT } from '@angular/common';
2
import { AfterContentInit, Component, ContentChildren, ElementRef, EventEmitter, HostBinding, HostListener, Inject, Input, NgZone, Output, QueryList, booleanAttribute, forwardRef } from '@angular/core';
3
import { DragDirection, IDragMoveEventArgs, IDragStartEventArgs, IgxDragDirective, IgxDragIgnoreDirective } from '../directives/drag-drop/drag-drop.directive';
4
import { IgxSplitterPaneComponent } from './splitter-pane/splitter-pane.component';
5
import { take } from 'rxjs';
6

7
/**
8
 * An enumeration that defines the `SplitterComponent` panes orientation.
9
 */
10
export enum SplitterType {
3✔
11
    Horizontal,
3✔
12
    Vertical
3✔
13
}
14

15
export declare interface ISplitterBarResizeEventArgs {
16
    pane: IgxSplitterPaneComponent;
17
    sibling: IgxSplitterPaneComponent;
18
}
19

20
/**
21
 * Provides a framework for a simple layout, splitting the view horizontally or vertically
22
 * into multiple smaller resizable and collapsible areas.
23
 *
24
 * @igxModule IgxSplitterModule
25
 *
26
 * @igxParent Layouts
27
 *
28
 * @igxTheme igx-splitter-theme
29
 *
30
 * @igxKeywords splitter panes layout
31
 *
32
 * @igxGroup presentation
33
 *
34
 * @example
35
 * ```html
36
 * <igx-splitter>
37
 *  <igx-splitter-pane>
38
 *      ...
39
 *  </igx-splitter-pane>
40
 *  <igx-splitter-pane>
41
 *      ...
42
 *  </igx-splitter-pane>
43
 * </igx-splitter>
44
 * ```
45
 */
46
@Component({
47
    selector: 'igx-splitter',
48
    templateUrl: './splitter.component.html',
UNCOV
49
    imports: [forwardRef(() => IgxSplitBarComponent)]
×
50
})
51
export class IgxSplitterComponent implements AfterContentInit {
3✔
52
    /**
53
     * Gets the list of splitter panes.
54
     *
55
     * @example
56
     * ```typescript
57
     * const panes = this.splitter.panes;
58
     * ```
59
     */
60
    @ContentChildren(IgxSplitterPaneComponent, { read: IgxSplitterPaneComponent })
61
    public panes!: QueryList<IgxSplitterPaneComponent>;
62

63
    /**
64
    * @hidden
65
    * @internal
66
    */
67
    @HostBinding('class.igx-splitter')
UNCOV
68
    public cssClass = 'igx-splitter';
×
69

70
    /**
71
     * @hidden @internal
72
     * Gets/Sets the `overflow` property of the current splitter.
73
     */
74
    @HostBinding('style.overflow')
UNCOV
75
    public overflow = 'hidden';
×
76

77
    /**
78
     * @hidden @internal
79
     * Sets/Gets the `display` property of the current splitter.
80
     */
81
    @HostBinding('style.display')
UNCOV
82
    public display = 'flex';
×
83

84
    /**
85
     * @hidden
86
     * @internal
87
     */
88
    @HostBinding('attr.aria-orientation')
89
    public get orientation() {
UNCOV
90
        return this.type === SplitterType.Horizontal ? 'horizontal' : 'vertical';
×
91
    }
92

93
    /**
94
     * Event fired when resizing of panes starts.
95
     *
96
     * @example
97
     * ```html
98
     * <igx-splitter (resizeStart)='resizeStart($event)'>
99
     *  <igx-splitter-pane>...</igx-splitter-pane>
100
     * </igx-splitter>
101
     * ```
102
     */
103
    @Output()
UNCOV
104
    public resizeStart = new EventEmitter<ISplitterBarResizeEventArgs>();
×
105

106
    /**
107
     * Event fired when resizing of panes is in progress.
108
     *
109
     * @example
110
     * ```html
111
     * <igx-splitter (resizing)='resizing($event)'>
112
     *  <igx-splitter-pane>...</igx-splitter-pane>
113
     * </igx-splitter>
114
     * ```
115
     */
116
    @Output()
UNCOV
117
    public resizing = new EventEmitter<ISplitterBarResizeEventArgs>();
×
118

119

120
    /**
121
     * Event fired when resizing of panes ends.
122
     *
123
     * @example
124
     * ```html
125
     * <igx-splitter (resizeEnd)='resizeEnd($event)'>
126
     *  <igx-splitter-pane>...</igx-splitter-pane>
127
     * </igx-splitter>
128
     * ```
129
     */
130
    @Output()
UNCOV
131
    public resizeEnd = new EventEmitter<ISplitterBarResizeEventArgs>();
×
132

UNCOV
133
    private _type: SplitterType = SplitterType.Horizontal;
×
134

135
    /**
136
     * @hidden @internal
137
     * A field that holds the initial size of the main `IgxSplitterPaneComponent` in each pair of panes divided by a splitter bar.
138
     */
139
    private initialPaneSize!: number;
140

141
    /**
142
     * @hidden @internal
143
     * A field that holds the initial size of the sibling pane in each pair of panes divided by a gripper.
144
     * @memberof SplitterComponent
145
     */
146
    private initialSiblingSize!: number;
147

148
    /**
149
     * @hidden @internal
150
     * The main pane in each pair of panes divided by a gripper.
151
     */
152
    private pane!: IgxSplitterPaneComponent;
153

154
    /**
155
     * The sibling pane in each pair of panes divided by a splitter bar.
156
     */
157
    private sibling!: IgxSplitterPaneComponent;
158

UNCOV
159
    constructor(@Inject(DOCUMENT) public document, private elementRef: ElementRef, private zone: NgZone) { }
×
160
    /**
161
     * Gets/Sets the splitter orientation.
162
     *
163
     * @example
164
     * ```html
165
     * <igx-splitter [type]="type">...</igx-splitter>
166
     * ```
167
     */
168
    @Input()
169
    public get type() {
UNCOV
170
        return this._type;
×
171
    }
172
    public set type(value) {
UNCOV
173
        this._type = value;
×
UNCOV
174
        this.resetPaneSizes();
×
UNCOV
175
        this.panes?.notifyOnChanges();
×
176
    }
177

178
    /**
179
     * Sets the visibility of the handle and expanders in the splitter bar.
180
     * False by default
181
     *
182
     * @example
183
     * ```html
184
     * <igx-splitter [nonCollapsible]='true'>
185
     * </igx-splitter>
186
     * ```
187
     */
188
    @Input({ transform: booleanAttribute })
UNCOV
189
    public nonCollapsible = false; // Input to toggle showing/hiding expanders
×
190

191
    /**
192
     * @hidden @internal
193
     * Gets the `flex-direction` property of the current `SplitterComponent`.
194
     */
195
    @HostBinding('style.flex-direction')
196
    public get direction(): string {
UNCOV
197
        return this.type === SplitterType.Horizontal ? 'row' : 'column';
×
198
    }
199

200
    /** @hidden @internal */
201
    public ngAfterContentInit(): void {
UNCOV
202
        this.zone.onStable.pipe(take(1)).subscribe(() => {
×
UNCOV
203
            this.initPanes();
×
204
        });
UNCOV
205
        this.panes.changes.subscribe(() => {
×
UNCOV
206
            this.initPanes();
×
207
        });
208
    }
209

210
    /**
211
     * @hidden @internal
212
     * This method performs  initialization logic when the user starts dragging the splitter bar between each pair of panes.
213
     * @param pane - the main pane associated with the currently dragged bar.
214
     */
215
    public onMoveStart(pane: IgxSplitterPaneComponent) {
UNCOV
216
        const panes = this.panes.toArray();
×
UNCOV
217
        this.pane = pane;
×
UNCOV
218
        this.sibling = panes[panes.indexOf(this.pane) + 1];
×
219

UNCOV
220
        const paneRect = this.pane.element.getBoundingClientRect();
×
UNCOV
221
        this.initialPaneSize = this.type === SplitterType.Horizontal ? paneRect.width : paneRect.height;
×
222

UNCOV
223
        const siblingRect = this.sibling.element.getBoundingClientRect();
×
UNCOV
224
        this.initialSiblingSize = this.type === SplitterType.Horizontal ? siblingRect.width : siblingRect.height;
×
UNCOV
225
        const args: ISplitterBarResizeEventArgs = { pane: this.pane, sibling: this.sibling };
×
UNCOV
226
        this.resizeStart.emit(args);
×
227
    }
228

229
    /**
230
     * @hidden @internal
231
     * This method performs calculations concerning the sizes of each pair of panes when the bar between them is dragged.
232
     * @param delta - The difference along the X (or Y) axis between the initial and the current point when dragging the bar.
233
     */
234
    public onMoving(delta: number) {
UNCOV
235
        const [ paneSize, siblingSize ] = this.calcNewSizes(delta);
×
236

UNCOV
237
        this.pane.dragSize = paneSize + 'px';
×
UNCOV
238
        this.sibling.dragSize = siblingSize + 'px';
×
239

UNCOV
240
        const args: ISplitterBarResizeEventArgs = { pane: this.pane, sibling: this.sibling };
×
UNCOV
241
        this.resizing.emit(args);
×
242
    }
243

244
    public onMoveEnd(delta: number) {
UNCOV
245
        let [ paneSize, siblingSize ] = this.calcNewSizes(delta);
×
246

UNCOV
247
        if (paneSize + siblingSize > this.getTotalSize() && delta < 0) {
×
248
            paneSize = this.getTotalSize();
×
249
            siblingSize = 0;
×
UNCOV
250
        } else if(paneSize + siblingSize > this.getTotalSize() && delta > 0) {
×
251
            paneSize = 0;
×
252
            siblingSize = this.getTotalSize();
×
253
        }
254

UNCOV
255
        if (this.pane.isPercentageSize) {
×
256
            // handle % resizes
UNCOV
257
            const totalSize = this.getTotalSize();
×
UNCOV
258
            const percentPaneSize = (paneSize / totalSize) * 100;
×
UNCOV
259
            this.pane.size = percentPaneSize + '%';
×
260
        } else {
261
            // px resize
UNCOV
262
            this.pane.size = paneSize + 'px';
×
263
        }
264

UNCOV
265
        if (this.sibling.isPercentageSize) {
×
266
            // handle % resizes
UNCOV
267
            const totalSize = this.getTotalSize();
×
UNCOV
268
            const percentSiblingPaneSize = (siblingSize / totalSize) * 100;
×
UNCOV
269
            this.sibling.size = percentSiblingPaneSize + '%';
×
270
        } else {
271
            // px resize
UNCOV
272
            this.sibling.size = siblingSize + 'px';
×
273
        }
UNCOV
274
        this.pane.dragSize = null;
×
UNCOV
275
        this.sibling.dragSize = null;
×
276

UNCOV
277
        const args: ISplitterBarResizeEventArgs = { pane: this.pane, sibling: this.sibling };
×
UNCOV
278
        this.resizeEnd.emit(args);
×
279
    }
280

281
    /** @hidden @internal */
282
    public getPaneSiblingsByOrder(order: number, barIndex: number): Array<IgxSplitterPaneComponent> {
UNCOV
283
        const panes = this.panes.toArray();
×
UNCOV
284
        const prevPane = panes[order - barIndex - 1];
×
UNCOV
285
        const nextPane = panes[order - barIndex];
×
UNCOV
286
        const siblings = [prevPane, nextPane];
×
UNCOV
287
        return siblings;
×
288
    }
289

290
    private getTotalSize() {
UNCOV
291
        const computed = this.document.defaultView.getComputedStyle(this.elementRef.nativeElement);
×
UNCOV
292
        const totalSize = this.type === SplitterType.Horizontal ? computed.getPropertyValue('width') : computed.getPropertyValue('height');
×
UNCOV
293
        return parseFloat(totalSize);
×
294
    }
295

296

297
    /**
298
     * @hidden @internal
299
     * This method inits panes with properties.
300
     */
301
    private initPanes() {
UNCOV
302
        this.panes.forEach(pane => {
×
UNCOV
303
            pane.owner = this;
×
UNCOV
304
            if (this.type === SplitterType.Horizontal) {
×
UNCOV
305
                pane.minWidth = pane.minSize ?? '0';
×
UNCOV
306
                pane.maxWidth = pane.maxSize ?? '100%';
×
307
            } else {
UNCOV
308
                pane.minHeight = pane.minSize ?? '0';
×
UNCOV
309
                pane.maxHeight = pane.maxSize ?? '100%';
×
310
            }
311
        });
UNCOV
312
        this.assignFlexOrder();
×
UNCOV
313
        if (this.panes.filter(x => x.collapsed).length > 0) {
×
314
            // if any panes are collapsed, reset sizes.
UNCOV
315
            this.resetPaneSizes();
×
316
        }
317
    }
318

319
    /**
320
     * @hidden @internal
321
     * This method reset pane sizes.
322
     */
323
    private resetPaneSizes() {
UNCOV
324
        if (this.panes) {
×
325
            // if type is changed runtime, should reset sizes.
UNCOV
326
            this.panes.forEach(x => {
×
UNCOV
327
                x.size = 'auto'
×
UNCOV
328
                x.minWidth = '0';
×
UNCOV
329
                x.maxWidth = '100%';
×
UNCOV
330
                x.minHeight = '0';
×
UNCOV
331
                x.maxHeight = '100%';
×
332
            });
333
        }
334
    }
335

336
    /**
337
     * @hidden @internal
338
     * This method assigns the order of each pane.
339
     */
340
    private assignFlexOrder() {
UNCOV
341
        let k = 0;
×
UNCOV
342
        this.panes.forEach((pane: IgxSplitterPaneComponent) => {
×
UNCOV
343
            pane.order = k;
×
UNCOV
344
            k += 2;
×
345
        });
346
    }
347

348
    /**
349
     * @hidden @internal
350
     * Calculates new sizes for the panes based on move delta and initial sizes
351
     */
352
    private calcNewSizes(delta: number): [number, number] {
UNCOV
353
        const min = parseInt(this.pane.minSize, 10) || 0;
×
UNCOV
354
        const minSibling = parseInt(this.sibling.minSize, 10) || 0;
×
UNCOV
355
        const max = parseInt(this.pane.maxSize, 10) || this.initialPaneSize + this.initialSiblingSize - minSibling;
×
UNCOV
356
        const maxSibling = parseInt(this.sibling.maxSize, 10) || this.initialPaneSize + this.initialSiblingSize - min;
×
357

UNCOV
358
        if (delta < 0) {
×
UNCOV
359
            const maxPossibleDelta = Math.min(
×
360
                max - this.initialPaneSize,
361
                this.initialSiblingSize - minSibling,
362
            )
UNCOV
363
            delta = Math.min(maxPossibleDelta, Math.abs(delta)) * -1;
×
364
        } else {
UNCOV
365
            const maxPossibleDelta = Math.min(
×
366
                this.initialPaneSize - min,
367
                maxSibling - this.initialSiblingSize
368
            )
UNCOV
369
            delta = Math.min(maxPossibleDelta, Math.abs(delta));
×
370
        }
UNCOV
371
        return [this.initialPaneSize - delta, this.initialSiblingSize + delta];
×
372
    }
373
}
374

375
/**
376
 * @hidden @internal
377
 * Represents the draggable bar that visually separates panes and allows for changing their sizes.
378
 */
379
@Component({
380
    selector: 'igx-splitter-bar',
381
    templateUrl: './splitter-bar.component.html',
382
    imports: [IgxDragDirective, IgxDragIgnoreDirective]
383
})
384
export class IgxSplitBarComponent {
3✔
385
    /**
386
     * Set css class to the host element.
387
     */
388
    @HostBinding('class.igx-splitter-bar-host')
UNCOV
389
    public cssClass = 'igx-splitter-bar-host';
×
390

391
     /**
392
     * Sets the visibility of the handle and expanders in the splitter bar.
393
     */
394
    @Input({ transform: booleanAttribute })
395
    public nonCollapsible;
396

397
    /**
398
     * Gets/Sets the orientation.
399
     */
400
    @Input()
UNCOV
401
    public type: SplitterType = SplitterType.Horizontal;
×
402

403
    /**
404
     * Sets/gets the element order.
405
     */
406
    @HostBinding('style.order')
407
    @Input()
408
    public order!: number;
409

410
    /**
411
     * @hidden
412
     * @internal
413
     */
414
    @HostBinding('attr.tabindex')
415
    public get tabindex() {
UNCOV
416
        return this.resizeDisallowed ? null : 0;
×
417
    }
418

419
    /**
420
     * @hidden
421
     * @internal
422
     */
423
    @HostBinding('attr.aria-orientation')
424
    public get orientation() {
UNCOV
425
        return this.type === SplitterType.Horizontal ? 'horizontal' : 'vertical';
×
426
    }
427

428
    /**
429
     * @hidden
430
     * @internal
431
     */
432
    public get cursor() {
UNCOV
433
        if (this.resizeDisallowed) {
×
UNCOV
434
            return '';
×
435
        }
UNCOV
436
        return this.type === SplitterType.Horizontal ? 'col-resize' : 'row-resize';
×
437
    }
438

439
    /**
440
     * Sets/gets the `SplitPaneComponent` associated with the current `SplitBarComponent`.
441
     *
442
     * @memberof SplitBarComponent
443
     */
444
    @Input()
445
    public pane!: IgxSplitterPaneComponent;
446

447
    /**
448
     * Sets/Gets the `SplitPaneComponent` sibling components associated with the current `SplitBarComponent`.
449
     */
450
    @Input()
451
    public siblings!: Array<IgxSplitterPaneComponent>;
452

453
    /**
454
     * An event that is emitted whenever we start dragging the current `SplitBarComponent`.
455
     */
456
    @Output()
UNCOV
457
    public moveStart = new EventEmitter<IgxSplitterPaneComponent>();
×
458

459
    /**
460
     * An event that is emitted while we are dragging the current `SplitBarComponent`.
461
     */
462
    @Output()
UNCOV
463
    public moving = new EventEmitter<number>();
×
464

465
    @Output()
UNCOV
466
    public movingEnd = new EventEmitter<number>();
×
467

468
    /**
469
     * A temporary holder for the pointer coordinates.
470
     */
471
    private startPoint!: number;
472

UNCOV
473
    private interactionKeys = new Set('right down left up arrowright arrowdown arrowleft arrowup'.split(' '));
×
474

475
    /**
476
     * @hidden @internal
477
     */
478
    public get prevButtonHidden() {
UNCOV
479
        return this.siblings[0]?.collapsed && !this.siblings[1]?.collapsed;
×
480
    }
481

482
    /**
483
     * @hidden @internal
484
     */
485
    @HostListener('keydown', ['$event'])
486
    public keyEvent(event: KeyboardEvent) {
UNCOV
487
        const key = event.key.toLowerCase();
×
UNCOV
488
        const ctrl = event.ctrlKey;
×
UNCOV
489
        event.stopPropagation();
×
UNCOV
490
        if (this.interactionKeys.has(key)) {
×
UNCOV
491
            event.preventDefault();
×
492
        }
UNCOV
493
        switch (key) {
×
494
            case 'arrowup':
495
            case 'up':
UNCOV
496
                if (this.type === SplitterType.Vertical) {
×
UNCOV
497
                    if (ctrl) {
×
UNCOV
498
                        this.onCollapsing(false);
×
UNCOV
499
                        break;
×
500
                    }
UNCOV
501
                    if (!this.resizeDisallowed) {
×
UNCOV
502
                        event.preventDefault();
×
UNCOV
503
                        this.moveStart.emit(this.pane);
×
UNCOV
504
                        this.moving.emit(10);
×
505
                    }
506
                }
UNCOV
507
                break;
×
508
            case 'arrowdown':
509
            case 'down':
UNCOV
510
                if (this.type === SplitterType.Vertical) {
×
UNCOV
511
                    if (ctrl) {
×
UNCOV
512
                        this.onCollapsing(true);
×
UNCOV
513
                        break;
×
514
                    }
UNCOV
515
                    if (!this.resizeDisallowed) {
×
UNCOV
516
                        event.preventDefault();
×
UNCOV
517
                        this.moveStart.emit(this.pane);
×
UNCOV
518
                        this.moving.emit(-10);
×
519
                    }
520
                }
UNCOV
521
                break;
×
522
            case 'arrowleft':
523
            case 'left':
UNCOV
524
                if (this.type === SplitterType.Horizontal) {
×
UNCOV
525
                    if (ctrl) {
×
UNCOV
526
                        this.onCollapsing(false);
×
UNCOV
527
                        break;
×
528
                    }
UNCOV
529
                    if (!this.resizeDisallowed) {
×
UNCOV
530
                        event.preventDefault();
×
UNCOV
531
                        this.moveStart.emit(this.pane);
×
UNCOV
532
                        this.moving.emit(10);
×
533
                    }
534
                }
UNCOV
535
                break;
×
536
            case 'arrowright':
537
            case 'right':
UNCOV
538
                if (this.type === SplitterType.Horizontal) {
×
UNCOV
539
                    if (ctrl) {
×
UNCOV
540
                        this.onCollapsing(true);
×
UNCOV
541
                        break;
×
542
                    }
UNCOV
543
                    if (!this.resizeDisallowed) {
×
UNCOV
544
                        event.preventDefault();
×
UNCOV
545
                        this.moveStart.emit(this.pane);
×
UNCOV
546
                        this.moving.emit(-10);
×
547
                    }
548
                }
UNCOV
549
                break;
×
550
            default:
551
                break;
×
552
        }
553
    }
554

555
    /**
556
     * @hidden @internal
557
     */
558
    public get dragDir() {
UNCOV
559
        return this.type === SplitterType.Horizontal ? DragDirection.VERTICAL : DragDirection.HORIZONTAL;
×
560
    }
561

562
    /**
563
     * @hidden @internal
564
     */
565
    public get nextButtonHidden() {
UNCOV
566
        return this.siblings[1]?.collapsed && !this.siblings[0]?.collapsed;
×
567
    }
568

569
    /**
570
     * @hidden @internal
571
     */
572
    public onDragStart(event: IDragStartEventArgs) {
UNCOV
573
        if (this.resizeDisallowed) {
×
UNCOV
574
            event.cancel = true;
×
UNCOV
575
            return;
×
576
        }
577
        this.startPoint = this.type === SplitterType.Horizontal ? event.startX : event.startY;
×
578
        this.moveStart.emit(this.pane);
×
579
    }
580

581
    /**
582
     * @hidden @internal
583
     */
584
    public onDragMove(event: IDragMoveEventArgs) {
585
        const isHorizontal = this.type === SplitterType.Horizontal;
×
586
        const curr = isHorizontal ? event.pageX : event.pageY;
×
587
        const delta = this.startPoint - curr;
×
588
        if (delta !== 0) {
×
589
            this.moving.emit(delta);
×
590
            event.cancel = true;
×
591
            event.owner.element.nativeElement.style.transform = '';
×
592
        }
593
    }
594

595
    public onDragEnd(event: any) {
596
        const isHorizontal = this.type === SplitterType.Horizontal;
×
597
        const curr = isHorizontal ? event.pageX : event.pageY;
×
598
        const delta = this.startPoint - curr;
×
599
        if (delta !== 0) {
×
600
            this.movingEnd.emit(delta);
×
601
        }
602
    }
603

604
    protected get resizeDisallowed() {
UNCOV
605
        const relatedTabs = this.siblings;
×
UNCOV
606
        return !!relatedTabs.find(x => x?.resizable === false || x?.collapsed === true);
×
607
    }
608

609
    /**
610
     * @hidden @internal
611
     */
612
    public onCollapsing(next: boolean) {
UNCOV
613
        const prevSibling = this.siblings[0];
×
UNCOV
614
        const nextSibling = this.siblings[1];
×
615
        let target;
UNCOV
616
        if (next) {
×
617
            // if next is clicked when prev pane is hidden, show prev pane, else hide next pane.
UNCOV
618
            target = prevSibling.collapsed ? prevSibling : nextSibling;
×
619
        } else {
620
            // if prev is clicked when next pane is hidden, show next pane, else hide prev pane.
UNCOV
621
            target = nextSibling.collapsed ? nextSibling : prevSibling;
×
622
        }
UNCOV
623
        target.toggle();
×
624
    }
625
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc