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

Multi-step wizard #888

Open
1 task done
mohammadaffaneh opened this issue Nov 19, 2024 · 3 comments
Open
1 task done

Multi-step wizard #888

mohammadaffaneh opened this issue Nov 19, 2024 · 3 comments
Assignees
Labels
Feature Request 🤓 Issue contains a feature request

Comments

@mohammadaffaneh
Copy link

Which package?

vizro

What's the problem this feature will solve?

Vizro excels at creating modular dashboards, but as users tackle more sophisticated applications, the need arises for reusable and extensible complex UI components. These include multi-step wizards with dynamic behavior, CRUD operations, and seamless integration with external systems. Currently, building such components requires significant effort, often resulting in custom, non-reusable code. This limits the scalability and maintainability of applications developed with Vizro.

I’m working on applications that require complex workflows, such as multi-step wizards with real-time input validation and CRUD operations. While I’ve managed to achieve this using Dash callbacks and custom Python code, the lack of modularity and reusability makes the process cumbersome. Every new project requires re-implementing these components, which is time-consuming and error-prone.

Describe the solution you'd like

I envision Vizro evolving to support the creation of highly reusable and extensible complex components, which could transform how users approach sophisticated Dash applications. Here’s what this could look like:

  • Object-Oriented Component Development: Provide the ability to encapsulate UI components and their logic (advanced dynamic callbacks) in Python classes, making them easy to reuse and extend across projects. This could be similar to the component architecture found in frameworks like React.

  • Modular Multi-Step Wizard: A powerful wizard component with:

    • Configurable steps that can be added or modified dynamically.
    • Real-time input validation and dynamic data population based on user inputs or external data.
    • Visual progress indicators and intuitive navigation controls (Next, Previous, Save & Exit).
  • Integrated CRUD Operations: Built-in support for Create, Read, Update, and Delete functionality, ensuring data security and consistency:

    • Temporary data storage during user navigation.
    • Soft-delete functionality and version control for changes.
    • Seamless integration with external databases or APIs.
  • Dynamic Callback Management: Enable advanced callbacks that can be registered and updated dynamically, reducing the complexity of handling inter-component interactions.

  • Extensibility Features:

    • Plug-and-play custom components (e.g., specialized form elements, interactive charts).
    • Hooks for integrating with external systems, allowing data exchange and advanced workflows.
    • Flexible step-specific logic for conditional rendering and data pre-filling.

How This Could Enhance Vizro
By introducing such capabilities, Vizro would empower users to go beyond dashboards and build complex, enterprise-level applications more efficiently. These features could help attract a broader audience, including those who require not only dashboards but also robust, interactive data workflows in their data applications.

Similar Solutions for Inspiration

  • Material-UI Stepper: Offers a modular multi-step workflow component.
  • Appsmith multistep wizard: Facilitates reusable, custom UI components, example.

My imagination:

Below is an high-level and simple implementation of the multistep wizard, where all wizard components and functionalities are isolated into a class (Wizard) following the Facade Design Pattern, complemented by elements of the Factory Pattern and the State Pattern. This class dynamically creates the logic based on the parameters and integration with the steps. The Step class represents individual steps.

wizard_module.py

from dash import html, dcc, Input, Output, State, MATCH, ALL, ctx

class Step:
    def __init__(self, id, label, components, validation_rules):
        self.id = id
        self.label = label
        self.components = components
        self.validation_rules = validation_rules

class Wizard:
    def __init__(
        self,
        steps,
        title=None,
        previous_button_text='Previous',
        next_button_text='Next',
        current_step_store_id='current_step',
        form_data_store_id='form_data',
        wizard_content_id='wizard_content',
        wizard_message_id='wizard_message',
        prev_button_id='prev_button',
        next_button_id='next_button',
        message_style=None,
        navigation_style=None,
        validate_on_next=True,
        custom_callbacks=None,
    ):
        # Instance attributes


    def render_layout(self):
        # Returns the UI Components of the form, tabs, buttons ..etc


    def render_step(self, step):
        # Returns the UI Components of a step

    def register_callbacks(self, app):
        # Dynamic callbacks for the multistep logic such as navigation, and feedback.

app.py

from dash import Dash
from wizard_module import Wizard, Step

# Define the wizard steps
steps = [
    Step(
        id=1,
        label="Step 1: User Info",
        components=[
            {"id": "name_input", "placeholder": "Enter your name"},
            {"id": "email_input", "placeholder": "Enter your email", "input_type": "email"},
            {"id": "password_input", "placeholder": "Enter your password", "input_type": "password"},
        ],
        validation_rules=[
            {"id": "name_input", "property": "value"},
            {"id": "email_input", "property": "value"},
            {"id": "password_input", "property": "value"},
        ],
    ),
    Step(
        id=2,
        label="Step 2: Address Info",
        components=[
            {"id": "address_input", "placeholder": "Enter your address"},
            {"id": "city_input", "placeholder": "Enter your city"},
            {"id": "state_input", "placeholder": "Enter your state"},
        ],
        validation_rules=[
            {"id": "address_input", "property": "value"},
            {"id": "city_input", "property": "value"},
            {"id": "state_input", "property": "value"},
        ],
    )
]

# Initialize the wizard
wizard = Wizard(
    steps=steps,
    title="User Registration Wizard",
    previous_button_text='Back',
    next_button_text='Continue',
    message_style={'color': 'blue', 'marginTop': '10px'},
    navigation_style={'marginTop': '30px'},
    validate_on_next=True,
    custom_callbacks={'on_complete': some_completion_function}
)

# Create the Dash app
app = Dash(__name__)
app.layout = wizard.render_layout()

# Register wizard callbacks
wizard.register_callbacks(app)

if __name__ == '__main__':
    app.run_server(debug=True)

Explanation:

  • Isolation of Components and Logic: All wizard functionalities, including rendering and navigation logic, are encapsulated within the Wizard class. Each step is represented by a Step class instance.

  • Dynamic Logic Creation: The Wizard class dynamically generates the layout and callbacks based on the steps provided. The validation logic is applied dynamically using the validation_rules defined in each Step instance.

  • Ease of Extension: To add more steps or modify existing ones, you simply need to create or update instances of the Step class. The Wizard class handles the integration and navigation between steps without any additional changes.

  • Validation Rules: Each Step contains a validation_rules list, which specifies which input components need to be validated. This allows for flexible validation logic that can be customized per step.

Code of Conduct

@mohammadaffaneh mohammadaffaneh added Feature Request 🤓 Issue contains a feature request Needs triage 🔍 Issue needs triaging labels Nov 19, 2024
@antonymilne antonymilne self-assigned this Nov 20, 2024
@antonymilne antonymilne removed the Needs triage 🔍 Issue needs triaging label Nov 20, 2024
@antonymilne
Copy link
Contributor

Wow, this is an amazing issue, thank you for taking the time to write everything up so carefully and thoroughly @mohammadaffaneh! 🌟

You are absolutely right about the current strengths and weaknesses in Vizro in providing this functionality. I've actually made very similar Vizro apps to what you describe, with Previous/Next buttons stepping through a form. They're internal dashboards but if you're interested I can try to dig them out and upload a sanitised version.

Something similar is already on our roadmap, although it might take a while before we get to something that closely resembles your Wizard functionality, which has a lot of features!

Here's very roughly where I hope things will go:

  1. we implement an MVP Form component or at least show how Container can be used similarly, together with any remaining form input components. As you can from the files with prefix _ these have existed for a while but are not officially supported/documented yet although some people have used them already and they generally work fine
  2. we make it possible to step through a form like a wizard. Not sure whether each step would be a separate page on the dashboard or it would be a single page. Multi-page forms are a bit more challenging because it means persisting user input between pages, which is possible but adds a bit of complexity.
  3. we add some more bells and whistles like conditional form fields (e.g. depending on radio item selected, a different form input may or may not display), validation, etc.

Currently the main thing stopping this from being developed is that we're busy reworking the actions system which I think will need to be more robust and flexible to handle this sort of thing. After that it's towards the top of our list of priorities, so no promises but hopefully at least some of this will be possible within a few months 🤞

One challenge here is that we need to develop something that is general enough to be useful across a variety of use cases while specific and powerful enough that it's easy to use for each person's individual use case. e.g. we would like to make it easier for a user to configure machine learning model parameters and then run a pipeline from Vizro. This is quite different from what you're trying to do but would need to use similar wizard/form components.

I have a few questions for you, just to understand your requirements a bit better:

Configurable steps that can be added or modified dynamically.

What do you mean by this? The sort of thing like form fields appearing/disappearing depending on the values of previously input fields?

Seamless integration with external databases or APIs.

Is this achieved just by connecting your custom_callbacks={'on_complete': some_completion_function} to something that does request.post or inserts a row into a database or similar? What else are you imagining here? e.g. maybe something that loads up a row from a database, allows the user to edit the values and then save back into the database?

The validation logic is applied dynamically using the validation_rules defined in each Step instance.

How would you expect this validation logic to be applied? On the server side or clientside? e.g. one good option here might be to use pydantic to do validation.

It's not exactly what you describe, but this may be of interest to you: https://community.plotly.com/t/dash-pydantic-form/84435/31

@mohammadaffaneh
Copy link
Author

@antonymilne Thank you for your prompt and detailed response! I appreciate the insights you've shared and the effort you're putting into enhancing Vizro's capabilities.

Regarding your questions:


  1. Configurable steps that can be added or modified dynamically.

    What do you mean by this? The sort of thing like form fields appearing/disappearing depending on the values of previously input fields?

    Answer:

    By "configurable steps that can be added or modified dynamically," I mean the ability to adjust the wizard's flow based on user inputs or external data during runtime. This includes:

    • Adding or Removing Steps: Introducing new steps or skipping existing ones based on conditions met in earlier steps.
    • Modifying Step Content: Dynamically changing the components or questions within a step depending on prior responses.
    • Branching Logic: Allowing the wizard to follow different paths (e.g., if a user selects "Yes," proceed to Step 3; if "No," skip to Step 5).

    This functionality enables the creation of more personalized and efficient user experiences, similar to conditional routing in forms. And yes this can be achieved by changing the 'style' property of a component within a callback.

  2. Seamless integration with external databases or APIs.

    Is this achieved just by connecting your custom_callbacks={'on_complete': some_completion_function} to something that does request.post or inserts a row into a database or similar? What else are you imagining here? e.g., maybe something that loads up a row from a database, allows the user to edit the values, and then save back into the database?

    Answer:

    Yes, integrating with external databases or APIs would involve using callbacks to perform operations like requests.post or database transactions. Additionally, I envision:

    • Real-Time Validation: Validating inputs against external systems (e.g., checking if a username is already taken).
    • CRUD Operations: Allowing users to create, read, update, or delete records within the wizard interface.
    • Two-Way Data Binding: Ensuring changes in the wizard are reflected in external systems and vice versa, possibly using websockets or integrating a semantic layer similar to what Apache Superset offers.
  3. The validation logic is applied dynamically using the validation_rules defined in each Step instance.

    How would you expect this validation logic to be applied? On the server side or client-side? e.g., one good option here might be to use Pydantic to do validation.

    Answer:

    I anticipate a hybrid approach for validation:

    • Client-Side Validation: For immediate feedback on input formats, required fields, and basic checks to enhance user experience and live validation.
    • Server-Side Validation: Utilizing libraries like Pydantic for more complex validation rules, data consistency checks, and security validations that require server resources or access to protected data.

    This ensures robust validation while maintaining responsiveness and security.


Thank you again for your efforts and quick reply. I'm excited about the direction Vizro is heading and am happy to assist further or provide feedback on early implementations.

@antonymilne
Copy link
Contributor

Thank you for all the answers @mohammadaffaneh, that's very helpful! 🙏 I think it will be a while before we can achieve many of the advanced features you're looking for here, but we'll definitely try and make some progress and refer back to these ideas when we develop the Form component. FYI @maxschulz-COL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Request 🤓 Issue contains a feature request
Projects
None yet
Development

No branches or pull requests

2 participants