Refactor upload component and add styled upload component (#3035)
This commit is contained in:
parent
cd1a30d758
commit
ce2bd2286e
@ -49,7 +49,7 @@ def UploadFile():
|
||||
id="token",
|
||||
),
|
||||
rx.heading("Default Upload"),
|
||||
rx.upload(
|
||||
rx.upload.root(
|
||||
rx.vstack(
|
||||
rx.button("Select File"),
|
||||
rx.text("Drag and drop files here or click to select files"),
|
||||
@ -73,7 +73,7 @@ def UploadFile():
|
||||
id="clear_button",
|
||||
),
|
||||
rx.heading("Secondary Upload"),
|
||||
rx.upload(
|
||||
rx.upload.root(
|
||||
rx.vstack(
|
||||
rx.button("Select File"),
|
||||
rx.text("Drag and drop files here or click to select files"),
|
||||
|
@ -16,7 +16,7 @@ from .responsive import (
|
||||
tablet_only,
|
||||
)
|
||||
from .upload import (
|
||||
Upload,
|
||||
UploadNamespace,
|
||||
cancel_upload,
|
||||
clear_selected_files,
|
||||
get_upload_dir,
|
||||
@ -31,4 +31,4 @@ debounce_input = DebounceInput.create
|
||||
foreach = Foreach.create
|
||||
html = Html.create
|
||||
match = Match.create
|
||||
upload = Upload.create
|
||||
upload = UploadNamespace()
|
||||
|
@ -9,7 +9,7 @@ from typing import Any, Callable, ClassVar, Dict, List, Optional, Union
|
||||
from reflex import constants
|
||||
from reflex.components.chakra.forms.input import Input
|
||||
from reflex.components.chakra.layout.box import Box
|
||||
from reflex.components.component import Component, MemoizationLeaf
|
||||
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
|
||||
from reflex.constants import Dirs
|
||||
from reflex.event import (
|
||||
CallableEventSpec,
|
||||
@ -299,3 +299,38 @@ class Upload(MemoizationLeaf):
|
||||
return {
|
||||
(5, "UploadFilesProvider"): UploadFilesProvider.create(),
|
||||
}
|
||||
|
||||
|
||||
class StyledUpload(Upload):
|
||||
"""The styled Upload Component."""
|
||||
|
||||
@classmethod
|
||||
def create(cls, *children, **props) -> Component:
|
||||
"""Create the styled upload component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
**props: The properties of the component.
|
||||
|
||||
Returns:
|
||||
The styled upload component.
|
||||
"""
|
||||
# Set default props.
|
||||
props.setdefault("border", "1px dashed var(--accent-12)")
|
||||
props.setdefault("padding", "5em")
|
||||
props.setdefault("textAlign", "center")
|
||||
|
||||
# Mark the Upload component as used in the app.
|
||||
Upload.is_used = True
|
||||
|
||||
return super().create(
|
||||
*children,
|
||||
**props,
|
||||
)
|
||||
|
||||
|
||||
class UploadNamespace(ComponentNamespace):
|
||||
"""Upload component namespace."""
|
||||
|
||||
root = Upload.create
|
||||
__call__ = StyledUpload.create
|
||||
|
@ -13,7 +13,7 @@ from typing import Any, Callable, ClassVar, Dict, List, Optional, Union
|
||||
from reflex import constants
|
||||
from reflex.components.chakra.forms.input import Input
|
||||
from reflex.components.chakra.layout.box import Box
|
||||
from reflex.components.component import Component, MemoizationLeaf
|
||||
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
|
||||
from reflex.constants import Dirs
|
||||
from reflex.event import (
|
||||
CallableEventSpec,
|
||||
@ -219,3 +219,201 @@ class Upload(MemoizationLeaf):
|
||||
"""
|
||||
...
|
||||
def get_event_triggers(self) -> dict[str, Union[Var, Any]]: ...
|
||||
|
||||
class StyledUpload(Upload):
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
accept: Optional[
|
||||
Union[Var[Optional[Dict[str, List]]], Optional[Dict[str, List]]]
|
||||
] = None,
|
||||
disabled: Optional[Union[Var[bool], bool]] = None,
|
||||
max_files: Optional[Union[Var[int], int]] = None,
|
||||
max_size: Optional[Union[Var[int], int]] = None,
|
||||
min_size: Optional[Union[Var[int], int]] = None,
|
||||
multiple: Optional[Union[Var[bool], bool]] = None,
|
||||
no_click: Optional[Union[Var[bool], bool]] = None,
|
||||
no_drag: Optional[Union[Var[bool], bool]] = None,
|
||||
no_keyboard: Optional[Union[Var[bool], bool]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_drop: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_focus: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mount: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_scroll: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
**props
|
||||
) -> "StyledUpload":
|
||||
"""Create the styled upload component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
accept: The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as values. supported MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
disabled: Whether the dropzone is disabled.
|
||||
max_files: The maximum number of files that can be uploaded.
|
||||
max_size: The maximum file size (bytes) that can be uploaded.
|
||||
min_size: The minimum file size (bytes) that can be uploaded.
|
||||
multiple: Whether to allow multiple files to be uploaded.
|
||||
no_click: Whether to disable click to upload.
|
||||
no_drag: Whether to disable drag and drop.
|
||||
no_keyboard: Whether to disable using the space/enter keys to upload.
|
||||
style: The style of the component.
|
||||
key: A unique key for the component.
|
||||
id: The id for the component.
|
||||
class_name: The class name for the component.
|
||||
autofocus: Whether the component should take the focus once the page is loaded
|
||||
custom_attrs: custom attribute
|
||||
**props: The properties of the component.
|
||||
|
||||
Returns:
|
||||
The styled upload component.
|
||||
"""
|
||||
...
|
||||
|
||||
class UploadNamespace(ComponentNamespace):
|
||||
root = Upload.create
|
||||
|
||||
@staticmethod
|
||||
def __call__(
|
||||
*children,
|
||||
accept: Optional[
|
||||
Union[Var[Optional[Dict[str, List]]], Optional[Dict[str, List]]]
|
||||
] = None,
|
||||
disabled: Optional[Union[Var[bool], bool]] = None,
|
||||
max_files: Optional[Union[Var[int], int]] = None,
|
||||
max_size: Optional[Union[Var[int], int]] = None,
|
||||
min_size: Optional[Union[Var[int], int]] = None,
|
||||
multiple: Optional[Union[Var[bool], bool]] = None,
|
||||
no_click: Optional[Union[Var[bool], bool]] = None,
|
||||
no_drag: Optional[Union[Var[bool], bool]] = None,
|
||||
no_keyboard: Optional[Union[Var[bool], bool]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_drop: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_focus: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mount: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_scroll: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
**props
|
||||
) -> "StyledUpload":
|
||||
"""Create the styled upload component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
accept: The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as values. supported MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
disabled: Whether the dropzone is disabled.
|
||||
max_files: The maximum number of files that can be uploaded.
|
||||
max_size: The maximum file size (bytes) that can be uploaded.
|
||||
min_size: The minimum file size (bytes) that can be uploaded.
|
||||
multiple: Whether to allow multiple files to be uploaded.
|
||||
no_click: Whether to disable click to upload.
|
||||
no_drag: Whether to disable drag and drop.
|
||||
no_keyboard: Whether to disable using the space/enter keys to upload.
|
||||
style: The style of the component.
|
||||
key: A unique key for the component.
|
||||
id: The id for the component.
|
||||
class_name: The class name for the component.
|
||||
autofocus: Whether the component should take the focus once the page is loaded
|
||||
custom_attrs: custom attribute
|
||||
**props: The properties of the component.
|
||||
|
||||
Returns:
|
||||
The styled upload component.
|
||||
"""
|
||||
...
|
||||
|
@ -1,5 +1,7 @@
|
||||
from reflex.components.core.upload import (
|
||||
StyledUpload,
|
||||
Upload,
|
||||
UploadNamespace,
|
||||
_on_drop_spec, # type: ignore
|
||||
cancel_upload,
|
||||
get_upload_url,
|
||||
@ -77,3 +79,47 @@ def test_upload_create():
|
||||
)
|
||||
assert isinstance(up_comp_4, Upload)
|
||||
assert up_comp_4.is_used
|
||||
|
||||
|
||||
def test_styled_upload_create():
|
||||
styled_up_comp_1 = StyledUpload.create()
|
||||
assert isinstance(styled_up_comp_1, StyledUpload)
|
||||
assert styled_up_comp_1.is_used
|
||||
|
||||
# reset is_used
|
||||
StyledUpload.is_used = False
|
||||
|
||||
styled_up_comp_2 = StyledUpload.create(
|
||||
id="foo_id",
|
||||
on_drop=TestUploadState.drop_handler([]), # type: ignore
|
||||
)
|
||||
assert isinstance(styled_up_comp_2, StyledUpload)
|
||||
assert styled_up_comp_2.is_used
|
||||
|
||||
# reset is_used
|
||||
StyledUpload.is_used = False
|
||||
|
||||
styled_up_comp_3 = StyledUpload.create(
|
||||
id="foo_id",
|
||||
on_drop=TestUploadState.drop_handler,
|
||||
)
|
||||
assert isinstance(styled_up_comp_3, StyledUpload)
|
||||
assert styled_up_comp_3.is_used
|
||||
|
||||
# reset is_used
|
||||
StyledUpload.is_used = False
|
||||
|
||||
styled_up_comp_4 = StyledUpload.create(
|
||||
id="foo_id",
|
||||
on_drop=TestUploadState.not_drop_handler([]), # type: ignore
|
||||
)
|
||||
assert isinstance(styled_up_comp_4, StyledUpload)
|
||||
assert styled_up_comp_4.is_used
|
||||
|
||||
|
||||
def test_upload_namespace():
|
||||
up_ns = UploadNamespace()
|
||||
assert isinstance(up_ns, UploadNamespace)
|
||||
|
||||
assert isinstance(up_ns(id="foo_id"), StyledUpload)
|
||||
assert isinstance(up_ns.root(id="foo_id"), Upload)
|
||||
|
@ -3,6 +3,24 @@ import pytest
|
||||
import reflex as rx
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def upload_root_component():
|
||||
"""A test upload component function.
|
||||
|
||||
Returns:
|
||||
A test upload component function.
|
||||
"""
|
||||
|
||||
def upload_root_component():
|
||||
return rx.upload.root(
|
||||
rx.button("select file"),
|
||||
rx.text("Drag and drop files here or click to select files"),
|
||||
border="1px dotted black",
|
||||
)
|
||||
|
||||
return upload_root_component()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def upload_component():
|
||||
"""A test upload component function.
|
||||
@ -41,13 +59,13 @@ def upload_component_with_props():
|
||||
return upload_component_with_props()
|
||||
|
||||
|
||||
def test_upload_component_render(upload_component):
|
||||
def test_upload_root_component_render(upload_root_component):
|
||||
"""Test that the render function is set correctly.
|
||||
|
||||
Args:
|
||||
upload_component: component fixture
|
||||
upload_root_component: component fixture
|
||||
"""
|
||||
upload = upload_component.render()
|
||||
upload = upload_root_component.render()
|
||||
|
||||
# upload
|
||||
assert upload["name"] == "ReactDropzone"
|
||||
@ -82,6 +100,47 @@ def test_upload_component_render(upload_component):
|
||||
)
|
||||
|
||||
|
||||
def test_upload_component_render(upload_component):
|
||||
"""Test that the render function is set correctly.
|
||||
|
||||
Args:
|
||||
upload_component: component fixture
|
||||
"""
|
||||
upload = upload_component.render()
|
||||
|
||||
# upload
|
||||
assert upload["name"] == "ReactDropzone"
|
||||
assert upload["props"] == [
|
||||
"id={`default`}",
|
||||
"multiple={true}",
|
||||
"onDrop={e => setFilesById(filesById => ({...filesById, default: e}))}",
|
||||
"ref={ref_default}",
|
||||
]
|
||||
assert upload["args"] == ("getRootProps", "getInputProps")
|
||||
|
||||
# box inside of upload
|
||||
[box] = upload["children"]
|
||||
assert box["name"] == "Box"
|
||||
assert box["props"] == [
|
||||
'sx={{"border": "1px dotted black", "padding": "5em", "textAlign": "center"}}',
|
||||
"{...getRootProps()}",
|
||||
]
|
||||
|
||||
# input, button and text inside of box
|
||||
[input, button, text] = box["children"]
|
||||
assert input["name"] == "Input"
|
||||
assert input["props"] == ["type={`file`}", "{...getInputProps()}"]
|
||||
|
||||
assert button["name"] == "RadixThemesButton"
|
||||
assert button["children"][0]["contents"] == "{`select file`}"
|
||||
|
||||
assert text["name"] == "RadixThemesText"
|
||||
assert (
|
||||
text["children"][0]["contents"]
|
||||
== "{`Drag and drop files here or click to select files`}"
|
||||
)
|
||||
|
||||
|
||||
def test_upload_component_with_props_render(upload_component_with_props):
|
||||
"""Test that the render function is set correctly.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user