• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-webcomponents / 7098961215

05 Dec 2023 09:50AM UTC coverage: 97.383% (+0.1%) from 97.251%
7098961215

push

github

web-flow
feat: Refactor toggle directive into popover component (#983)

* refactor(dropdown): Migrated to popover component
* extracted a base option-like class for dropdown item and select item
* utility classes refactoring
* refactor(select): Use the underlying popover
* Improved behavior to mimic the native select
* WAI-ARIA improvements
* refactor(dropdown): Detached target behavior
* refactor: Moved keybindings to handleEvent pattern
* chore: Improved storybook samples
* refactor: RootClickControllerConfig as type
Co-authored-by: Simeon Simeonoff <sim.simeonoff@gmail.com>

3281 of 3501 branches covered (0.0%)

Branch coverage included in aggregate %.

1514 of 1532 new or added lines in 16 files covered. (98.83%)

4 existing lines in 2 files now uncovered.

20569 of 20990 relevant lines covered (97.99%)

432.07 hits per line

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

95.18
/src/components/toggle/toggle.controller.ts
1
import { ReactiveController, ReactiveControllerHost } from 'lit';
32✔
2
import type { DirectiveResult } from 'lit/directive';
32✔
3

32✔
4
import { IgcToggleDirective, igcToggle } from './toggle.directive.js';
32✔
5
import type { IgcToggleComponent } from './types.js';
32✔
6

32✔
7
type ToggleHost = ReactiveControllerHost & IgcToggleComponent & HTMLElement;
32✔
8

32✔
9
/**
32✔
10
 * Toggle controller configuration
32✔
11
 */
32✔
12
interface ToggleControllerConfig {
32✔
13
  /** The element, relative to which, the toggle will be positioned. */
32✔
14
  target?: HTMLElement;
32✔
15
  /**
32✔
16
   * The function to call when closing the toggle element from an user interaction (scroll, click).
32✔
17
   */
32✔
18
  closeCallback?: Function;
32✔
19
}
32✔
20

32✔
21
/* blazorSuppress */
32✔
22
/**
32✔
23
 * Controller, bundling the creation of a toggle directive and handling global events,
32✔
24
 * related to the configuration of togglable components.
32✔
25
 */
32✔
26
export class IgcToggleController implements ReactiveController {
32✔
27
  private host: IgcToggleComponent & HTMLElement;
32✔
28
  private sourceElement?: Element;
32✔
29
  private initialScrollTop = 0;
32✔
30
  private initialScrollLeft = 0;
32✔
31
  private _target!: HTMLElement;
32✔
32
  private _hide?: Function;
32✔
33
  private _abortController = new AbortController();
32✔
34

32✔
35
  /**
32✔
36
   *  Abort controller used to clean up document level event listeners
32✔
37
   *  See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#add_an_abortable_listener
32✔
38
   */
32✔
39
  protected get abortController() {
32✔
40
    if (this._abortController.signal.aborted) {
181✔
41
      this._abortController = new AbortController();
75✔
42
    }
75✔
43
    return this._abortController;
181✔
44
  }
181✔
45

32✔
46
  /** The directive that marks the toggle. */
32✔
47
  public toggleDirective!: DirectiveResult<typeof IgcToggleDirective>;
32✔
48
  public rendered!: Promise<void>;
32✔
49

32✔
50
  public set target(value: HTMLElement) {
32✔
51
    this._target = value;
39✔
52
    this.update();
39✔
53
  }
39✔
54

32✔
55
  /** The element, relative to which, the toggle will be positioned. */
32✔
56
  public get target() {
32✔
57
    return this._target;
110✔
58
  }
110✔
59

32✔
60
  constructor(host: ToggleHost, config?: ToggleControllerConfig) {
32✔
61
    (this.host = host).addController(this);
71✔
62

71✔
63
    if (config?.target) {
71!
64
      this._target = config.target;
×
65
    }
×
66

71✔
67
    if (config?.closeCallback) {
71✔
68
      this._hide = config.closeCallback;
71✔
69
    }
71✔
70

71✔
71
    this.update();
71✔
72
  }
71✔
73

32✔
74
  public hostDisconnected() {
32✔
75
    this.abortController.abort();
71✔
76
  }
71✔
77

32✔
78
  public update() {
32✔
79
    this.toggleDirective = igcToggle(this.target, this.host, this);
110✔
80
    this.configureListeners();
110✔
81
  }
110✔
82

32✔
83
  protected hide() {
32✔
UNCOV
84
    this._hide ? this._hide() : this.host.hide();
×
UNCOV
85
  }
×
86

32✔
87
  private addEventListeners() {
32✔
88
    const options: AddEventListenerOptions = {
35✔
89
      capture: true,
35✔
90
      signal: this.abortController.signal,
35✔
91
    };
35✔
92

35✔
93
    if (!this.host.keepOpenOnOutsideClick) {
35✔
94
      document.addEventListener('click', this.documentClicked, options);
35✔
95
    }
35✔
96

35✔
97
    document.addEventListener('scroll', this.handleScroll, options);
35✔
98
  }
35✔
99

32✔
100
  private configureListeners() {
32✔
101
    this.host.open ? this.addEventListeners() : this.abortController.abort();
110✔
102
  }
110✔
103

32✔
104
  private blockScroll = (event: Event) => {
32✔
105
    event.preventDefault();
31✔
106
    if (!this.sourceElement || this.sourceElement !== event.target) {
31✔
107
      this.sourceElement = event.target as Element;
31✔
108
      this.initialScrollTop =
31✔
109
        this.sourceElement.scrollTop ??
31✔
110
        this.sourceElement.firstElementChild?.scrollTop;
31✔
111
      this.initialScrollLeft =
31✔
112
        this.sourceElement.scrollLeft ??
31✔
113
        this.sourceElement.firstElementChild?.scrollLeft;
31✔
114
    }
31✔
115

31✔
116
    this.sourceElement.scrollTop = this.initialScrollTop;
31✔
117
    this.sourceElement.scrollLeft = this.initialScrollLeft;
31✔
118
    if (this.sourceElement.firstElementChild) {
31✔
119
      this.sourceElement.firstElementChild.scrollTop = this.initialScrollTop;
31✔
120
      this.sourceElement.firstElementChild.scrollLeft = this.initialScrollLeft;
31✔
121
    }
31✔
122
  };
32✔
123

32✔
124
  /** The document's click event handler to override in the host component if necessary. */
32✔
125
  private documentClicked = (event: MouseEvent) => {
32✔
126
    if (!this.host.keepOpenOnOutsideClick) {
36✔
127
      const tree = event.composed ? event.composedPath() : [event.target];
36!
128

36✔
129
      if (tree.includes(this.host) || tree.includes(this.target)) {
36!
130
        return;
36✔
131
      }
36✔
132

31!
133
      this.hide();
31✔
134
    }
31✔
135
  };
32✔
136

32✔
137
  /** The document's scroll event handler to override in the host component if necessary. */
32✔
138
  private handleScroll = (event: Event) => {
32✔
139
    switch (this.host.scrollStrategy) {
31✔
140
      case 'scroll':
31✔
141
        break;
31✔
142
      case 'block':
31✔
143
        this.blockScroll(event);
31✔
144
        break;
31✔
145
      case 'close':
31✔
146
        this.hide();
31✔
147
        break;
31✔
148
    }
31✔
149
  };
32✔
150
}
32✔
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