From 46c36c459b9cf93a19059ddd2481bd6af0d43a6a Mon Sep 17 00:00:00 2001 From: Chris Howarth Date: Fri, 25 Apr 2025 23:48:14 +0100 Subject: [PATCH 1/5] feat: added endDate to return from parseEvent. Handled times which are not in UTC time format --- api/events.js | 91 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/api/events.js b/api/events.js index 532a46f10..9dec0b6b3 100644 --- a/api/events.js +++ b/api/events.js @@ -14,7 +14,7 @@ dayjs.extend(timezone) const TBD = 'TBD' export const getEvents = (year) => { - const events_path = year ? `content/${year}/events` : 'content/events'; + const events_path = year ? `content/${year}/events` : 'content/events' const eventFiles = fs.readdirSync(events_path) const events = eventFiles @@ -30,13 +30,15 @@ export const getEvents = (year) => { } export const getEventBySlug = (slug, year) => { - const event = getDataFromMD(year?`content/${year}/events/${slug}.md`:`content/events/${slug}.md`) - const link = year? `/${year}/schedule/${slug}` : `/schedule/${slug}`; + const event = getDataFromMD( + year ? `content/${year}/events/${slug}.md` : `content/events/${slug}.md`, + ) + const link = year ? `/${year}/schedule/${slug}` : `/schedule/${slug}` return { slug, ...event, - link + link, } } @@ -59,6 +61,7 @@ export const parseEvent = (event) => { event.date, event.UTCStartTime, event.UTCEndTime, + event.endDate, ) return { @@ -68,9 +71,14 @@ export const parseEvent = (event) => { } // TODO: refactor -const formatEventDateTime = (date = dayjs.utc(), startTime, endTime) => { +const formatEventDateTime = ( + startDate = dayjs.utc(), + startTime, + endTime, + endDate, +) => { // Date - const [month, day] = date.split('/') + const [month, day] = startDate.split('/') if (isNaN(month) || isNaN(day)) { throw new TypeError('date must be in mm/dd format (e.g. 06/12).') @@ -80,55 +88,71 @@ const formatEventDateTime = (date = dayjs.utc(), startTime, endTime) => { .utc() .date(day) .month(month - 1) + const formattedDate = UTCDate.format('MMM D') + let formattedEndDate = null + + if (endDate && endDate !== startDate) { + const [endMonth, endDay] = endDate.split('/') + + if (isNaN(endMonth) || isNaN(endDay)) { + throw new TypeError('date must be in mm/dd format (e.g. 06/12).') + } + + const endUTCDate = dayjs + .utc() + .date(endDay) + .month(endMonth - 1) + + formattedEndDate = endUTCDate.format('MMM D') + } + // Start time - let formattedStartTime = {} + let formattedStartTime = { + utc: TBD, + pt: TBD, + } - if (startTime && startTime !== TBD) { + if (startTime) { const [startHour, startMinute] = startTime.split(':') const UTCStartTime = UTCDate.hour(startHour).minute(startMinute) - if (isNaN(UTCStartTime)) { - throw new TypeError( - 'UTCStartTime must be in hh:mm format (e.g. 12:30) or be "TDB".', - ) - } - - const PTStartTime = UTCStartTime.tz('America/Los_Angeles') + if (!isNaN(UTCStartTime)) { + const PTStartTime = UTCStartTime.tz('America/Los_Angeles') - const formattedUTCStartTime = UTCStartTime.format('HH:mm a') - const formattedPTStartTime = PTStartTime.format('HH:mm a') + const formattedUTCStartTime = UTCStartTime.format('HH:mm a') + const formattedPTStartTime = PTStartTime.format('HH:mm a') - formattedStartTime = { - utc: formattedUTCStartTime, - pt: formattedPTStartTime, + formattedStartTime = { + utc: formattedUTCStartTime, + pt: formattedPTStartTime, + } } } // End time - let formattedEndTime = {} + let formattedEndTime = { + utc: TBD, + pt: TBD, + } if (endTime && endTime !== TBD) { const [endHour, endMinute] = endTime.split(':') const UTCEndTime = UTCDate.hour(endHour).minute(endMinute) - if (isNaN(UTCEndTime)) { - throw new TypeError( - 'UTCEndTime must be in hh:mm format (e.g. 12:30) or be "TDB".', - ) - } - - const PTEndTime = UTCEndTime.tz('America/Los_Angeles') + if (!isNaN(UTCEndTime)) { + const PTEndTime = UTCEndTime.tz('America/Los_Angeles') - const formattedUTCEndTime = UTCEndTime.format('HH:mm a') - const formattedPTEndTime = PTEndTime.format('HH:mm a') + const formattedUTCEndTime = UTCEndTime.format('HH:mm a') + const formattedPTEndTime = PTEndTime.format('HH:mm a') - formattedEndTime = { - utc: formattedUTCEndTime, - pt: formattedPTEndTime, + formattedEndTime = { + utc: formattedUTCEndTime, + pt: formattedPTEndTime, + } } } @@ -136,5 +160,6 @@ const formatEventDateTime = (date = dayjs.utc(), startTime, endTime) => { date: formattedDate, startTime: formattedStartTime, endTime: formattedEndTime, + endDate: formattedEndDate, } } From 0d9e43305cd269609442fca301042b0fcf6dbb06 Mon Sep 17 00:00:00 2001 From: Chris Howarth Date: Fri, 25 Apr 2025 23:48:54 +0100 Subject: [PATCH 2/5] Update event template to accept an end date for multi-day events (not required) --- .github/ISSUE_TEMPLATE/add-to-calendar.yml | 44 +++++++++++++--------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/add-to-calendar.yml b/.github/ISSUE_TEMPLATE/add-to-calendar.yml index 955e91e9d..faf59e7dd 100644 --- a/.github/ISSUE_TEMPLATE/add-to-calendar.yml +++ b/.github/ISSUE_TEMPLATE/add-to-calendar.yml @@ -1,13 +1,13 @@ name: 💁🏽‍♀️ Add an event for maintainers to the calendar 🎙 description: Use this form to add an event for maintainers or highlighting maintainers to the Maintainer Month calendar. -title: "EVENT_NAME" +title: 'EVENT_NAME' body: - type: markdown attributes: value: | :tada: Thanks for letting us know about an upcoming event! :tada: - + Use this form to add an event for maintainers or highlighting maintainers to the Maintainer Month calendar. Your request will be reviewed within 72 hours. - id: eventname @@ -16,20 +16,28 @@ body: label: Event Name validations: required: true - + - id: date type: input attributes: label: Event Date - placeholder: "MM/DD" + placeholder: 'MM/DD' validations: required: true + - id: endDate + type: input + attributes: + label: Event End Date + placeholder: 'MM/DD' + validations: + required: false + - id: UTCStartTime type: input attributes: label: Start time in UTC - placeholder: "14:00" + placeholder: '14:00' validations: required: true @@ -37,7 +45,7 @@ body: type: input attributes: label: End time in UTC - placeholder: "15:00" + placeholder: '15:00' validations: required: true @@ -45,7 +53,7 @@ body: type: dropdown attributes: label: Event Type - description: "Type of event" + description: 'Type of event' options: - conference - podcast @@ -61,7 +69,7 @@ body: type: input attributes: label: Event Language - description: "Primary language of the event (e.g., English, Spanish, Mandarin)" + description: 'Primary language of the event (e.g., English, Spanish, Mandarin)' validations: required: true @@ -69,7 +77,7 @@ body: type: input attributes: label: Event Location - description: "Primary location of the event - either Virtual or a City + Country" + description: 'Primary location of the event - either Virtual or a City + Country' validations: required: true @@ -85,25 +93,25 @@ body: type: input attributes: label: Event URL - description: "Web URL of event" - placeholder: "https://" + description: 'Web URL of event' + placeholder: 'https://' - id: eventdescription type: textarea attributes: label: Event Description - description: "What is this event about?" - + description: 'What is this event about?' + - id: expectedparticipants type: input attributes: label: Participants - description: "Expected number of participants" - placeholder: "###" - + description: 'Expected number of participants' + placeholder: '###' + - id: expectedmaintainers type: input attributes: label: Maintainers - description: "Expected number of maintainers participating" - placeholder: "###" + description: 'Expected number of maintainers participating' + placeholder: '###' From 3bc26dcd87762e5e92ea2135316851520fc55fac Mon Sep 17 00:00:00 2001 From: Chris Howarth Date: Fri, 25 Apr 2025 23:50:10 +0100 Subject: [PATCH 3/5] feat: Update label passed to Chip for multi day events --- components/events-list/EventsList.jsx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/components/events-list/EventsList.jsx b/components/events-list/EventsList.jsx index 0176023e6..d62846c76 100644 --- a/components/events-list/EventsList.jsx +++ b/components/events-list/EventsList.jsx @@ -12,16 +12,20 @@ import DateTimeChip from '../date-time-chip/DateTimeChip' import EventTypeChip from '../event-type-chip/EventTypeChip' import PlayLink from '../play-link/PlayLink' import Chip from '../chip/Chip' - const EventsList = ({ events }) => { + const dateLabel = (event) => + `${event.formattedDate.date} to ${event.formattedDate.endDate}` + return (

{getLiteral('schedule:title')}

-

{getLiteral('schedule:description')}

- + {getLiteral('schedule:description')} +

+ @@ -39,7 +43,11 @@ const EventsList = ({ events }) => { })} >
- + {event.formattedDate.endDate ? ( + + ) : ( + + )}
@@ -68,7 +76,7 @@ const EventsList = ({ events }) => {
-

{ ))}

- ); -}; + ) +} -export default EventsList; +export default EventsList From ffd293621d146f37a2f390e89c183e886ed42da1 Mon Sep 17 00:00:00 2001 From: Chris Howarth Date: Fri, 25 Apr 2025 23:50:26 +0100 Subject: [PATCH 4/5] feat: add tests for event end dates and create test data for Cookie Cutter and Typewind events --- tests/events.test.js | 53 +++++++++++++++++++++- tests/testData/2025-05-02-Cookie-Cutter.js | 25 ++++++++++ tests/testData/2025-05-02-typewind.js | 24 ++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/testData/2025-05-02-Cookie-Cutter.js create mode 100644 tests/testData/2025-05-02-typewind.js diff --git a/tests/events.test.js b/tests/events.test.js index 44024c550..62b40cc98 100644 --- a/tests/events.test.js +++ b/tests/events.test.js @@ -1,6 +1,8 @@ import fs from 'fs' import { getEventBySlug, parseEvent } from '../api/events' +import testTypewindEvent from './testData/2025-05-02-typewind' +import testCookieCutterEvent from './testData/2025-05-02-Cookie-Cutter' describe('Event files', () => { test('All event files have ".md" extension', () => { @@ -24,4 +26,53 @@ describe('Event files', () => { expect(() => parseEvent(event)).not.toThrowError() }) }) -}) \ No newline at end of file + + test('An event has an end date and does not throw an error', () => { + const eventFiles = fs.readdirSync('content/events') + + const events = eventFiles.map((fileName) => { + const slug = fileName.replace('.md', '') + const event = getEventBySlug(slug) + return event + }) + + events.forEach((event) => { + expect(() => parseEvent(event)).not.toThrowError() + }) + }) + + test('An event returns an endDate', () => { + const event = testCookieCutterEvent + const parsedEvent = parseEvent(event) + expect(parsedEvent).toEqual(expect.objectContaining({ endDate: '07/02' })) + }) + + test('An event has an invalid start time and does not throw an error', () => { + const eventFiles = fs.readdirSync('content/events') + + const events = eventFiles.map((fileName) => { + const slug = fileName.replace('.md', '') + const event = getEventBySlug(slug) + return event + }) + + events.forEach((event) => { + expect(() => parseEvent(event)).not.toThrowError() + }) + }) + + test('An event with an invalid start time retunrs TBD', () => { + const event = testTypewindEvent + const parsedEvent = parseEvent(event) + expect(parsedEvent).toEqual( + expect.objectContaining({ + formattedDate: expect.objectContaining({ + startTime: { + pt: 'TBD', + utc: 'TBD', + }, + }), + }), + ) + }) +}) diff --git a/tests/testData/2025-05-02-Cookie-Cutter.js b/tests/testData/2025-05-02-Cookie-Cutter.js new file mode 100644 index 000000000..187916198 --- /dev/null +++ b/tests/testData/2025-05-02-Cookie-Cutter.js @@ -0,0 +1,25 @@ +const cookieCutterEvent = { + slug: '2025-05-02-Cookie-Cutter', + title: 'Open Source Friday Brasil: Cookie Cutter and FastAPI', + metaTitle: 'Open Source Friday Brasil: Cookie Cutter and FastAPI', + metaDesc: + 'Join Arthur Henrique to discuss Cookiecutter template for FastAPI projects using Machine Learning, uv, GitHub Actions, and Pytests.', + date: '05/02', + endDate: '07/02', + UTCStartTime: '16:00', + UTCEndTime: '17:00', + type: 'stream', + location: 'Virtual', + language: 'Portuguese', + userName: 'GitHub', + userLink: 'https://www.youtube.com/github', + linkUrl: 'https://www.youtube.com/github', + content: + '\n' + + 'Join [Arthur Henrique](https://github.com/arthurhenrique) to discuss Cookiecutter template for FastAPI projects using Machine Learning, uv, GitHub Actions, and Pytests.\n' + + '\n' + + "Open Source Fridays Brasil stream weekly on GitHub's [Twitch](https://www.twitch.tv/githubbrasil), [YouTube](https://github.com/youtube), and [LinkedIn](https://www.linkedin.com/company/githubbrasil).\n", + link: '/schedule/2025-05-02-Cookie-Cutter', +} + +export default cookieCutterEvent diff --git a/tests/testData/2025-05-02-typewind.js b/tests/testData/2025-05-02-typewind.js new file mode 100644 index 000000000..b36ef6581 --- /dev/null +++ b/tests/testData/2025-05-02-typewind.js @@ -0,0 +1,24 @@ +const typewindEvent = { + slug: '2025-05-02-typewind', + title: 'Open Source Friday: Typewind', + metaTitle: 'Open Source Friday: Typewind', + metaDesc: + 'Chat with Mokshit Jain about Typewind, a build tool focused on improving web performance.', + date: '05/02', + UTCStartTime: 'G:00', + UTCEndTime: '7:00', + type: 'stream', + location: 'Virtual', + language: 'English', + userName: 'GitHub', + userLink: 'https://www.twitch.tv/github/schedule', + linkUrl: 'https://www.twitch.tv/github/schedule', + content: + '\n' + + 'Chat with [Mokshit Jain](https://github.com/Mokshit06) about [Typewind](https://github.com/Mokshit06/typewind), a build tool focused on improving web performance. Explore the journey of creating Typewind and contributions to the open-source build-tools space.\n' + + '\n' + + "[Open Source Fridays](https://www.youtube.com/playlist?list=PL0lo9MOBetEFmtstItnKlhJJVmMghxc0P) stream weekly on GitHub's [Twitch](https://www.twitch.tv/github), [YouTube](https://www.youtube.com/github), and [LinkedIn](https://www.linkedin.com/company/github).\n", + link: '/schedule/2025-05-02-typewind', +} + +export default typewindEvent From 5f255d8f5f1a8e5fe4730d55c97dee4c75fb355c Mon Sep 17 00:00:00 2001 From: Chris Howarth <49653208+cthow83@users.noreply.github.com> Date: Sat, 26 Apr 2025 00:05:45 +0100 Subject: [PATCH 5/5] Update tests/events.test.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/events.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/events.test.js b/tests/events.test.js index 62b40cc98..5584ecf0b 100644 --- a/tests/events.test.js +++ b/tests/events.test.js @@ -61,7 +61,7 @@ describe('Event files', () => { }) }) - test('An event with an invalid start time retunrs TBD', () => { + test('An event with an invalid start time returns TBD', () => { const event = testTypewindEvent const parsedEvent = parseEvent(event) expect(parsedEvent).toEqual(