Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.pyc
.vscode/
.idea/
.mypy_cache/
.DS_Store
node_modules/
Expand Down
12 changes: 10 additions & 2 deletions admin_ui/src/components/FormAdd.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
v-on:submit.prevent="submitForm($event)"
ref="form"
>
<NewForm :schema="schema" />
<NewForm :schema="schema" :etc="enumToChoices" />
<button data-uitest="submit_custom_form_button">
{{ $t("Submit") }}
</button>
Expand All @@ -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"

Expand Down Expand Up @@ -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 = []
Expand Down
48 changes: 35 additions & 13 deletions admin_ui/src/components/NewForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,37 @@
v-bind:key="property.title"
v-for="(property, columnName) in schema.properties"
>
<label>{{ property.title }}</label>
<InputField
v-bind:isFilter="false"
v-bind:key="columnName"
v-bind:columnName="String(columnName)"
v-bind:type="getType(property)"
v-bind:value="property.default"
v-bind:isNullable="isNullable(property)"
v-bind:timeResolution="
schema?.extra?.time_resolution[columnName]
"
v-bind:format="getFormat(property)"
/>
<div
v-if="Object.keys(property).includes('$ref')"
>
<label>{{ schema["$defs"][Number(property['$ref']?.split(/[//]+/).pop())].title }}</label>

Check warning

Code scanning / CodeQL

Duplicate character in character class Warning

Character '/' is
repeated in the same character class
.
<InputField
v-bind:choices="etc(schema['$defs'][Number(property['$ref']?.split(/[//]+/).pop())])"
v-bind:isFilter="false"
v-bind:key="columnName"
v-bind:columnName="String(columnName)"
v-bind:type="schema['$defs'][Number(property['$ref']?.split(/[//]+/).pop())].type"
v-bind:timeResolution="
schema?.extra?.time_resolution[columnName]
"
v-bind:value="property.default"
v-bind:format="property.format"
/>
</div>
<div v-else>
<label>{{ property.title }}</label>
<InputField
v-bind:isFilter="false"
v-bind:key="columnName"
v-bind:columnName="String(columnName)"
v-bind:type="getType(property)"
v-bind:value="property.default"
v-bind:timeResolution="
schema?.extra?.time_resolution[columnName]
"
v-bind:format="property.format"
/>
</div>
</div>
</div>
</template>
Expand All @@ -26,11 +44,15 @@
import InputField from "./InputField.vue"
import { type Schema, getType, getFormat, isNullable } from "@/interfaces"
export default defineComponent({
props: {
schema: {
type: Object as PropType<Schema>,
required: true
},
etc: {
type: Function,
}
},
components: {
Expand Down
12 changes: 12 additions & 0 deletions admin_ui/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -145,6 +156,7 @@ export interface Property {
anyOf?: AnyOf[]
format?: string
maxLength?: number
$ref: string | null
}

export interface ForeignKey {
Expand Down
2 changes: 1 addition & 1 deletion docs/source/sidebar_links/examples/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
tables=[Director, Movie],
sidebar_links={
"Top Movies": "/admin/#/movie?__order=-box_office",
"Google": "https://google.com"
"Google": "https://google.com",
},
),
),
Expand Down
15 changes: 15 additions & 0 deletions e2e/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
"""
Expand Down
2 changes: 2 additions & 0 deletions piccolo_admin/example/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
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 = [
CALCULATOR_FORM,
CSV_FORM,
EMAIL_FORM,
IMAGE_FORM,
SELECTION_FORM,
MEGA_FORM,
]
33 changes: 33 additions & 0 deletions piccolo_admin/example/forms/selections.py
Original file line number Diff line number Diff line change
@@ -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.",
)
Loading