Skip to content

Commit 65f2c29

Browse files
Allow user to pass extra props to components (#710)
1 parent d078d0a commit 65f2c29

4 files changed

Lines changed: 44 additions & 6 deletions

File tree

aiohttp_admin/routes.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Setup routes for admin app."""
22

3+
import copy
34
from pathlib import Path
45

56
from aiohttp import web
@@ -29,18 +30,26 @@ def setup_resources(admin: web.Application, schema: Schema) -> None:
2930

3031
repr_field = r.get("repr", m.primary_key)
3132

32-
for k, v in m.inputs.items():
33-
if k not in omit_fields:
34-
v["props"]["alwaysOn"] = "alwaysOn" # Always display filter
33+
# Don't modify the resource.
34+
fields = copy.deepcopy(m.fields)
35+
inputs = copy.deepcopy(m.inputs)
3536

36-
inputs = m.inputs.copy() # Don't modify the resource.
3737
for name, validators in r.get("validators", {}).items():
3838
if not all(v[0] in _VALIDATORS for v in validators):
3939
raise ValueError(f"First value in validators must be one of {_VALIDATORS}")
4040
inputs[name] = inputs[name].copy()
4141
inputs[name]["validators"] = tuple(inputs[name]["validators"]) + tuple(validators)
4242

43-
state = {"fields": m.fields, "inputs": inputs, "list_omit": tuple(omit_fields),
43+
input_props = r.get("input_props", {})
44+
for k, v in inputs.items():
45+
if k not in omit_fields:
46+
v["props"]["alwaysOn"] = "alwaysOn" # Always display filter
47+
v["props"].update(input_props.get(k, {}))
48+
49+
for name, props in r.get("field_props", {}).items():
50+
fields[name]["props"].update(props)
51+
52+
state = {"fields": fields, "inputs": inputs, "list_omit": tuple(omit_fields),
4453
"repr": repr_field, "label": r.get("label"), "icon": r.get("icon"),
4554
"bulk_update": r.get("bulk_update", {})}
4655
admin["state"]["resources"][m.name] = state

aiohttp_admin/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ class _Resource(TypedDict, total=False):
7171
bulk_update: dict[str, dict[str, Any]]
7272
# Custom validators to add to inputs.
7373
validators: dict[str, Sequence[Sequence[Union[str, int]]]]
74+
# Custom props to add to fields.
75+
field_props: dict[str, dict[str, Any]]
76+
# Custom props to add to inputs.
77+
input_props: dict[str, dict[str, Any]]
7478

7579

7680
class Resource(_Resource):

tests/test_admin.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,28 @@ def test_display_invalid() -> None:
103103

104104
with pytest.raises(ValueError, match=r"Display includes non-existent field \('bar',\)"):
105105
aiohttp_admin.setup(app, schema)
106+
107+
108+
def test_extra_props() -> None:
109+
app = web.Application()
110+
model = DummyResource(
111+
"test",
112+
{"id": {"type": "TextField", "props": {"textAlign": "right", "placeholder": "foo"}}},
113+
{"id": {"type": "TextInput", "props": {"resettable": False, "type": "text"},
114+
"show_create": False, "validators": ()}},
115+
"id")
116+
schema: aiohttp_admin.Schema = {
117+
"security": {"check_credentials": check_credentials},
118+
"resources": ({
119+
"model": model,
120+
"field_props": {"id": {"textAlign": "left", "label": "Spam"}},
121+
"input_props": {"id": {"type": "email", "multiline": True}}
122+
},)}
123+
124+
admin = aiohttp_admin.setup(app, schema)
125+
126+
test_state = admin["state"]["resources"]["test"]
127+
assert test_state["fields"]["id"]["props"] == {"textAlign": "left", "placeholder": "foo",
128+
"label": "Spam"}
129+
assert test_state["inputs"]["id"]["props"] == {"alwaysOn": "alwaysOn", "type": "email",
130+
"multiline": True, "resettable": False}

tests/test_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ async def test_admin_view(admin_client: TestClient) -> None:
2929

3030
r = state["resources"]["dummy"]
3131
assert r["list_omit"] == []
32-
assert r["fields"] == {"id": {"type": "NumberField", "props": {"alwaysOn": "alwaysOn"}}}
32+
assert r["fields"] == {"id": {"type": "NumberField", "props": {}}}
3333
assert r["inputs"] == {"id": {"type": "NumberInput", "props": {"alwaysOn": "alwaysOn"},
3434
"show_create": False, "validators": [["required"]]}}
3535
assert r["repr"] == "id"

0 commit comments

Comments
 (0)