Full Width Rows

Under normal operation, Dash AG Grid will render each row as a horizontal list of cells. Each cell in the row will
correspond to one column definition.

It is possible to switch this off and allow you to provide one component to span the entire width of the grid and not
use columns.

This is useful if you want to embed a complex component inside the grid instead of rendering a list of cells. This
technique can be used for displaying panels of information.

Define Full Width Rows

To use Full Width Rows, you must:

isFullWidthRow (Function) Tells the grid if this row should be rendered as full width.
fullWidthCellRenderer (Any) Provide your own cell renderer component to use for full width rows.

Those parameters are set in the Grid Options, for example:

dashGridOptions = {
    # sets full width rows if name in ['Peru', 'France', 'Italy']
    "isFullWidthRow": {"function": "['Peru', 'France', 'Italy'].includes(params.rowNode.data.name)"},
    # custom renderer for full width rows
    "fullWidthCellRenderer": "fullWidthCellRenderer"
}

Refer to Cell Renderer Components on how to build
Cell Renderers. The Cell Renderer for fullWidth has one difference to normal Cell Renderers: the parameters passed are
missing the value and column information as the cellRenderer, by definition, is not tied to a particular column.
Instead, you should work off the data parameter, which represents the value for the entire row.

The isFullWidthRow(params) function receives a params object containing the rowNode as its input and should return
a boolean true (use fullWidth) or false (do not use fullWidth and render as normal).

Note that Sorting and Filtering are NOT impacted by full width. Full width is a rendering time feature, the sorting and
filtering applied to the data is done before rendering and is not impacted.

Below shows an example using full width. The following can be noted:

View the CSS classes used for this example

These CSS classes must be added to any *.css file in the assets folder.
See Loading CSS files for more
information.

.full-width-panel {
    /* undo the white-space setting Fresh puts in */
    white-space: normal;
    height: 100%;
    width: 100%;
    border: 2px solid grey;
    border-style: ridge;
    box-sizing: border-box;
    padding: 5px;
    background-color: darkgray;
}

.full-width-flag {
    float: left;
    padding: 6px;
}

.full-width-summary {
    float: left;
    margin-right: 10px;
}

.full-width-panel label {
    padding-top: 3px;
    display: inline-block;
    font-size: 12px;
}

.full-width-center {
    overflow-y: scroll;
    border: 1px solid grey;
    padding: 2px;
    height: 100%;
    box-sizing: border-box;
    font-family: cursive;
    background-color: #fafafa;
}

.full-width-center p {
    color: black;
    margin-top: 0px !important;
}

.full-width-title {
    font-size: 20px;
}

View Custom Cell Renderer used for this example

This JavaScript function must be added to the dashAgGridComponentFunctions.js file in the assets folder.
See Custom Components for more
information.

var dagcomponentfuncs = (window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {});

dagcomponentfuncs.fullWidthCellRendererCountry = function (props) {
    return React.createElement('span', null, [
        React.createElement('img', {
            width: "17", height: "12", style: {verticalAlign: "baseline", marginRight: "5px"},
            src: "/assets/images/ag-grid/flags/" + props.data.code + ".svg"
        }, null),
        props.value,
    ])
}

dagcomponentfuncs.fullWidthCellRenderer = function (props) {
    const latinText = `<p>Sample Text in a Paragraph<p>
<p>Lorem ipsum dolor sit amet, his mazim necessitatibus te, mea volutpat intellegebat at. Ea nec perpetua liberavisse,
et modo rebum persius pri. Velit recteque reprimique quo at. Vis ex persius oporteat, esse voluptatum moderatius te vis.
 Ex agam suscipit aliquando eum. Mediocrem molestiae id pri, ei cibo facilisis mel. Ne sale nonumy sea. Et vel lorem
 omittam vulputate. Ne prima impedit percipitur vis, erat summo an pro. Id urbanitas deterruisset cum, at legere
 oportere has. No saperet lobortis elaboraret qui, alii zril at vix, nulla soluta ornatus per ad. Feugiat consequuntur
 vis ad, te sit quodsi persequeris, labore perpetua mei ad. Ex sea affert ullamcorper disputationi, sit nisl elit
 elaboraret te, quodsi doctus verear ut eam. Eu vel malis nominati, per ex melius delenit incorrupte. Partem
 complectitur sed in. Vix dicta tincidunt ea. Id nec urbanitas voluptaria, pri no nostro disputationi. Falli
 graeco salutatus pri ea.<p>
 <p>Quo ad omnesque phaedrum principes, tale urbanitas constituam et ius, pericula consequat ad est. Ius tractatos
 referrentur deterruisset an, odio consequuntur sed ad. Ea molestie adipiscing adversarium eos, tale veniam sea no.
 Mutat nullam philosophia sed ad. Pri eu dicta consulatu, te mollis quaerendum sea. Ei doming commodo euismod vis.
 Cu modus aliquip inermis his, eos et eirmod regione delicata, at odio definiebas vis.<p>`;

    return React.createElement('div', {className: 'full-width-panel'}, [
        React.createElement('div', {className: 'full-width-flag'},
            React.createElement('img', {
                width: "68", height: "40",
                src: "/assets/images/ag-grid/flags/" + props.data.code + ".svg"
            }, null),
        ),
        React.createElement('div', {className: 'full-width-summary'}, [
            React.createElement('span', {className: 'full-width-title'}, props.data.name),
            React.createElement('br'),
            React.createElement('label', {style: {display: "inline"},}, [
                React.createElement('b', null, "Population: "),
                props.data.population
            ]),
            React.createElement('br'),
            React.createElement('label', {style: {display: "inline"},}, [
                React.createElement('b', null, "Known For: "),
                props.data.summary
            ]),
            React.createElement('br'),
        ]),
        React.createElement('div', {className: 'full-width-center', dangerouslySetInnerHTML: {__html: latinText},})
    ]);
}

Detailed Full Width Example

Below shows a detailed full width example including pinned rows and columns. The example’s data is minimalistic to focus
on how the full width impacts rows. For demonstration, the pinned rows are shaded blue and the Full Width Rows have a
dark blue border.

The following points should be noted:

View the CSS classes used for this example

These CSS classes must be added to any *.css file in the assets folder.
See Loading CSS files for more information.

span.legend-box {
    display: inline-block;
    border: 1px solid #aaa;
    height: 15px;
    width: 15px;
    margin-left: 15px;
    margin-right: 5px;
}

.pinned-row {
    background-color: lightblue;
}

.full-width-row {
    border: 2px solid darkblue !important;
    box-sizing: border-box;
    height: 100%;
    white-space: normal;
}

View Custom Cell Renderer used for this example

This JavaScript function must be added to the dashAgGridComponentFunctions.js file in the assets folder.
See Custom Components for more
information.

var dagcomponentfuncs = (window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {});

dagcomponentfuncs.fullWidthCellRendererDetailed = function (props) {
    const [cssClass] = React.useState(
        props.node.rowPinned
            ? 'full-width-row pinned-row'
            : 'full-width-row'
    );

    const [message] = React.useState(
        props.node.rowPinned
            ? `Pinned full width row at index ${props.rowIndex}`
            : `Body full width row at index${props.rowIndex}`
    );

    return React.createElement('div', {className: cssClass},
        React.createElement(
            'button',
            {onClick: () => alert('button clicked')},
            "Click"
        ),
        message
    )
}

Embedded Full Width Rows

By default, Full Width Rows remain in place while the grid is scrolled horizontally. However, this may be undesirable
for some applications which need to horizontally scroll the full-width rows together the rest of the rows.

In order to have Full Width Rows scroll like normal rows, set the Grid Option:

dashGridOptions = {"embedFullWidthRows": True}

The example below demonstrates the behaviour when Full Width Rows are embedded in the same container as regular rows.
Note the following:

Full Width Keyboard Navigation

When using full width rows, the full width cell renderer is responsible for implementing support for keyboard navigation
among its focusable elements. This is why by default, focusing a grid cell with a full width cell renderer will focus
the entire cell instead of any of the elements inside the full width cell renderer.

Adding support for keyboard navigation and focus requires a custom suppressKeyboardEvent function in grid options.
See Suppress Keyboard Events.

An example of this is shown below, enabling keyboard navigation through the full width cell elements when pressing <kbd>
Tab<kbd> and <kbd>Shift<kbd>+<kbd>Tab<kbd>:

The suppressKeyboardEvent function is used to capture tab events and determine if the user is tabbing forward or
backwards. It also suppresses the default behaviour of moving to the next cell if tabbing within the child elements.

If the focus is at the beginning or the end of the cell children and moving out of the cell, the keyboard event is not
suppressed, so focus can move between the children elements. Also, when moving backwards, the focus needs to be manually
set while preventing the default behaviour of the keyboard press event.

View the CSS classes used for this example

These CSS classes must be added to any *.css file in the assets folder.
See Loading CSS files for more information.


.full-width-panel {
    white-space: normal;
    height: 100%;
    width: 100%;
    border: 2px solid blue;
    box-sizing: border-box;
    padding: 5px;
    background-color: lightblue;
}

.ag-row:focus .full-width-panel {
  background-color: #e9d985;
}

View the JavaScript functions used for this example

These JavaScript functions must be added to the dashAgGridFunctions.js file in the assets folder.
See JavaScript Functions
for more information.

var dagcomponentfuncs = (window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {});

const GRID_CELL_CLASSNAME = 'ag-full-width-row';

const getAllFocusableElementsOf = (el) => {
    return Array.from(
        el.querySelectorAll(
            'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
        )
    ).filter((focusableEl) => {
        return focusableEl.tabIndex !== -1;
    });
};

function getEventPath(event) {
    const path = [];
    let currentTarget = event.target;
    while (currentTarget) {
        path.push(currentTarget);
        currentTarget = currentTarget.parentElement;
    }
    return path;
}


dagfuncs.suppressKeyboardEvent = function (params) {
    console.log(params);
    const {key, shiftKey} = params.event;
    const path = getEventPath(params.event);
    const isTabForward = key === 'Tab' && shiftKey === false;
    const isTabBackward = key === 'Tab' && shiftKey === true;
    let suppressEvent = false;
    // Handle cell children tabbing
    if (isTabForward || isTabBackward) {
        const eGridCell = path.find((el) => {
            if (el.classList === undefined) {
                return false;
            }
            return el.classList.contains(GRID_CELL_CLASSNAME);
        });
        if (!eGridCell) {
            return suppressEvent;
        }
        const focusableChildrenElements = getAllFocusableElementsOf(eGridCell);
        const lastCellChildEl = focusableChildrenElements[focusableChildrenElements.length - 1];
        const firstCellChildEl = focusableChildrenElements[0];
        // Suppress keyboard event if tabbing forward within the cell and the current focused element is not the last child
        if (isTabForward && focusableChildrenElements.length > 0) {
            const isLastChildFocused = lastCellChildEl && document.activeElement === lastCellChildEl;
            if (!isLastChildFocused) {
                suppressEvent = true;
            }
        }
        // Suppress keyboard event if tabbing backwards within the cell, and the current focused element is not the first child
        else if (isTabBackward && focusableChildrenElements.length > 0) {
            const cellHasFocusedChildren = eGridCell.contains(document.activeElement) && eGridCell !== document.activeElement;
            // Manually set focus to the last child element if cell doesn't have focused children
            if (!cellHasFocusedChildren) {
                lastCellChildEl.focus();
                // Cancel keyboard press, so that it doesn't focus on the last child and then pass through the keyboard press to
                // move to the 2nd last child element
                params.event.preventDefault();
            }
            const isFirstChildFocused = firstCellChildEl && document.activeElement === firstCellChildEl;
            if (!isFirstChildFocused) {
                suppressEvent = true;
            }
        }
    }
    return suppressEvent;
};

View Custom Cell Renderer used for this example

This JavaScript function must be added to the dashAgGridComponentFunctions.js file in the assets folder.
See Custom Components for more
information.

var dagcomponentfuncs = (window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {});

dagcomponentfuncs.fullWidthCellRendererKeyboard = function (props) {
    const {code, name, language} = props.data;

    return React.createElement('div', {className: 'full-width-panel'}, [
            React.createElement(
                'button', null,
                React.createElement('img', {
                    width: "25", height: "20", src: "/assets/images/ag-grid/flags/" + code + ".svg"
                }),
            ),
            React.createElement('input', {defaultValue: name}),
            React.createElement('a', {
                href: `https://www.google.com/search?q=${language}`, target: "_blank", style: {color: "blue"},
            }, language),
        ]
    )
}