Skip to content

Commit 158552b

Browse files
committed
fix: show offer button when event has reg. url without enrolment times
TH-1460. The offer button for event should be rendered when there is a registration URL given without enrolment times.
1 parent 054d3ac commit 158552b

2 files changed

Lines changed: 104 additions & 40 deletions

File tree

packages/components/src/components/eventHero/EventHero.tsx

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,10 @@ export type EventHeroProps = {
5959
* remaining capacity for attendees, or if a waiting list exists and it has
6060
* available spots.
6161
*
62-
* @param {EventHeroProps['event']} event - The event object containing
63-
* details about attendee and waiting list capacities.
64-
* @param {boolean} enrolmentFormAvailableBeforeHand - handle enrolment as open when it has not started yet.
6562
* @returns {boolean} Returns `true` if enrolment is open
6663
* (either for direct attendance or the waiting list), otherwise `false`.
6764
*/
68-
export const useIsEnrolmentOpen = (
65+
export const getIsEnrolmentOpen = (
6966
event?: EventHeroProps['event'],
7067
superEvent?: EventHeroProps['superEvent'],
7168
enrolmentFormAvailableBeforeHand = false
@@ -188,6 +185,40 @@ const EventPriceInfo: React.FC<Pick<EventHeroProps, 'event'>> = ({ event }) => {
188185
);
189186
};
190187

188+
/**
189+
* Determines whether the enrolment or registration button should be displayed.
190+
*
191+
* The button is shown if:
192+
* 1. An **external registration URL** is provided, AND there is **no enrolment start time** specified
193+
* (e.g., direct ticket link).
194+
* OR
195+
* 2. An **external registration URL** is provided, AND **enrolment is currently open**.
196+
*
197+
* @param {object} params - The event enrolment and registration details.
198+
* @returns {boolean} True if the enrolment/registration button should be visible, otherwise false.
199+
*/
200+
function getShouldShowEnrolmentButton({
201+
externalRegistrationUrl,
202+
isEnrolmentOpen,
203+
enrolmentStartTime,
204+
}: {
205+
externalRegistrationUrl?: string | null;
206+
isEnrolmentOpen: boolean;
207+
enrolmentStartTime?: string | null;
208+
}): boolean {
209+
// NOTE: Shows the button for direct registration links without an explicit enrolment period.
210+
const hasRegistrationUrlWithoutEnrolmentTime =
211+
externalRegistrationUrl && !enrolmentStartTime;
212+
// NOTE: Shows the button when registration is open and a URL is provided.
213+
const hasRegistrationUrlAndEnrolmentIsOpen =
214+
externalRegistrationUrl && isEnrolmentOpen;
215+
216+
return Boolean(
217+
hasRegistrationUrlWithoutEnrolmentTime ||
218+
hasRegistrationUrlAndEnrolmentIsOpen
219+
);
220+
}
221+
191222
export const OfferButton: React.FC<
192223
Pick<EventHeroProps, 'event' | 'superEvent'>
193224
> = ({ event, superEvent }) => {
@@ -199,12 +230,27 @@ export const OfferButton: React.FC<
199230

200231
// NOTE: Should something else than offerInfoURl be used here?
201232
// E.g. event?.registration?.signupUrl? or registration link from from event?.externalLinks?.
202-
const { offerInfoUrl: externalRegistrationUrl } = getEventFields(
233+
const { offerInfoUrl, registrationUrl, enrolmentStartTime } = getEventFields(
203234
event,
204235
locale
205236
);
237+
const externalRegistrationUrl = offerInfoUrl || registrationUrl;
206238

207-
const shouldShowEnrolmentButton = useIsEnrolmentOpen(event, superEvent, true);
239+
const isEnrolmentOpen = getIsEnrolmentOpen(event, superEvent, true);
240+
241+
if (superEvent?.status === 'pending') {
242+
return <SkeletonLoader />;
243+
}
244+
245+
const shouldShowEnrolmentButton = getShouldShowEnrolmentButton({
246+
externalRegistrationUrl,
247+
isEnrolmentOpen,
248+
enrolmentStartTime,
249+
});
250+
251+
if (!externalRegistrationUrl || !shouldShowEnrolmentButton) {
252+
return null;
253+
}
208254

209255
const buttonText = getEventHeroButtonText(
210256
event,
@@ -219,14 +265,6 @@ export const OfferButton: React.FC<
219265
superEvent?.data ?? undefined
220266
);
221267

222-
if (superEvent?.status === 'pending') {
223-
return <SkeletonLoader />;
224-
}
225-
226-
if (!externalRegistrationUrl || !shouldShowEnrolmentButton) {
227-
return null;
228-
}
229-
230268
return (
231269
<div className={styles.registrationButtonWrapper}>
232270
<Button

packages/components/src/components/eventHero/__tests__/enrolment.test.tsx

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render, screen, renderHook } from '@testing-library/react';
1+
import { render, screen } from '@testing-library/react';
22

33
import {
44
endOfTomorrow,
@@ -15,7 +15,7 @@ import * as GetEventEnrolmentStatus from '../../../utils/getEventEnrolmentStatus
1515
import EventHero, {
1616
EnrolmentStatusInfo,
1717
OfferButton,
18-
useIsEnrolmentOpen,
18+
getIsEnrolmentOpen,
1919
} from '../EventHero';
2020

2121
vi.mock('react-helsinki-headless-cms', async (importOriginal) => ({
@@ -89,22 +89,20 @@ afterAll(() => {
8989
vi.restoreAllMocks();
9090
});
9191

92-
describe('useIsEnrolmentOpen', () => {
92+
describe('getIsEnrolmentOpen', () => {
9393
it('should return false if event is not defined', () => {
94-
const { result } = renderHook(() => useIsEnrolmentOpen(undefined));
95-
expect(result.current).toBe(false);
94+
const result = getIsEnrolmentOpen(undefined);
95+
expect(result).toBe(false);
9696
});
9797

9898
it('should return false if registration is not defined', () => {
99-
const { result } = renderHook(() =>
100-
useIsEnrolmentOpen({
101-
...baseEvent,
102-
registration: undefined,
103-
enrolmentEndTime: undefined,
104-
enrolmentStartTime: undefined,
105-
})
106-
);
107-
expect(result.current).toBe(false);
99+
const result = getIsEnrolmentOpen({
100+
...baseEvent,
101+
registration: undefined,
102+
enrolmentEndTime: undefined,
103+
enrolmentStartTime: undefined,
104+
});
105+
expect(result).toBe(false);
108106
});
109107

110108
it('should return true if remainingAttendeeCapacity is greater than 0', () => {
@@ -116,8 +114,8 @@ describe('useIsEnrolmentOpen', () => {
116114
remainingAttendeeCapacity: 10,
117115
},
118116
};
119-
const { result } = renderHook(() => useIsEnrolmentOpen(event));
120-
expect(result.current).toBe(true);
117+
const result = getIsEnrolmentOpen(event);
118+
expect(result).toBe(true);
121119
});
122120

123121
it('should return true event with waiting list capacity and remainingWaitingListCapacity is greater than 0', () => {
@@ -129,8 +127,8 @@ describe('useIsEnrolmentOpen', () => {
129127
remainingWaitingListCapacity: 5,
130128
},
131129
};
132-
const { result } = renderHook(() => useIsEnrolmentOpen(event));
133-
expect(result.current).toBe(true);
130+
const result = getIsEnrolmentOpen(event);
131+
expect(result).toBe(true);
134132
});
135133

136134
it('should return true if event is queueable', () => {
@@ -145,8 +143,8 @@ describe('useIsEnrolmentOpen', () => {
145143
},
146144
};
147145

148-
const { result } = renderHook(() => useIsEnrolmentOpen(event));
149-
expect(result.current).toBe(true);
146+
const result = getIsEnrolmentOpen(event);
147+
expect(result).toBe(true);
150148
});
151149

152150
it('should return false if both remaining capacities are 0', () => {
@@ -158,8 +156,8 @@ describe('useIsEnrolmentOpen', () => {
158156
remainingWaitingListCapacity: 0,
159157
},
160158
};
161-
const { result } = renderHook(() => useIsEnrolmentOpen(event));
162-
expect(result.current).toBe(false);
159+
const result = getIsEnrolmentOpen(event);
160+
expect(result).toBe(false);
163161
});
164162

165163
it.each([
@@ -176,10 +174,13 @@ describe('useIsEnrolmentOpen', () => {
176174
remainingAttendeeCapacity: 10,
177175
},
178176
};
179-
const { result } = renderHook(() =>
180-
useIsEnrolmentOpen(event, undefined, enrolmentFormAvailableBeforeHand)
177+
const result = getIsEnrolmentOpen(
178+
event,
179+
undefined,
180+
enrolmentFormAvailableBeforeHand
181181
);
182-
expect(result.current).toBe(expectedResult);
182+
183+
expect(result).toBe(expectedResult);
183184
}
184185
);
185186

@@ -245,7 +246,31 @@ describe('OfferButton', () => {
245246
).toBeInTheDocument();
246247
});
247248

248-
it('should not render if offerInfoUrl exists but enrolment is not open', () => {
249+
it('should render when offerInfoUrl exists and enrolment time is not given', () => {
250+
const eventWithClosedEnrolment: EventFields = {
251+
...baseEvent,
252+
offers: [
253+
{ isFree: false, price: null, infoUrl: { fi: 'https://hel.fi' } },
254+
],
255+
registration: {
256+
...baseEvent.registration,
257+
remainingAttendeeCapacity: 0,
258+
waitingListCapacity: 0,
259+
remainingWaitingListCapacity: 0,
260+
},
261+
enrolmentStartTime: undefined,
262+
};
263+
264+
const { container } = render(
265+
<OfferButton event={eventWithClosedEnrolment} />
266+
);
267+
expect(container).not.toBeEmptyDOMElement();
268+
expect(
269+
screen.getByRole('button', { name: 'hero.ariaLabelEnrol' })
270+
).toBeInTheDocument();
271+
});
272+
273+
it('should not render if offerInfoUrl exists, enrolment time is given, but enrolment is not open', () => {
249274
const eventWithClosedEnrolment: EventFields = {
250275
...baseEvent,
251276
offers: [
@@ -257,6 +282,7 @@ describe('OfferButton', () => {
257282
waitingListCapacity: 0,
258283
remainingWaitingListCapacity: 0,
259284
},
285+
enrolmentStartTime: startOfYesterday().toISOString(),
260286
};
261287

262288
const { container } = render(

0 commit comments

Comments
 (0)