Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Display in Details #326

Merged
merged 5 commits into from
May 30, 2024
Merged
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
28 changes: 13 additions & 15 deletions demo/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from fastapi.testclient import TestClient

from . import app
from .forms import ToolEnum


@pytest.fixture
Expand Down Expand Up @@ -75,20 +74,19 @@ def test_menu_links(client: TestClient, url: str):
assert isinstance(data, list)


def test_forms_validate_correct_select_multiple():
with client as _client:
countries = _client.get('api/forms/search', params={'q': None})
countries_options = countries.json()['options']
r = client.post(
'api/forms/select',
data={
'select_single': ToolEnum._member_names_[0],
'select_multiple': ToolEnum._member_names_[0],
'search_select_single': countries_options[0]['options'][0]['value'],
'search_select_multiple': countries_options[0]['options'][0]['value'],
},
)
assert r.status_code == 200
# def test_forms_validate_correct_select_multiple(client: TestClient):
# countries = client.get('api/forms/search', params={'q': None})
# countries_options = countries.json()['options']
# r = client.post(
# 'api/forms/select',
# data={
# 'select_single': ToolEnum._member_names_[0],
# 'select_multiple': ToolEnum._member_names_[0],
# 'search_select_single': countries_options[0]['options'][0]['value'],
# 'search_select_multiple': countries_options[0]['options'][0]['value'],
# },
# )
# assert r.status_code == 200


# TODO tests for forms, including submission
23 changes: 18 additions & 5 deletions src/npm-fastui/src/components/details.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'

import type { Details } from '../models'
import type { Details, Display, DisplayMode } from '../models'

import { asTitle } from '../tools'
import { useClassName } from '../hooks/className'
Expand All @@ -15,13 +15,26 @@ export const DetailsComp: FC<Details> = (props) => (
</dl>
)

const FieldDetail: FC<{ props: Details; fieldDisplay: DisplayLookupProps }> = ({ props, fieldDisplay }) => {
const { field, title, onClick, ...rest } = fieldDisplay
const value = props.data[field]
const FieldDetail: FC<{ props: Details; fieldDisplay: DisplayLookupProps | Display }> = ({ props, fieldDisplay }) => {
const onClick = fieldDisplay.onClick
let title = fieldDisplay.title
const rest: { mode?: DisplayMode; tableWidthPercent?: number } = { mode: fieldDisplay.mode }
let value: any

if ('type' in fieldDisplay && fieldDisplay.type === 'Display') {
// fieldDisplay is Display
value = fieldDisplay.value
} else if ('field' in fieldDisplay) {
// fieldDisplay is DisplayLookupProps
const field = fieldDisplay.field
title = title ?? asTitle(field)
value = props.data[field]
rest.tableWidthPercent = fieldDisplay.tableWidthPercent
}
const renderedOnClick = renderEvent(onClick, props.data)
return (
<>
<dt className={useClassName(props, { el: 'dt' })}>{title ?? asTitle(field)}</dt>
<dt className={useClassName(props, { el: 'dt' })}>{title}</dt>
<dd className={useClassName(props, { el: 'dd' })}>
<DisplayComp type="Display" onClick={renderedOnClick} value={value !== undefined ? value : null} {...rest} />
</dd>
Expand Down
2 changes: 1 addition & 1 deletion src/npm-fastui/src/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ export interface Display {
*/
export interface Details {
data: DataModel
fields: DisplayLookup[]
fields: (DisplayLookup | Display)[]
className?: ClassName
type: 'Details'
}
Expand Down
11 changes: 7 additions & 4 deletions src/python-fastui/fastui/components/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Details(BaseModel, extra='forbid'):
data: pydantic.SerializeAsAny[_types.DataModel]
"""Data model to display."""

fields: _t.Union[_t.List[DisplayLookup], None] = None
fields: _t.Union[_t.List[_t.Union[DisplayLookup, Display]], None] = None
"""Fields to display."""

class_name: _class_name.ClassNameField = None
Expand All @@ -86,9 +86,12 @@ def _fill_fields(self) -> _te.Self:
else:
# add pydantic titles to fields that don't have them
for field in (c for c in self.fields if c.title is None):
pydantic_field = fields.get(field.field)
if pydantic_field and pydantic_field.title:
field.title = pydantic_field.title
if isinstance(field, DisplayLookup):
pydantic_field = self.data.model_fields.get(field.field)
if pydantic_field and pydantic_field.title:
field.title = pydantic_field.title
elif isinstance(field, Display):
field.title = field.title
return self

@classmethod
Expand Down
22 changes: 22 additions & 0 deletions src/python-fastui/tests/test_tables_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,28 @@ def test_display_fields():
}


def test_details_with_display_lookup_and_display():
d = components.Details(
data=users[0],
fields=[
display.DisplayLookup(field='id', title='ID'),
display.DisplayLookup(field='name'),
display.Display(value='display value', title='Display Title'),
],
)

# insert_assert(d.model_dump(by_alias=True, exclude_none=True))
assert d.model_dump(by_alias=True, exclude_none=True) == {
'data': {'id': 1, 'name': 'john', 'representation': '1: john'},
'fields': [
{'title': 'ID', 'field': 'id'},
{'title': 'Name', 'field': 'name'},
{'title': 'Display Title', 'value': 'display value', 'type': 'Display'},
],
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved
'type': 'Details',
}


def test_table_respect_computed_field_title():
class Foo(BaseModel):
id: int
Expand Down
Loading