Skip to content

Commit 828b780

Browse files
authored
Merge pull request #2834 from bcgov/DEP-259-fix-more-engagements-filter
Fix filters for "more engagements" authoring section
2 parents 72e54a2 + a138f4a commit 828b780

3 files changed

Lines changed: 200 additions & 3 deletions

File tree

CHANGELOG.MD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## April 20, 2026
2+
3+
- **Bugfix** Use EngagementStatus instead of SubmissionStatus [🎟️ DEP-259](https://citz-gdx.atlassian.net/browse/DEP-259)
4+
- Updated the AuthoringMore component to use the correct EngagementStatus enum values when filtering suggested engagements. This fixes a bug where engagements in the Upcoming status were not being suggested as possible related engagements.
5+
- Added a unit test to ensure this behavior is correct and to prevent regressions in the future.
6+
17
## April 16, 2026
28

39
- **Feature** Always show InternalHeader when logged in, add admin/public view switcher [🎟️ DEP-257](https://citz-gdx.atlassian.net/browse/DEP-257)

web/src/components/engagement/admin/create/authoring/AuthoringMore.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { defaultValuesObject, EngagementUpdateData } from './AuthoringContext';
77
import { AuthoringTemplateOutletContext } from './types';
88
import UnsavedWorkConfirmation from 'components/common/Navigation/UnsavedWorkConfirmation';
99
import { AuthoringFormContainer, AuthoringFormSection } from './AuthoringFormLayout';
10-
import { SubmissionStatus } from 'constants/engagementStatus';
10+
import { EngagementStatus } from 'constants/engagementStatus';
1111
import { AuthoringLoaderData } from './authoringLoader';
1212
import { Engagement } from 'models/engagement';
1313
import { Page } from 'services/type';
@@ -76,8 +76,7 @@ const AuthoringMore = () => {
7676
eng.tenant_id === tenantId && // Must be engagements from same tenant
7777
eng.id !== engagementId && // Can't suggest the current engagement
7878
// Only suggest open or closed engagements, not drafts or unpublished
79-
(eng.submission_status === SubmissionStatus.Open ||
80-
eng.submission_status === SubmissionStatus.Closed)
79+
(eng.status_id === EngagementStatus.Published || eng.status_id === EngagementStatus.Closed)
8180
) {
8281
filteredOptions.push({
8382
label: eng?.name || '',
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import React, { ReactNode } from 'react';
2+
import { render, screen, waitFor } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
import AuthoringMore from '../../../../src/components/engagement/admin/create/authoring/AuthoringMore';
5+
import { EngagementStatus, SubmissionStatus } from '../../../../src/constants/engagementStatus';
6+
import { createDefaultEngagement, Engagement } from '../../../../src/models/engagement';
7+
import { SuggestedEngagement } from '../../../../src/models/suggestedEngagement';
8+
import { Page } from '../../../../src/services/type';
9+
10+
const mockUseLoaderData = jest.fn();
11+
const mockUseOutletContext = jest.fn();
12+
const mockSetValue = jest.fn();
13+
const mockGetValues = jest.fn(() => ({}));
14+
const mockReset = jest.fn();
15+
const mockSetDefaultValues = jest.fn();
16+
const mockFieldOnChange = jest.fn();
17+
18+
jest.mock('react-router', () => ({
19+
...jest.requireActual('react-router'),
20+
useLoaderData: () => mockUseLoaderData(),
21+
useOutletContext: () => mockUseOutletContext(),
22+
}));
23+
24+
jest.mock('react-hook-form', () => ({
25+
...jest.requireActual('react-hook-form'),
26+
Controller: ({ name, render }: { name: string; render: (props: unknown) => ReactNode }) =>
27+
render({
28+
field: {
29+
name,
30+
value: -1,
31+
onChange: mockFieldOnChange,
32+
},
33+
}),
34+
useFormContext: () => ({
35+
setValue: mockSetValue,
36+
getValues: mockGetValues,
37+
reset: mockReset,
38+
control: {},
39+
formState: {
40+
errors: {},
41+
isDirty: false,
42+
isSubmitting: false,
43+
},
44+
}),
45+
}));
46+
47+
jest.mock('components/common/Input', () => ({
48+
FormField: ({ children }: { children: ReactNode }) => <div>{children}</div>,
49+
TextField: ({ id, value, onChange }: { id: string; value?: string; onChange?: (value: string) => void }) => (
50+
<input id={id} value={value ?? ''} onChange={(event) => onChange?.(event.target.value)} />
51+
),
52+
Select: ({
53+
id,
54+
options,
55+
value,
56+
onChange,
57+
}: {
58+
id: string;
59+
options: { label: string; value: number }[];
60+
value: number;
61+
onChange: (event: { target: { value: string } }) => void;
62+
}) => (
63+
<select
64+
aria-label={id}
65+
data-testid={id}
66+
value={value}
67+
onChange={(event) => onChange({ target: { value: event.target.value } })}
68+
>
69+
{options.map((option) => (
70+
<option key={`${id}-${option.value}`} value={option.value}>
71+
{option.label}
72+
</option>
73+
))}
74+
</select>
75+
),
76+
}));
77+
78+
jest.mock('../../../../src/components/engagement/admin/create/authoring/AuthoringFormLayout', () => ({
79+
AuthoringFormContainer: ({ children }: { children: ReactNode }) => <div>{children}</div>,
80+
AuthoringFormSection: ({ children }: { children: ReactNode }) => <section>{children}</section>,
81+
}));
82+
83+
jest.mock('components/common/Navigation/UnsavedWorkConfirmation', () => ({
84+
__esModule: true,
85+
default: () => null,
86+
}));
87+
88+
jest.mock('../../../../src/components/engagement/admin/create/authoring/AuthoringContext', () => ({
89+
defaultValuesObject: {},
90+
}));
91+
92+
const buildEngagement = (
93+
overrides: Partial<Engagement> & Pick<Engagement, 'id' | 'name' | 'status_id' | 'tenant_id'>,
94+
): Engagement => ({
95+
...createDefaultEngagement(),
96+
...overrides,
97+
});
98+
99+
describe('AuthoringMore', () => {
100+
beforeEach(() => {
101+
jest.clearAllMocks();
102+
103+
const currentEngagement = buildEngagement({
104+
id: 42,
105+
name: 'Current Engagement',
106+
status_id: EngagementStatus.Published,
107+
submission_status: SubmissionStatus.Open,
108+
tenant_id: 7,
109+
});
110+
111+
const engagementList: Page<Engagement> = {
112+
items: [
113+
currentEngagement,
114+
buildEngagement({
115+
id: 1,
116+
name: 'Upcoming Engagement',
117+
status_id: EngagementStatus.Published,
118+
submission_status: SubmissionStatus.Upcoming,
119+
tenant_id: 7,
120+
}),
121+
buildEngagement({
122+
id: 2,
123+
name: 'Open Engagement',
124+
status_id: EngagementStatus.Published,
125+
submission_status: SubmissionStatus.Open,
126+
tenant_id: 7,
127+
}),
128+
buildEngagement({
129+
id: 3,
130+
name: 'Closed Engagement',
131+
status_id: EngagementStatus.Closed,
132+
submission_status: SubmissionStatus.Closed,
133+
tenant_id: 7,
134+
}),
135+
buildEngagement({
136+
id: 4,
137+
name: 'Draft Engagement',
138+
status_id: EngagementStatus.Draft,
139+
submission_status: SubmissionStatus.Upcoming,
140+
tenant_id: 7,
141+
}),
142+
buildEngagement({
143+
id: 5,
144+
name: 'Scheduled Engagement',
145+
status_id: EngagementStatus.Scheduled,
146+
submission_status: SubmissionStatus.Upcoming,
147+
tenant_id: 7,
148+
}),
149+
buildEngagement({
150+
id: 6,
151+
name: 'Unpublished Engagement',
152+
status_id: EngagementStatus.Unpublished,
153+
submission_status: SubmissionStatus.Unpublished,
154+
tenant_id: 7,
155+
}),
156+
],
157+
total: 7,
158+
};
159+
160+
const suggestions: SuggestedEngagement[] = [];
161+
162+
mockUseOutletContext.mockReturnValue({
163+
setDefaultValues: mockSetDefaultValues,
164+
fetcher: { data: null },
165+
pageName: 'more',
166+
engagement: currentEngagement,
167+
});
168+
169+
mockUseLoaderData.mockReturnValue({
170+
engagement: Promise.resolve(currentEngagement),
171+
engagementList: Promise.resolve(engagementList),
172+
suggestions: Promise.resolve(suggestions),
173+
});
174+
});
175+
176+
test('shows upcoming, open, and closed engagements while excluding draft, scheduled, and unpublished', async () => {
177+
render(<AuthoringMore />);
178+
179+
await waitFor(() => {
180+
expect(screen.getAllByText('Upcoming Engagement').length).toBeGreaterThan(0);
181+
});
182+
183+
expect(screen.getAllByText('Upcoming Engagement').length).toBeGreaterThan(0);
184+
expect(screen.getAllByText('Open Engagement').length).toBeGreaterThan(0);
185+
expect(screen.getAllByText('Closed Engagement').length).toBeGreaterThan(0);
186+
187+
expect(screen.queryAllByText('Draft Engagement')).toHaveLength(0);
188+
expect(screen.queryAllByText('Scheduled Engagement')).toHaveLength(0);
189+
expect(screen.queryAllByText('Unpublished Engagement')).toHaveLength(0);
190+
expect(screen.queryAllByText('Current Engagement')).toHaveLength(0);
191+
});
192+
});

0 commit comments

Comments
 (0)