How to add expandable row details?

@Aroflote,

Here you go:

app.py -

import dash
from dash import html, dcc, Input, Output, dash_table
from dash.exceptions import PreventUpdate
import pandas as pd
import dash_bootstrap_components as dbc

data = pd.DataFrame({'showMore':['','',''],
                    'FullName':['Caesar Vance', 'Cara Stevens', 'Cedric Kelly'],
                     'JobDescription':['Pre-Sales Support', 'Sales Assistant', 'Senior JavaScript Developer'],
                     'Location':['New York','New York', 'Edinburgh'],
                     'Salary(Local)':['$106,450', '$145,600', '$433,060']})

data2 = data.copy()

data2['Salary(Local)'] = 'nada'

app = dash.Dash(__name__, external_stylesheets=[dbc.icons.FONT_AWESOME],
                external_scripts=[{'src':"https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"}])
app.layout = html.Div(id="parent", children=[
    html.Button('refresh',id='refresh', n_clicks=0),
    dash_table.DataTable(data=data.to_dict('records'),
                         columns=[{'name': i, 'id':i} for i in data.columns],
                         id='employeeTable',
                         style_cell={'textAlign': 'left'})
])

@app.callback(
    Output('employeeTable','data'),
    Input('refresh','n_clicks'),
    prevent_initial_call=True
)
def refreshData(n1):
    if n1 > 0:
        return data2.to_dict('records')
    return data.to_dict('records')



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

effects.js


function coolEffects() {
    console.log('adding effects')
    $("#employeeTable tr>td:first-of-type").unbind()
    $("#employeeTable tr>td:first-of-type").on("click",
        function() {
            row = $(this).closest('tr')
            if ($(row).hasClass("expanded")) {
                $(".extraInfo").remove()
                $(".expanded").removeClass("expanded")
            } else {
                $(".extraInfo").remove()
                $(".expanded").removeClass("expanded")
                $(row).addClass("expanded")
            }
            if ($(row).hasClass('expanded')) {
                info = ''
                $(row).find("td:nth-child(n+4)").each(function() {
                    info += $(this).attr('data-dash-column') + ": "+ $(this).text() +'\n'
                })
                $(row).after('<tr class="extraInfo"><td colspan="3" style="width: 100%; max-width:100%"><pre>'
                +info + '</pre></td></tr>')
            }
        }
    )
}

window.fetch = new Proxy(window.fetch, {
    apply(fetch, that, args) {
        // Forward function call to the original fetch
        const result = fetch.apply(that, args);

        // Do whatever you want with the resulting Promise
        result.then((response) => {
            if (args[0] == '/_dash-update-component') {
                setTimeout(function() {coolEffects()}, 1000)
            }})
        return result
        }
    }
    )

$(document).ready(function() {
    setTimeout(function() {coolEffects()}, 1000)
})

custom.css


#employeeTable tr:not(.extraInfo) > td:first-of-type:before {
    content: "\2b";
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    background-color: green;
    border-radius: 50%;
    color: white;
}

#employeeTable tr:not(.extraInfo).expanded > td:first-of-type:before {
    content: "\f068";
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    background-color: red;
    border-radius: 50%;
    color: white;
}

#employeeTable tr>td:nth-child(n+4), tr>th:nth-child(n+4) {
    display: none;
}

.extraInfo {
    border: 1pt solid black;
}

.extraInfo pre {
    text-align: left;
    white-space: pre-line;
}

The n+4 is where the magic happens, starting from that column, all the rest are hidden.

Here is the result:

The refresh button was to make sure that the table would still receive updates even in the expanded form, the extraInfo wont update until you open and close. In the current layout, there can only be one expanded details section.

5 Likes