Skip to content

Instantly share code, notes, and snippets.

@indirectlylit
Last active June 1, 2022 19:53
Show Gist options
  • Save indirectlylit/665ba47aaaddddc76380591fe93a281e to your computer and use it in GitHub Desktop.
Save indirectlylit/665ba47aaaddddc76380591fe93a281e to your computer and use it in GitHub Desktop.
notes on sortable table

Notes on new table component

ref: learningequality/kolibri-design-system#330

Naming and scope

Presumably this component will be used for all CoreTable instances, not just sortable ones?

I'd recommend using simply KTable (with option to enable sorting on a per-column basis) rather than KSortableTable which implies that it's not meant for static tables.

Column header component API

I would not recommend building off of the SortableHeader component from KDP because it is built to handle one very specific scenario and makes heavy use of global state (like columnSorting) and props that don't generalize (like pos).

Instead, I'd recommend making the header component fairly "dumb" and let the user control it from the outside. For example:

  • Props
    • boolean sortable (whether or not the column is clickable and can be sorted)
    • string order (if sortable, one of an enumerated list of sort types, e.g. for ascending, descending, or unsorted)
  • Events
    • simple click handler, just like a button. Only used if sortable.

Resource sorting special-case

Folders of resources in a channel have a built-in sort order which reflects a learning pathway of sorts, as defined by the content curator. This means that sometimes e.g. in progress reports it's not sufficient to simply toggle between sorted ascending and descending: there's a third "original" or "unsorted" state that needs to be reachable. For example:

Unsorted Descending Ascending
Introduction Final quiz Second video
First video First video Introduction
Second video Introduction First video
Final quiz Second video Final quiz

Most columns will not need the unsorted state, so this would need to be an "opt-in" feature to give the user the ability to "clear" the sorting when necessary.

There are really three kinds of sorting behaviors possible. From a UX perspective, the question is what happens when a user clicks repeatedly on the same header. Does it:

  1. Stay sorted in a single fixed order (probably unusual?)
  2. Toggle between ascending and descending order (most common)
  3. Cycle between ascending, descending, and some "original" order (applies to resources)

Composability

Currently the CoreTable relies heavily on /deep/ selectors and some special CSS classes (core-table-checkbox-col and core-table-button-col). The developer then uses "standard" table sub-elements such as th and td within the named slots.

We also use transition group 'move' transitions extensively to help users visually see what happens when they change sort order, and this behavior should preferably become part of the KSortableTable.

All of this would make the current CoreTable component API hard to document in the Design System, which relies on components and props as the fundamental vocabulary - not CSS classes and native HTML tags.

An alternative strategy which would fit better with the design system is would be to create a set of components that "play nicely" together, similar to [KButton, KButtonGroup] and [KGrid, KGridItem].

For example, instead of:

<CoreTable>
  <template #headers>
    <th>...</th>
    <th>...</th>
    <th></th>
  </template>
  <template #tbody>
    <transition-group tag="tbody" name="list">
      <tr v-for="tableRow in tableData" :key="tableRow.id">
        <td>...</td>
        <td>...</td>
        <td class="core-table-button-col">...</td>
      </tr>
    </transition-group>
  </template>
</CoreTable>

It might be something along the lines of:

<KTable>
  <template #headers>
    <KTableHeader sortable>...</KTableHeader>
    <KTableHeader sortable>...</KTableHeader>
    <KTableHeader />
  </template>
  <template #rows>
    <KTableRow v-for="tableRow in tableData" :key="tableRow.id">
      <KTableCell>...</KTableCell>
      <KTableCell>...</KTableCell>
      <KTableCell button-col>...</KTableCell>
    </KTableRow>
  </template>
</KTable>

[CoreTable, transition-group, tbody, th, tr, td, .core-table-button-col]

vs

[KTable, KTableHeader, KTableRow, KTableCell] with associated props

Notes:

  • Above, tbody is handled by KTable and the named slot is renamed to rows
  • sometimes th tags appear in rows because both columns and rows can have header cells. So with this API <KTableCell> would need a default-false "header" flag like <KTableCell th> which would render a th instead of a td for that cell

Async data and pagination

When pagination is involved, sorting requires a round-trip request to the server which means introducing a 'loading' state. It is probably out-of-scope for this work, but it would be worth auditing how many of our CoreTable instances use pagination. If it's significant, it might be worth building in consistent pagination functionality along with async loading states.

@nucleogenesis
Copy link

You rule @indirectlylit thanks for this. I love the idea of abstracting all that table HTML business and behavior away into some simple components. This is going to be a great help!

@indirectlylit
Copy link
Author

glad to help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment