Generators - Custom view Generator

Automated listing visualization in custom views New

See it live!

Based on the listing generator, this version will fit perfectly into a custom view by reusing dataset filters and search on the left. Embed the view to see how this template activated it’s own filters and search bar..

<!-- V1 :
  - listing generator adapted to custom view :
      - no more context to init, and using existing one "ctx"
  - filters are hidden in /explore/datasets/ view, but visible in embed mode
-->


<!-- IMPORTANT ******** MUST READ !

    In the following settings declaration :

    A common error is forget to escape (protect) apostrophe with a leading backslash
    As apostrophes are used to declare values of variable it will break the settings

    Ex:
    wrongVariable = 'I'll be freed from apostrophes'
    correctVariable = 'I\'ll be freed from apostrophes'
-->

<!-- SETTINGS START HERE -->
<div class="container custom-view-generator"



     ng-init="  filters = [
              {'id':'language','multiple':true},
              {'id':'country','multiple':true},
              {'id':'purpose','multiple':true}
              ];
              fieldDate = '';
              resetFiltersButton = true;
              resetFiltersButtonLabel = 'Supprimer tous les filtres';
              fieldDefaultRangeStartsNow = true;

              view = 'cards';
              fieldsList = ['powered_by','description_en'];
              fieldLink = 'reference_url';
              fieldLinkLabel = 'See it live';

              cardTitle = 'name_of_the_initiative_en';
              fieldPhoto = '';
              imagePosition = 'left';
              itemsPerRow = '2';

              kpis = [
              {
              'title': 'Number of initiatives',
              'function': 'COUNT'
              }
              ];

              DO_NOT_MODIFY_BELOW;

              ctxfields = {};
              values = {};
              activeFilters = {};
              ">
    <!-- ### GENERAL SETTINGS ### -->

    <!-- ### FILTERS SETTINGS ### -->
    <!-- filters (Filters) : List of object that contains the IDs to generate the filters pannel.
    and multiple true or false to allow the user to select multiple values in the filter.
    NB: the field must be a facet in the dataset
    NB: alphanumerical sort is applied in the filter view
     ex: [
              {'id':'filterid','multiple':true},
              {'id':'filterid2','multiple':false}
         ]
    -->
    <!-- resetFiltersButton (boolean) : add a reset filters button after filters block -->
    <!-- resetFiltersButtonLabel (Label of the button) : test to display when a filter is selected
        ex: 'Clear all filters'
    -->

    <!-- ### DATE SETTINGS -->
    <!-- fieldDate : a date type field to display a date-range-slider -->
    <!-- resetFiltersButton : reset date period button true/false -->
    <!-- fieldDefaultRangeStartsNow : false make the default range selection start from the first date, and end to the last date, if set to true, the range starts from now, to the last date -->

    <!-- ### LIST VIEW SETTINGS ### -->
    <!-- view (List view type) : Type of the view to list results, can be 'table' or 'cards'
    -->
    <!-- fieldsList (List configuration) : Set the list of field IDs
     ex: ['title','category','genre','date']
    -->
    <!-- fieldLink (Link to an external resource) : If available, the field ID of some external resource as a web URL
     ex: 'link'
    -->
    <!-- fieldLinkLabel (The label of that link) : Label of the link button
     ex: 'Read more here'
    -->

    <!-- Specific to the 'cards' view mode, set a title and a background image if any -->
    <!-- cardTitle (Title of the card) : Field id of the card title
     ex: 'title'
-->
    <!-- fieldPhoto (Field id of the image field if any)
     ex: 'image'
-->
    <!-- imagePosition (Image position) : Image position in the card, can be 'top' or 'left'
     ex: 'left'
-->
    <!-- itemsPerRow (Number of columns) : Modify the columns layout. The division by 12 must be a whole number, ie it can be 1, 2, 3, 4, 6 or 12. But 6 and 12 will generaly be two norrow
     ex: '3'
-->

    <!-- ### KPIS SETTINGS ### -->
    <!-- KPIS settings is a list of object that describes each KPI
     List of available keys are :
    - title (Name of the KPI) ex: 'Average # of citizens'
    - function (function of the aggregation) ex: 'SUM'
    - expression (field id that contains numerical values to aggregate) ex: 'population'
    - precision (Decimal precision of the KPI) ex: 2
    - unit (KPI unit) ex: 'citizens'
    - faicon (FontAwesome icon id) ex: 'square-o'

    title, function, expression are MANDATORY
    the others are optionnal

    Available functions are SUM, AVG, COUNT, STD, MAX, MIN.
    Please see the documentation for more information
    https://help.opendatasoft.com/widgets/#/api/ods-widgets.directive:odsAggregation

    Please see all available icons here
    https://fontawesome.com/v4.7.0/icons/

    ex:
    kpis = [
              {
                  'title': 'Taille moyenne',
                  'function': 'AVG',
                  'expression': 'surface',
                  'precision': 2,
                  'unit': 'm2',
                  'faicon': 'square-o'
              },
              {
                  'title': 'Nombre d\'oeuvre référencées',
                  'function': 'COUNT'
              }
           ];

-->

    <!-- DO NOT MODIFY -->
    <!-- Technical fields, do not modify please -->


    <ods-dataset-context context="ctxdate"
                         ctxdate-dataset="{{ ctx.dataset.datasetid }}"
                         ctxdate-parameters="ctx.parameters">

        <span ng-if="fieldDefaultRangeStartsNow">
            {{ctxdate.parameters={'q.date': fieldDate + '&gt;#now()'};''}}
        </span>

        <span ng-repeat="field in ctx.dataset.fields">
            {{ ctxfields[field.name] = field.label; '' }}
        </span>

        <div ng-if="!$parent.$parent.$parent">
            <span ng-repeat="filter in filters">
                {{ ctx.parameters['refine.' + filter.id] = activeFilters[filter.id] ; '' }}
            </span>
        </div>

        <div ng-if="!$parent.$parent.$parent" class="content-card search-module-container">
            <!-- SEARCH -->
            <div class="search-module">
                <i class="fa fa-search search-module-icon" aria-hidden="true"></i>
                <input placeholder="Rechercher"
                       ng-model="ctx.parameters['q']"
                       ng-model-options="{ updateOn: 'keyup', debounce: { 'default': 300, 'blur': 0 }}"
                       class="search-module-input"
                       type="text"/>
                <button class="search-module-clear"
                        ng-if="ctx.parameters['q']"
                        ng-click="ctx.parameters['q'] = undefined">
                    <i class="fa fa-times-circle" aria-hidden="true"></i>
                </button>
            </div>

            <!-- FILTERS Search & Select -->
            <div class="filter-list"
                 ng-init="dropdown.open = '';
                          select = {}">
                <div ng-repeat="filter in filters">
                    {{ ctx.parameters['disjunctive.' + filter.id] = true; '' }}
                    <div ods-facet-results="categories"
                         ods-facet-results-facet-name="{{ filter.id }}"
                         ods-facet-results-context="ctx"
                         ods-facet-results-sort="alphanum">
                        <ods-select ng-if="categories"
                                    selected-values="activeFilters[filter.id]"
                                    multiple="filter.multiple"
                                    options="categories"
                                    label-modifier="name"
                                    value-modifier="name"
                                    placeholder="{{ ctxfields[filter.id] }}"></ods-select>
                    </div>
                </div>

                <div class="clear-filters"
                     ng-show="(activeFilters | values).join('')">
                    <div class="clear-filters-button"
                         role="button"
                         ng-click="activeFilters = {}">
                        {{ resetFiltersButtonLabel }}
                        <i class="fa fa-times-circle" aria-hidden="true"></i>
                    </div>
                </div>
            </div>

            <!-- FILTERS date (if any) -->
            <div class="filter-date"
                 ng-if="fieldDate">
                <!--
                    On récupère la plage de date du jeu de données. On fait une analyse pour récupérer la première et dernière date du champs date_de_restitution
                    pour le min: conditionnelle pour ajouter le 0 si le mois ou le jour est inférieur à 10 pour avoir une date iso
                    [dateminmax.results.length-1] : permet de récupérer dynamiquement le dernier élément de l'array du results => donc ici on calcule le nombre d'éléments dans l'array pour la date, donc 2, et 2-1= 1 donc 2019
                    Pareil que précédemment, ne pas hésiter à enlever le '' pour voir le comportement
                -->
                <span ods-analysis="dateminmax"
                      ods-analysis-context="ctxdate"
                      ods-analysis-x-year="{{ fieldDate }}.year"
                      ods-analysis-x-month="{{ fieldDate }}.month"
                      ods-analysis-x-day="{{ fieldDate }}.day"
                      ods-analysis-serie-c="COUNT()"
                      ods-analysis-sort="x.{{ fieldDate }}.year,x.{{ fieldDate }}.month,x.{{ fieldDate }}.day">
                    <span ng-if="dateminmax.results">
                        {{ values['periode']['min'] = dateminmax.results[0].x.year + '-' + (10 > dateminmax.results[0].x.month?'0':'') + dateminmax.results[0].x.month + '-' + (10 > dateminmax.results[0].x.day?'0':'') + dateminmax.results[0].x.day;
                        values['periode']['max'] = dateminmax.results[dateminmax.results.length-1].x.year + '-' + (10 > dateminmax.results[dateminmax.results.length-1].x.month?'0':'') + dateminmax.results[dateminmax.results.length-1].x.month + '-' + (10 > dateminmax.results[dateminmax.results.length-1].x.day?'0':'') + dateminmax.results[dateminmax.results.length-1].x.day;
                        ''}}
                    </span>
                </span>

                <!-- Affichage du range slider: les dates récupérées dans l'analyse pour initialiser les bounds
                                     la sélection période par défaut avec le mois + 1
                                     le min/maxselection qui correspondent aux bornes choisies par le user -->

                <ods-date-range-slider ng-if="values.periode.min && values.periode.max"
                                       context="ctx"
                                       initial-from="{{ values.periode.min }}"
                                       initial-to="{{ values.periode.max }}"
                                       start-bound="values.periode.min"
                                       end-bound="values.periode.max"
                                       date-field="{{ fieldDate }}"
                                       precision="day"
                                       from="values.periode.minselection"
                                       to="values.periode.maxselection">
                </ods-date-range-slider>

                <!-- le click sur toute la période resette la période choisie sur la date min et max -->
                <div ng-if="resetFiltersButton" class="filter-date-button"
                     ng-class="{'filter-date-button-disabled':
                               values.periode.minselection == values.periode.min &&
                               values.periode.maxselection == values.periode.max}"
                     ng-click="values.periode.minselection = values.periode.min;
                               values.periode.maxselection = values.periode.max">
                    Toute la période <i class="fa fa-arrows-h" aria-hidden="true"></i>
                </div>

            </div>
        </div>

        <!-- KPIs -->
        <section class="kpis-container row row-equal-height">
            <div class="{{ 'col-md-' + (12/itemsPerRow) }} margin-bottom-20"
                 ng-repeat="kpi in kpis">
                <!-- KPI box component -->
                <div class="kpi-card"
                     ods-aggregation="agg"
                     ods-aggregation-context="ctx"
                     ods-aggregation-function="{{ kpi.function }}"
                     ods-aggregation-expression="{{ kpi.expression }}">
                    <i class="kpi-icon fa fa-{{ kpi.faicon || 'gitlab' }}" aria-hidden="true"></i>
                    <h2 class="kpi-title">
                        {{ (agg || 0) | number : (kpi.precision || 0) }}
                        <span ng-if="kpi.unit" class="kpi-unit">{{ kpi.unit }}</span>
                    </h2>
                    <p class="kpi-description">
                        {{ kpi.title }}
                    </p>
                </div>
            </div>
        </section>

        <!-- TABLE -->
        <section ng-if="view == 'table'"
                 class="content-card">
            <div class="table-module">
                <table class="table-basic"
                       ods-results="items"
                       ods-results-context="ctx"
                       ods-results-max="20">
                    <thead>
                        <tr>
                            <th ng-repeat="field in fieldsList">{{ ctxfields[field] }}</th>
                            <th ng-if="fieldLink"></th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="item in items">
                            <td ng-repeat="field in fieldsList"
                                style="max-width: calc(100vh / {{ fieldsList.length }});"
                                title="{{ item.fields[field] }}">
                                {{ item.fields[field] }}
                            </td>
                            <td ng-if="fieldLink">
                                <a href="{{ item.fields[fieldLink] }}"
                                   tilte="{{ fieldLinkLabel }}"
                                   target="_blank"
                                   class="table-dropdown-button">
                                    <i class="fa fa-external-link"></i>
                                </a>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </section>

        <!-- CARDS -->
        <section ng-if="view == 'cards'">
            <div class="row row-equal-height"
                 ods-results="items"
                 ods-results-context="ctx"
                 ods-results-max="{{ 8 * itemsPerRow }}">
                <div ng-repeat="item in items"
                     class="{{ 'col-md-' + (12/itemsPerRow) }} margin-bottom-20">
                    <div class="content-card"
                         ng-class="{'content-card-horizontal': imagePosition === 'left' }">
                        <div class="content-card-img"
                             ng-if="item.fields[fieldPhoto]"
                             style="{{ 'background-image: url(/explore/dataset/' + ctx.dataset.datasetid + '/files/' + item.fields[fieldPhoto].id + '/300/);' }}">
                        </div>
                        <div class="content-card-body">
                            <h2 class="content-card-title text-center">
                                {{ item.fields[cardTitle] }}
                            </h2>
                            <div class="content-card-fields">
                                <dl>
                                    <dt ng-repeat-start="field in fieldsList">{{ ctxfields[field] }}</dt>
                                    <dd ng-repeat-end>{{ item.fields[field] }}</dd>
                                </dl>
                            </div>
                            <div ng-if="fieldLink" class="text-center">
                                <!-- if fieldLink is a json and contains the 'id' key, it means that the link points to a file hosted on ODS platform and can be downloaded. If it's an external link, the other link tag is used -->
                                <a ng-if="item.fields[fieldLink].id"
                                   href="/explore/dataset/{{ ctx.dataset.datasetid }}/files/{{ item.fields[fieldLink].id }}/download/"
                                   target="_blank"
                                   class="content-card-button">
                                    {{ fieldLinkLabel }}
                                </a>

                                <!-- fieldLink is used here
                                            For more advanced scenario, you can send the user to :

                                            - the dataset table filtered with the fieldLink value, through a text query
                                            href="/explore/dataset/{{ datasetid }}/table?q={{ item.fields[fieldLink] }}"

                                            - the dataset table filtered with the fieldLink value, through a refine on the field
                                            href="/explore/dataset/{{ datasetid }}/table?refine.{{ fieldLink }}={{ item.fields[fieldLink] }}"

                                            - a page using url-sync=true setting :
                                            href="/pages/yourpage/?refine.{{ fieldLink }}={{ item.fields[fieldLink] }}"
                                            -->
                                <a ng-if="!item.fields[fieldLink].id" href="{{ item.fields[fieldLink] }}"
                                   target="_blank"
                                   class="content-card-button">
                                    {{ fieldLinkLabel }}
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </ods-dataset-context>
</div>
/* General Layout
========================================================================== */
:root {
    --secondary-color: black;
}
main {
    margin: 6rem 0 3em 0;
}

@media screen and (min-width: 992px) {
    .row-equal-height {
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex;
        -ms-flex-wrap: wrap;
        flex-wrap: wrap;
        margin-bottom: 20px;
    }
    /* Fix for early content wrapping in Safari*/
    .row-equal-height:before,
    .row-equal-height:after {
        content: normal;
    }
}

.page-title {
    font-size: 3rem;
    font-weight: bold;
    margin-top: 0;
    margin-bottom: 1rem;
}

.page-subtitle {
    font-size: 1.2rem;
    line-height: 2;
    margin-top: 0;
    margin-bottom: 3rem;
}

.margin-bottom-20 {
    margin-bottom: 20px;
}


/* Search Module
========================================================================== */

.search-module-container {
    padding: 26px;
    margin-bottom: 20px;
}

.search-module {
    display: flex;
    align-items: stretch;
    border-bottom: 1px solid #dee5ef;
    margin-bottom: 13px;
    transition: all .2s;
}

.search-module:hover,
.search-module:focus-within {
    border-bottom-color: var(--links);
}

.search-module-icon {
    color: #898d92;
    margin-right: 8px;
    align-self: center;
}

.search-module-input {
    background-color: transparent;
    width: 100%;
    outline: none;
    border: none;
    padding: 12px 0;
    transition: all .2s;
    color: var(--text);
}

.search-module-input::placeholder {
    transition: all .2s;
}

.search-module-clear {
    color: #898d92;
    font-size: 1rem;
    background: transparent;
    border: none;
    margin: 0;
    outline: none;
    padding: 0 0 0 12px;
    transition: all .2s;
}

.search-module-clear:hover {
    opacity: .65;
}

.search-module:hover .search-module-icon,
.search-module:focus-within .search-module-icon,
.search-module:hover .search-module-input::placeholder,
.search-module:focus-within .search-module-input::placeholder {
    color: var(--links)
}


/* Filters
========================================================================== */

.filter-list {
    display: flex;
    flex-wrap: wrap;
    position: relative;
}
.filter-list > * {
    margin: 0 0 10px;
    width: 100%;
}
.odswidget-select .odswidget-select-dropdown.open .odswidget-select-dropdown-menu {
    width: 100%
}
.clear-filters {
    display: flex;
    align-items: center;
    justify-content: center;
}
.clear-filters-button:hover {
    opacity: 0.65;
}
.odswidget-select,
.odswidget-select .odswidget-select-dropdown {
    width: 100%;
}
@media screen and (min-width: 500px) {
    .filter-list > * {
        margin: 0 10px 10px 0;
        width: inherit;
    }
    .odswidget-select .odswidget-select-dropdown.open .odswidget-select-dropdown-menu {
        width: max-content;
        min-width: 240px;
    }
}

/*********** Filter date ************/
.filter-date {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 13px 26px 32px 26px;
}
.odswidget-date-range-slider {
    width: 100%;
}
.filter-date-button {
    margin-left: 0;
    margin-top: 13px;
    white-space: nowrap;
    text-decoration: underline;
}
.filter-date-button:not(.filter-date-button-disabled):hover {
    opacity: 0.65;
}
.filter-date-button-disabled {
    opacity: 0.5;
    pointer-event: none;
    text-decoration: none;
}

@media screen and (min-width: 500px) {
    .filter-date {
        flex-direction: row;
    }
    .filter-date-button {
        margin-left: 50px;
        margin-top: 0;
    }
}

/* date range slider style override */
.odswidget-date-range-slider .irs--flat .irs-from, .odswidget-date-range-slider .irs--flat .irs-single, .odswidget-date-range-slider .irs--flat .irs-to {
    color: var(--text);
    border: 1px solid #cbd2db;
    border-radius: 2rem;
    background: #FFFFFF;
}
.odswidget-date-range-slider .irs--flat .irs-from:before, .odswidget-date-range-slider .irs--flat .irs-single:before, .odswidget-date-range-slider .irs--flat .irs-to:before {
    border-top-color: var(--text);
}
.odswidget-date-range-slider .irs--flat .irs-bar {
    background-color: var(--highlight);
}
.odswidget-date-range-slider .irs--flat .irs-handle>i:first-child {
    background-color: var(--highlight);
}
.odswidget-date-range-slider .irs--flat .irs-handle.state_hover>i:first-child, .odswidget-date-range-slider .irs--flat .irs-handle:hover>i:first-child {
    background-color: var(--text);
}



/* Content Card
========================================================================== */

.content-card {
    background-color: var(--boxes-background);
    border-radius: 4px;
    height: 100%;
    box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.13);
}

.content-card-horizontal {
    display: flex;
}

.content-card-horizontal .content-card-img {
    height: auto;
    flex: 0 0 25%;
    width: 25%;
    border-radius: 4px 0 0 4px;
}

.content-card-img {
    display: block;
    height: 110px;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
}

.content-card-body {
    padding: 26px;
    flex: 1 1 auto;
}

.content-card-title {
    color: var(--titles);
    font-size: 1.2rem;
    line-height: 1.5;
    font-weight: normal;
    margin-top: 0;
    margin-bottom: 13px;
    max-width: 100%;
}

.content-card-description {
    color: var(--text);
    font-size: 1rem;
    line-height: 1.5;
    font-weight: normal;
    margin-top: 0;
    margin-bottom: 26px;
    max-width: 100%;
}

.content-card-fields dt {
    font-size: 0.8rem;
    opacity: 0.8;
}

.content-card-fields dd {
    margin-left: 0;
}

.content-card-icon {
    color: var(--highlight);
    font-size: 2rem;
    margin-bottom: 13px;
    max-width: 100%;
}

.content-card-link {
    color: var(--links);
    font-weight: bold;
    transition: all .2s;
    opacity: 1;
    max-width: 100%;
}

.content-card-link:hover {
    opacity: .7;
    text-decoration: none;
}

.content-card-button {
    color: var(--highlight);
    border: 1px solid var(--highlight);
    background: transparent;
    display: inline-block;
    text-align: center;
    font-size: .867rem;
    border-radius: 4px;
    padding: .5rem 1.15rem;
    text-decoration: none;
    transition: all .2s;
}

.content-card-button:hover {
    background-color: var(--highlight);
    color: #FFFFFF;
    text-decoration: none;
}


/* KPI Card
========================================================================== */

@media screen and (min-width: 992px) {
    .kpis-container {
        display: flex;
        justify-content: center;
    }
}

.kpi-card {
    background-color: var(--boxes-background);
    height: 100%;
    padding: 39px;
    border-radius: 4px;
    box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.13);
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    text-align: center;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    text-align: center;
}

.kpi-icon {
    color: var(--highlight);
    color: var(--secondary-color);
    font-size: 4rem;
    margin-top: 0;
    margin-bottom: 13px;
    max-width: 100%;
}

.kpi-title {
    font-weight: normal;
    color: var(--highlight);
    font-size: 3.2rem;
    margin-top: 0;
    margin-bottom: 13px;
    max-width: 100%;
}

.kpi-unit {
    font-size: 0.8em;
    color: var(--secondary-color);
}

.kpi-description {
    color: var(--text);
    font-size: 1rem;
    line-height: 1.5;
    font-weight: normal;
    margin-top: 0;
    margin-bottom: 0;
    max-width: 100%;
}


/* Table Module Basic
========================================================================== */

.table-module {
    height: 1000px;
    overflow-y: auto;
}

.table-basic {
    display: table;
    border-collapse: collapse;
    width: 100%;
    white-space: nowrap;
    background-color: #FFFFFF;
    margin-bottom: 20px;
}

.table-basic thead th {
    color: var(--titles);
    background-color: #f6f8fb;
    font-weight: 500;
    padding: 13px 3px;
    position: sticky;
    top: 0;
    z-index: 1;
}

.table-basic thead th:first-child,
.table-basic tbody td:first-child {
    padding-left: 13px;
}

.table-basic tr td,
.table-basic tbody th {
    font-weight: normal;
    border-top: 1px solid #dee5ef;
}

.table-basic tr td {
    padding: 13px 3px;
}

.table-basic td {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.table-dropdown-button {
    width: 28px;
    height: 28px;
    padding: 0;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: white;
    border: 1px solid transparent;
    border-radius: 4px;
    margin-left: auto;
}

.table-basic tr:hover .table-dropdown-button {
    border-color: var(--highlight);
    text-decoration: none;
}

/** Specific override for custom views **/
@media screen and (min-width: 1408px) {
    .ods-dataset-visualization .ods-tabs__pane .container:not(.is-max-desktop):not(.is-max-widescreen) {
        max-width: 100%;
    }
}
@media screen and (min-width: 1216px) {
    .ods-dataset-visualization .ods-tabs__pane .container:not(.is-max-desktop) {
        max-width: 100%;
    }
}
@media screen and (min-width: 1024px) {
    .ods-dataset-visualization .ods-tabs__pane .container {
        max-width: 100%;
    }
}