Skip to content

Commit 04a9cb4

Browse files
authored
Allow skipping of header selection page (#306)
It's possible that a spreadsheet does not have headers, and so this PR makes it possible for a user to skip the header selection step by clicking the Skip button. <img width="1310" height="723" alt="Upload_monthly_pension_return_–_GOV_UK_Prototype_Kit" src="https://github.com/user-attachments/assets/793c811f-2e69-4f4e-b723-df0e9787cf5b" /> Before this button existed, not selecting a header row achieved the same result, which was that the first row was chosen as headers. This means that when, for instance, using tribble.xlsx we get the following result on the mapping page: <img width="1043" height="463" alt="Upload_monthly_pension_return_–_GOV_UK_Prototype_Kit" src="https://github.com/user-attachments/assets/0e479383-faff-4878-8e1e-5a6065570048" /> This isn't ideal as the first row (the default when nothing selected) could contain useful data. To work around this assumption of a non-null header, we instead make the row for the header range out of bounds (e.g. -1) so that we can use it as a sentinel. This allows us to fetch the column names during mapping as the base26 value of the index, which results in a mapping screen of <img width="1031" height="760" alt="Upload_monthly_pension_return_–_GOV_UK_Prototype_Kit" src="https://github.com/user-attachments/assets/51b7ee0e-1411-4bfc-9cf9-b054852daa0a" /> Unfortunately this approach requires that the backend expose the raw dimensions of the sheet so that the appropriate number of column headings can be generated.
1 parent e7d59c6 commit 04a9cb4

File tree

6 files changed

+58
-12
lines changed

6 files changed

+58
-12
lines changed

lib/importer/src/dudk/backend.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,15 @@ function pickUniqueSortedIndexesInRange(start, end, count) {
206206
return indexes;
207207
}
208208

209+
exports.SessionGetSheetDimensions = (sid, sheetName) => {
210+
const dimensions = getDimensions(sid);
211+
const sheetDimensions = dimensions.sheetDimensions.get(sheetName);
212+
213+
return {
214+
rows: sheetDimensions.rows,
215+
columns: sheetDimensions.columns
216+
};
217+
}
209218
// Returns a suggested range containing just the data, given (optional) header
210219
// and footer ranges. If neither are specified, it will take all of the first
211220
// sheet except the top row. If the header range is specified, it will take all

lib/importer/src/dudk/sheets.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,30 @@ exports.ListSheets = (sid) => {
1717
// it's intended for driving a list of available columns in the header.
1818
exports.GetHeader = (sid, sheet) => {
1919
const headerRange = backend.SessionGetHeaderRange(sid, sheet)
20-
const headerSample = backend.SessionGetInputSampleRows(sid, headerRange, 1, 0, 0);
21-
22-
const samples = headerSample[0][0].row;
2320
const columns = new Array();
2421

25-
for (let i = 0; i < samples.length; i++) {
26-
if (samples[i]) {
27-
columns.push(samples[i].value)
28-
} else {
22+
// If headerRange starts on row -1 then we know it is a null header range
23+
// and we should instead return the indices for the dimensions
24+
if (headerRange.start.row == -1) {
25+
const dimensions = backend.SessionGetSheetDimensions(sid, sheet);
26+
for (let i = 0; i < dimensions.columns; i++) {
2927
const name = b26.toBase26(i + 1);
3028
columns.push(name)
3129
}
30+
} else {
31+
// We have a valid header range selected by a user, get the real
32+
// values from the sheet
33+
const headerSample = backend.SessionGetInputSampleRows(sid, headerRange, 1, 0, 0);
34+
const samples = headerSample[0][0].row;
35+
36+
for (let i = 0; i < samples.length; i++) {
37+
if (samples[i]) {
38+
columns.push(samples[i].value)
39+
} else {
40+
const name = b26.toBase26(i + 1);
41+
columns.push(name)
42+
}
43+
}
3244
}
3345

3446
return columns

lib/importer/src/index.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ exports.Initialise = (config, router, prototypeKit) => {
218218

219219
session.headerRange = {
220220
sheet: session.sheet,
221-
start: { row: 0, column: 0 },
222-
end: { row: 0, column: maxCol - 1 },
221+
start: { row: -1, column: 0 },
222+
end: { row: -1, column: maxCol - 1 },
223223
};
224224

225225
// Ensure the session is persisted. Currently in session, eventually another way
@@ -244,9 +244,19 @@ exports.Initialise = (config, router, prototypeKit) => {
244244
session.headerRange = getSelectionFromRequest(
245245
request,
246246
session,
247-
/* optional */ false,
247+
/* optional */ true,
248248
);
249249

250+
// If the user did not select a header range, then we should create one
251+
if (session.headerRange == null) {
252+
let [minCol, maxCol] = sheets_lib.GuessHeaderRange(session.backendSid, session.sheet);
253+
session.headerRange = {
254+
sheet: session.sheet,
255+
start: { row: -1, column: minCol },
256+
end: { row: -1, column: maxCol },
257+
};
258+
}
259+
250260
// Ensure the session is persisted. Currently in session, eventually another way
251261
request.session.data[IMPORTER_SESSION_KEY] = session;
252262

@@ -579,13 +589,13 @@ const getSelectionFromRequest = (request, session, optional = false) => {
579589
defaultMaxCol,
580590
);
581591

582-
// If optional is set to true and we don't have any valid values, we will bail
592+
// If optional is set to true and we don't have enough valid values, we will bail
583593
// early
584594
if (
585595
optional &&
586596
[tlRow, tlCol, brRow, brCol].filter((x) => x >= 0).length < 4
587597
) {
588-
return null;
598+
return null
589599
}
590600

591601
// Normalise the rows

lib/importer/src/session.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class Session {
2727
}
2828

2929
set headerRange(range) {
30+
if (!range) return;
31+
3032
const r = range;
3133
if (!r.sheet) {
3234
r.sheet = this.sheet
@@ -114,6 +116,17 @@ var validateUpload = (file) => {
114116
return undefined;
115117
};
116118

119+
// It's possible the user has not specified any headers, and so
120+
// that we can construct a header range, we need to know the
121+
// possible widest row so that we can later map those values.
122+
exports.GuessHeaderRange = (session, sheet) => {
123+
// If we have to guess the header range, then we will ask for the
124+
// dimensions and use whatever the sheets library tells us is the
125+
// end column
126+
const dimensions = backend.SessionGetSheetDimensions(session.backendSid, sheet);
127+
return [-1, dimensions.columns]
128+
}
129+
117130
/*
118131
* Returns a header row for display, based on the requested mode and the data
119132
* currently available in the state.

lib/importer/templates/select_header_row.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ <h1 class="govuk-fieldset__heading">
3535
</div>
3636

3737
<div class="govuk-button-group">
38+
{{ govukButton({ text: "Skip", classes: "govuk-button--secondary"}) }}
3839
{{ govukButton({ text: "Next" }) }}
3940
</div>
4041
</form>

prototypes/basic/app/views/select_header_row.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ <h1 class="govuk-heading-l">
5757
</div>
5858

5959
<div class="govuk-button-group">
60+
{{ govukButton({ text: "Skip", classes: "govuk-button--secondary"}) }}
6061
{{ govukButton({ text: "Next" }) }}
6162
</div>
6263
</form>

0 commit comments

Comments
 (0)