Skip to content

Commit 0b4f24f

Browse files
authored
Merge pull request #556 from mrholek/main
Minor improvements in Calendar and RangeSlider
2 parents e8fed30 + 59e119e commit 0b4f24f

File tree

4 files changed

+304
-34
lines changed

4 files changed

+304
-34
lines changed

js/src/calendar.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,11 +920,11 @@ class Calendar extends BaseComponent {
920920
return
921921
}
922922

923-
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
923+
if (typeof data[config] === 'undefined') {
924924
throw new TypeError(`No method named "${config}"`)
925925
}
926926

927-
data[config](this)
927+
data[config]()
928928
})
929929
}
930930
}

js/src/range-slider.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ class RangeSlider extends BaseComponent {
357357
}
358358
}
359359

360-
return null
360+
return '1rem'
361361
}
362362

363363
_positionTooltip(tooltip, input) {

js/tests/unit/calendar.spec.js

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/* eslint-env jasmine */
2+
3+
import Calendar from '../../src/calendar.js'
4+
import {
5+
getFixture, clearFixture, createEvent, jQueryMock
6+
} from '../helpers/fixture.js'
7+
import EventHandler from '../../src/dom/event-handler.js'
8+
9+
describe('Calendar', () => {
10+
let fixtureEl
11+
12+
beforeAll(() => {
13+
fixtureEl = getFixture()
14+
})
15+
16+
afterEach(() => {
17+
clearFixture()
18+
})
19+
20+
describe('Default', () => {
21+
it('should return plugin default config', () => {
22+
expect(Calendar.Default).toEqual(jasmine.any(Object))
23+
})
24+
})
25+
26+
describe('DefaultType', () => {
27+
it('should return plugin default type config', () => {
28+
expect(Calendar.DefaultType).toEqual(jasmine.any(Object))
29+
})
30+
})
31+
32+
describe('NAME', () => {
33+
it('should return plugin NAME', () => {
34+
expect(Calendar.NAME).toEqual('calendar')
35+
})
36+
})
37+
38+
describe('constructor', () => {
39+
it('should create a Calendar instance with default config if no config is provided', () => {
40+
fixtureEl.innerHTML = '<div></div>'
41+
42+
const div = fixtureEl.querySelector('div')
43+
const calendar = new Calendar(div)
44+
45+
expect(calendar).toBeInstanceOf(Calendar)
46+
expect(calendar._config).toBeDefined()
47+
expect(calendar._element).toEqual(div)
48+
})
49+
50+
it('should allow overriding default config', () => {
51+
fixtureEl.innerHTML = '<div></div>'
52+
53+
const div = fixtureEl.querySelector('div')
54+
const calendar = new Calendar(div, {
55+
locale: 'fr',
56+
calendars: 2,
57+
selectionType: 'month'
58+
})
59+
60+
expect(calendar._config.locale).toEqual('fr')
61+
expect(calendar._config.calendars).toEqual(2)
62+
expect(calendar._config.selectionType).toEqual('month')
63+
})
64+
65+
it('should set `_view` based on `selectionType`', () => {
66+
fixtureEl.innerHTML = '<div></div>'
67+
68+
const div = fixtureEl.querySelector('div')
69+
const calendar = new Calendar(div, {
70+
selectionType: 'year'
71+
})
72+
73+
expect(calendar._view).toEqual('years')
74+
})
75+
76+
it('should properly create the initial markup for days view', () => {
77+
fixtureEl.innerHTML = '<div></div>'
78+
79+
const div = fixtureEl.querySelector('div')
80+
new Calendar(div) // eslint-disable-line no-new
81+
82+
// Check if .calendar container is created
83+
expect(div.querySelector('.calendar')).not.toBeNull()
84+
85+
// Check if .calendar-nav exists
86+
expect(div.querySelector('.calendar-nav')).not.toBeNull()
87+
88+
// Check if a table with <thead> is created (days view)
89+
expect(div.querySelector('.calendar table thead')).not.toBeNull()
90+
expect(div.querySelector('.calendar table tbody')).not.toBeNull()
91+
})
92+
})
93+
94+
describe('update', () => {
95+
it('should clear the calendar HTML and create a new one', () => {
96+
fixtureEl.innerHTML = '<div></div>'
97+
98+
const div = fixtureEl.querySelector('div')
99+
const calendar = new Calendar(div)
100+
const oldHtml = div.innerHTML
101+
102+
calendar.update({
103+
selectionType: 'month'
104+
})
105+
106+
expect(div.innerHTML).not.toEqual(oldHtml)
107+
// Now we should see the months view
108+
expect(div.querySelector('.calendar table thead')).toBeNull()
109+
expect(div.querySelector('.calendar table tbody')).not.toBeNull()
110+
expect(div.querySelector('.month')).not.toBeNull()
111+
})
112+
113+
it('should use the new config object after update', () => {
114+
fixtureEl.innerHTML = '<div></div>'
115+
116+
const div = fixtureEl.querySelector('div')
117+
const calendar = new Calendar(div)
118+
119+
calendar.update({
120+
showWeekNumber: true
121+
})
122+
123+
expect(calendar._config.showWeekNumber).toBeTrue()
124+
expect(div.classList).toContain('show-week-numbers')
125+
})
126+
})
127+
128+
describe('keyboard navigation', () => {
129+
it('should select a date on Enter key press', () => {
130+
return new Promise(resolve => {
131+
fixtureEl.innerHTML = '<div></div>'
132+
const div = fixtureEl.querySelector('div')
133+
const calendar = new Calendar(div)
134+
const showSpy = spyOn(calendar, '_handleCalendarClick').and.callThrough()
135+
136+
setTimeout(() => {
137+
const dayCell = div.querySelector('.calendar-cell[tabindex="0"]')
138+
expect(dayCell).not.toBeNull()
139+
140+
const keydownEvent = createEvent('keydown')
141+
keydownEvent.key = 'Enter'
142+
dayCell.dispatchEvent(keydownEvent)
143+
144+
expect(showSpy).toHaveBeenCalled()
145+
resolve()
146+
}, 10)
147+
})
148+
})
149+
})
150+
151+
describe('events', () => {
152+
it('should emit `calendarDateChange.coreui.calendar` when the calendar date changes', () => {
153+
fixtureEl.innerHTML = '<div></div>'
154+
const div = fixtureEl.querySelector('div')
155+
const calendar = new Calendar(div)
156+
const listener = jasmine.createSpy('listener')
157+
158+
div.addEventListener('calendarDateChange.coreui.calendar', listener)
159+
// For example, go to next month
160+
calendar._modifyCalendarDate(0, 1)
161+
162+
expect(listener).toHaveBeenCalled()
163+
})
164+
165+
it('should emit `startDateChange.coreui.calendar` when the start date is set', () => {
166+
fixtureEl.innerHTML = '<div></div>'
167+
const div = fixtureEl.querySelector('div')
168+
const calendar = new Calendar(div)
169+
const listener = jasmine.createSpy('listener')
170+
171+
div.addEventListener('startDateChange.coreui.calendar', listener)
172+
calendar._setStartDate(new Date())
173+
174+
expect(listener).toHaveBeenCalled()
175+
})
176+
177+
it('should emit `endDateChange.coreui.calendar` when the end date is set', () => {
178+
fixtureEl.innerHTML = '<div></div>'
179+
const div = fixtureEl.querySelector('div')
180+
const calendar = new Calendar(div)
181+
const listener = jasmine.createSpy('listener')
182+
183+
div.addEventListener('endDateChange.coreui.calendar', listener)
184+
calendar._setEndDate(new Date())
185+
186+
expect(listener).toHaveBeenCalled()
187+
})
188+
})
189+
190+
describe('dispose', () => {
191+
it('should dispose Calendar instance', () => {
192+
fixtureEl.innerHTML = '<div></div>'
193+
const div = fixtureEl.querySelector('div')
194+
const calendar = new Calendar(div)
195+
const spy = spyOn(EventHandler, 'off').and.callThrough()
196+
197+
expect(calendar._element).toEqual(div)
198+
calendar.dispose()
199+
200+
// Typically, you'd set `_element` to null after disposing
201+
expect(calendar._element).toBeNull()
202+
// Should remove all event handlers
203+
expect(spy.calls.count()).toBeGreaterThan(0)
204+
})
205+
})
206+
207+
describe('jQueryInterface', () => {
208+
it('should create a calendar via jQueryInterface', () => {
209+
fixtureEl.innerHTML = '<div data-coreui-toggle="calendar"></div>'
210+
const element = fixtureEl.querySelector('[data-coreui-toggle="calendar"]')
211+
212+
jQueryMock.fn.calendar = Calendar.jQueryInterface
213+
jQueryMock.elements = [element]
214+
jQueryMock.fn.calendar.call(jQueryMock)
215+
216+
expect(Calendar.getInstance(element)).not.toBeNull()
217+
})
218+
219+
it('should throw error on undefined method', () => {
220+
fixtureEl.innerHTML = '<div data-coreui-toggle="calendar"></div>'
221+
const element = fixtureEl.querySelector('[data-coreui-toggle="calendar"]')
222+
223+
jQueryMock.fn.calendar = Calendar.jQueryInterface
224+
jQueryMock.elements = [element]
225+
226+
expect(() => {
227+
jQueryMock.fn.calendar.call(jQueryMock, 'noMethod')
228+
}).toThrowError(TypeError, 'No method named "noMethod"')
229+
})
230+
})
231+
232+
describe('getInstance', () => {
233+
it('should return calendar instance', () => {
234+
fixtureEl.innerHTML = '<div></div>'
235+
236+
const div = fixtureEl.querySelector('div')
237+
const calendar = new Calendar(div)
238+
239+
expect(Calendar.getInstance(div)).toEqual(calendar)
240+
expect(Calendar.getInstance(div)).toBeInstanceOf(Calendar)
241+
})
242+
243+
it('should return null when there is no calendar instance', () => {
244+
fixtureEl.innerHTML = '<div></div>'
245+
const div = fixtureEl.querySelector('div')
246+
247+
expect(Calendar.getInstance(div)).toBeNull()
248+
})
249+
})
250+
251+
describe('getOrCreateInstance', () => {
252+
it('should return calendar instance', () => {
253+
fixtureEl.innerHTML = '<div></div>'
254+
255+
const div = fixtureEl.querySelector('div')
256+
const calendar = new Calendar(div)
257+
258+
expect(Calendar.getOrCreateInstance(div)).toEqual(calendar)
259+
expect(Calendar.getInstance(div)).toEqual(Calendar.getOrCreateInstance(div, {}))
260+
expect(Calendar.getOrCreateInstance(div)).toBeInstanceOf(Calendar)
261+
})
262+
263+
it('should return new instance when there is no calendar instance', () => {
264+
fixtureEl.innerHTML = '<div></div>'
265+
266+
const div = fixtureEl.querySelector('div')
267+
268+
expect(Calendar.getInstance(div)).toBeNull()
269+
expect(Calendar.getOrCreateInstance(div)).toBeInstanceOf(Calendar)
270+
})
271+
272+
it('should return new instance when there is no calendar instance with given configuration', () => {
273+
fixtureEl.innerHTML = '<div></div>'
274+
275+
const div = fixtureEl.querySelector('div')
276+
277+
expect(Calendar.getInstance(div)).toBeNull()
278+
const calendar = Calendar.getOrCreateInstance(div, {
279+
calendars: 3
280+
})
281+
expect(calendar).toBeInstanceOf(Calendar)
282+
expect(calendar._config.calendars).toEqual(3)
283+
})
284+
285+
it('should return the same instance when exists, ignoring new configuration', () => {
286+
fixtureEl.innerHTML = '<div></div>'
287+
288+
const div = fixtureEl.querySelector('div')
289+
const calendar = new Calendar(div, {
290+
calendars: 2
291+
})
292+
293+
const calendar2 = Calendar.getOrCreateInstance(div, {
294+
calendars: 5
295+
})
296+
expect(calendar2).toEqual(calendar)
297+
// Original config is still used
298+
expect(calendar2._config.calendars).toEqual(2)
299+
})
300+
})
301+
})

js/tests/unit/range-slider.spec.js

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -153,37 +153,6 @@ describe('RangeSlider', () => {
153153
resolve()
154154
})
155155
})
156-
157-
it('should handle dragging interactions', () => {
158-
return new Promise(resolve => {
159-
fixtureEl.innerHTML = `
160-
<div data-coreui-toggle="range-slider" data-coreui-value="25,75"></div>
161-
`
162-
163-
const element = fixtureEl.querySelector('[data-coreui-toggle="range-slider"]')
164-
const rangeSlider = new RangeSlider(element)
165-
166-
element.addEventListener('change.coreui.range-slider', event => {
167-
const newValue = event.value
168-
expect(newValue).toEqual([50, 75])
169-
resolve()
170-
})
171-
172-
const container = element.querySelector('.range-slider-inputs-container')
173-
174-
// Simulate mousedown on the first input
175-
const mousedownEvent = createEvent('mousedown', { clientX: 100 })
176-
container.dispatchEvent(mousedownEvent)
177-
178-
// Simulate mousemove to update the slider
179-
const mousemoveEvent = createEvent('mousemove', { clientX: 200 })
180-
document.documentElement.dispatchEvent(mousemoveEvent)
181-
182-
// Simulate mouseup to end dragging
183-
const mouseupEvent = createEvent('mouseup')
184-
document.documentElement.dispatchEvent(mouseupEvent)
185-
})
186-
})
187156
})
188157

189158
describe('Configuration Options', () => {

0 commit comments

Comments
 (0)