@sealcode/sealgen 0.15.6 → 0.15.8
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.
- package/@types/src/builder.d.ts +0 -1
- package/@types/src/esbuild-plugins/load-assets.d.ts +2 -1
- package/@types/src/esbuild-plugins/rewrite-asset-imports.d.ts +2 -1
- package/@types/src/forms/controls/autocomplete.d.ts +2 -1
- package/@types/src/forms/controls/checkbox-group.d.ts +1 -2
- package/@types/src/forms/controls/collapsible-field-group.d.ts +2 -1
- package/@types/src/forms/controls/form-control.d.ts +1 -1
- package/@types/src/forms/controls/multiple-files.d.ts +3 -3
- package/@types/src/forms/controls/photo.d.ts +1 -1
- package/@types/src/forms/controls/simple-input.d.ts +1 -1
- package/@types/src/forms/controls/textarea.d.ts +1 -1
- package/@types/src/forms/controls/tickable.d.ts +2 -2
- package/@types/src/forms/fields/field.d.ts +2 -2
- package/@types/src/forms/fields/file.d.ts +1 -1
- package/@types/src/forms/fields/structured-array.d.ts +1 -1
- package/@types/src/forms/form.d.ts +1 -1
- package/@types/src/mount.d.ts +2 -1
- package/@types/src/page/sealious-list.d.ts +1 -1
- package/@types/src/page/stateful-page.d.ts +6 -6
- package/@types/src/page/stateful-page.test.d.ts +1 -0
- package/@types/src/utils/translate.d.ts +1 -1
- package/lib/forms/controls/multiple-files.js +5 -3
- package/lib/page/stateful-page.js +4 -2
- package/lib/page/stateful-page.test.js +26 -0
- package/lib/templates/jdd-component.js +0 -1
- package/lib/templates/page.js +1 -1
- package/lib/templates/route-test.js +5 -7
- package/package.json +3 -1
- package/src/forms/controls/multiple-files.ts +5 -3
- package/src/page/stateful-page.test.ts +32 -0
- package/src/page/stateful-page.ts +15 -5
- package/src/templates/jdd-component.ts +0 -1
- package/src/templates/page.ts +1 -1
- package/src/templates/route-test.ts +5 -7
package/@types/src/builder.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
1
2
|
import { Context } from "koa";
|
|
2
3
|
import { PickFromListField } from "../fields/pick-from-list.js";
|
|
3
4
|
import { FormControlContext } from "./form-control.js";
|
|
@@ -18,5 +19,5 @@ export declare class Autocomplete extends SimpleInput<string | null> {
|
|
|
18
19
|
getInputAttributes(fctx: FormControlContext): Promise<{
|
|
19
20
|
list: string;
|
|
20
21
|
}>;
|
|
21
|
-
postInput(ctx: Context): Promise<
|
|
22
|
+
postInput(ctx: Context): Promise<import("stream").Readable>;
|
|
22
23
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { FlatTemplatable } from "tempstream";
|
|
2
2
|
import { EnumMultipleField } from "../fields/enum-multiple.js";
|
|
3
|
-
import { FormField } from "../fields/field.js";
|
|
4
3
|
import { CheckboxWithValue } from "./checkbox.js";
|
|
5
4
|
import { FormControlContext } from "./form-control.js";
|
|
6
5
|
import { FormFieldControl } from "./form-field-control.js";
|
|
@@ -18,5 +17,5 @@ export declare class CheckboxGroup<Options extends DefaultCheckboxOptions = Defa
|
|
|
18
17
|
options: Options;
|
|
19
18
|
constructor(field: EnumMultipleField<readonly string[], boolean>, options?: Options);
|
|
20
19
|
wrapGroup(fctx: FormControlContext, checkboxes: CheckboxWithValue[]): Promise<FlatTemplatable>;
|
|
21
|
-
render
|
|
20
|
+
render(fctx: FormControlContext): Promise<FlatTemplatable>;
|
|
22
21
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
1
2
|
import { FlatTemplatable } from "tempstream";
|
|
2
3
|
import { FieldGroup } from "./field-group.js";
|
|
3
4
|
import { FormControl } from "./form-control.js";
|
|
@@ -14,7 +15,7 @@ export declare class CollapsibleFieldGroup extends FieldGroup {
|
|
|
14
15
|
options: CollapsibleFieldGroupOptions;
|
|
15
16
|
collapseID: string;
|
|
16
17
|
constructor(controls: FormControl[], options?: CollapsibleFieldGroupOptions);
|
|
17
|
-
getGroupLabelProps():
|
|
18
|
+
getGroupLabelProps(): import("stream").Readable;
|
|
18
19
|
preLabelContainer(): FlatTemplatable;
|
|
19
20
|
postLabel(): FlatTemplatable;
|
|
20
21
|
}
|
|
@@ -15,5 +15,5 @@ export declare class FormControlContext {
|
|
|
15
15
|
export declare abstract class FormControl {
|
|
16
16
|
abstract render(fctx: FormControlContext): FlatTemplatable | Promise<FlatTemplatable>;
|
|
17
17
|
abstract role: "input" | "decoration" | "messages" | "submit";
|
|
18
|
-
mount(
|
|
18
|
+
mount(_router: Router): void;
|
|
19
19
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="koa__router" />
|
|
2
2
|
/// <reference types="node" resolution-mode="require"/>
|
|
3
3
|
import Router from "@koa/router";
|
|
4
|
-
import { CollectionItem, Field as SealiousField } from "sealious";
|
|
4
|
+
import { Collection, CollectionItem, Field as SealiousField } from "sealious";
|
|
5
5
|
import { Context } from "koa";
|
|
6
6
|
import { FlatTemplatable } from "tempstream";
|
|
7
7
|
import { MultipleFiles as MultipleFilesField } from "../fields/multiple-files.js";
|
|
@@ -25,9 +25,9 @@ export declare class MultipleFiles extends FormFieldControl {
|
|
|
25
25
|
getFrameID(): string;
|
|
26
26
|
renderFrame(_ctx: Context, content?: FlatTemplatable): import("stream").Readable;
|
|
27
27
|
render(fctx: FormControlContext): FlatTemplatable | Promise<FlatTemplatable>;
|
|
28
|
-
getReferencingCollection():
|
|
28
|
+
getReferencingCollection(): Collection;
|
|
29
29
|
getFileField(): SealiousField | null;
|
|
30
|
-
extractFileFromItem(item: CollectionItem): Promise<FilePointer | null>;
|
|
30
|
+
extractFileFromItem(item: CollectionItem<Collection>): Promise<FilePointer | null>;
|
|
31
31
|
renderFileItemPreview(_fileItem: CollectionItem, file: FilePointer): Promise<FlatTemplatable>;
|
|
32
32
|
renderFileItem(fileItem: CollectionItem, file: FilePointer): Promise<FlatTemplatable>;
|
|
33
33
|
renderFileRemoveButton(fileItem: CollectionItem): FlatTemplatable;
|
|
@@ -12,7 +12,7 @@ export declare class Photo extends File {
|
|
|
12
12
|
imageRouter: KoaResponsiveImageRouter;
|
|
13
13
|
constructor(field: FormField<boolean, FileContainer>, imageRouter: KoaResponsiveImageRouter, options?: SimpleInputOptions);
|
|
14
14
|
getImgStyle(): Promise<string>;
|
|
15
|
-
renderFilePreview(
|
|
15
|
+
renderFilePreview(_ctx: Context, file: FilePointer | null): Promise<FlatTemplatable>;
|
|
16
16
|
getWrapperModifiers(): string[];
|
|
17
17
|
getInputAttributes(fctx: FormControlContext): Promise<{
|
|
18
18
|
accept: string;
|
|
@@ -29,5 +29,5 @@ export declare class SimpleInput<ParsedValue, Options extends SimpleInputOptions
|
|
|
29
29
|
postInput(_: Context): Promise<FlatTemplatable>;
|
|
30
30
|
renderInput(_ctx: Context, attributes_str: string, _data: Record<string, FormDataValue>): Promise<FlatTemplatable>;
|
|
31
31
|
getWrapperModifiers(): string[];
|
|
32
|
-
render
|
|
32
|
+
render(fctx: FormControlContext): Promise<FlatTemplatable>;
|
|
33
33
|
}
|
|
@@ -3,7 +3,7 @@ import { FormField } from "../fields/field.js";
|
|
|
3
3
|
import { FormDataValue } from "../form-types.js";
|
|
4
4
|
import { FormControlContext } from "./form-control.js";
|
|
5
5
|
import { SimpleInput, SimpleInputOptions } from "./simple-input.js";
|
|
6
|
-
export declare class Textarea
|
|
6
|
+
export declare class Textarea extends SimpleInput<string | null> {
|
|
7
7
|
field: FormField<boolean, string | null>;
|
|
8
8
|
options: SimpleInputOptions & {
|
|
9
9
|
label?: string;
|
|
@@ -11,7 +11,7 @@ export type TickableOptions = {
|
|
|
11
11
|
hide_errors?: boolean;
|
|
12
12
|
readonly?: boolean;
|
|
13
13
|
};
|
|
14
|
-
export declare abstract class Tickable<T
|
|
14
|
+
export declare abstract class Tickable<T> extends FormFieldControl {
|
|
15
15
|
field: FormField<boolean, T>;
|
|
16
16
|
options: TickableOptions;
|
|
17
17
|
constructor(field: FormField<boolean, T>, options?: TickableOptions);
|
|
@@ -20,5 +20,5 @@ export declare abstract class Tickable<T extends unknown> extends FormFieldContr
|
|
|
20
20
|
abstract isChecked(ctx: Context, data: Record<string, FormDataValue>, value: T): boolean;
|
|
21
21
|
makeInputID(ctx: Context, data: Record<string, FormDataValue>, value: T): string;
|
|
22
22
|
getWrapperClasses(ctx: Context, data: Record<string, FormDataValue>, value: T): string[];
|
|
23
|
-
render
|
|
23
|
+
render(fctx: FormControlContext): Promise<FlatTemplatable>;
|
|
24
24
|
}
|
|
@@ -21,7 +21,7 @@ export type FieldParseResult<T> = {
|
|
|
21
21
|
error: string;
|
|
22
22
|
parsed_value: null;
|
|
23
23
|
};
|
|
24
|
-
export declare abstract class FormField<Required extends boolean = boolean, ParsedValue
|
|
24
|
+
export declare abstract class FormField<Required extends boolean = boolean, ParsedValue = unknown> {
|
|
25
25
|
readonly required: Required;
|
|
26
26
|
name: string;
|
|
27
27
|
constructor(required: Required);
|
|
@@ -30,7 +30,7 @@ export declare abstract class FormField<Required extends boolean = boolean, Pars
|
|
|
30
30
|
init(fieldname: string): Promise<void>;
|
|
31
31
|
_validate(ctx: Context, value: ParsedValue): Promise<FormFieldValidationResponse>;
|
|
32
32
|
abstract getEmptyValue(): ParsedValue;
|
|
33
|
-
isValueValid(
|
|
33
|
+
isValueValid(_ctx: Context, _value: ParsedValue): Promise<FormFieldValidationResponse>;
|
|
34
34
|
abstract parse(ctx: Context, raw_value: FormDataValue): Promise<FieldParseResult<ParsedValue>>;
|
|
35
35
|
getValue(ctx: Context, all_form_values: Record<string, FormDataValue>): Promise<{
|
|
36
36
|
valid: boolean;
|
|
@@ -10,7 +10,7 @@ export declare class File<Required extends boolean> extends FormField<Required,
|
|
|
10
10
|
readonly required: Required;
|
|
11
11
|
file_manager: FileManager;
|
|
12
12
|
constructor(required: Required, file_manager: FileManager);
|
|
13
|
-
parse(
|
|
13
|
+
parse(_ctx: Context, raw_value: FormDataValue): Promise<FieldParseResult<FileContainer>>;
|
|
14
14
|
getDatabaseValue(ctx: Context, data: Record<string, FormDataValue>): Promise<FilePointer | undefined>;
|
|
15
15
|
getEmptyValue(): {
|
|
16
16
|
old: null;
|
|
@@ -5,7 +5,7 @@ import { FormField } from "./field.js";
|
|
|
5
5
|
export declare class StructuredArrayField<Fields extends Record<string, Field>> extends FormField<true, FieldTypes.ArrayStorageInput<FieldsetOutput<Fields>>> {
|
|
6
6
|
sealious_field: FieldTypes.StructuredArray<Fields>;
|
|
7
7
|
constructor(sealious_field: FieldTypes.StructuredArray<Fields>);
|
|
8
|
-
parse(
|
|
8
|
+
parse(_context: Context, value: FormDataValue): Promise<{
|
|
9
9
|
parsable: true;
|
|
10
10
|
error: null;
|
|
11
11
|
parsed_value: FieldsetOutput<Fields>;
|
|
@@ -4,7 +4,7 @@ import Router from "@koa/router";
|
|
|
4
4
|
import { FlatTemplatable } from "tempstream";
|
|
5
5
|
import { FormData, FormDataValue, FormMessage, FormReaction } from "./form-types.js";
|
|
6
6
|
import { Fields, MountableWithFields, PageErrorMessage } from "../page/mountable-with-fields.js";
|
|
7
|
-
export declare abstract class Form<F extends Fields, SubmitResult
|
|
7
|
+
export declare abstract class Form<F extends Fields, SubmitResult> extends MountableWithFields<F> {
|
|
8
8
|
defaultSuccessMessage: string;
|
|
9
9
|
submitButtonText: string;
|
|
10
10
|
action: string;
|
package/@types/src/mount.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="koa__router" />
|
|
2
2
|
import Router from "@koa/router";
|
|
3
|
+
import { FileManager } from "@sealcode/file-manager";
|
|
3
4
|
import { Mountable } from "./page/mountable.js";
|
|
4
5
|
export declare function mount(router: Router, url: string | {
|
|
5
6
|
rawURL: string;
|
|
6
|
-
}, mountable: Mountable, use_dummy_app?: boolean): void;
|
|
7
|
+
}, mountable: Mountable, use_dummy_app?: boolean, file_manager?: FileManager | undefined): void;
|
|
@@ -11,7 +11,7 @@ export declare abstract class SealiousItemListPage<C extends Collection, F exten
|
|
|
11
11
|
makeQuery(ctx: Context): Promise<ItemList<C>>;
|
|
12
12
|
getItems(ctx: Context, page: number, itemsPerPage: number): Promise<CollectionItem<C>[]>;
|
|
13
13
|
getFilterValues(ctx: Context): Promise<Record<string, string | undefined>>;
|
|
14
|
-
renderItem(_: Context, item: CollectionItem<C>,
|
|
14
|
+
renderItem(_: Context, item: CollectionItem<C>, _index: number): Promise<FlatTemplatable>;
|
|
15
15
|
canAccess: (ctx: Context) => Promise<{
|
|
16
16
|
canAccess: boolean;
|
|
17
17
|
message: string;
|
|
@@ -21,9 +21,9 @@ export type StateAndMetadata<State, Actions> = {
|
|
|
21
21
|
action_args: null;
|
|
22
22
|
$: Record<string, unknown>;
|
|
23
23
|
};
|
|
24
|
-
export declare abstract class StatefulPage<State extends Record<string, unknown>, Actions extends Record<string, (state: State, inputs: Record<string, string>, ...args: unknown[]) => State | Promise<State>>> extends Mountable {
|
|
24
|
+
export declare abstract class StatefulPage<State extends Record<string, unknown>, Actions extends Record<string, (ctx: BaseContext, state: State, inputs: Record<string, string>, ...args: unknown[]) => State | Promise<State>>> extends Mountable {
|
|
25
25
|
abstract actions: Actions;
|
|
26
|
-
abstract getInitialState(): State | Promise<State>;
|
|
26
|
+
abstract getInitialState(ctx: BaseContext): State | Promise<State>;
|
|
27
27
|
abstract render(ctx: BaseContext, state: State, inputs: Record<string, string>): Templatable | Promise<Templatable>;
|
|
28
28
|
canAccess(): Promise<{
|
|
29
29
|
readonly canAccess: true;
|
|
@@ -37,15 +37,15 @@ export declare abstract class StatefulPage<State extends Record<string, unknown>
|
|
|
37
37
|
label: string;
|
|
38
38
|
content: string;
|
|
39
39
|
};
|
|
40
|
-
makeActionURL<ActionName extends keyof Actions>(action_description: StatefulPageActionDescription<ActionName>, ...args: ExtractTail<ExtractTail<Parameters<Actions[ActionName]
|
|
41
|
-
makeActionButton<ActionName extends keyof Actions>(_state: State, action_description: StatefulPageActionDescription<ActionName>, ...args: ExtractTail<ExtractTail<Parameters<Actions[ActionName]
|
|
40
|
+
makeActionURL<ActionName extends keyof Actions>(action_description: StatefulPageActionDescription<ActionName>, ...args: ExtractTail<ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>>): string;
|
|
41
|
+
makeActionButton<ActionName extends keyof Actions>(_state: State, action_description: StatefulPageActionDescription<ActionName>, ...args: ExtractTail<ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>>): string;
|
|
42
42
|
makeActionCallback<ActionName extends keyof Actions>(action_description: {
|
|
43
43
|
action: ActionName;
|
|
44
44
|
label?: string;
|
|
45
|
-
} | ActionName, ...args: ExtractTail<ExtractTail<Parameters<Actions[ActionName]
|
|
45
|
+
} | ActionName, ...args: ExtractTail<ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>>): string;
|
|
46
46
|
rerender(): string;
|
|
47
47
|
preprocessState(values: State): Promise<State>;
|
|
48
|
-
preprocessOverrides(
|
|
48
|
+
preprocessOverrides(_context: BaseContext, _state: State, values: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
49
49
|
serializeState(_context: BaseContext, state: State): Promise<string>;
|
|
50
50
|
deserializeState(_context: BaseContext, s: string): Promise<State>;
|
|
51
51
|
extractState(ctx: BaseContext): Promise<StateAndMetadata<State, Actions>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Context } from "koa";
|
|
2
2
|
import { Translation } from "sealious";
|
|
3
|
-
export declare function t(ctx: Context, key: string, props:
|
|
3
|
+
export declare function t(ctx: Context, key: string, props: unknown[], default_translation: Translation): string;
|
|
@@ -102,9 +102,11 @@ class MultipleFiles extends FormFieldControl {
|
|
|
102
102
|
if (!sealious_field) {
|
|
103
103
|
return null;
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
const token = item.get(sealious_field.name).token;
|
|
106
|
+
if (!token) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return await item.collection.app.FileManager.fromToken(token);
|
|
108
110
|
}
|
|
109
111
|
async renderFileItemPreview(_fileItem, file) {
|
|
110
112
|
return file.getOriginalFilename();
|
|
@@ -84,7 +84,7 @@ class StatefulPage extends Mountable {
|
|
|
84
84
|
async preprocessState(values) {
|
|
85
85
|
return values;
|
|
86
86
|
}
|
|
87
|
-
async preprocessOverrides(_state, values) {
|
|
87
|
+
async preprocessOverrides(_context, _state, values) {
|
|
88
88
|
return values;
|
|
89
89
|
}
|
|
90
90
|
async serializeState(_context, state) {
|
|
@@ -123,6 +123,7 @@ class StatefulPage extends Mountable {
|
|
|
123
123
|
const $body = ctx.$body;
|
|
124
124
|
let state_overrides = $body.$ || {};
|
|
125
125
|
state_overrides = await this.preprocessOverrides(
|
|
126
|
+
ctx,
|
|
126
127
|
original_state,
|
|
127
128
|
state_overrides
|
|
128
129
|
);
|
|
@@ -169,12 +170,13 @@ class StatefulPage extends Mountable {
|
|
|
169
170
|
}
|
|
170
171
|
mount(router, path) {
|
|
171
172
|
router.get(path, async (ctx) => {
|
|
172
|
-
ctx.body = this.render(ctx, await this.getInitialState(), {});
|
|
173
|
+
ctx.body = this.render(ctx, await this.getInitialState(ctx), {});
|
|
173
174
|
});
|
|
174
175
|
router.post(path, async (ctx) => {
|
|
175
176
|
const { action, state, inputs, action_args } = await this.extractState(ctx);
|
|
176
177
|
if (action) {
|
|
177
178
|
const new_state = await this.actions[action](
|
|
179
|
+
ctx,
|
|
178
180
|
state,
|
|
179
181
|
inputs,
|
|
180
182
|
...JSON.parse(
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { tempstream } from "tempstream";
|
|
2
|
+
import { StatefulPage } from "./stateful-page.js";
|
|
3
|
+
describe("stateful page", () => {
|
|
4
|
+
it("handles a basic case with action buttons", () => {
|
|
5
|
+
const actions = {
|
|
6
|
+
some_action: async (_ctx, _state, _inputs, _arg1) => {
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
new class extends StatefulPage {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.actions = actions;
|
|
13
|
+
}
|
|
14
|
+
getInitialState(_ctx) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
wrapInLayout(_ctx, content, _state) {
|
|
18
|
+
return content;
|
|
19
|
+
}
|
|
20
|
+
render(_ctx, state) {
|
|
21
|
+
return tempstream`<div>${this.makeActionButton(state, "some_action", 2)}</div>`;
|
|
22
|
+
}
|
|
23
|
+
}();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=stateful-page.test.js.map
|
package/lib/templates/page.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { curryImportPath } from "../utils/import-path.js";
|
|
2
2
|
async function pageTemplate(action_name, newfilefullpath) {
|
|
3
3
|
const rel = curryImportPath(newfilefullpath);
|
|
4
|
-
return `import { Context } from "koa";
|
|
4
|
+
return `import type { Context } from "koa";
|
|
5
5
|
import { TempstreamJSX } from "tempstream";
|
|
6
6
|
import { Page } from "@sealcode/sealgen";
|
|
7
7
|
import html from "${rel("src/back/html.js")}";
|
|
@@ -9,7 +9,7 @@ import { VERY_LONG_TEST_TIMEOUT, webhintURL } from "${rel(
|
|
|
9
9
|
)}";
|
|
10
10
|
import { ${action_name}URL } from "${rel("src/back/routes/urls.js")}";
|
|
11
11
|
import { getBrowser } from "${rel("src/back/test_utils/browser-creator.js")}";
|
|
12
|
-
import { Browser, BrowserContext, Page } from "@playwright/test";
|
|
12
|
+
import type { Browser, BrowserContext, Page } from "@playwright/test";
|
|
13
13
|
|
|
14
14
|
describe("${action_name} webhint", () => {
|
|
15
15
|
it(
|
|
@@ -23,9 +23,8 @@ describe("${action_name} webhint", () => {
|
|
|
23
23
|
// const response = await rest_api.get(${action_name}URL);
|
|
24
24
|
// await webhintHTML(response);
|
|
25
25
|
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
);
|
|
26
|
+
}
|
|
27
|
+
).timeout(VERY_LONG_TEST_TIMEOUT);
|
|
29
28
|
});
|
|
30
29
|
|
|
31
30
|
describe("${action_name}", () => {
|
|
@@ -49,9 +48,8 @@ describe("${action_name}", () => {
|
|
|
49
48
|
return withProdApp(async ({ base_url }) => {
|
|
50
49
|
await page.goto(base_url + ${action_name}URL);
|
|
51
50
|
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
);
|
|
51
|
+
}
|
|
52
|
+
).timeout(VERY_LONG_TEST_TIMEOUT);
|
|
55
53
|
});
|
|
56
54
|
`;
|
|
57
55
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealcode/sealgen",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.8",
|
|
4
4
|
"description": "Module to automate adding routes and collections to a sealious application",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"prebuild": "npm run lint && rm -rf lib",
|
|
9
9
|
"build": "node ./build.js",
|
|
10
|
+
"postbuild": "npm run build-types",
|
|
10
11
|
"pretest": "npm run build",
|
|
11
12
|
"test": "node test.cjs",
|
|
12
13
|
"typecheck": "tsc --noemit",
|
|
14
|
+
"build-types": "tsc --emitDeclarationOnly",
|
|
13
15
|
"prepare": "npm run build",
|
|
14
16
|
"lint": "eslint src",
|
|
15
17
|
"preinstrument": "npm run build && rm -fr .xunit coverage lib-instrumented",
|
|
@@ -146,9 +146,11 @@ export class MultipleFiles extends FormFieldControl {
|
|
|
146
146
|
if (!sealious_field) {
|
|
147
147
|
return null;
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
const token = (item.get(sealious_field.name) as FilePointer).token;
|
|
150
|
+
if (!token) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
return await item.collection.app.FileManager.fromToken(token);
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
async renderFileItemPreview(
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BaseContext } from "koa";
|
|
2
|
+
import { Templatable, tempstream } from "tempstream";
|
|
3
|
+
import { StatefulPage } from "./stateful-page.js";
|
|
4
|
+
|
|
5
|
+
describe("stateful page", () => {
|
|
6
|
+
it("handles a basic case with action buttons", () => {
|
|
7
|
+
type TestState = {};
|
|
8
|
+
const actions = <const>{
|
|
9
|
+
some_action: async (
|
|
10
|
+
_ctx: BaseContext,
|
|
11
|
+
_state: TestState,
|
|
12
|
+
_inputs: Record<string, unknown>,
|
|
13
|
+
_arg1: number
|
|
14
|
+
) => {},
|
|
15
|
+
};
|
|
16
|
+
new (class extends StatefulPage<TestState, typeof actions> {
|
|
17
|
+
actions = actions;
|
|
18
|
+
|
|
19
|
+
getInitialState(_ctx: BaseContext) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
wrapInLayout(_ctx: BaseContext, content: Templatable, _state: TestState): Templatable {
|
|
24
|
+
return content;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
render(_ctx: BaseContext, state: TestState) {
|
|
28
|
+
return tempstream`<div>${this.makeActionButton(state, "some_action", 2)}</div>`;
|
|
29
|
+
}
|
|
30
|
+
})();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -37,6 +37,7 @@ export abstract class StatefulPage<
|
|
|
37
37
|
Actions extends Record<
|
|
38
38
|
string,
|
|
39
39
|
(
|
|
40
|
+
ctx: BaseContext,
|
|
40
41
|
state: State,
|
|
41
42
|
inputs: Record<string, string>,
|
|
42
43
|
...args: unknown[]
|
|
@@ -44,7 +45,7 @@ export abstract class StatefulPage<
|
|
|
44
45
|
>
|
|
45
46
|
> extends Mountable {
|
|
46
47
|
abstract actions: Actions;
|
|
47
|
-
abstract getInitialState(): State | Promise<State>;
|
|
48
|
+
abstract getInitialState(ctx: BaseContext): State | Promise<State>;
|
|
48
49
|
abstract render(
|
|
49
50
|
ctx: BaseContext,
|
|
50
51
|
state: State,
|
|
@@ -118,7 +119,9 @@ export abstract class StatefulPage<
|
|
|
118
119
|
|
|
119
120
|
makeActionURL<ActionName extends keyof Actions>(
|
|
120
121
|
action_description: StatefulPageActionDescription<ActionName>,
|
|
121
|
-
...args: ExtractTail<
|
|
122
|
+
...args: ExtractTail<
|
|
123
|
+
ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>
|
|
124
|
+
>
|
|
122
125
|
) {
|
|
123
126
|
const { action } = this.extractActionAndLabel(action_description);
|
|
124
127
|
return `./?action=${action}&action_args=${encodeURIComponent(
|
|
@@ -130,7 +133,9 @@ export abstract class StatefulPage<
|
|
|
130
133
|
makeActionButton<ActionName extends keyof Actions>(
|
|
131
134
|
_state: State,
|
|
132
135
|
action_description: StatefulPageActionDescription<ActionName>,
|
|
133
|
-
...args: ExtractTail<
|
|
136
|
+
...args: ExtractTail<
|
|
137
|
+
ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>
|
|
138
|
+
>
|
|
134
139
|
) {
|
|
135
140
|
const { label, content } =
|
|
136
141
|
this.extractActionAndLabel(action_description);
|
|
@@ -152,7 +157,9 @@ export abstract class StatefulPage<
|
|
|
152
157
|
label?: string;
|
|
153
158
|
}
|
|
154
159
|
| ActionName,
|
|
155
|
-
...args: ExtractTail<
|
|
160
|
+
...args: ExtractTail<
|
|
161
|
+
ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>
|
|
162
|
+
>
|
|
156
163
|
) {
|
|
157
164
|
return `(()=>{const form = this.closest('form'); form.action='${this.makeActionURL(
|
|
158
165
|
action_description,
|
|
@@ -169,6 +176,7 @@ export abstract class StatefulPage<
|
|
|
169
176
|
}
|
|
170
177
|
|
|
171
178
|
async preprocessOverrides(
|
|
179
|
+
_context: BaseContext,
|
|
172
180
|
_state: State,
|
|
173
181
|
values: Record<string, unknown>
|
|
174
182
|
): Promise<Record<string, unknown>> {
|
|
@@ -223,6 +231,7 @@ export abstract class StatefulPage<
|
|
|
223
231
|
const $body = ctx.$body;
|
|
224
232
|
let state_overrides = $body.$ || {};
|
|
225
233
|
state_overrides = await this.preprocessOverrides(
|
|
234
|
+
ctx,
|
|
226
235
|
original_state,
|
|
227
236
|
state_overrides
|
|
228
237
|
);
|
|
@@ -283,7 +292,7 @@ export abstract class StatefulPage<
|
|
|
283
292
|
|
|
284
293
|
mount(router: Router, path: string) {
|
|
285
294
|
router.get(path, async (ctx) => {
|
|
286
|
-
ctx.body = this.render(ctx, await this.getInitialState(), {});
|
|
295
|
+
ctx.body = this.render(ctx, await this.getInitialState(ctx), {});
|
|
287
296
|
});
|
|
288
297
|
|
|
289
298
|
router.post(path, async (ctx) => {
|
|
@@ -291,6 +300,7 @@ export abstract class StatefulPage<
|
|
|
291
300
|
await this.extractState(ctx);
|
|
292
301
|
if (action) {
|
|
293
302
|
const new_state = await this.actions[action](
|
|
303
|
+
ctx,
|
|
294
304
|
state,
|
|
295
305
|
inputs,
|
|
296
306
|
...(JSON.parse(
|
package/src/templates/page.ts
CHANGED
|
@@ -4,7 +4,7 @@ export async function pageTemplate(
|
|
|
4
4
|
newfilefullpath: string
|
|
5
5
|
): Promise<string> {
|
|
6
6
|
const rel = curryImportPath(newfilefullpath);
|
|
7
|
-
return `import { Context } from "koa";
|
|
7
|
+
return `import type { Context } from "koa";
|
|
8
8
|
import { TempstreamJSX } from "tempstream";
|
|
9
9
|
import { Page } from "@sealcode/sealgen";
|
|
10
10
|
import html from "${rel("src/back/html.js")}";
|
|
@@ -10,7 +10,7 @@ import { VERY_LONG_TEST_TIMEOUT, webhintURL } from "${rel(
|
|
|
10
10
|
)}";
|
|
11
11
|
import { ${action_name}URL } from "${rel("src/back/routes/urls.js")}";
|
|
12
12
|
import { getBrowser } from "${rel("src/back/test_utils/browser-creator.js")}";
|
|
13
|
-
import { Browser, BrowserContext, Page } from "@playwright/test";
|
|
13
|
+
import type { Browser, BrowserContext, Page } from "@playwright/test";
|
|
14
14
|
|
|
15
15
|
describe("${action_name} webhint", () => {
|
|
16
16
|
it(
|
|
@@ -24,9 +24,8 @@ describe("${action_name} webhint", () => {
|
|
|
24
24
|
// const response = await rest_api.get(${action_name}URL);
|
|
25
25
|
// await webhintHTML(response);
|
|
26
26
|
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
);
|
|
27
|
+
}
|
|
28
|
+
).timeout(VERY_LONG_TEST_TIMEOUT);
|
|
30
29
|
});
|
|
31
30
|
|
|
32
31
|
describe("${action_name}", () => {
|
|
@@ -50,9 +49,8 @@ describe("${action_name}", () => {
|
|
|
50
49
|
return withProdApp(async ({ base_url }) => {
|
|
51
50
|
await page.goto(base_url + ${action_name}URL);
|
|
52
51
|
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
);
|
|
52
|
+
}
|
|
53
|
+
).timeout(VERY_LONG_TEST_TIMEOUT);
|
|
56
54
|
});
|
|
57
55
|
`;
|
|
58
56
|
}
|