Skip to content

Commit 85e2d7f

Browse files
committed
Merge branch 'master' into kristkvch/PDSC-485-service-items-sidebar
2 parents 7984163 + 4cf574e commit 85e2d7f

File tree

16 files changed

+280
-61
lines changed

16 files changed

+280
-61
lines changed

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
## [4.30.1](https://github.com/zendesk/copenhagen_theme/compare/v4.30.0...v4.30.1) (2026-02-24)
2+
3+
4+
### Bug Fixes
5+
6+
* hide category lookup field from submit request form ([4d9986f](https://github.com/zendesk/copenhagen_theme/commit/4d9986fc441fe3149335a07c68c607a46fa1dbc3))
7+
8+
# [4.30.0](https://github.com/zendesk/copenhagen_theme/compare/v4.29.3...v4.30.0) (2026-02-23)
9+
10+
11+
### Features
12+
13+
* preserve newlines in Approval request decription ([5b49c4d](https://github.com/zendesk/copenhagen_theme/commit/5b49c4d9ec124efb3879d3a0a3cbfafbd9ef0bc5))
14+
15+
## [4.29.3](https://github.com/zendesk/copenhagen_theme/compare/v4.29.2...v4.29.3) (2026-02-19)
16+
17+
18+
### Bug Fixes
19+
20+
* service catalog link in ticket comment uses absolute path ([d0640c5](https://github.com/zendesk/copenhagen_theme/commit/d0640c57c47919c7dc1db108d82d048d7408b152))
21+
22+
## [4.29.2](https://github.com/zendesk/copenhagen_theme/compare/v4.29.1...v4.29.2) (2026-02-11)
23+
24+
25+
### Bug Fixes
26+
27+
* add additional if clause for subject fields ([cc02488](https://github.com/zendesk/copenhagen_theme/commit/cc02488c4708871a49d0effbaa061a5ca6beed99))
28+
129
## [4.29.1](https://github.com/zendesk/copenhagen_theme/compare/v4.29.0...v4.29.1) (2026-02-05)
230

331

assets/approval-requests-bundle.js

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/request-list-bundle.js

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

assets/service-catalog-bundle.js

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

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "Copenhagen",
33
"author": "Zendesk",
4-
"version": "4.29.1",
4+
"version": "4.30.1",
55
"api_version": 4,
66
"default_locale": "en-us",
77
"settings": [

src/modules/approval-requests/ApprovalRequestPage.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,48 @@ describe("ApprovalRequestPage", () => {
176176

177177
expect(screen.queryByText(/clarification/i)).not.toBeInTheDocument();
178178
});
179+
180+
it("renders the message with newlines preserved", () => {
181+
const messageWithNewlines = "Line 1\nLine 2\n\nLine 3";
182+
const approvalRequestWithNewlines = {
183+
...mockApprovalRequest,
184+
message: messageWithNewlines,
185+
};
186+
187+
mockUseApprovalRequest.mockReturnValue({
188+
approvalRequest: approvalRequestWithNewlines,
189+
setApprovalRequest: jest.fn(),
190+
isLoading: false,
191+
errorFetchingApprovalRequest: null,
192+
});
193+
194+
render(<ApprovalRequestPage {...baseProps} userId={1} />);
195+
196+
const messageElement = screen.getByText(/Line 1.*Line 2.*Line 3/s);
197+
expect(messageElement).toBeInTheDocument();
198+
199+
expect(messageElement.textContent).toBe(messageWithNewlines);
200+
});
201+
202+
it("collapses multiple consecutive newlines in the message to two newlines", () => {
203+
const messageWithMultipleNewlines = "Line 1\n\n\n\nLine 2\n\n\nLine 3";
204+
const approvalRequestWithNewlines = {
205+
...mockApprovalRequest,
206+
message: messageWithMultipleNewlines,
207+
};
208+
209+
mockUseApprovalRequest.mockReturnValue({
210+
approvalRequest: approvalRequestWithNewlines,
211+
setApprovalRequest: jest.fn(),
212+
isLoading: false,
213+
errorFetchingApprovalRequest: null,
214+
});
215+
216+
render(<ApprovalRequestPage {...baseProps} userId={1} />);
217+
218+
const messageElement = screen.getByText(/Line 1.*Line 2.*Line 3/s);
219+
expect(messageElement).toBeInTheDocument();
220+
221+
expect(messageElement.textContent).toBe("Line 1\n\nLine 2\n\nLine 3");
222+
});
179223
});

src/modules/approval-requests/ApprovalRequestPage.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ const ApproverActionsWrapper = styled.div`
6969
margin-top: ${(props) => props.theme.space.lg};
7070
`;
7171

72+
const StyledDescription = styled(MD)`
73+
white-space: pre-wrap;
74+
`;
75+
7276
export interface ApprovalRequestPageProps {
7377
approvalWorkflowInstanceId: string;
7478
approvalRequestId: string;
@@ -138,6 +142,8 @@ function ApprovalRequestPage({
138142
const hasClarificationEnabled =
139143
approvalRequest?.clarification_flow_messages !== undefined;
140144

145+
const collapsedMessage = approvalRequest.message.replace(/\n{3,}/g, "\n\n");
146+
141147
return (
142148
<>
143149
<ApprovalRequestBreadcrumbs
@@ -147,7 +153,7 @@ function ApprovalRequestPage({
147153
<Container>
148154
<LeftColumn>
149155
<XXL isBold>{approvalRequest.subject}</XXL>
150-
<MD>{approvalRequest.message}</MD>
156+
<StyledDescription>{collapsedMessage}</StyledDescription>
151157
{approvalRequest.ticket_details && (
152158
<ApprovalTicketDetails ticket={approvalRequest.ticket_details} />
153159
)}

src/modules/request-list/components/requests-table/requests-table-row/RequestsTableCell.test.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,35 @@ describe("RequestsTableCell", () => {
244244
}
245245
);
246246
});
247+
248+
describe("Subject column", () => {
249+
test("renders subject as a clickable link", () => {
250+
renderCell({ identifier: "subject" });
251+
const link = screen.getByRole("link");
252+
expect(link).toHaveAttribute("href", "/hc/requests/123");
253+
expect(screen.getAllByText("Test Subject")[0]).toBeInTheDocument();
254+
});
255+
256+
test("renders description when subject is not available", () => {
257+
const requestWithoutSubject = {
258+
...mockRequest,
259+
subject: "",
260+
};
261+
renderCell({ identifier: "subject", request: requestWithoutSubject });
262+
expect(screen.getAllByText("No Jenny uhuh")[0]).toBeInTheDocument();
263+
});
264+
265+
test("renders subject even when subject ticket field is not in ticketFields", () => {
266+
const ticketFieldsWithoutSubject = mockTicketFields.filter(
267+
(field) => field.type !== "subject"
268+
);
269+
renderCell({
270+
identifier: "subject",
271+
ticketFields: ticketFieldsWithoutSubject,
272+
});
273+
const link = screen.getByRole("link");
274+
expect(link).toHaveAttribute("href", "/hc/requests/123");
275+
expect(screen.getAllByText("Test Subject")[0]).toBeInTheDocument();
276+
});
277+
});
247278
});

src/modules/request-list/components/requests-table/requests-table-row/RequestsTableCell.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,23 @@ The same applies for the description. */
188188
);
189189
}
190190

191+
if (identifier === "subject") {
192+
const requestUrl = `/hc/requests/${id}`;
193+
194+
const navigateToRequestPage = (e: MouseEvent) => {
195+
e.preventDefault();
196+
location.assign(requestUrl);
197+
};
198+
199+
return (
200+
<Table.Cell role="rowheader" data-test-id={`table-cell-${identifier}`}>
201+
<Subject href={requestUrl} onClick={navigateToRequestPage}>
202+
<TruncatedText>{subject || description}</TruncatedText>
203+
</Subject>
204+
</Table.Cell>
205+
);
206+
}
207+
191208
const ticketField = ticketFields.find(
192209
(field) => String(field.id) === identifier || field.type === identifier
193210
);
@@ -197,23 +214,6 @@ The same applies for the description. */
197214
}
198215

199216
switch (ticketField.type) {
200-
case "subject": {
201-
const requestUrl = `/hc/requests/${id}`;
202-
203-
const navigateToRequestPage = (e: MouseEvent) => {
204-
e.preventDefault();
205-
location.assign(requestUrl);
206-
};
207-
208-
return (
209-
<Table.Cell role="rowheader" data-test-id={`table-cell-${identifier}`}>
210-
<Subject href={requestUrl} onClick={navigateToRequestPage}>
211-
<TruncatedText>{subject || description}</TruncatedText>
212-
</Subject>
213-
</Table.Cell>
214-
);
215-
}
216-
217217
case "priority":
218218
return (
219219
<TruncatedTableCell identifier={identifier}>

src/modules/request-list/hooks/useTicketFields.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,30 @@ test("handles exceptions", async () => {
5959
isLoading: true,
6060
});
6161
});
62+
63+
test("filters out inactive subject field", async () => {
64+
const inactiveSubjectField: TicketField = {
65+
id: 30,
66+
type: "subject",
67+
active: false,
68+
title: "Subject",
69+
title_in_portal: "Subject",
70+
description: "",
71+
custom_field_options: [],
72+
};
73+
74+
fetchAllCursorPages.mockReturnValueOnce([
75+
activeTicketField,
76+
inactiveSubjectField,
77+
]);
78+
79+
const { result, waitForNextUpdate } = renderHook(() => useTicketFields("dk"));
80+
81+
await waitForNextUpdate();
82+
83+
expect(result.current).toEqual({
84+
ticketFields: [activeTicketField],
85+
error: undefined,
86+
isLoading: false,
87+
});
88+
});

0 commit comments

Comments
 (0)