Skip to content

Commit 58d7a52

Browse files
authored
Merge pull request #378 from US-CBP/bugfix/form-investigations
Bugfix/form investigations
2 parents bbd4bdd + b963483 commit 58d7a52

File tree

3 files changed

+110
-24
lines changed

3 files changed

+110
-24
lines changed

packages/web-components/src/components/cbp-file-input/cbp-file-input.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class CbpFileInput {
2020
@Element() host: HTMLElement;
2121

2222
/** The `name` attribute of the input, which is passed as part of formData (as a key). */
23-
@Prop() name: string;
23+
@Prop({ mutable: true }) name: string;
2424

2525
/**
2626
* Optionally specify the ID of the input here, which is used to generate related pattern
@@ -29,7 +29,7 @@ export class CbpFileInput {
2929
@Prop({ mutable: true }) fieldId: string = createNamespaceKey('cbp-file-input');
3030

3131
/** Specifies whether the file input accepts multiple files rather than a single file (may also be set directly on the slotted input). */
32-
@Prop() multiple: boolean;
32+
@Prop({ reflect: true }) multiple: boolean;
3333

3434
/**
3535
* Specifies the files types accepted by the file input (may also be set directly on the slotted input).
@@ -110,18 +110,38 @@ export class CbpFileInput {
110110
this.valueChange.emit({
111111
host: this.host,
112112
nativeElement: this.formField,
113+
name: this.name,
113114
value: this.files,
114115
nativeEvent: e
115116
});
116117
}
117118

119+
120+
/**
121+
* A custom method to reset the file input component (for enhanced/multi-file inputs) to its initial state and value
122+
* since it does not update properly on a native form reset. This method may be called manually, but is automatically
123+
* called on form reset when using the `cbp-form` component.
124+
*/
118125
@Method()
119126
async reset() {
120127
this.formField.value = this.initialValue ? this.initialValue: ''; // reset to empty string if undefined, which should always be the case
121128
this.files=[];
122129
}
123130

124131

132+
/* Testing: This method not yet fully supported */
133+
@Method()
134+
async getData() {
135+
const Data={
136+
host: this.host,
137+
name: this.name,
138+
files: this.files
139+
}
140+
//console.log('getData(): ', Data);
141+
return Data;
142+
}
143+
144+
125145
@Watch('disabled')
126146
watchDisabledHandler(newValue: boolean) {
127147
if (this.formField) {
@@ -168,6 +188,7 @@ export class CbpFileInput {
168188
this.valueChange.emit({
169189
host: this.host,
170190
nativeElement: this.formField,
191+
name: this.name,
171192
value: this.files,
172193
nativeEvent: e
173194
});
@@ -187,9 +208,11 @@ export class CbpFileInput {
187208
if (this.formField) {
188209
const Id = this.formField.getAttribute('id');
189210
Id ? this.fieldId = Id : this.formField.setAttribute('id', this.fieldId);
211+
const Name = this.formField.getAttribute('name');
212+
Name ? this.name = Name : this.formField.setAttribute('Name', this.name);
213+
190214
if (this.multiple) this.formField.setAttribute('multiple', '');
191215
if (this.accept) this.formField.setAttribute('accept', this.accept);
192-
if (this.name) this.formField.setAttribute('name', this.name);
193216
if (this.disabled) this.formField.setAttribute('disabled', ``);
194217
// store the initialValue for reset functionality
195218
this.initialValue = this.formField?.value;

packages/web-components/src/components/cbp-form/cbp-form.tsx

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Element, Host, h } from '@stencil/core';
1+
import { Component, Prop, Element, Event, EventEmitter, Host, h } from '@stencil/core';
22

33
/**
44
* The Form component may optionally be used to wrap a native HTML form, providing enhanced functionality and support
@@ -13,39 +13,90 @@ import { Component, Element, Host, h } from '@stencil/core';
1313
export class CbpForm {
1414

1515
private form: HTMLFormElement;
16+
private enhancedFileInputs: HTMLCbpFileInputElement[] = [];
17+
private files: object = {};
1618

1719
@Element() host: HTMLCbpFormElement;
18-
19-
handleSubmit(e) {
20-
/*
21-
Update FormData (or create a copy) with fields not supported natively:
22-
* File input (allowing manipulation of FileList)
23-
*/
24-
console.log('cbp-form - Submit event: ', e);
25-
e.preventDefault();
26-
27-
// Empty FormData object
28-
//let formData = new FormData();
29-
// FormData object populated from an existing form
30-
let formData = new FormData(this?.form);
3120

32-
console.log('cbp-form - formData: ', formData);
33-
// Spreading the formData as an array seems to give the same results as above.
34-
console.log('formData (array spread): ',[...formData]);
35-
}
21+
/** When specified, applies preventDefault() to the submit event and emits a custom event with the formData to hand off to the application. */
22+
@Prop() preventSubmit: boolean;
23+
24+
@Event() suppressedSubmit: EventEmitter;
3625

26+
3727
handleReset() {
3828
// Call reset methods on form fields that do not natively support it.
3929
const SpecialCases: any = this.form.querySelectorAll('cbp-dropdown,cbp-slider,cbp-segmented-button-group,cbp-checkbox,cbp-radio,cbp-toggle,cbp-file-input');
4030
SpecialCases.forEach( item => {
4131
item.reset();
4232
});
33+
// reset enhanced/multi-file inputs to []
34+
Object.keys(this.files).forEach( key => {
35+
this.files[key] = [];
36+
});
37+
}
38+
39+
handleSubmit(e) {
40+
const form = e.srcElement;
41+
// FormData object populated from an existing form
42+
let formData = new FormData(form);
43+
44+
e.preventDefault();
45+
46+
// Add files from enhanced/multi-file inputs
47+
formData = this.addFiles(formData);
48+
49+
//console.log('resulting formData (array spread): ',[...formData]);
50+
51+
// If the form submission is prevented, emit an event with the data instead
52+
if (this.preventSubmit) {
53+
this.suppressedSubmit.emit({
54+
host: this.host,
55+
form: form,
56+
formData: formData,
57+
nativeEvent: e
58+
});
59+
}
60+
// otherwise submit after updating the formData
61+
else form.submit();
62+
}
63+
64+
addFiles(formData) {
65+
/*
66+
Update FormData (or create a copy) with fields not supported natively:
67+
File input (multiple+enhanced, allowing manipulation of FileList)
68+
*/
69+
Object.keys(this.files).forEach( key => {
70+
if (this.files?.[`${key}`]?.length > 0){
71+
// delete the empty key if there are files specified
72+
formData.delete(key);
73+
// loop over the files and add each as a new entry using append (set overrides the same entry)
74+
this.files?.[key].forEach( file => {
75+
formData.append(key, file);
76+
});
77+
}
78+
formData[`${key}`] = this.files[`${key}`];
79+
});
80+
return formData;
81+
}
82+
83+
handleEnhancedFileInput(e) {
84+
// Keep tabs on enhanced/multi-file inputs' values
85+
const {name, value} = e.detail;
86+
this.files[name] = value;
87+
console.log('cbp-form - Tracking enhanced/multi-file input: ', this.files);
4388
}
4489

45-
componentWillLoad(){
90+
componentWillLoad() {
4691
this.form=this.host.querySelector('form');
4792
this.form.addEventListener('submit', e => this.handleSubmit(e));
4893
this.form.addEventListener('reset', () => this.handleReset());
94+
95+
// Listen for changes to enhanced/multi-file inputs
96+
this.enhancedFileInputs=Array.from(this.host.querySelectorAll('cbp-file-input[multiple]'));
97+
this.enhancedFileInputs.forEach( item => {
98+
item.addEventListener('valueChange', e => this.handleEnhancedFileInput(e));
99+
});
49100
}
50101

51102
render() {

packages/web-components/src/stories/tests/form-processing.stories.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,21 @@ function HTMLForm( {name, action, method, enctype, prefix, firstName, middleInit
271271
<!-- File Inputs -->
272272
<label>
273273
Native File Input<br />
274-
<input type="file" name="nativefileinput" />
274+
<input type="file" name="nativefileinput" multiple/>
275275
</label><br /><br />
276276
277+
278+
<cbp-form-field label="Native File Input" description="Although styled as a design system input, this is a native file input in function.">
279+
<cbp-form-field-wrapper>
280+
<input type="file" name="nativefileinput" multiple>
281+
<span slot="cbp-form-field-attached-button">
282+
<cbp-button fill="solid" color="secondary" aria-describedby="undefined-label" onclick="event.target.closest('cbp-form-field').querySelector('input').click()">
283+
Browse
284+
</cbp-button>
285+
</span>
286+
</cbp-form-field-wrapper>
287+
</cbp-form-field>
288+
277289
<cbp-form-field
278290
label="Single File"
279291
description="Field description."
@@ -427,7 +439,7 @@ const FormComponentProcessingTemplate = (args) => {
427439
Some component-enhanced functionality may not work with the native platform without using the cbp-form component.
428440
This page demonstrates component interactions using a cbp-form wrapping a native HTML form and handling the form events such as submit and reset.
429441
</p>
430-
<cbp-form>
442+
<cbp-form prevent-submit>
431443
${HTMLForm(args)}
432444
</cbp-form>
433445
`;

0 commit comments

Comments
 (0)