bootstrap-vue-next icon indicating copy to clipboard operation
bootstrap-vue-next copied to clipboard

BTable: Setting Head Slot overwrites sort icon

Open BenediktHeinrichs opened this issue 1 year ago • 6 comments

Describe the bug

We wanted to set a custom head column in a BTable that is searchable, but overwriting that slot removes the sort icon. This was previously doable in bootstrap-vue.

Reproduction

https://stackblitz.com/edit/github-yk2gus?file=src%2Fcomponents%2FComp.vue

Used Package Manager

npm

Validations

  • [X] Have tested with the latest version. This is still alpha version and sometime things change rapidly.
  • [X] Follow our Code of Conduct
  • [X] Read the Contributing Guide.
  • [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • [X] Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
  • [X] The provided reproduction is a minimal reproducible of the bug.

BenediktHeinrichs avatar Jun 28 '24 12:06 BenediktHeinrichs

This is by design, but I wonder if there should be a "icon-direction" slot scope

VividLemon avatar Jun 28 '24 14:06 VividLemon

To maybe elaborate on our problem, it's just very annoying that if I want to overwrite a head cell, that I technically need to reimplement all the sorting icon logic. (Our use case is removable columns, so we added an x-icon that on click removes a column)

Making the icon its own slot or even separating the head slots (e.g. head-label, head-sort-icon) would be very much appreciated.

BenediktHeinrichs avatar Jun 28 '24 19:06 BenediktHeinrichs

I understand why it would want to be preserved, but the idea is more-so that by changing the head slot, you are changing the entire head

VividLemon avatar Jun 28 '24 21:06 VividLemon

I just ran into the same issue today. It makes something relatively simple to do in bootstrap-vue a little more convoluted in bootstrap-vue-next.

The scenario I have is similar. I have about a dozen usages in my project which separately override the head slot for different reasons (dom changes, etc.). However, now in all those cases the sort icons are gone.

The way I see it there are a few solutions to consider to make this more simple.

  1. Don't add aria-sort="none" to fields which have sortable=false.

This change would match the behaviour in bootstrap-vue for when aria-sort was applied. Instead now we add aria-sort="none" even for fields which are not sortable.

With this change I could leave the sortAsc, sortDesc, sortDefault slots blank and reimplement icons with css.

I'm not 100% sure on the accessibility impact, but I believe it's negligible.

  1. Reissue the head slot in BTable.vue.

Something like this:

<template #head()="scope">
      <slot
        name="head()"
        :label="scope.field.label"
        :column="scope.field.key"
        :field="scope.field"
        :is-foot="false"
        v-bind="{...scope}"
      >
        {{ getTableFieldHeadLabel(scope.field) }}
      </slot>
      <template v-if="isSortable && !!scope.field.sortable && props.noSortableIcon === false">
      ... sort icon slot definitions ...

Now using the head slot will keep the sort icons.

  1. Expose the variables used to define sortAsc, sortDesc and sortDefault through the scope of the head slot.

Even if we keep the head slot the way it is, I can't easily reimplement these svg icons because there are variables I can't access through the slot scope. i.e. isSortable, sortByModel.

Even method getTableFieldHeadLabel is not exposed through the head slot.

If I wanted to reimplement the code for these icons (based on the intent of the head slot) it's going to look like this:

<b-table v-bind="defaultAttrs">
    <template #head()="{ label, column, field, isFoot}">
        <slot name="headInner" :label="label" :column="column" :field="field" :isFoot="isFoot">
            {{ getTableFieldHeadLabel(field) }}
        </slot>
        <template v-if="isSortable && !!field.sortable && noSortableIcon === false">
            <template v-if="sortBy?.find((el) => el.key === field.key)?.order === 'asc'">
                <span class="sr-only">{{ labelSortDesc }}</span>
                <svg aria-hidden="true" width="14" height="22" viewBox="0 0 14 22" xmlns="http://www.w3.org/2000/svg">
                    <title>{{ labelSortDesc }}</title>
                    <g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
                        <path d="m1.021 7.021 5.397-5.396a.5.5 0 0 1 .707 0l5.396 5.396" stroke="#BFBFBF" />
                        <path d="m12.522 15.358-5.397 5.396a.5.5 0 0 1-.707 0l-5.396-5.396" stroke="#1B75BC" />
                    </g>
                </svg>
            </template>
            <template v-else-if="sortBy?.find((el) => el.key === field.key)?.order === 'desc'">
                <span class="sr-only">{{ labelSortClear }}</span>
                <svg aria-hidden="true" width="14" height="22" viewBox="0 0 14 22" xmlns="http://www.w3.org/2000/svg">
                    <title>{{ labelSortClear }}</title>
                    <g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
                        <path d="m1.021 7.021 5.397-5.396a.5.5 0 0 1 .707 0l5.396 5.396" stroke="#1B75BC" />
                        <path d="m12.522 15.358-5.397 5.396a.5.5 0 0 1-.707 0l-5.396-5.396" stroke="#BFBFBF" />
                    </g>
                </svg>
            </template>
            <template v-else>
                <span class="sr-only">{{ labelSortAsc }}</span>
                <svg aria-hidden="true" width="14" height="22" viewBox="0 0 14 22" xmlns="http://www.w3.org/2000/svg">
                    <title>{{ labelSortAsc }}</title>
                    <g stroke="#1b75bc" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
                        <path
                            d="m1.021 7.021 5.397-5.396a.5.5 0 0 1 .707 0l5.396 5.396M12.522 15.358l-5.397 5.396a.5.5 0 0 1-.707 0l-5.396-5.396"
                        />
                    </g>
                </svg>
            </template>
        </template>
    </template>
</b-table>

I have a wrapper component with these overrides which I use in place of b-table. Now any component which uses this wrapper can use slot headInner to change the header content but keep the icons. This is just an example, it lacks being able to have slot names based on field keys. I also have to make copies of getTableFieldHeadLabel and titleCase as I don't believe they are exported from bv-next.

paulkirow avatar Jul 02 '24 19:07 paulkirow

Feel free to make a PR to which the head slot does not overwrite the sort icons

VividLemon avatar Jul 02 '24 23:07 VividLemon