Description
Goal
Add a collapsible()
component to Preswald to support collapsible sections in the layout, enabling users to group related UI components inside expandable/collapsible containers.
📌 Motivation
Many data apps and dashboards built with Preswald involve long sequences of components—sliders, charts, tables, etc. Without layout control, the UI can become overwhelming. Grouping components within collapsible panels will:
- Improve readability by reducing visible clutter
- Allow logical grouping of related inputs and outputs
- Enhance UX for interactive filtering and step-by-step exploration
This aligns with Preswald’s goal of creating structured, clean, and responsive data apps from simple Python scripts.
✅ Acceptance Criteria
- Introduce
collapsible()
inpreswald/preswald/interfaces/components.py
- Render collapsible UI using ShadCN’s
<Collapsible />
component fromfrontend/src/components/ui/collapsible.tsx
- Frontend wrapper:
/frontend/src/components/widgets/CollapsibleWidget.jsx
- Register the component in
DynamicComponents.jsx
- Support optional parameters:
label: str
(title/header of the collapsible section)open: bool
(default open/closed)size: float
(for layout control)
- Ensure child components nested inside the collapsible container are properly rendered
- Ensure compatibility with current backend → frontend component structure and UI state flow via WebSocket
- Document usage in the SDK docs
🛠️ Implementation Plan
1. Backend – Add Component
preswald/interfaces/components.py
def collapsible(
label: str,
open: bool = True,
size: float = 1.0,
) -> None:
service = PreswaldService.get_instance()
component_id = f"collapsible-{hashlib.md5(label.encode()).hexdigest()[:8]}"
component = {
"type": "collapsible",
"id": component_id,
"label": label,
"open": open,
"size": size,
}
service.append_component(component)
Register it in interfaces/__init__.py
:
from .components import collapsible
2. Frontend – Add Widget
Create: frontend/src/components/widgets/CollapsibleWidget.jsx
import React from 'react';
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/collapsible';
import { Card } from '@/components/ui/card';
import { ChevronDown } from 'lucide-react';
const CollapsibleWidget = ({ _label, _open = true, children }) => {
const [isOpen, setIsOpen] = React.useState(_open);
return (
<Card className="mb-4 p-4 rounded-2xl shadow-md">
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<div className="flex justify-between items-center cursor-pointer" onClick={() => setIsOpen(!isOpen)}>
<h2 className="font-semibold text-lg">{_label}</h2>
<ChevronDown className={`transition-transform ${isOpen ? 'rotate-180' : ''}`} />
</div>
<CollapsibleContent>
<div className="mt-4">{children}</div>
</CollapsibleContent>
</Collapsible>
</Card>
);
};
export default CollapsibleWidget;
3. DynamicComponents.jsx – Register
import CollapsibleWidget from '@/components/widgets/CollapsibleWidget';
case 'collapsible':
return (
<CollapsibleWidget
{...commonProps}
_label={component.label}
_open={component.open}
>
{children}
</CollapsibleWidget>
);
✨ Optional: Nest
children
by pushing future components into a sublist within this container, depending on how Preswald manages layout/component trees internally.
🧪 Testing Plan
- Add
collapsible(label="Advanced Filters")
above a group of sliders inexamples/iris/hello.py
- Confirm collapsible behavior toggles content visibility in browser
- Confirm responsiveness and sizing rules
- Run
preswald run
and validate via localhost:8501
🧾 Example Usage
from preswald import collapsible, slider, table, text
collapsible("Advanced Filters")
slider("Sepal Width", min_val=0, max_val=10, default=5.5)
slider("Sepal Length", min_val=0, max_val=10, default=4.5)
📚 Docs To Update
-
/docs/sdk/collapsible.mdx
– with parameters, return type, image -
/docs/layout/guide.mdx
– mention collapsible as layout tool
📂 Related Files
frontend/src/components/ui/collapsible.tsx
frontend/src/components/widgets
preswald/interfaces/components.py
DynamicComponents.jsx
🧩 Additional Notes
collapsible()
should not return a value — it's purely structural- Consider nesting logic if children grouping becomes supported
- Follow style consistency (rounded-2xl, card padding, hover icons)