@sealcode/sealgen 0.15.18 → 0.15.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,6 @@ export declare class Checkbox extends Tickable<boolean> {
10
10
  export declare class CheckboxWithValue extends Checkbox {
11
11
  field: FormField<boolean, boolean>;
12
12
  value: string;
13
- constructor(field: FormField<boolean, boolean>, options: TickableOptions, value: string);
13
+ constructor(field: FormField<boolean, boolean>, options: TickableOptions<boolean>, value: string);
14
14
  getValueAttribute(): string;
15
15
  }
@@ -4,17 +4,18 @@ import { FormField } from "../fields/field.js";
4
4
  import { FormDataValue } from "../form-types.js";
5
5
  import { FormControlContext } from "./form-control.js";
6
6
  import { FormFieldControl } from "./form-field-control.js";
7
- export type TickableOptions = {
7
+ export type TickableOptions<T> = {
8
8
  id?: string;
9
9
  name?: string;
10
10
  label?: string;
11
11
  hide_errors?: boolean;
12
12
  readonly?: boolean;
13
+ default_value: T;
13
14
  };
14
15
  export declare abstract class Tickable<T> extends FormFieldControl {
15
16
  field: FormField<boolean, T>;
16
- options: TickableOptions;
17
- constructor(field: FormField<boolean, T>, options?: TickableOptions);
17
+ options: TickableOptions<T>;
18
+ constructor(field: FormField<boolean, T>, options: TickableOptions<T>);
18
19
  type: "checkbox" | "radio";
19
20
  abstract getValueAttribute(ctx: Context, data: Record<string, FormDataValue>, value: T): string;
20
21
  abstract isChecked(ctx: Context, data: Record<string, FormDataValue>, value: T): boolean;
@@ -32,10 +32,15 @@ export declare abstract class FormField<Required extends boolean = boolean, Pars
32
32
  abstract getEmptyValue(): ParsedValue;
33
33
  isValueValid(_ctx: Context, _value: ParsedValue): Promise<FormFieldValidationResponse>;
34
34
  abstract parse(ctx: Context, raw_value: FormDataValue): Promise<FieldParseResult<ParsedValue>>;
35
- getValue(ctx: Context, all_form_values: Record<string, FormDataValue>): Promise<{
35
+ getValue(ctx: Context, all_form_values: Record<string, FormDataValue>, validate: true): Promise<{
36
36
  valid: boolean;
37
37
  message: string;
38
- parsed: ParsedValue;
38
+ parsed: ParsedValue | null;
39
+ raw: FormDataValue;
40
+ }>;
41
+ getValue(ctx: Context, all_form_values: Record<string, FormDataValue>, validate?: false): Promise<{
42
+ message: string;
43
+ parsed: ParsedValue | null;
39
44
  raw: FormDataValue;
40
45
  }>;
41
46
  setMapToFilter(fn: (parsed: ParsedValue) => unknown): this;
@@ -19,7 +19,7 @@ export type ListSort = {
19
19
  order: "asc" | "desc";
20
20
  };
21
21
  export declare abstract class ListPage<ItemType, F extends typeof BaseListPageFields> extends Page<F> {
22
- abstract getItems(ctx: Context, page: number, itemsPerPage: number, values: Record<string, FormDataValue>): Promise<ItemType[]>;
22
+ abstract getItems(ctx: Context, page: number, itemsPerPage: number | null, values: Record<string, FormDataValue>): Promise<ItemType[]>;
23
23
  abstract getTotalPages(ctx: Context, itemsPerPage: number, values: Record<string, FormDataValue>): Promise<number>;
24
24
  abstract renderItem(ctx: Context, item: ItemType, index: number): Promise<FlatTemplatable>;
25
25
  filterFields: Record<string, FormField>;
@@ -28,7 +28,7 @@ export declare abstract class ListPage<ItemType, F extends typeof BaseListPageFi
28
28
  renderListContainer(_: Context, content: Templatable): FlatTemplatable;
29
29
  getPaginationConfig(ctx: Context): Promise<{
30
30
  page: number;
31
- itemsPerPage: number;
31
+ itemsPerPage: number | null;
32
32
  }>;
33
33
  renderItems(ctx: Context, values?: Record<string, FormDataValue>, items?: ItemType[]): Promise<FlatTemplatable>;
34
34
  render(ctx: Context): Promise<FlatTemplatable>;
@@ -28,11 +28,12 @@ class CheckboxGroup extends FormFieldControl {
28
28
  false,
29
29
  this.field.name,
30
30
  false,
31
- values.includes(option)
31
+ (values || []).includes(option)
32
32
  ),
33
33
  {
34
34
  id: this.field.name + "__" + option,
35
- label: (this.options.checkboxNameToLabel || ((s) => s))(option)
35
+ label: (this.options.checkboxNameToLabel || ((s) => s))(option),
36
+ default_value: false
36
37
  },
37
38
  option
38
39
  )
@@ -12,7 +12,7 @@ class CheckboxedListInput extends FormFieldControl {
12
12
  fctx.ctx,
13
13
  fctx.data
14
14
  );
15
- const pickedValues = Object.entries(currentValue).filter(([, is_on]) => is_on).map(([key]) => key);
15
+ const pickedValues = Object.entries(currentValue || {}).filter(([, is_on]) => is_on).map(([key]) => key);
16
16
  const [options, isVisible] = await Promise.all([
17
17
  this.field.generateOptions(fctx.ctx),
18
18
  this.field.isVisible(fctx.ctx)
@@ -16,7 +16,10 @@ class File extends SimpleInput {
16
16
  </div>`;
17
17
  }
18
18
  async preInput(fctx, data) {
19
- const { parsed } = await this.field.getValue(fctx.ctx, data);
19
+ let { parsed } = await this.field.getValue(fctx.ctx, data);
20
+ if (parsed === null) {
21
+ parsed = { old: null, new: null };
22
+ }
20
23
  return tempstream`${this.renderFilePreview(
21
24
  fctx.ctx,
22
25
  parsed.old
@@ -36,7 +39,7 @@ class File extends SimpleInput {
36
39
  return {
37
40
  ...original,
38
41
  name: this.field.name + "[new]",
39
- required: original.required && !parsed.old,
42
+ required: original.required && !parsed?.old,
40
43
  value: false
41
44
  };
42
45
  }
@@ -1,7 +1,7 @@
1
1
  import { Tickable } from "./tickable.js";
2
2
  class Radio extends Tickable {
3
3
  constructor(field, value, options) {
4
- super(field, options);
4
+ super(field, { default_value: "", ...options });
5
5
  this.field = field;
6
6
  this.value = value;
7
7
  this.type = "radio";
@@ -28,7 +28,8 @@ class SimpleInput extends FormFieldControl {
28
28
  const required = this.field.required;
29
29
  const { parsed, raw, valid } = await this.field.getValue(
30
30
  fctx.ctx,
31
- fctx.data
31
+ fctx.data,
32
+ true
32
33
  );
33
34
  return Object.fromEntries([
34
35
  ["id", this.getID()],
@@ -68,7 +69,8 @@ class SimpleInput extends FormFieldControl {
68
69
  const label = this.getLabel();
69
70
  const { valid, message } = await this.field.getValue(
70
71
  fctx.ctx,
71
- fctx.data
72
+ fctx.data,
73
+ true
72
74
  );
73
75
  return inputWrapper(
74
76
  this.getWrapperModifiers(),
@@ -2,7 +2,7 @@ import { attribute } from "../../sanitize.js";
2
2
  import { getRequiredClass, inputWrapper } from "../../utils/input-wrapper.js";
3
3
  import { FormFieldControl } from "./form-field-control.js";
4
4
  class Tickable extends FormFieldControl {
5
- constructor(field, options = {}) {
5
+ constructor(field, options) {
6
6
  super([field]);
7
7
  this.field = field;
8
8
  this.options = options;
@@ -21,10 +21,12 @@ class Tickable extends FormFieldControl {
21
21
  ];
22
22
  }
23
23
  async render(fctx) {
24
- const { parsed, valid, message } = await this.field.getValue(
25
- fctx.ctx,
26
- fctx.data
27
- );
24
+ const {
25
+ parsed: _parsed,
26
+ valid,
27
+ message
28
+ } = await this.field.getValue(fctx.ctx, fctx.data, true);
29
+ const parsed = _parsed === null ? this.options.default_value : _parsed;
28
30
  const value = this.getValueAttribute(fctx.ctx, fctx.data, parsed);
29
31
  const id = this.makeInputID(fctx.ctx, fctx.data, parsed);
30
32
  const label = this.options.label != void 0 ? this.options.label : this.field.name;
@@ -35,16 +35,16 @@ class FormField {
35
35
  async isValueValid(_ctx, _value) {
36
36
  return { valid: true, message: "" };
37
37
  }
38
- async getValue(ctx, all_form_values) {
39
- if (Object.keys(all_form_values || {}).length === 0) {
38
+ async getValue(ctx, all_form_values, validate = false) {
39
+ const raw = all_form_values[this.name];
40
+ if (raw == void 0 || raw == null || raw == "") {
40
41
  return {
41
- parsed: this.getEmptyValue(),
42
- valid: true,
43
- message: "",
44
- raw: ""
42
+ parsed: null,
43
+ message: "empty value",
44
+ raw,
45
+ ...validate ? { valid: !this.required } : {}
45
46
  };
46
47
  }
47
- const raw = all_form_values[this.name];
48
48
  const {
49
49
  parsable,
50
50
  parsed_value,
@@ -52,19 +52,23 @@ class FormField {
52
52
  } = await this.parse(ctx, raw);
53
53
  if (!parsable) {
54
54
  return {
55
- valid: false,
55
+ ...validate ? { valid: false } : {},
56
56
  parsed: this.getEmptyValue(),
57
57
  raw,
58
58
  message: parse_error || "Error"
59
59
  };
60
60
  }
61
- const { valid, message } = await this._validate(ctx, parsed_value);
62
- return {
63
- parsed: parsed_value,
64
- valid,
65
- message,
66
- raw: all_form_values[this.name]
67
- };
61
+ if (validate) {
62
+ const { valid, message } = await this._validate(ctx, parsed_value);
63
+ return {
64
+ parsed: parsed_value,
65
+ ...validate ? { valid } : {},
66
+ message,
67
+ raw: all_form_values[this.name]
68
+ };
69
+ } else {
70
+ return { parsed: parsed_value, raw, message: "parsed" };
71
+ }
68
72
  }
69
73
  setMapToFilter(fn) {
70
74
  this.mapToFilter = fn;
@@ -54,18 +54,23 @@ describe("basic field", () => {
54
54
  page = await context.newPage();
55
55
  await page.goto(`http://localhost:${port}`);
56
56
  });
57
- it("Doesn't even run `isValueValid` if the value is empty", async () => {
57
+ it("Doesn't call `isValueValid` and `parse` if the value is empty", async () => {
58
58
  const port = await getPort();
59
59
  console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
60
60
  const app = new Koa();
61
61
  const router = new Router();
62
62
  let is_value_valid_called = false;
63
+ let parse_called = false;
63
64
  const fields = {
64
65
  text: new class MockField extends SimpleFormField {
65
66
  async isValueValid() {
66
67
  is_value_valid_called = true;
67
68
  return true;
68
69
  }
70
+ async parse() {
71
+ parse_called = true;
72
+ return true;
73
+ }
69
74
  }(false)
70
75
  };
71
76
  mount(
@@ -93,6 +98,7 @@ describe("basic field", () => {
93
98
  await page.goto(`http://localhost:${port}`);
94
99
  await page.getByRole("button", { name: "Wy\u015Blij" }).click();
95
100
  assert(is_value_valid_called == false);
101
+ assert(parse_called == false);
96
102
  });
97
103
  });
98
104
  });
@@ -50,7 +50,7 @@ class File extends FormField {
50
50
  }
51
51
  async getDatabaseValue(ctx, data) {
52
52
  const { parsed } = await this.getValue(ctx, data);
53
- return parsed.new || void 0;
53
+ return parsed?.new || void 0;
54
54
  }
55
55
  getEmptyValue() {
56
56
  return { old: null, new: null };
@@ -81,6 +81,7 @@ describe("form test", () => {
81
81
  }
82
82
  }
83
83
  );
84
+ console.log(res_axios.data);
84
85
  if (!res_axios.data.includes("Some fields are invalid")) {
85
86
  throw new Error("when sending a empty request with axios, the error didnt appear");
86
87
  }
package/lib/page/list.js CHANGED
@@ -6,9 +6,10 @@ import qs from "qs";
6
6
  import { NumberField } from "../forms/fields/number.js";
7
7
  import { SimpleFormField } from "../forms/fields/simple-form-field.js";
8
8
  const BasePagePropsShape = {};
9
+ const DEFAULT_ITEMS_PER_PAGE = 12;
9
10
  const BaseListPageFields = {
10
11
  page: new NumberField(false, 1),
11
- itemsPerPage: new NumberField(false, 12),
12
+ itemsPerPage: new NumberField(false, DEFAULT_ITEMS_PER_PAGE),
12
13
  sort: new SimpleFormField(false)
13
14
  };
14
15
  const SORT_SEPARATOR = ":";
@@ -48,7 +49,10 @@ class ListPage extends Page {
48
49
  }
49
50
  async getPaginationConfig(ctx) {
50
51
  const values = await this.extractRawValues(ctx);
51
- const { parsed: page } = await this.fields.page.getValue(ctx, values);
52
+ let { parsed: page } = await this.fields.page.getValue(ctx, values);
53
+ if (!page) {
54
+ page = 1;
55
+ }
52
56
  const { parsed: itemsPerPage } = await this.fields.itemsPerPage.getValue(ctx, values);
53
57
  return { page, itemsPerPage };
54
58
  }
@@ -70,7 +74,11 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
70
74
  }
71
75
  async renderPagination(ctx, values) {
72
76
  const { itemsPerPage, page } = await this.getPaginationConfig(ctx);
73
- const totalIems = await this.getTotalPages(ctx, itemsPerPage, values);
77
+ const totalIems = await this.getTotalPages(
78
+ ctx,
79
+ itemsPerPage || DEFAULT_ITEMS_PER_PAGE,
80
+ values
81
+ );
74
82
  return tempstream`<div class="list-pagination">
75
83
  <div class="list-pagination__left">
76
84
  ${page > 1 ? this.renderPageButton(ctx, 1, "Pierwsza strona") : ""}
@@ -42,9 +42,9 @@ class MountableWithFields extends Mountable {
42
42
  let valid = true;
43
43
  const form_messages = [];
44
44
  await Promise.all(
45
- Object.entries(values).filter(([fieldname]) => fieldname in this.fields).map(async ([key]) => {
45
+ Object.keys(this.fields).map(async (key) => {
46
46
  const field = this.fields[key];
47
- const { valid: fieldvalid, message: fieldmessage } = await field.getValue(ctx, values);
47
+ const { valid: fieldvalid, message: fieldmessage } = await field.getValue(ctx, values, true);
48
48
  if (!fieldvalid) {
49
49
  valid = false;
50
50
  field_errors[field.name] = fieldmessage;
@@ -81,7 +81,7 @@ class MountableWithFields extends Mountable {
81
81
  const result = {};
82
82
  const promises = Object.entries(this.fields).map(
83
83
  async ([key, field]) => {
84
- const { parsed } = await field.getValue(ctx, raw_values);
84
+ const { parsed } = await field.getValue(ctx, raw_values, false);
85
85
  result[key] = parsed;
86
86
  }
87
87
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealcode/sealgen",
3
- "version": "0.15.18",
3
+ "version": "0.15.19",
4
4
  "description": "Module to automate adding routes and collections to a sealious application",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
@@ -51,7 +51,7 @@ export class CheckboxGroup<
51
51
  false,
52
52
  this.field.name,
53
53
  false,
54
- values.includes(option)
54
+ (values || []).includes(option)
55
55
  ),
56
56
  {
57
57
  id: this.field.name + "__" + option,
@@ -59,6 +59,7 @@ export class CheckboxGroup<
59
59
  this.options.checkboxNameToLabel ||
60
60
  ((s: string) => s)
61
61
  )(option),
62
+ default_value: false,
62
63
  },
63
64
  option
64
65
  )
@@ -22,7 +22,7 @@ export class Checkbox extends Tickable<boolean> {
22
22
  export class CheckboxWithValue extends Checkbox {
23
23
  constructor(
24
24
  public field: FormField<boolean, boolean>,
25
- options: TickableOptions,
25
+ options: TickableOptions<boolean>,
26
26
  public value: string
27
27
  ) {
28
28
  super(field, options);
@@ -17,7 +17,7 @@ export class CheckboxedListInput extends FormFieldControl {
17
17
  fctx.ctx,
18
18
  fctx.data
19
19
  );
20
- const pickedValues = Object.entries(currentValue)
20
+ const pickedValues = Object.entries(currentValue || {})
21
21
  .filter(([, is_on]) => is_on)
22
22
  .map(([key]) => key);
23
23
  const [options, isVisible] = await Promise.all([
@@ -30,7 +30,10 @@ export class File extends SimpleInput<FileContainer> {
30
30
  fctx: FormControlContext,
31
31
  data: Record<string, FormDataValue>
32
32
  ) {
33
- const { parsed } = await this.field.getValue(fctx.ctx, data);
33
+ let { parsed } = await this.field.getValue(fctx.ctx, data);
34
+ if (parsed === null) {
35
+ parsed = { old: null, new: null };
36
+ }
34
37
  return tempstream/* HTML */ `${this.renderFilePreview(
35
38
  fctx.ctx,
36
39
  parsed.old
@@ -53,7 +56,7 @@ export class File extends SimpleInput<FileContainer> {
53
56
  return {
54
57
  ...original,
55
58
  name: this.field.name + "[new]",
56
- required: original.required && !parsed.old,
59
+ required: original.required && !parsed?.old,
57
60
  value: false,
58
61
  };
59
62
  }
@@ -11,7 +11,7 @@ export class Radio extends Tickable<string> {
11
11
  public value: string,
12
12
  options: { label?: string }
13
13
  ) {
14
- super(field, options);
14
+ super(field, { default_value: "", ...options });
15
15
  }
16
16
 
17
17
  makeInputID(): string {
@@ -80,7 +80,8 @@ export class SimpleInput<
80
80
  const required = this.field.required;
81
81
  const { parsed, raw, valid } = await this.field.getValue(
82
82
  fctx.ctx,
83
- fctx.data
83
+ fctx.data,
84
+ true
84
85
  );
85
86
  return Object.fromEntries([
86
87
  <const>["id", this.getID()],
@@ -132,7 +133,8 @@ export class SimpleInput<
132
133
 
133
134
  const { valid, message } = await this.field.getValue(
134
135
  fctx.ctx,
135
- fctx.data
136
+ fctx.data,
137
+ true
136
138
  );
137
139
  return inputWrapper(
138
140
  this.getWrapperModifiers(),
@@ -7,18 +7,19 @@ import { FormDataValue } from "../form-types.js";
7
7
  import { FormControlContext } from "./form-control.js";
8
8
  import { FormFieldControl } from "./form-field-control.js";
9
9
 
10
- export type TickableOptions = {
10
+ export type TickableOptions<T> = {
11
11
  id?: string;
12
12
  name?: string;
13
13
  label?: string;
14
14
  hide_errors?: boolean;
15
15
  readonly?: boolean;
16
+ default_value: T;
16
17
  };
17
18
 
18
19
  export abstract class Tickable<T> extends FormFieldControl {
19
20
  constructor(
20
21
  public field: FormField<boolean, T>,
21
- public options: TickableOptions = {}
22
+ public options: TickableOptions<T>
22
23
  ) {
23
24
  super([field]);
24
25
  }
@@ -65,10 +66,13 @@ export abstract class Tickable<T> extends FormFieldControl {
65
66
 
66
67
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
67
68
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
68
- const { parsed, valid, message } = await this.field.getValue(
69
- fctx.ctx,
70
- fctx.data
71
- );
69
+ const {
70
+ parsed: _parsed,
71
+ valid,
72
+ message,
73
+ } = await this.field.getValue(fctx.ctx, fctx.data, true);
74
+ const parsed = _parsed === null ? this.options.default_value : _parsed;
75
+
72
76
  const value = this.getValueAttribute(fctx.ctx, fctx.data, parsed);
73
77
  const id = this.makeInputID(fctx.ctx, fctx.data, parsed);
74
78
  const label =
@@ -65,13 +65,14 @@ describe("basic field", () => {
65
65
  await page.goto(`http://localhost:${port}`);
66
66
  });
67
67
 
68
- it("Doesn't even run `isValueValid` if the value is empty", async () => {
68
+ it("Doesn't call `isValueValid` and `parse` if the value is empty", async () => {
69
69
  const port = await getPort();
70
70
  console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
71
71
  const app = new Koa();
72
72
  const router = new Router();
73
73
 
74
74
  let is_value_valid_called = false;
75
+ let parse_called = false;
75
76
 
76
77
  const fields = {
77
78
  text: new (class MockField extends SimpleFormField<false> {
@@ -79,6 +80,11 @@ describe("basic field", () => {
79
80
  is_value_valid_called = true;
80
81
  return true as any;
81
82
  }
83
+
84
+ public async parse() {
85
+ parse_called = true;
86
+ return true as any;
87
+ }
82
88
  })(false),
83
89
  };
84
90
 
@@ -108,6 +114,7 @@ describe("basic field", () => {
108
114
  await page.goto(`http://localhost:${port}`);
109
115
  await page.getByRole("button", { name: "Wyślij" }).click();
110
116
  assert(is_value_valid_called == false);
117
+ assert(parse_called == false);
111
118
  });
112
119
  });
113
120
  });
@@ -99,23 +99,42 @@ export abstract class FormField<
99
99
 
100
100
  async getValue(
101
101
  ctx: Context,
102
- all_form_values: Record<string, FormDataValue>
102
+ all_form_values: Record<string, FormDataValue>,
103
+ validate: true
103
104
  ): Promise<{
104
105
  valid: boolean;
105
106
  message: string;
106
- parsed: ParsedValue;
107
+ parsed: ParsedValue | null;
108
+ raw: FormDataValue;
109
+ }>;
110
+ async getValue(
111
+ ctx: Context,
112
+ all_form_values: Record<string, FormDataValue>,
113
+ validate?: false
114
+ ): Promise<{
115
+ message: string;
116
+ parsed: ParsedValue | null;
117
+ raw: FormDataValue;
118
+ }>;
119
+ async getValue(
120
+ ctx: Context,
121
+ all_form_values: Record<string, FormDataValue>,
122
+ validate = false
123
+ ): Promise<{
124
+ valid?: boolean;
125
+ message: string;
126
+ parsed: ParsedValue | null;
107
127
  raw: FormDataValue;
108
128
  }> {
109
- if (Object.keys(all_form_values || {}).length === 0) {
110
- // don't validate on virgin form
129
+ const raw = all_form_values[this.name];
130
+ if (raw == undefined || raw == null || raw == "") {
111
131
  return {
112
- parsed: this.getEmptyValue(),
113
- valid: true,
114
- message: "",
115
- raw: "",
132
+ parsed: null,
133
+ message: "empty value",
134
+ raw,
135
+ ...(validate ? { valid: !this.required } : {}),
116
136
  };
117
137
  }
118
- const raw = all_form_values[this.name];
119
138
  const {
120
139
  parsable,
121
140
  parsed_value,
@@ -123,19 +142,23 @@ export abstract class FormField<
123
142
  } = await this.parse(ctx, raw);
124
143
  if (!parsable) {
125
144
  return {
126
- valid: false,
145
+ ...(validate ? { valid: false } : {}),
127
146
  parsed: this.getEmptyValue(),
128
147
  raw,
129
148
  message: parse_error || "Error",
130
149
  };
131
150
  }
132
- const { valid, message } = await this._validate(ctx, parsed_value);
133
- return {
134
- parsed: parsed_value,
135
- valid,
136
- message,
137
- raw: all_form_values[this.name],
138
- };
151
+ if (validate) {
152
+ const { valid, message } = await this._validate(ctx, parsed_value);
153
+ return {
154
+ parsed: parsed_value,
155
+ ...(validate ? { valid } : {}),
156
+ message,
157
+ raw: all_form_values[this.name],
158
+ };
159
+ } else {
160
+ return { parsed: parsed_value, raw, message: "parsed" };
161
+ }
139
162
  }
140
163
 
141
164
  setMapToFilter(fn: (parsed: ParsedValue) => unknown) {
@@ -76,7 +76,7 @@ export class File<Required extends boolean> extends FormField<
76
76
 
77
77
  async getDatabaseValue(ctx: Context, data: Record<string, FormDataValue>) {
78
78
  const { parsed } = await this.getValue(ctx, data);
79
- return parsed.new || undefined;
79
+ return parsed?.new || undefined;
80
80
  }
81
81
 
82
82
  getEmptyValue() {
@@ -91,6 +91,7 @@ describe("form test", () => {
91
91
  },
92
92
  }
93
93
  );
94
+ console.log(res_axios.data);
94
95
  if (!res_axios.data.includes("Some fields are invalid")) {
95
96
  throw new Error("when sending a empty request with axios, the error didnt appear");
96
97
  }
package/src/page/list.ts CHANGED
@@ -14,9 +14,11 @@ import { FormDataValue } from "../forms/form-types.js";
14
14
  export const BasePagePropsShape = <const>{};
15
15
  export type BasePageProps = ShapeToType<typeof BasePagePropsShape>;
16
16
 
17
+ const DEFAULT_ITEMS_PER_PAGE = 12;
18
+
17
19
  export const BaseListPageFields = <const>{
18
20
  page: new NumberField(false, 1),
19
- itemsPerPage: new NumberField(false, 12),
21
+ itemsPerPage: new NumberField(false, DEFAULT_ITEMS_PER_PAGE),
20
22
  sort: new SimpleFormField(false),
21
23
  };
22
24
 
@@ -51,7 +53,7 @@ export abstract class ListPage<
51
53
  abstract getItems(
52
54
  ctx: Context,
53
55
  page: number,
54
- itemsPerPage: number,
56
+ itemsPerPage: number | null,
55
57
  values: Record<string, FormDataValue>
56
58
  ): Promise<ItemType[]>;
57
59
 
@@ -83,7 +85,10 @@ export abstract class ListPage<
83
85
 
84
86
  async getPaginationConfig(ctx: Context) {
85
87
  const values = await this.extractRawValues(ctx);
86
- const { parsed: page } = await this.fields.page.getValue(ctx, values);
88
+ let { parsed: page } = await this.fields.page.getValue(ctx, values);
89
+ if (!page) {
90
+ page = 1;
91
+ }
87
92
  const { parsed: itemsPerPage } =
88
93
  await this.fields.itemsPerPage.getValue(ctx, values);
89
94
  return { page, itemsPerPage };
@@ -120,7 +125,11 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
120
125
  values: Record<string, FormDataValue>
121
126
  ): Promise<FlatTemplatable> {
122
127
  const { itemsPerPage, page } = await this.getPaginationConfig(ctx);
123
- const totalIems = await this.getTotalPages(ctx, itemsPerPage, values);
128
+ const totalIems = await this.getTotalPages(
129
+ ctx,
130
+ itemsPerPage || DEFAULT_ITEMS_PER_PAGE,
131
+ values
132
+ );
124
133
 
125
134
  return tempstream/* HTML */ `<div class="list-pagination">
126
135
  <div class="list-pagination__left">
@@ -82,18 +82,17 @@ export abstract class MountableWithFields<
82
82
  const field_errors = {} as Record<keyof Fields, string>;
83
83
  let valid = true;
84
84
  const form_messages = [] as FormMessage[];
85
+
85
86
  await Promise.all(
86
- Object.entries(values)
87
- .filter(([fieldname]) => fieldname in this.fields)
88
- .map(async ([key]: [keyof F, FormDataValue]) => {
89
- const field = this.fields[key];
90
- const { valid: fieldvalid, message: fieldmessage } =
91
- await field.getValue(ctx, values);
92
- if (!fieldvalid) {
93
- valid = false;
94
- field_errors[field.name] = fieldmessage;
95
- }
96
- })
87
+ Object.keys(this.fields).map(async (key: keyof F) => {
88
+ const field = this.fields[key];
89
+ const { valid: fieldvalid, message: fieldmessage } =
90
+ await field.getValue(ctx, values, true);
91
+ if (!fieldvalid) {
92
+ valid = false;
93
+ field_errors[field.name] = fieldmessage;
94
+ }
95
+ })
97
96
  );
98
97
  const formValidationResult = await this.validateValues(ctx, values);
99
98
  if (!formValidationResult.valid) {
@@ -143,7 +142,7 @@ export abstract class MountableWithFields<
143
142
  const promises = Object.entries(this.fields).map(
144
143
  async ([key, field]) => {
145
144
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
146
- const { parsed } = await field.getValue(ctx, raw_values);
145
+ const { parsed } = await field.getValue(ctx, raw_values, false);
147
146
  result[key] = parsed;
148
147
  }
149
148
  );