Skip to content

Commit e11e42e

Browse files
authored
Merge pull request #6567 from rubyforgood/replace-calendar-library
replace calendar library
2 parents dfda22f + b585cbd commit e11e42e

File tree

7 files changed

+246
-25
lines changed

7 files changed

+246
-25
lines changed

app/assets/stylesheets/application.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// links to stylesheets inside node_modules
2-
@use "add2calendar/css/add2calendar";
32
@use "bootstrap/scss/bootstrap.scss" with (
43
$primary: #00447c
54
);

app/assets/stylesheets/pages/casa_cases.scss

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ body.casa_cases {
1919

2020
.a2cldr .a2cldr-btn {
2121
width: 190px;
22-
text-align: center
22+
text-align: center;
2323
}
2424

2525
.a2cldr-list {
@@ -30,6 +30,31 @@ body.casa_cases {
3030
display: inline-block;
3131
}
3232

33+
.court-date-row {
34+
display: flex;
35+
align-items: center;
36+
gap: 10px;
37+
38+
.court-date-link {
39+
flex: 1;
40+
}
41+
42+
.cal-btn {
43+
flex-shrink: 0;
44+
45+
.a2cldr {
46+
width: 120px !important;
47+
48+
.a2cldr-btn {
49+
width: 120px !important;
50+
font-size: 12px;
51+
padding: 4px 8px;
52+
text-align: center;
53+
}
54+
}
55+
}
56+
}
57+
3358
#cc-check {
3459
p {
3560
display: flex;
@@ -130,7 +155,6 @@ body.casa_cases-show {
130155
display: flex;
131156
flex-direction: column;
132157
row-gap: 1rem;
133-
134158
}
135159

136160
.modal-footer {
@@ -162,10 +186,8 @@ body.casa_cases-show {
162186
text-align: left;
163187
h3 {
164188
margin-block-end: .25rem;
165-
166189
}
167190
p {
168-
169191
}
170192
}
171193
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/* eslint-env jest */
2+
/**
3+
* @jest-environment jsdom
4+
*/
5+
6+
require('jest')
7+
8+
// Mock the add-to-calendar-button module
9+
jest.mock('add-to-calendar-button', () => ({}))
10+
11+
describe('add_to_calendar_button', () => {
12+
let mockJQuery
13+
14+
beforeEach(() => {
15+
// Clear the document body
16+
document.body.innerHTML = ''
17+
18+
// Mock jQuery
19+
mockJQuery = jest.fn((callback) => {
20+
if (typeof callback === 'function') {
21+
callback()
22+
}
23+
})
24+
global.$ = mockJQuery
25+
})
26+
27+
afterEach(() => {
28+
jest.resetModules()
29+
delete global.$
30+
})
31+
32+
const createCalendarButton = (dataset = {}) => {
33+
const div = document.createElement('div')
34+
div.className = 'cal-btn'
35+
Object.assign(div.dataset, {
36+
title: 'Court Hearing',
37+
start: '2025-11-15',
38+
end: '2025-11-15',
39+
tooltip: 'Add to calendar',
40+
...dataset
41+
})
42+
return div
43+
}
44+
45+
describe('createCalendarEvents', () => {
46+
test('creates add-to-calendar-button elements for all cal-btn divs', () => {
47+
// Setup
48+
const calBtn1 = createCalendarButton()
49+
const calBtn2 = createCalendarButton({
50+
title: 'Court Date 2',
51+
start: '2025-12-01',
52+
end: '2025-12-01'
53+
})
54+
document.body.appendChild(calBtn1)
55+
document.body.appendChild(calBtn2)
56+
57+
// Execute
58+
require('../src/add_to_calendar_button')
59+
60+
// Verify
61+
const buttons = document.querySelectorAll('add-to-calendar-button')
62+
expect(buttons.length).toBe(2)
63+
})
64+
65+
test('sets correct attributes on the calendar button', () => {
66+
// Setup
67+
const calBtn = createCalendarButton({
68+
title: 'Important Meeting',
69+
start: '2025-11-20',
70+
end: '2025-11-20',
71+
tooltip: 'Add this event'
72+
})
73+
document.body.appendChild(calBtn)
74+
75+
// Execute
76+
require('../src/add_to_calendar_button')
77+
78+
// Verify
79+
const button = document.querySelector('add-to-calendar-button')
80+
expect(button.getAttribute('name')).toBe('Important Meeting')
81+
expect(button.getAttribute('startDate')).toBe('2025-11-20')
82+
expect(button.getAttribute('endDate')).toBe('2025-11-20')
83+
expect(button.getAttribute('description')).toBe('Important Meeting')
84+
expect(button.getAttribute('options')).toBe("'Apple','Google','iCal','Microsoft365','Outlook.com','Yahoo'")
85+
expect(button.getAttribute('timeZone')).toBe('currentBrowser')
86+
expect(button.getAttribute('lightMode')).toBe('bodyScheme')
87+
expect(button.title).toBe('Add this event')
88+
})
89+
90+
test('clears existing content in cal-btn div', () => {
91+
// Setup
92+
const calBtn = createCalendarButton()
93+
calBtn.innerHTML = '<p>Old content</p>'
94+
document.body.appendChild(calBtn)
95+
96+
// Execute
97+
require('../src/add_to_calendar_button')
98+
99+
// Verify
100+
const oldContent = calBtn.querySelector('p')
101+
expect(oldContent).toBeNull()
102+
const button = calBtn.querySelector('add-to-calendar-button')
103+
expect(button).not.toBeNull()
104+
})
105+
106+
test('handles empty dataset gracefully', () => {
107+
// Setup
108+
const div = document.createElement('div')
109+
div.className = 'cal-btn'
110+
document.body.appendChild(div)
111+
112+
// Execute
113+
require('../src/add_to_calendar_button')
114+
115+
// Verify
116+
const button = document.querySelector('add-to-calendar-button')
117+
expect(button).not.toBeNull()
118+
expect(button.getAttribute('name')).toBe('undefined')
119+
expect(button.getAttribute('startDate')).toBe('undefined')
120+
expect(button.getAttribute('endDate')).toBe('undefined')
121+
})
122+
123+
test('does nothing when no cal-btn elements exist', () => {
124+
// Setup
125+
document.body.innerHTML = '<div class="other-content"></div>'
126+
127+
// Execute
128+
require('../src/add_to_calendar_button')
129+
130+
// Verify
131+
const buttons = document.querySelectorAll('add-to-calendar-button')
132+
expect(buttons.length).toBe(0)
133+
})
134+
135+
test('calls createCalendarEvents on DOM ready', () => {
136+
// Setup
137+
const calBtn = createCalendarButton()
138+
document.body.appendChild(calBtn)
139+
140+
// Execute
141+
require('../src/add_to_calendar_button')
142+
143+
// Verify jQuery was called
144+
expect(mockJQuery).toHaveBeenCalled()
145+
expect(mockJQuery).toHaveBeenCalledWith(expect.any(Function))
146+
147+
// Verify the calendar button was created
148+
const button = document.querySelector('add-to-calendar-button')
149+
expect(button).not.toBeNull()
150+
})
151+
152+
test('preserves cal-btn class on parent div', () => {
153+
// Setup
154+
const calBtn = createCalendarButton()
155+
document.body.appendChild(calBtn)
156+
157+
// Execute
158+
require('../src/add_to_calendar_button')
159+
160+
// Verify
161+
const divs = document.querySelectorAll('div.cal-btn')
162+
expect(divs.length).toBe(1)
163+
expect(divs[0].querySelector('add-to-calendar-button')).not.toBeNull()
164+
})
165+
})
166+
})

app/javascript/src/add_to_calendar_button.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
/* global $ */
22

3-
const Add2Calendar = require('add2calendar')
3+
import 'add-to-calendar-button'
44

55
function createCalendarEvents () {
66
const calendarButtons = document.querySelectorAll('div.cal-btn')
77

88
for (const calendarButton of calendarButtons) {
9-
const calendarEvent = new Add2Calendar({
10-
title: calendarButton.dataset.title,
11-
start: calendarButton.dataset.start,
12-
end: calendarButton.dataset.end,
13-
description: calendarButton.dataset.title,
14-
isAllDay: true
15-
})
9+
// Create the add-to-calendar-button web component
10+
const button = document.createElement('add-to-calendar-button')
1611

17-
calendarEvent.createWidget(`#${calendarButton.id}`)
18-
calendarButton.title = calendarButton.dataset.tooltip
12+
// Set attributes from data attributes
13+
button.setAttribute('name', calendarButton.dataset.title)
14+
button.setAttribute('startDate', calendarButton.dataset.start)
15+
button.setAttribute('endDate', calendarButton.dataset.end)
16+
button.setAttribute('description', calendarButton.dataset.title)
17+
button.setAttribute('options', "'Apple','Google','iCal','Microsoft365','Outlook.com','Yahoo'")
18+
button.setAttribute('timeZone', 'currentBrowser')
19+
button.setAttribute('lightMode', 'bodyScheme')
20+
21+
// Set tooltip
22+
button.title = calendarButton.dataset.tooltip
23+
24+
// Replace the div content with the button
25+
calendarButton.innerHTML = ''
26+
calendarButton.appendChild(button)
1927
}
2028
}
2129

app/views/casa_cases/_court_dates.html.erb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
<% court_dates = casa_case.court_dates.includes(:hearing_type).ordered_ascending.load %>
2-
<label>Court dates:</label>
2+
<label>Court dates: </label>
33
<% if court_dates.empty? %>
44
No Court Dates
55
<% else %>
66
<ul>
77
<% court_dates.each do |pcd| %>
88
<p>
9-
<%= link_to(pcd.decorate.court_date_info, casa_case_court_date_path(casa_case, pcd)) %>
9+
<div class="court-date-row">
10+
<%= link_to(pcd.decorate.court_date_info, casa_case_court_date_path(casa_case, pcd), class: "court-date-link") %>
11+
<%= render 'calendar_button',
12+
id: "court-date-#{pcd.id}",
13+
title: "Court Date #{pcd.casa_case.case_number}",
14+
date: {
15+
start: pcd.date.strftime("%Y-%m-%d"),
16+
end: pcd.date.strftime("%Y-%m-%d")
17+
},
18+
tooltip: "Add court date to calendar" %>
19+
</div>
1020
<% if report = pcd.latest_associated_report %>
1121
<%= link_to(rails_blob_path(report, disposition: 'attachment')) do %>
1222
(Attached Report)

package-lock.json

Lines changed: 23 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"@rails/activestorage": "^8.1.0",
2323
"@rails/ujs": "^7.1.502",
2424
"@stimulus-components/rails-nested-form": "^5.0.0",
25-
"add2calendar": "^1.1.8",
25+
"add-to-calendar-button": "^2.12.12",
2626
"bootstrap": "5.3.6",
2727
"bootstrap-datepicker": "^1.10.1",
2828
"bootstrap-scss": "^5.3.4",

0 commit comments

Comments
 (0)