Skip to content

Commit f48a224

Browse files
authored
Configuration for auto-sheet selection when only one (#316)
* Add lib changes to support a new config parameter Adds support to the core lib for a new config parameter which specifies what do to when an upload has only one sheet. 'Manual' will, as now, expect the user to select it (perhaps as a check that the correct file was used), while 'automatic' will progress the user straight onto the next page. To support this behaviour, we have added support for an 'other' link alongside the 'next' link when specifying form paths. * Explain where the form action link has two paths * Add auto-skip options to configuration screen * Update tests with new default sheet mode
1 parent af2776b commit f48a224

File tree

6 files changed

+147
-4
lines changed

6 files changed

+147
-4
lines changed

lib/importer/src/config.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ exports.PluginConfig = class {
3131
this.uploadPath = config.uploadPath;
3232
this.uploadPathDefault = false;
3333

34+
// Should we auto-select a sheet when there is only one sheet?
35+
// manual - No, always select a sheet even if there is only one.
36+
// automatic - Yes, always use the single sheet when there is only one.
37+
this.sheetSelection = config.sheetSelection || "manual";
38+
3439
// If the config fields are in the old format (just names) then move them to
3540
// the new structure with default values of text for the type, and not required.
3641
if (this.fields.find(Boolean)) {
@@ -77,10 +82,18 @@ exports.PluginConfig = class {
7782
}
7883
};
7984

85+
setSheetSelection = (mode) => {
86+
if (!["manual", "automatic"].includes(mode)) {
87+
throw (`'${mode}' is not a valid sheet selection mode`)
88+
}
89+
this.sheetSelection = mode
90+
}
91+
8092
as_object = () => {
8193
return {
8294
uploadPath: this.uploadPathDefault ? TEMP_DIRECTORY_LABEL : this.uploadPath,
83-
fields: this.fields
95+
fields: this.fields,
96+
sheetSelection: this.sheetSelection
8497
}
8598
}
8699

@@ -95,6 +108,7 @@ exports.PluginConfig = class {
95108
let current = fse.readJsonSync(configFilePath);
96109

97110
current.fields = this.fields;
111+
current.sheetSelection = this.sheetSelection
98112

99113
fse.writeJsonSync(configFilePath, current);
100114
};

lib/importer/src/config.test.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ describe("Configuration tests", () => {
3636
expect(original.uploadPath).not.toBeNull()
3737
expect(updated.uploadPath).not.toBeNull()
3838
expect(c.uploadPath).not.toBeNull()
39+
expect(updated.sheetSelection).not.toBeNull()
40+
expect(updated.sheetSelection).toStrictEqual("manual")
3941
}
4042
);
4143

@@ -133,6 +135,69 @@ describe("Configuration tests", () => {
133135
mock_files.restore();
134136
});
135137

138+
test('manual sheet selection', () => {
139+
mock_files({
140+
'./fields/app/config.json': '{"fields": [{"name":"A"}, {"name": "B"}, {"name": "C"}]}',
141+
})
142+
143+
withCurrent(
144+
"./fields",
145+
new cfg.PluginConfig(),
146+
(c) => {
147+
c.setSheetSelection("manual")
148+
c.persistConfig()
149+
},
150+
(original, updated) => {
151+
expect(updated.sheetSelection).toStrictEqual("manual")
152+
}
153+
);
154+
155+
mock_files.restore();
156+
});
157+
158+
test('default sheet selection', () => {
159+
mock_files({
160+
'./fields/app/config.json': '{"fields": [{"name":"A"}, {"name": "B"}, {"name": "C"}]}',
161+
})
162+
163+
withCurrent(
164+
"./fields",
165+
new cfg.PluginConfig(),
166+
(c) => {
167+
c.persistConfig()
168+
},
169+
(original, updated) => {
170+
expect(updated.sheetSelection).toStrictEqual("manual")
171+
}
172+
);
173+
174+
mock_files.restore();
175+
});
176+
177+
178+
test('invalid sheet selection', () => {
179+
mock_files({
180+
'./fields/app/config.json': '{"fields": [{"name":"A"}, {"name": "B"}, {"name": "C"}]}',
181+
})
182+
183+
withCurrent(
184+
"./fields",
185+
new cfg.PluginConfig(),
186+
(c) => {
187+
expect(() => {
188+
c.setSheetSelection("invalid")
189+
}
190+
).toThrow("'invalid' is not a valid sheet selection mode")
191+
c.persistConfig()
192+
},
193+
(original, updated) => {
194+
expect(updated.sheetSelection).toStrictEqual("manual")
195+
}
196+
);
197+
198+
mock_files.restore();
199+
});
200+
136201
})
137202

138203

lib/importer/src/index.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ exports.Initialise = (config, router, prototypeKit) => {
100100

101101
prototypeKit.views.addFunction(
102102
k,
103-
(next) => {
103+
(next, other = null) => {
104+
if (next && other) {
105+
return `${v}?next=${encodeURIComponent(next)}&other=${encodeURIComponent(other)}`;
106+
}
107+
104108
return `${v}?next=${encodeURIComponent(next)}`;
105109
},
106110
{},
@@ -171,7 +175,16 @@ exports.Initialise = (config, router, prototypeKit) => {
171175

172176
if (session.sheets.length == 1) {
173177
session.sheet = session.sheets[0];
178+
179+
// When there is only a single sheet, and if the prototype is configured to
180+
// automatically progress, then this will do so if the 'other' url is
181+
// specified.
182+
if (plugin_config.sheetSelection.toLowerCase() == "automatic" && "other" in request.query) {
183+
const otherPage = decodeURIComponent(request.query.other)
184+
if (otherPage) { request.query.next = request.query.other }
185+
}
174186
}
187+
175188
// Ensure the session is persisted. Currently in session, eventually another way
176189
request.session.data[IMPORTER_SESSION_KEY] = session;
177190
redirectOnwards(request, response);
@@ -392,6 +405,7 @@ exports.Initialise = (config, router, prototypeKit) => {
392405
config: {
393406
fields: plugin_config.fields
394407
},
408+
sheetMode: plugin_config.sheetSelection || "manual",
395409
collectUsageData: usageRecorder.enabled,
396410
}
397411

@@ -432,6 +446,12 @@ exports.Initialise = (config, router, prototypeKit) => {
432446
usageRecorder.setPermission(false)
433447
}
434448

449+
const sheetMode = request.body.sheetMode || "manual"
450+
if (["manual", "automatic"].includes(sheetMode.toLowerCase())) {
451+
plugin_config.sheetSelection = sheetMode.toLowerCase()
452+
} else {
453+
plugin_config.sheetSelection = "manual"
454+
}
435455

436456
setTimeout(function () {
437457
plugin_config.persistConfig()

lib/importer/templates/upload.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
<div class="govuk-grid-column-two-thirds">
1313
<h1 class="govuk-heading-l">Upload your data</h1>
1414

15-
<form action="{{importerUploadPath('/select_sheet')}}" method="POST" enctype="multipart/form-data">
15+
<!-- Upload to select sheet, but in automatic mode, will go to header selection instead -->
16+
<form action="{{importerUploadPath('/select_sheet', '/select_header_row')}}" method="POST" enctype="multipart/form-data">
1617
<div>
1718
{{ govukFileUpload({
1819
id: "file-upload",

lib/importer/views/plugin_config.html

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,48 @@ <h2 class="govuk-heading-l">Column settings</h2>
9797

9898
<div class="govuk-grid-column-full govuk-!-padding-top-6" >
9999
<h2 class="govuk-heading-l"></h2>
100+
<div class="govuk-form-group">
101+
<fieldset class="govuk-fieldset">
102+
<legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
103+
<h1 class="govuk-fieldset__heading">
104+
Auto sheet selection
105+
</h1>
106+
</legend>
107+
108+
<div class="govuk-radios" data-module="govuk-radios">
109+
<p class="govuk-body">
110+
When a user uploads a spreadsheet and there is only a single sheet,
111+
the plugin can either automatically select the first sheet
112+
or allow the user to choose a specific sheet.
113+
114+
The default is to allow the user to choose the sheet,
115+
but by selecting 'automatic' here the single sheet
116+
will always be used without user intevention skipping the sheet
117+
selection screen.
118+
</p>
119+
120+
<div id="type-hint" class="govuk-hint">
121+
Choose auto-sheet selection mode
122+
</div>
123+
124+
<div class="govuk-radios__item">
125+
<input class="govuk-radios__input" id="field-sheet-manual" name="sheetMode" type="radio" value="manual" {% if sheetMode == "manual" %}checked{% endif %}>
126+
<label class="govuk-label govuk-radios__label" for="field-sheet-manual">
127+
Manual
128+
</label>
129+
</div>
130+
<div class="govuk-radios__item">
131+
<input class="govuk-radios__input" id="field-sheet-automatic" name="sheetMode" type="radio" value="automatic" {% if sheetMode == "automatic" %}checked{% endif %}>
132+
<label class="govuk-label govuk-radios__label" for="field-sheet-automatic">
133+
Automatic
134+
</label>
135+
</div>
136+
</div>
137+
</div> <!-- fieldset -->
138+
</div> <!-- form-group -->
139+
140+
<div class="govuk-grid-column-full govuk-!-padding-top-6" >
141+
<h2 class="govuk-heading-l"></h2>
100142

101143
<div class="govuk-form-group">
102144
<fieldset class="govuk-fieldset">

prototypes/basic/app/views/upload.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ <h1 class="govuk-heading-s">Check your dates</h1>
5656
</ul>
5757
</p>
5858

59-
<form action="{{importerUploadPath('/select_sheet')}}" method="POST" enctype="multipart/form-data">
59+
<!-- Upload to select sheet, but in automatic mode, will go to header selection instead -->
60+
<form action="{{importerUploadPath('/select_sheet', '/select_header_row')}}" method="POST" enctype="multipart/form-data">
6061
<div>
6162
{{ govukFileUpload({
6263
id: "file-upload",

0 commit comments

Comments
 (0)