Skip to content

[FEATURE] Implement collapsible() Component for UI Layout #519

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

Open
10 tasks
amrutha97 opened this issue Mar 24, 2025 · 1 comment · May be fixed by #682
Open
10 tasks

[FEATURE] Implement collapsible() Component for UI Layout #519

amrutha97 opened this issue Mar 24, 2025 · 1 comment · May be fixed by #682
Labels
enhancement New feature or request

Comments

@amrutha97
Copy link
Member

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() in preswald/preswald/interfaces/components.py
  • Render collapsible UI using ShadCN’s <Collapsible /> component from frontend/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 in examples/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)
@amrutha97 amrutha97 added the enhancement New feature or request label Mar 24, 2025
@benpaul2002
Copy link

@amrutha97 I wanted to try working on this, but had a doubt - Are all components after the collapsible() considered to be its children? Or are the components supposed to be explicitly passed as children to collapsible()?

@sandeepsalwan1 sandeepsalwan1 linked a pull request Apr 19, 2025 that will close this issue
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants