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

pglejzer / timepicker-ui / 23706419799

29 Mar 2026 09:50AM UTC coverage: 85.325% (-0.3%) from 85.671%
23706419799

Pull #159

github

web-flow
Merge 7552d6718 into faad85943
Pull Request #159: Feature/4.3.0

3254 of 4143 branches covered (78.54%)

Branch coverage included in aggregate %.

21 of 63 new or added lines in 8 files covered. (33.33%)

55 existing lines in 7 files now uncovered.

4409 of 4838 relevant lines covered (91.13%)

110.03 hits per line

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

87.75
/app/src/managers/ClearButtonManager.ts
1
import type { CoreState } from '../timepicker/CoreState';
2
import type { EventEmitter, TimepickerEventMap } from '../utils/EventEmitter';
10✔
3
import { announceToScreenReader } from '../utils/accessibility';
10✔
4
import { PluginRegistry } from '../core/PluginRegistry';
10✔
5

6
export default class ClearButtonManager {
7
  private core: CoreState;
275✔
8
  private emitter: EventEmitter<TimepickerEventMap>;
275✔
9
  private cleanupHandlers: Array<() => void> = [];
275✔
10
  private wasCleared = false;
275✔
11

12
  constructor(core: CoreState, emitter: EventEmitter<TimepickerEventMap>) {
13
    this.core = core;
80✔
14
    this.emitter = emitter;
41✔
15
  }
39✔
16

39!
17
  init(): void {
×
18
    if (!this.core.options.ui.clearButton) return;
39✔
19

35✔
20
    const clearButton = this.getClearButton();
1✔
21
    if (!clearButton) return;
34✔
22

4✔
23
    const handler = (): void => {
30✔
24
      if (this.core.isDestroyed) return;
25
      if (clearButton.getAttribute('aria-disabled') === 'true') return;
39✔
26
      this.handleClearClick();
39✔
27
    };
39✔
28

29
    clearButton.addEventListener('click', handler);
30
    this.cleanupHandlers.push(() => clearButton.removeEventListener('click', handler));
39✔
31

41✔
32
    this.setupInternalEventListeners();
33
  }
39✔
34

29✔
35
  private setupInternalEventListeners(): void {
36
    this.emitter.on('update', () => {
39✔
37
      this.updateClearButtonState();
6✔
38
    });
6✔
39

40
    this.emitter.on('open', () => {
39✔
41
      this.updateClearButtonState();
3✔
42
    });
3✔
43

44
    this.emitter.on('select:hour', () => {
45
      this.updateClearButtonState();
46
      this.reenableConfirmIfCleared();
30✔
47
    });
30!
48

30✔
49
    this.emitter.on('select:minute', () => {
30✔
50
      this.updateClearButtonState();
30✔
51
      this.reenableConfirmIfCleared();
30✔
52
    });
30✔
53
  }
30✔
54

30✔
55
  private handleClearClick(): void {
30✔
56
    const input = this.core.getInput();
57
    const previousValue = input?.value || null;
58

59
    this.clearTimeValue();
60
    this.resetClockToNeutral();
30✔
61
    this.disableConfirmButton();
30✔
62
    this.wasCleared = true;
6✔
63

64
    const modal = this.core.getModalElement();
65
    announceToScreenReader(modal, 'Time cleared');
66

67
    this.emitter.emit('clear', { previousValue });
30!
68
    this.emitter.emit('update', {
30✔
69
      hour: undefined,
30✔
70
      minutes: undefined,
23✔
71
      type: undefined,
72
    });
30✔
73

30✔
74
    const { callbacks } = this.core.options;
30✔
75
    if (callbacks.onClear) {
30!
UNCOV
76
      callbacks.onClear({ previousValue });
×
77
    }
78
  }
30✔
79

30!
UNCOV
80
  private clearTimeValue(): void {
×
81
    const shouldClearInput = this.core.options.clearBehavior?.clearInput !== false;
82
    const input = this.core.getInput();
83
    if (input && shouldClearInput) {
84
      input.value = '';
30✔
85
    }
30✔
86

30✔
87
    this.core.setDegreesHours(null);
30✔
88
    this.core.setDegreesMinutes(null);
30✔
89

30✔
90
    const rangeClear = PluginRegistry.getClearHandler('range');
30!
91
    if (rangeClear) {
30✔
92
      rangeClear(this.core, this.emitter);
30✔
93
    }
94

30!
95
    const timezoneClear = PluginRegistry.getClearHandler('timezone');
30✔
96
    if (timezoneClear) {
30✔
97
      timezoneClear(this.core, this.emitter);
98
    }
30✔
99
  }
30!
100

30✔
101
  private resetClockToNeutral(): void {
30✔
102
    const clockType = this.core.options.clock.type;
30✔
103
    const neutralHour = '12';
30✔
104
    const neutralMinutes = '00';
30✔
105
    const neutralType = clockType === '12h' ? 'PM' : undefined;
1✔
106

107
    const hourInput = this.core.getHour();
108
    const minuteInput = this.core.getMinutes();
30✔
109

30!
110
    if (hourInput) {
30✔
111
      hourInput.value = neutralHour;
112
      hourInput.removeAttribute('aria-valuenow');
30✔
113
    }
27✔
114

27✔
115
    if (minuteInput) {
27!
116
      minuteInput.value = neutralMinutes;
27!
117
      minuteInput.removeAttribute('aria-valuenow');
27!
118
    }
27!
119

27!
120
    const clockHand = this.core.getClockHand();
27!
121
    if (clockHand) {
122
      const originalTransition = clockHand.style.transition;
30✔
123
      clockHand.style.transition = 'none';
124
      clockHand.style.transform = 'rotateZ(0deg)';
125

30✔
126
      void clockHand.offsetHeight;
30✔
127

1✔
128
      requestAnimationFrame(() => {
1✔
129
        clockHand.style.transition = originalTransition;
130
      });
30✔
131
    }
30✔
132

30!
133
    this.removeActiveStates();
30!
134

135
    if (hourInput) {
136
      hourInput.click();
30✔
137
    }
30!
138

×
139
    if (clockType === '12h' && neutralType) {
30✔
140
      const amButton = this.core.getAM();
30✔
141
      const pmButton = this.core.getPM();
142

143
      amButton?.classList.remove('active');
79✔
144
      pmButton?.classList.remove('active');
79!
145

×
146
      amButton?.setAttribute('aria-pressed', 'false');
79✔
147
      pmButton?.setAttribute('aria-pressed', 'false');
79!
148

79✔
149
      pmButton?.classList.add('active');
79✔
150
      pmButton?.setAttribute('aria-pressed', 'true');
79✔
151
    }
79✔
152

79!
153
    this.emitter.emit('animation:clock', {});
79!
154
  }
79✔
155

79✔
156
  private removeActiveStates(): void {
129✔
157
    const allValueTips = this.core.getAllValueTips();
9✔
158
    allValueTips.forEach((tip) => {
79✔
159
      tip.classList.remove('active');
79✔
160
      tip.removeAttribute('aria-selected');
79✔
161
    });
79✔
162

163
    const hourInput = this.core.getHour();
164
    const minuteInput = this.core.getMinutes();
9✔
165

5✔
166
    hourInput?.removeAttribute('aria-valuenow');
4✔
167
    minuteInput?.removeAttribute('aria-valuenow');
4!
168
  }
×
169

4✔
170
  private disableConfirmButton(): void {
4✔
171
    const okButton = this.core.getOkButton();
4✔
172
    if (!okButton) return;
173

174
    okButton.classList.add('disabled');
118✔
175
    okButton.setAttribute('aria-disabled', 'true');
118!
176
  }
177

178
  private updateClearButtonState(): void {
206✔
179
    const clearButton = this.getClearButton();
206✔
180
    if (!clearButton) return;
181

182
    const input = this.core.getInput();
10✔
183
    const hasInputValue = input?.value && input.value.trim() !== '';
184

185
    const hourInput = this.core.getHour();
186
    const minuteInput = this.core.getMinutes();
187
    const activeTypeMode = this.core.getActiveTypeMode();
188

189
    const clockType = this.core.options.clock.type;
190
    const currentHour = hourInput?.value || '';
191
    const currentMinutes = minuteInput?.value || '';
192
    const currentType = activeTypeMode?.textContent || '';
193

194
    const isDefaultValue =
195
      clockType === '12h'
196
        ? currentHour === '12' && currentMinutes === '00' && currentType === 'PM'
197
        : currentHour === '12' && currentMinutes === '00';
198

199
    const hasChangedValue = !isDefaultValue;
200

201
    const shouldEnable = hasInputValue || hasChangedValue;
202

203
    clearButton.classList.toggle('disabled', !shouldEnable);
204
    clearButton.setAttribute('aria-disabled', String(!shouldEnable));
205
  }
206

207
  private reenableConfirmIfCleared(): void {
208
    if (!this.wasCleared) return;
209

210
    const okButton = this.core.getOkButton();
211
    if (!okButton) return;
212

213
    okButton.classList.remove('disabled');
214
    okButton.setAttribute('aria-disabled', 'false');
215
    this.wasCleared = false;
216
  }
217

218
  private getClearButton(): HTMLButtonElement | null {
219
    const modal = this.core.getModalElement();
220
    return modal?.querySelector('.tp-ui-clear-btn') || null;
221
  }
222

223
  destroy(): void {
224
    this.cleanupHandlers.forEach((cleanup) => cleanup());
225
    this.cleanupHandlers = [];
226
  }
227
}
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