diff --git a/.gitignore b/.gitignore index b005cd56..1a8c145d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.pyc .vscode/ +.idea/ .mypy_cache/ .DS_Store node_modules/ diff --git a/admin_ui/src/components/FormAdd.vue b/admin_ui/src/components/FormAdd.vue index f024a1ff..1cc5cffe 100644 --- a/admin_ui/src/components/FormAdd.vue +++ b/admin_ui/src/components/FormAdd.vue @@ -33,7 +33,7 @@ v-on:submit.prevent="submitForm($event)" ref="form" > - + @@ -47,7 +47,7 @@ import axios from "axios" import { defineComponent, type PropType } from "vue" import NewForm from "./NewForm.vue" -import type { APIResponseMessage, FormConfig, Schema } from "../interfaces" +import type { APIResponseMessage, Enum, Choices, FormConfig, Schema } from "../interfaces" import { convertFormValue, parseErrorResponse } from "@/utils" import FormErrors from "./FormErrors.vue" @@ -92,6 +92,14 @@ export default defineComponent({ } }, methods: { + enumToChoices (data: Enum): Choices { + var choices: Choices = {} + for (let index = 0; index < data.enum.length; index++) { + const element = data.enum[index]; + choices[element] = {display_name: element, value: element} + } + return choices + }, resetForm() { this.successMessage = null this.errors = [] diff --git a/admin_ui/src/components/NewForm.vue b/admin_ui/src/components/NewForm.vue index d31a7703..3a8d423b 100644 --- a/admin_ui/src/components/NewForm.vue +++ b/admin_ui/src/components/NewForm.vue @@ -4,19 +4,37 @@ v-bind:key="property.title" v-for="(property, columnName) in schema.properties" > - - +
+ + +
+
+ + +
@@ -26,11 +44,15 @@ import { defineComponent, type PropType } from "vue" import InputField from "./InputField.vue" import { type Schema, getType, getFormat, isNullable } from "@/interfaces" + export default defineComponent({ props: { schema: { type: Object as PropType, required: true + }, + etc: { + type: Function, } }, components: { diff --git a/admin_ui/src/interfaces.ts b/admin_ui/src/interfaces.ts index d3bf2c84..43d00aa1 100644 --- a/admin_ui/src/interfaces.ts +++ b/admin_ui/src/interfaces.ts @@ -114,12 +114,23 @@ export interface SchemaExtra { time_resolution: { [key: string]: number } } +export interface Enum { + title: string + type: string + enum: string[] +} + +export interface SchemaEnum { + [key: string]: Enum +} + export interface Schema { extra: SchemaExtra properties: Properties required: string[] title: string type: string + $defs: SchemaEnum } export interface Properties { @@ -145,6 +156,7 @@ export interface Property { anyOf?: AnyOf[] format?: string maxLength?: number + $ref: string | null } export interface ForeignKey { diff --git a/docs/source/sidebar_links/examples/app.py b/docs/source/sidebar_links/examples/app.py index e8e7ed39..72bef042 100644 --- a/docs/source/sidebar_links/examples/app.py +++ b/docs/source/sidebar_links/examples/app.py @@ -14,7 +14,7 @@ tables=[Director, Movie], sidebar_links={ "Top Movies": "/admin/#/movie?__order=-box_office", - "Google": "https://google.com" + "Google": "https://google.com", }, ), ), diff --git a/e2e/test_forms.py b/e2e/test_forms.py index 9af3cd92..b2121684 100644 --- a/e2e/test_forms.py +++ b/e2e/test_forms.py @@ -2,6 +2,7 @@ from piccolo_admin.example.forms.csv import FORM as CSV_FORM from piccolo_admin.example.forms.image import FORM as IMAGE_FORM +from piccolo_admin.example.forms.selections import FORM as SELECTION_FORM from piccolo_admin.example.forms.nullable import FORM as NULLABLE_FORM from .conftest import BASE_URL @@ -57,6 +58,20 @@ def test_image_form(page: Page, dev_server): download = download_info.value assert download.suggested_filename == "movie_listings.jpg" +def test_form_enum_support(page: Page, dev_server): + """ + Make sure custom forms support the usage of Enum's. + + form_page = FormPage( + page=page, + form_slug=SELECTION_FORM.slug, + ) + form_page.reset() + + page.locator('input[name="option"]').select_option("One") + form_page.submit_form() + + assert f"You selected Options.ONE from the drop down." in page.content() def test_nullable_form(page: Page, dev_server): """ diff --git a/piccolo_admin/example/forms/__init__.py b/piccolo_admin/example/forms/__init__.py index 6cf310ae..204171a1 100644 --- a/piccolo_admin/example/forms/__init__.py +++ b/piccolo_admin/example/forms/__init__.py @@ -2,6 +2,7 @@ from .csv import FORM as CSV_FORM from .email import FORM as EMAIL_FORM from .image import FORM as IMAGE_FORM +from .selections import FORM as SELECTION_FORM from .nullable import FORM as MEGA_FORM FORMS = [ @@ -9,5 +10,6 @@ CSV_FORM, EMAIL_FORM, IMAGE_FORM, + SELECTION_FORM, MEGA_FORM, ] diff --git a/piccolo_admin/example/forms/selections.py b/piccolo_admin/example/forms/selections.py new file mode 100644 index 00000000..4d003651 --- /dev/null +++ b/piccolo_admin/example/forms/selections.py @@ -0,0 +1,33 @@ +from enum import Enum + +from pydantic import BaseModel +from starlette.requests import Request + +from piccolo_admin.endpoints import FormConfig + + +class Options(Enum): + ONE = "One" + TWO = "Two" + THREE = "Three" + + +class SelectionModel(BaseModel): + option: Options = Options.ONE + + +def selection(request: Request, data: SelectionModel): + """ + A very simple example of a form which provides a selection drop down. + """ + return ( + f"You selected {data.option} from the drop down." + ) + + +FORM = FormConfig( + name="Selection", + pydantic_model=SelectionModel, + endpoint=selection, + description="Provides an example usage of drop downs within custom forms.", +)