Skip to content

Commit 9a9dc28

Browse files
authored
refactor(form-value): Cleaned Form value helper class (#1801)
Abstracted data type transformers into a separate module and improved type inference inside elements usage.
1 parent 774899c commit 9a9dc28

File tree

16 files changed

+208
-263
lines changed

16 files changed

+208
-263
lines changed

src/components/checkbox/checkbox-base.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ import { blazorDeepImport } from '../common/decorators/blazorDeepImport.js';
66
import type { Constructor } from '../common/mixins/constructor.js';
77
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
88
import { FormAssociatedCheckboxRequiredMixin } from '../common/mixins/forms/associated-required.js';
9-
import {
10-
createFormValueState,
11-
defaultBooleanTransformers,
12-
type FormValueOf,
13-
} from '../common/mixins/forms/form-value.js';
9+
import { FormValueBooleanTransformers } from '../common/mixins/forms/form-transformers.js';
10+
import { createFormValueState } from '../common/mixins/forms/form-value.js';
1411
import type { ToggleLabelPosition } from '../types.js';
1512
import { checkBoxValidators } from './validators.js';
1613

@@ -44,11 +41,10 @@ export class IgcCheckboxBaseComponent extends FormAssociatedCheckboxRequiredMixi
4441
});
4542

4643
protected readonly _focusRingManager = addKeyboardFocusRing(this);
47-
protected override readonly _formValue: FormValueOf<boolean> =
48-
createFormValueState(this, {
49-
initialValue: false,
50-
transformers: defaultBooleanTransformers,
51-
});
44+
protected override readonly _formValue = createFormValueState(this, {
45+
initialValue: false,
46+
transformers: FormValueBooleanTransformers,
47+
});
5248
protected _value!: string;
5349

5450
@query('input', true)
@@ -65,7 +61,7 @@ export class IgcCheckboxBaseComponent extends FormAssociatedCheckboxRequiredMixi
6561
public set value(value: string) {
6662
this._value = value;
6763
if (this.checked) {
68-
this._setFormValue(this._value || 'on');
64+
this._formValue.setValueAndFormState(this.checked);
6965
}
7066
}
7167

src/components/combo/combo.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ import { registerComponent } from '../common/definitions/register.js';
1616
import type { Constructor } from '../common/mixins/constructor.js';
1717
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
1818
import { FormAssociatedRequiredMixin } from '../common/mixins/forms/associated-required.js';
19-
import {
20-
createFormValueState,
21-
type FormValueOf,
22-
} from '../common/mixins/forms/form-value.js';
19+
import { createFormValueState } from '../common/mixins/forms/form-value.js';
2320
import { partMap } from '../common/part-map.js';
2421
import {
2522
addSafeEventListener,
@@ -147,14 +144,15 @@ export default class IgcComboComponent<
147144
},
148145
});
149146

150-
protected override readonly _formValue: FormValueOf<ComboValue<T>[]> =
151-
createFormValueState<ComboValue<T>[]>(this, {
152-
initialValue: [],
153-
transformers: {
154-
setValue: asArray,
155-
setDefaultValue: asArray,
156-
},
157-
});
147+
protected override readonly _formValue = createFormValueState<
148+
ComboValue<T>[]
149+
>(this, {
150+
initialValue: [],
151+
transformers: {
152+
setValue: asArray,
153+
setDefaultValue: asArray,
154+
},
155+
});
158156
private _data: T[] = [];
159157

160158
private _valueKey?: Keys<T>;

src/components/common/mixins/form-associated.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
type Validator,
1616
} from '../validators.js';
1717
import { FormAssociatedRequiredMixin } from './forms/associated-required.js';
18-
import { createFormValueState, type FormValueOf } from './forms/form-value.js';
18+
import { createFormValueState } from './forms/form-value.js';
1919
import type {
2020
FormAssociatedElementInterface,
2121
FormRequiredInterface,
@@ -52,8 +52,9 @@ describe('Form associated mixin tests', () => {
5252
return [requiredValidator, minLengthValidator, maxLengthValidator];
5353
}
5454

55-
protected override _formValue: FormValueOf<string> =
56-
createFormValueState(this, { initialValue: '' });
55+
protected override _formValue = createFormValueState(this, {
56+
initialValue: '',
57+
});
5758

5859
private _minLength!: number;
5960
private _maxLength!: number;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import {
2+
convertToDate,
3+
convertToDateRange,
4+
getDateFormValue,
5+
} from '../../../calendar/helpers.js';
6+
import type { DateRangeValue } from '../../../date-range-picker/date-range-picker.js';
7+
import { asNumber } from '../../util.js';
8+
import type { FormValueType, IgcFormControl } from './types.js';
9+
10+
export type FormValueTransformers<T> = {
11+
setValue: (value: T) => T;
12+
getValue: (value: T) => T;
13+
setDefaultValue: (value: T) => T;
14+
getDefaultValue: (value: T) => T;
15+
setFormValue: (value: T, host: IgcFormControl) => FormValueType;
16+
};
17+
18+
export type FormValueConfig<T> = {
19+
initialValue: T;
20+
initialDefaultValue?: T;
21+
transformers?: Partial<FormValueTransformers<T>>;
22+
};
23+
24+
// Transformers
25+
26+
export const FormValueDefaultTransformers: FormValueTransformers<string> = {
27+
setValue: (value) => value || '',
28+
getValue: (value) => value,
29+
setDefaultValue: (value) => value || '',
30+
getDefaultValue: (value) => value,
31+
setFormValue: (value, _) => value || null,
32+
};
33+
34+
export const FormValueBooleanTransformers: Partial<
35+
FormValueTransformers<boolean>
36+
> = {
37+
setValue: Boolean,
38+
setDefaultValue: Boolean,
39+
setFormValue: (checked, host) =>
40+
checked && 'value' in host ? (host.value as string) || 'on' : null,
41+
};
42+
43+
export const FormValueNumberTransformers: Partial<
44+
FormValueTransformers<number>
45+
> = {
46+
setValue: asNumber,
47+
setDefaultValue: asNumber,
48+
setFormValue: (value) => value.toString(),
49+
};
50+
51+
export const FormValueDateTimeTransformers: Partial<
52+
FormValueTransformers<Date | null>
53+
> = {
54+
setValue: convertToDate,
55+
setDefaultValue: convertToDate,
56+
setFormValue: getDateFormValue,
57+
};
58+
59+
export const FormValueDateRangeTransformers: Partial<
60+
FormValueTransformers<DateRangeValue | null>
61+
> = {
62+
setValue: convertToDateRange,
63+
setDefaultValue: convertToDateRange,
64+
setFormValue: (value, host) => {
65+
if (!host.name) {
66+
return null;
67+
}
68+
const start = value?.start?.toISOString();
69+
const end = value?.end?.toISOString();
70+
71+
const formData = new FormData();
72+
73+
if (start) {
74+
formData.append(`${host.name}-start`, start);
75+
}
76+
if (end) {
77+
formData.append(`${host.name}-end`, end);
78+
}
79+
80+
return formData;
81+
},
82+
};
83+
84+
export const FormValueFileListTransformers: FormValueTransformers<FileList | null> =
85+
{
86+
setValue: (value) => value || null,
87+
getValue: (value) => value,
88+
setDefaultValue: (value) => value || null,
89+
getDefaultValue: (value) => value,
90+
setFormValue: (files, host) => {
91+
if (!(host.name && files)) {
92+
return null;
93+
}
94+
95+
const formData = new FormData();
96+
for (const file of files) {
97+
formData.append(host.name, file);
98+
}
99+
100+
return formData;
101+
},
102+
};
103+
104+
export const FormValueSelectTransformers: Partial<
105+
FormValueTransformers<string | undefined>
106+
> = {
107+
setValue: (value) => value || undefined,
108+
setDefaultValue: (value) => value || undefined,
109+
};
Lines changed: 13 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,21 @@
1+
import type { LitElement } from 'lit';
12
import {
2-
convertToDate,
3-
convertToDateRange,
4-
getDateFormValue,
5-
} from '../../../calendar/helpers.js';
6-
import type { DateRangeValue } from '../../../date-range-picker/date-range-picker.js';
7-
import { asNumber } from '../../util.js';
8-
import type { FormValueType, IgcFormControl } from './types.js';
9-
10-
type FormValueTransformers<T> = {
11-
setValue: (value: T) => T;
12-
getValue: (value: T) => T;
13-
setDefaultValue: (value: T) => T;
14-
getDefaultValue: (value: T) => T;
15-
setFormValue: (value: T, host: IgcFormControl) => FormValueType;
16-
};
17-
18-
type FormValueConfig<T> = {
19-
initialValue: T;
20-
initialDefaultValue?: T;
21-
transformers?: Partial<FormValueTransformers<T>>;
22-
};
23-
24-
const defaultTransformers: FormValueTransformers<string> = {
25-
setValue: (value) => value || '',
26-
getValue: (value) => value,
27-
setDefaultValue: (value) => value || '',
28-
getDefaultValue: (value) => value,
29-
setFormValue: (value, _: IgcFormControl) => value || null,
30-
};
31-
32-
export const defaultBooleanTransformers: Partial<
33-
FormValueTransformers<boolean>
34-
> = {
35-
setValue: Boolean,
36-
setDefaultValue: Boolean,
37-
setFormValue: (checked, host) => {
38-
return checked && 'value' in host ? (host.value as string) || 'on' : null;
39-
},
40-
};
41-
42-
export const defaultNumberTransformers: Partial<FormValueTransformers<number>> =
43-
{
44-
setValue: asNumber,
45-
setDefaultValue: asNumber,
46-
setFormValue: (value) => value.toString(),
47-
};
48-
49-
export const defaultDateTimeTransformers: Partial<
50-
FormValueTransformers<Date | null>
51-
> = {
52-
setValue: convertToDate,
53-
setDefaultValue: convertToDate,
54-
setFormValue: getDateFormValue,
55-
};
56-
57-
export const defaultFileListTransformer: Partial<
58-
FormValueTransformers<FileList | null>
59-
> = {
60-
setValue: (value) => value || null,
61-
getValue: (value) => value,
62-
setDefaultValue: (value) => value || null,
63-
getDefaultValue: (value) => value,
64-
setFormValue: (files: FileList | null, host: IgcFormControl) => {
65-
if (!host.name || !files) {
66-
return null;
67-
}
68-
69-
const data = new FormData();
70-
71-
for (const file of Array.from(files)) {
72-
data.append(host.name, file);
73-
}
74-
75-
return data;
76-
},
77-
};
78-
79-
/**
80-
* Converts a DateDateRangeValue object to FormData with
81-
* start and end Date values as ISO 8601 strings.
82-
* The keys are prefixed with the host name
83-
* and suffixed with 'start' or 'end' accordingly.
84-
* In case the host does not have a name, it does not participate in form submission.
85-
*
86-
* If the date values are null or undefined, the form data values
87-
* are empty strings ''.
88-
*/
89-
export function getDateRangeFormValue(
90-
value: DateRangeValue | null,
91-
host: IgcFormControl
92-
): FormValueType {
93-
if (!host.name) {
94-
return null;
95-
}
96-
97-
const start = value?.start?.toISOString();
98-
const end = value?.end?.toISOString();
99-
100-
const fd = new FormData();
101-
const prefix = `${host.name}-`;
102-
103-
if (start) {
104-
fd.append(`${prefix}start`, start);
105-
}
106-
if (end) {
107-
fd.append(`${prefix}end`, end);
108-
}
109-
110-
return fd;
111-
}
112-
113-
export const defaultDateRangeTransformers: Partial<
114-
FormValueTransformers<DateRangeValue | null>
115-
> = {
116-
setValue: convertToDateRange,
117-
setDefaultValue: convertToDateRange,
118-
setFormValue: getDateRangeFormValue,
119-
};
3+
type FormValueConfig,
4+
FormValueDefaultTransformers,
5+
type FormValueTransformers,
6+
} from './form-transformers.js';
7+
import type { IgcFormControl } from './types.js';
1208

1219
/* blazorSuppress */
12210
export class FormValue<T> {
12311
private static readonly setFormValueKey = '_setFormValue' as const;
12412

125-
private _host: IgcFormControl;
13+
private readonly _host: IgcFormControl;
14+
private readonly _transformers: FormValueTransformers<T>;
15+
private readonly _setFormValue: IgcFormControl[typeof FormValue.setFormValueKey];
16+
12617
private _value: T;
12718
private _defaultValue: T;
128-
private _transformers: FormValueTransformers<T>;
129-
private _setFormValue: IgcFormControl[typeof FormValue.setFormValueKey];
13019

13120
constructor(host: IgcFormControl, config: FormValueConfig<T>) {
13221
this._host = host;
@@ -135,7 +24,7 @@ export class FormValue<T> {
13524
this._setFormValue = host[FormValue.setFormValueKey];
13625

13726
this._transformers = {
138-
...defaultTransformers,
27+
...FormValueDefaultTransformers,
13928
...config.transformers,
14029
} as FormValueTransformers<T>;
14130
}
@@ -166,10 +55,8 @@ export class FormValue<T> {
16655
}
16756

16857
export function createFormValueState<T>(
169-
host: IgcFormControl,
58+
host: LitElement,
17059
config: FormValueConfig<T>
17160
): FormValue<T> {
172-
return new FormValue(host, config);
61+
return new FormValue(host as IgcFormControl, config);
17362
}
174-
175-
export type FormValueOf<T> = ReturnType<typeof createFormValueState<T>>;

0 commit comments

Comments
 (0)