Skip to content

Commit 6f983ba

Browse files
refactor(print): make printed time labels more compact
Refs: PS-275
1 parent 3e59200 commit 6f983ba

7 files changed

Lines changed: 291 additions & 10 deletions

File tree

src/components/form/EventForm.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ const EventForm: React.FC<EventFormProps> = ({
135135
selectedAddress={selectedAddress}
136136
selectedContractZone={selectedContractZone}
137137
/>
138-
<Row>
138+
<Row className="event-form-section-heading">
139139
<Col sm="12" md={{ size: 8, offset: 1 }}>
140140
<FormattedMessage tagName="h2" id="form.event.title.time" />
141141
<div className="event-form-print-skip">
@@ -280,7 +280,13 @@ const EventForm: React.FC<EventFormProps> = ({
280280
/>
281281
</Col>
282282
</Row>
283-
<Row className="event-form-print-skip">
283+
<Row
284+
className={`event-form-print-note ${
285+
values.additional_information.trim()
286+
? ''
287+
: 'event-form-print-note--empty'
288+
}`}
289+
>
284290
<Col sm="12" md={{ size: 8, offset: 1 }} lg={{ size: 8, offset: 1 }}>
285291
<Input
286292
type="textarea"
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import { IntlProvider } from 'react-intl';
4+
import { vi } from 'vitest';
5+
6+
import EventForm from '../EventForm';
7+
8+
vi.mock('../../../store/hooks', () => ({
9+
useAppSelector: (selector: unknown) => {
10+
if (typeof selector === 'function') {
11+
return selector({
12+
geo: {
13+
addressCoordinates: null,
14+
selectedAddress: null,
15+
selectedContractZone: { id: 1 },
16+
unavailableDates: [],
17+
},
18+
});
19+
}
20+
21+
return undefined;
22+
},
23+
}));
24+
25+
vi.mock('../fields/Input', () => ({
26+
default: (props: Record<string, unknown>) => (
27+
<div data-testid={String(props.id)} />
28+
),
29+
}));
30+
31+
vi.mock('../fields/NumericInput', () => ({
32+
default: (props: Record<string, unknown>) => (
33+
<div data-testid={String(props.id)} />
34+
),
35+
}));
36+
37+
vi.mock('../partitions/DateRange', () => ({
38+
default: () => <div data-testid="date-range" />,
39+
}));
40+
41+
vi.mock('../partitions/Location', () => ({
42+
default: () => <div data-testid="location" />,
43+
}));
44+
45+
const renderComponent = (additionalInformation: string) =>
46+
render(
47+
<IntlProvider
48+
locale="fi"
49+
messages={{
50+
'form.event.title.name_and_description': 'Tapahtuman perustiedot',
51+
'form.event.title.time': 'Aika',
52+
'form.event.title.contact_person': 'Yhteyshenkilö',
53+
'form.event.title.tools_and_suplies': 'Työkalut ja tarvikkeet',
54+
'form.event.subtitle.time':
55+
'Tapahtuman alkamispäivä voi olla tästä päivästä viikko eteenpäin.',
56+
'form.event.subtitle.contact_person':
57+
'Järjestäjän yhteystiedot tarvitaan urakoitsijoita varten.',
58+
'form.event.subtitle.tools_and_suplies':
59+
'Ilmoita tarvittavat työkalut. Urakoitsijat hoitavat oikean määrän tarvikkeita paikan päälle.',
60+
}}
61+
>
62+
<EventForm
63+
errors={{} as never}
64+
handleBlur={vi.fn()}
65+
handleChange={vi.fn()}
66+
handleSubmit={vi.fn()}
67+
touched={{} as never}
68+
values={
69+
{
70+
name: '',
71+
description: '',
72+
estimated_attendee_count: undefined,
73+
targets: '',
74+
location: undefined,
75+
organizer_first_name: '',
76+
organizer_last_name: '',
77+
organizer_email: '',
78+
organizer_phone: '',
79+
large_trash_bag_count: undefined,
80+
small_trash_bag_count: undefined,
81+
trash_picker_count: undefined,
82+
maintenance_location: '',
83+
additional_information: additionalInformation,
84+
start_time: '',
85+
end_time: '',
86+
} as never
87+
}
88+
/>
89+
</IntlProvider>
90+
);
91+
92+
describe('<EventForm />', () => {
93+
it('renders additional information as a printable field when populated', () => {
94+
const { container } = renderComponent('Muita lisätietoja toimitukselle');
95+
96+
const additionalInformation = container.querySelector(
97+
'[data-testid="additional_information"]'
98+
);
99+
100+
expect(additionalInformation).toBeInTheDocument();
101+
expect(
102+
additionalInformation?.closest('.event-form-print-note')
103+
).toBeInTheDocument();
104+
expect(
105+
additionalInformation?.closest('.event-form-print-skip')
106+
).not.toBeInTheDocument();
107+
expect(
108+
additionalInformation?.closest('.event-form-print-note--empty')
109+
).not.toBeInTheDocument();
110+
});
111+
112+
it('hides empty additional information from the printable layout', () => {
113+
const { container } = renderComponent('');
114+
115+
expect(
116+
container.querySelector('[data-testid="additional_information"]')
117+
).toBeInTheDocument();
118+
expect(
119+
container.querySelector('.event-form-print-note--empty')
120+
).toBeInTheDocument();
121+
});
122+
});

src/components/form/partitions/DateRange.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,20 @@ const DateRange: React.FC<Props> = ({
150150
value ? format(value, dateFormat) : '';
151151
const formatTimeValue = (value: Date | undefined) =>
152152
value ? formatTime(value, timeFormat, locale) : '';
153+
const formatPrintSummary = (labelId: string, value: string) => (
154+
<p className="print-only date-range-print-summary">
155+
<strong className="date-range-print-summary-label">
156+
{formatMessage({ id: labelId })}
157+
</strong>
158+
<span className="date-range-print-summary-value">{value}</span>
159+
</p>
160+
);
153161

154162
return (
155-
<div className="printable">
163+
<div className="printable date-range-printable">
156164
<Row>
157165
<Col
166+
className="date-range-field"
158167
sm="12"
159168
md={{ size: 4, offset: 1 }}
160169
id="date_range_start_date_wrapper"
@@ -186,7 +195,7 @@ const DateRange: React.FC<Props> = ({
186195
/>
187196
<PrintValue value={formatDateValue(selectedStartTime)} />
188197
</Col>
189-
<Col sm="12" md={{ size: 4 }}>
198+
<Col className="date-range-field" sm="12" md={{ size: 4 }}>
190199
<TimePicker
191200
id="date_range_start_time"
192201
defaultDate={minDate}
@@ -205,8 +214,19 @@ const DateRange: React.FC<Props> = ({
205214
<PrintValue value={formatTimeValue(selectedStartTime)} />
206215
</Col>
207216
</Row>
217+
<div className="print-only date-range-print-summary-row">
218+
{formatPrintSummary(
219+
'form.event.partitions.date_range.start_date.label',
220+
formatDateValue(selectedStartTime)
221+
)}
222+
{formatPrintSummary(
223+
'form.event.partitions.date_range.start_time.label',
224+
formatTimeValue(selectedStartTime)
225+
)}
226+
</div>
208227
<Row>
209228
<Col
229+
className="date-range-field"
210230
sm="12"
211231
md={{ size: 4, offset: 1 }}
212232
id="date_range_end_date_wrapper"
@@ -237,7 +257,7 @@ const DateRange: React.FC<Props> = ({
237257
/>
238258
<PrintValue value={formatDateValue(selectedEndTime)} />
239259
</Col>
240-
<Col sm="12" md={{ size: 4 }}>
260+
<Col className="date-range-field" sm="12" md={{ size: 4 }}>
241261
<TimePicker
242262
id="date_range_end_time"
243263
defaultDate={ensureDate(values.start_time) || minDate}
@@ -256,6 +276,16 @@ const DateRange: React.FC<Props> = ({
256276
<PrintValue value={formatTimeValue(selectedEndTime)} />
257277
</Col>
258278
</Row>
279+
<div className="print-only date-range-print-summary-row">
280+
{formatPrintSummary(
281+
'form.event.partitions.date_range.end_date.label',
282+
formatDateValue(selectedEndTime)
283+
)}
284+
{formatPrintSummary(
285+
'form.event.partitions.date_range.end_time.label',
286+
formatTimeValue(selectedEndTime)
287+
)}
288+
</div>
259289
</div>
260290
);
261291
};

src/components/form/partitions/Location.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ const Location: React.FC<Props> = ({
169169
</Row>
170170
<Row>
171171
<Col sm="12" md={{ size: 8, offset: 1 }} lg={{ size: 8, offset: 1 }}>
172+
<p className="print-only">
173+
{formatMessage({
174+
id: 'form.event.field.trash_location.placeholder',
175+
})}
176+
</p>
172177
<Label htmlFor="maintenance_location" srOnly>
173178
{formatMessage({
174179
id: 'form.event.field.trash_location.placeholder',

src/components/form/partitions/__tests__/DateRange.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ describe('<DateRange />', () => {
113113
);
114114

115115
expect(screen.getAllByTestId('print-value')).toHaveLength(4);
116+
expect(
117+
container.querySelectorAll('.date-range-print-summary')
118+
).toHaveLength(4);
116119
expect(container.querySelectorAll('.printable')).toHaveLength(1);
117120
expect(printValueMock.mock.calls[0][0]).toMatchObject({
118121
value: expectedStartDate,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import { IntlProvider } from 'react-intl';
4+
import { vi } from 'vitest';
5+
6+
import Location from '../Location';
7+
8+
vi.mock('../../../../store/hooks', () => ({
9+
useAppDispatch: () => vi.fn(),
10+
}));
11+
12+
vi.mock('../../../../hooks/useAuth', () => ({
13+
default: () => ({
14+
getApiToken: () => 'mock-token',
15+
}),
16+
}));
17+
18+
vi.mock('../../fields/Map', () => ({
19+
default: () => <div data-testid="map" />,
20+
}));
21+
22+
vi.mock('../../fields/AutoSuggest', () => ({
23+
default: () => <div data-testid="autosuggest" />,
24+
}));
25+
26+
vi.mock('../../fields/Input', () => ({
27+
default: (props: Record<string, unknown>) => (
28+
<div data-testid={String(props.id)} />
29+
),
30+
}));
31+
32+
vi.mock('../../fields/Label', () => ({
33+
default: ({ children }: { children: React.ReactNode }) => <>{children}</>,
34+
}));
35+
36+
const renderComponent = () =>
37+
render(
38+
<IntlProvider
39+
locale="fi"
40+
messages={{
41+
'form.event.field.trash_location.placeholder':
42+
'Lisätietoja tarvikkeiden toimittamiseen (valinnainen)',
43+
}}
44+
>
45+
<Location
46+
addressFeatures={[]}
47+
errors={{} as never}
48+
handleBlur={vi.fn()}
49+
handleChange={vi.fn()}
50+
selectedAddress={undefined}
51+
selectedContractZone={undefined}
52+
touched={{} as never}
53+
values={
54+
{
55+
maintenance_location: 'Tivolikuja 1',
56+
} as never
57+
}
58+
/>
59+
</IntlProvider>
60+
);
61+
62+
describe('<Location />', () => {
63+
it('renders a printable label for delivery notes', () => {
64+
const { container } = renderComponent();
65+
66+
expect(
67+
container.querySelector('.print-only')
68+
).toHaveTextContent('Lisätietoja tarvikkeiden toimittamiseen (valinnainen)');
69+
});
70+
});

src/components/pages/events/print.scss

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
* lines fit on one page.
4545
*/
4646
.printable-area .form-group {
47-
font-size: 11pt;
48-
line-height: 1.35;
47+
font-size: 10.5pt;
48+
line-height: 1.3;
4949
}
5050

5151
/*
@@ -154,7 +154,7 @@
154154
}
155155

156156
.form-group {
157-
margin-bottom: 0.5rem;
157+
margin-bottom: 0.35rem;
158158
}
159159

160160
h1,
@@ -163,7 +163,52 @@
163163
h4,
164164
h5,
165165
h6 {
166-
margin-top: 0.5rem;
167-
margin-bottom: 0.5rem;
166+
margin-top: 0.35rem;
167+
margin-bottom: 0.35rem;
168168
}
169+
170+
.printable-area .event-form .event-form-section-heading {
171+
margin-bottom: 0.15rem;
172+
}
173+
174+
.printable-area .event-form .event-form-print-note {
175+
margin-bottom: 0.15rem;
176+
}
177+
178+
.printable-area .event-form .event-form-print-note--empty {
179+
display: none;
180+
}
181+
182+
/*
183+
* Event form print: replace the stacked DateRange controls with compact
184+
* inline summary lines so the time section reads on one line per field.
185+
*/
186+
.printable-area .date-range-printable .form-group {
187+
display: none !important;
188+
}
189+
190+
.printable-area .date-range-printable .date-range-field {
191+
display: none !important;
192+
}
193+
194+
.printable-area .date-range-printable .date-range-print-summary {
195+
display: block !important;
196+
margin: 0;
197+
line-height: 1.1;
198+
}
199+
200+
.printable-area .date-range-printable .date-range-print-summary:not(:last-child) {
201+
margin-bottom: 0.2rem;
202+
}
203+
204+
.printable-area .date-range-printable .date-range-print-summary-label {
205+
display: inline;
206+
margin: 0;
207+
}
208+
209+
.printable-area .date-range-printable .date-range-print-summary-value {
210+
display: inline;
211+
margin-left: 0.35rem;
212+
}
213+
169214
}

0 commit comments

Comments
 (0)