Codepen custom dropdown external css not working

Hello,
After extensive back & forth Q/A with @AnnMarieW I was able to workout my dash App. but then I came across Adding custom css from external resources I tried implementing a dropdown menu I found on codepen Custom Dropdown. but It doesn’t drop the options/pop-out


import os,sys,time
import numpy as np
import pandas as pd
import polars as pl
import polars.selectors as cs
import duckdb
import dash
from dash import html, dcc, callback, Input, Output,jupyter_dash,ctx
jupyter_dash.default_mode = "external"
import dash_bootstrap_components as dbc

app = dash.Dash(
    external_stylesheets = [
        dbc.themes.DARKLY,
        "https://codepen.io/jkantner/pen/YzbwqZN.css"
        ],
    external_scripts=[
        "https://codepen.io/jkantner/pen/YzbwqZN.js"
    ],
        suppress_callback_exceptions = True
    )

app.layout = dcc.Loading(
    [
        dcc.Location("url",refresh=False),
        html.Main(
            [
                html.Div(
                    [
                        html.Button(
                            "Index",
                            className="drop__btn",
                            type="button",
                            style={
                                "aria-expanded":"false",
                                "aria-haspopup":"true"
                            }
                        ),
                        html.Div(
                            [
                                html.Div(
                                    [
                                        html.Button(dcc.Link("Index",href="/"),id="index",className="drop__btn",type="button"),
                                        html.Button(dcc.Link("First Page",href="/first_page"),id="first_page",className="drop__btn",type="button"),
                                        html.Button(dcc.Link("Second Page",href="/second_page"),id="second_page",className="drop__btn",type="button"),
                                        html.Button(dcc.Link("Third Page",href="/third_page"),id="third_page",className="drop__btn",type="button"),
                                    ],
                                    className="drop__items-inner"
                                )
                            ],
                            className="drop__items",
                            style={
                                "data-items":True
                            }
                        )
                    ],
                    className="drop"
                )
            ]
        ),
        html.Div(
            id="main-page-content",
            style=dict(
                position="relative",
                top="200px"
            )
        )
    ]
)

index_layout = html.Div([html.H1("This is index page")])
first_page_layout = html.Div([html.H1("This is first page")])
second_page_layout = html.Div([html.H1("This is second page")])
third_page_layout = html.Div([html.H1("This is second page")])


@callback(
        Output("main-page-content","children"),
        [
            Input("index","n_clicks"),
            Input("first_page","n_clicks"),
            Input("second_page","n_clicks"),
            Input("third_page","n_clicks")
    ]
)
def display_page(*args):
    if ctx.triggered_id == "first_page":
        return first_page_layout
    elif ctx.triggered_id == "second_page":
        return second_page_layout
    elif ctx.triggered_id == "third_page":
        return third_page_layout
    else:
        return index_layout


app.run_server(debug=True)

This is the html for the dropdown.

<main>
  <div class="drop" id="dummy">
    <button class="drop__btn" aria-expanded="false" aria-haspopup="true" type="button"></button>
    <div class="drop__items" data-items>
      <div class="drop__items-inner">
        <button class="drop__btn" type="button" value="windows">Windows</button>
        <button class="drop__btn" type="button" value="mac">Mac</button>
        <button class="drop__btn" type="button" value="linux">Linux</button>
      </div>
    </div>
  </div>
</main>

I tried with the relevant dbc components too…It didn’t behave like a dropdown.
Thank You!

Hello @H4CK3R,

In order to use a custom dropdown like this, you’d have to also pass it:

window.addEventListener("DOMContentLoaded",() => {
  const drop = new DropdownMenu("#dummy");
});

Or something similar. For this to work, you’d need to pass an id to the div that has the class name of drop. Then do this:

app.clientside_callback(
'''(id) => {new DropdownMenu(`#${id}`); return window.dash_clientside.no_update}''',
Output(id, 'id'), Input(id,'id')
)

There was some other stuff I had to do as well:

app.py

# import os,sys,time
# import numpy as np
# import pandas as pd
# import polars as pl
# import polars.selectors as cs
# import duckdb
import dash
from dash import html, dcc, callback, Input, Output,jupyter_dash,ctx
jupyter_dash.default_mode = "external"
import dash_bootstrap_components as dbc

app = dash.Dash(
    external_stylesheets = [
        dbc.themes.DARKLY,
        "https://codepen.io/jkantner/pen/YzbwqZN.css"
        ],
    external_scripts=[
        "https://codepen.io/jkantner/pen/YzbwqZN.js"
    ],
        suppress_callback_exceptions = True
    )

app.layout = dcc.Loading(
    [
        dcc.Location("url",refresh=False),
        html.Main(
            [
                html.Div(
                    [
                        html.Button(
                            "Index",
                            className="drop__btn",
                            type="button",
                            **{"aria-expanded":False,
                                "aria-haspopup":True
                            }
                        ),
                        html.Div(
                            [
                                html.Div(
                                    [
                                        html.Button(dcc.Link("Index",href="/"),id="index",className="drop__btn",type="button"),
                                        html.Button(dcc.Link("First Page",href="/first_page"),id="first_page",className="drop__btn",type="button"),
                                        html.Button(dcc.Link("Second Page",href="/second_page"),id="second_page",className="drop__btn",type="button"),
                                        html.Button(dcc.Link("Third Page",href="/third_page"),id="third_page",className="drop__btn",type="button"),
                                    ],
                                    className="drop__items-inner"
                                )
                            ],
                            className="drop__items",
                            **{"data-items":True}

                        )
                    ],
                    className="drop",
                    id='test'
                )
            ]
        ),
        html.Div(
            id="main-page-content",
            style=dict(
                position="relative",
                top="200px"
            )
        )
    ]
)

index_layout = html.Div([html.H1("This is index page")])
first_page_layout = html.Div([html.H1("This is first page")])
second_page_layout = html.Div([html.H1("This is second page")])
third_page_layout = html.Div([html.H1("This is third page")])


@callback(
        Output("main-page-content","children"),
        [
            Input("index","n_clicks"),
            Input("first_page","n_clicks"),
            Input("second_page","n_clicks"),
            Input("third_page","n_clicks")
    ]
)
def display_page(*args):
    if ctx.triggered_id == "first_page":
        return first_page_layout
    elif ctx.triggered_id == "second_page":
        return second_page_layout
    elif ctx.triggered_id == "third_page":
        return third_page_layout
    else:
        return index_layout

app.clientside_callback(
'''(id) => {window.drop = new DropdownMenu(`#${id}`); return window.dash_clientside.no_update}''',
Output('test', 'id'), Input('test','id')
)

app.run_server(debug=True)

To have the actual value display change other than “Windows”, you’ll need to make your own typescript file and import it. The code provided in codepen was an example of how you would do this.

Where id is the id of the component I mentioned above. This should make it look like the dropdown in the example.


This is a lot of work while there are dash built components like dbc, dmc, and even dcc dropdowns.

1 Like

Hey bro!,

Thanks for the answer. I got an error on the exact copy of code, but I got gist of the concept. So you mean to say that I gotta download the files into assets and the change the respective values in the files then add them?.
Yes I got the same exact doubt that where should I use ID. I just want a template/ understand the template where I can implement my own from codepen.

If it worked for you can you post the other stuff as well I want to look at it as well cuz this will probably be one time thing for dash app cuz I will implement this same dropdown in rest of the app as well

the error i got is

Cannot read properties of undefined (reading 'd8265eca5ea2bbe2b7ccc891860ef7174b3de29bf0d25716bd57a56fda812c70')

(This error originated from the built-in JavaScript code that runs Dash apps. Click to see the full stack trace or open your browser's console.)
TypeError: Cannot read properties of undefined (reading 'd8265eca5ea2bbe2b7ccc891860ef7174b3de29bf0d25716bd57a56fda812c70')

    at _callee2$ (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:601:56)

    at tryCatch (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:423:1062)

    at Generator.<anonymous> (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:423:3006)

    at Generator.next (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:423:1699)

    at asyncGeneratorStep (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:429:103)

    at _next (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:430:194)

    at http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:430:364

    at new Promise (<anonymous>)

    at http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:430:97

    at handleClientside (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_17_1m1719717448.dev.js:553:28)

I would like to know that are simple animations like that possible using dcc.Dropdowns or even dbc.Dropdowns

thankyou verymuch

@H4CK3R Just curious - why use the custom dropdown rather than dbc.DropdownMenu ?

1 Like

If you just made this change without refreshing the page, then you will encounter this issue. Refresh your page, and then you’ll do it.

Having typescript files is a little bit more difficult than just placing them into the assets folder. You’ll need to place them instead in your static directory and then treat them as an external source, using your app’s base url.

1 Like

Hey!

To be perfectly honest I just want to test whether small animations on things that are already there in dash are possible. I on know python for the most part so if i can use dash and make it work without much knowing of js and css I will implement them in my app.

See I will be updating my app with like 20 or so datasets that I’ve worked on and write it on my resume. I am just curious about the limit of dash for a beginner like me.

Thankyou very much brother. It works!

Now I can use your code at to understand basics of how you can implement other things. I would also like to know that do you think downloading these as files and chainging the defualt values would be better than importing externally?

Yes, I would suggest using internal, it gives you more flexibility and control.

However, I am with @AnnMarieW in thinking that you should really check out a lot of the components that are already at your finger-tips and readily available for dash and only go hunting if you need something different.

This component in particular would take a bit of work to be properly configured for the ability to be completely dependent upon the props that you would pass it from the dash backend. And of course the tricky fact about the stepping that you need to do to get the TypeScript file into the app itself.

1 Like

Yes I am too actually going with @AnnMarieW suggestion to use dbc which come with good set of css on them out of the box. As someone who doesn’t know css and js i was just curious how much effort was involved in using these types of pre-built components on net. It does seem to give some errors, I was gonna implement this if this my initial code that I posted only had to incorporate some extra arguments, but yes I would rather use simple components that wrap my head around components involving languages that I know nothing about.

But thankyou for answering tho, you actually did give me a working answer that was unexpected and good that no matter how tough the question is you can expect an answer from plotly community.

and have a great day to both of you.

1 Like