Page MenuHomePhabricator

Table: Add basic pagination support
Open, MediumPublic3 Estimated Story Points

Description

Background

Pagination is an optional feature of the Table component that can be added to the footer section when needed. We will provide a standard pagination UI built into the Table component, but dev users could add their own in the footer slot if desired. This task covers implementation of the standard Table pagination.

The pagination UI will consist of the following elements:

  • Items per page selector
  • Number of items indicator (optional; not recommended for large datasets)
  • Page arrows

User story

As a user, I want to be able to look through data in an efficient, readable way

As a developer, I want to improve performance by fetching and displaying small portions of data at once

Design spec

image.png (768×690 px, 34 KB)

Guidelines

Open questions

  • Can the number of rows in the Select to be customizable by whoever is implementing the table?
    • Yes, this should be customizable. A default set will exist in the Table component, but it can be overridden.
  • Are there performance concerns at a certain level of rows? e.g. Should we enforce a max?
    • When fetching data from the MediaWiki API, a limit of 50 items is enforced, so 50 seems like a sensible limit.
  • Is it possible to only show the text "rows" once, for the selected amount so we don't repeat it for every option?
    • Yes, this is possible with our Select component.
  • Can the usage of this pagination be optional?
    • Yes - users will be able to turn on the default pagination we build into the Table component, or add their own custom pagination using the Table component's #footer slot.

API and UI details

API

Part of the implementation work of this task will be to determine how to enable, control, and respond to pagination. See Vuetify's data table for an example. The parent component will need to know which items to show at a given time, and when to fetch/provide new rows to show when the user interacts with the pagination.

All user-facing strings will need to come from props with a default English value, including:

  • aria-label for the items per page selector
  • "rows" label in the items per page selector
  • "all" option label in the items per page selector
  • "X-Y of Z items" in the number of items indicator
  • aria-labels for all of the icon-only navigation buttons
Positioning

Pagination will be displayed at the bottom as default, while the top pagination could be optionally included.

Items per page selector
  • The selected option will have word "rows" after it when displayed in the Select handle
  • The default set of options will be 10, 20, and 50. This can be overridden via a prop.
  • The default option will be 10
  • Optional improvement (could be done as a separate task later): If the total number of items has been passed in (to show the number of items indicator), and one of the options doesn't makes sense, that option should not be shown. For example, if there are 18 total items, the "50" option should not be shown because "20" already shows all items.
Number of items indicator
  • Will be displayed if the dev user provides the total number of items in the dataset
  • Otherwise, it will not be displayed
  • Will be hidden on small screens to save space
  • Ensure the default example uses an en-dash e.g. "X–Y of Z items"
Page arrows
  • Need aria-labels for accessibility
  • On the first page, the first and previous buttons should be disabled. Same for the last and next buttons on the last page.

Acceptance criteria (or Done)

Design

  • Design the Figma spec sheet and add it in this task
  • Update the component in the Figma library. This step will be done by a DST member.
  • Document the Guidelines for this Table's pagination and include them in the Table's guidelines in Codex

Code

  • Implement the component in Codex
Follow-up tasks
  • T370445 (Server-side pagination)
  • T370447 (CSS-only pagination example)

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Restricted Application added a subscriber: Aklapper. · View Herald Transcript
CCiufo-WMF triaged this task as Medium priority.Mar 15 2024, 3:04 PM
CCiufo-WMF moved this task from Inbox to Up Next on the Design-System-Team board.

@Sarai-WMDE I discussed table pagination with the DST engineers today and we came up with some more option questions, even beyond what you and I had discussed earlier. One big blocker may be the translatable label thing - this really does demand a solution like the one proposed in T345386, so that users don't have to pass in numerous translated strings that are the same across most projects. Because of this, and the general complexity here, we think pagination should be removed from the MVP scope and tackled as a separate feature post-MVP. I still strong believe that we should build the pagination UI for consistency's sake, and to prevent Codex users from having to replicate it across projects, but I think we should complete the rest of the Table component first, then tackle pagination as a new feature.

What do you think? Happy to chat about this if it helps!

That sounds sensible to me. I wonder if "Simplifying the pagination UI to us as little text/labels as possible" could still have an impact on this decision? My plan was to explore that next.

On the other hand, from the active use cases that the Table component could cover, both Wikifunctions (Abstract Wikipedia) and Global user contributions (Trust & Safety) have a need for (some kind of) table pagination. I guess it will still be possible for them to "compose" a pagination functionality in context (within their instances of the component) following our specifications. Maybe we could provide a custom demo of the table they can follow? This would also serve as a placeholder solution while we figure the internal component out.

I wonder if "Simplifying the pagination UI to us as little text/labels as possible" could still have an impact on this decision? My plan was to explore that next.

Absolutely; I think we should do this exploration first because it will impact the implementation a lot.

On the other hand, from the active use cases that the Table component could cover, both Wikifunctions (Abstract Wikipedia) and Global user contributions (Trust & Safety) have a need for (some kind of) table pagination. I guess it will still be possible for them to "compose" a pagination functionality in context (within their instances of the component) following our specifications. Maybe we could provide a custom demo of the table they can follow? This would also serve as a placeholder solution while we figure the internal component out.

This is a good point, and a reason for us to prioritize building out table pagination in Codex. In the meantime: for Wikifunctions, adding custom pagination to the Vue component via the footer slot would be doable. I'm more concerned about the GUC project, since that would ideally involve us migrating MediaWiki's TablePager to use Codex (the CSS-only version of the Table, I assume). I think we need to evaluate what that would mean, both in terms of design changes to the existing TablePager UI and in terms of effort. cc @mwilliams because we were just chatting about this!

See T361048 for the TablePager evaluation task

@mwilliams @DTorsani-WMF, and I met today to discuss on the most useful version of the Table's pagination for the upcoming use cases (GUC, Metrics Platform, Wishlist, etc.) and we decided on this simplified version based on the one designed by @Sarai-WMDE.

Captura de pantalla 2024-04-17 a las 17.49.06.png (408×900 px, 22 KB)

The pagination will consist of the following elements:

  • Items per page selector
  • Number of items indicator
  • Page arrows

Thanks for providing this updated design! I've responded below to the open questions in the task description.

Can the number of rows in the Select to be customizable by whoever is implementing the table?

I don't think it should be. IMO, this is something we should curate and automate based on research on best practices. I think we should:

  • Determine which options to show for a maximum number of results (e.g. the longest list of results we can expect). The list in the design makes sense to me (5, 10, 20, 50, All) - we probably never want to show more than 50 rows in a paged table (more on this below).
  • (Maybe) Only show options that make sense. For example, if there are 15 total results, we should not show "50" as an option, since "20" will show all results. If the number of results is unknown, we will just show all options. I'm not really sure if this is necessary, though.

Are there performance concerns at a certain level of rows? e.g. Should we enforce a max?

FWIW, when fetching data from the MediaWiki API, a limit of 50 items is enforced, so 50 seems like a sensible limit. This also appears to be the limit in TablePager.

Is it possible to only show the text "rows" once, for the selected amount so we don't repeat it for every option?

Yes, this is possible with our Select component.

Can the usage of this pagination be optional?

Yes - users will be able to turn on the default pagination we build into the Table component, or add their own custom pagination using the Table component's #footer slot.

Thanks for providing this updated design! I've responded below to the open questions in the task description.

Can the number of rows in the Select to be customizable by whoever is implementing the table?

I don't think it should be. IMO, this is something we should curate and automate based on research on best practices. I think we should:

  • Determine which options to show for a maximum number of results (e.g. the longest list of results we can expect). The list in the design makes sense to me (5, 10, 20, 50, All) - we probably never want to show more than 50 rows in a paged table (more on this below).
  • (Maybe) Only show options that make sense. For example, if there are 15 total results, we should not show "50" as an option, since "20" will show all results. If the number of results is unknown, we will just show all options. I'm not really sure if this is necessary, though.

@AnneT it makes sense, we could use this "5, 10, 20, 50, All" logic and adjust it based on the number of rows. This would involve removing unnecessary numbers and always displaying options up to the number of rows below it. For example, if the table has 19 rows, the selection would be "5, 10, All".

CCiufo-WMF moved this task from Inbox to Planned Component Updates on the Codex board.

Unlinking from the parent task as this is no longer in scope for the MVP of the Table component.

Once this has a design spec and the two remaining open questions have been resolved (they're in the "API and UI details" section and bolded), this will be ready for development.

@KColeman-WMF When we chatted I believe I pitched this with rows being customizable since your GUC use case could have some very long lists so starting with 5 isn't particularly useful. Anne has given some good reasoning here why we wouldn't go in that direction but could you give a bit of insight into your scenario and how many rows you were hoping this could handle?

Hi @mwilliams - A common scenario for cross-wiki patrollers using GUC, is that they want to rapidly see all edits from all wikis for a given IP address/range, because most of the time someone is asking for something to be blocked immediately.

During user testing two Stewards said they would look at 500 results initially, as it helps to see the big picture first before sorting by column. Another Steward said: "50 is fine for most people I assume. I personally have it set to 125." One user also said they manually update the URL to display 1000 results. So if we can provide high limits that would be beneficial to these power users.

In the prototype I showed Stewards pagination of 50 | 100 | 250 | 500. This was based on the current XTools GUC table pagination

My take on the two open questions:

  1. Which option (5, 10, 20, 50, and all) for the items per page selector should be the default?
    1. My suggestion is to make the default customizable by the implementor. Is that possible? My suggestion for the default from Codex, and if one is not defined during implementation, is 10. This amount of rows feels like a fair amount of data to understand at one time and will be a good amount to generally fit on a page for common screen sizes without being such a small amount of rows (5) and not too much to run off the edge of the screen (20+).
  2. Should the pages loop? E.g. on the last page, if the user presses "next", should the first page be shown?
    1. I'm not sure I've seen this done much before, and so it might not be very familiar or expected. That doesn't mean we shouldn't do it, but familiarity is valuable. Additionally, since it looks like we will have an icon button for first and last then these can be easily navigated to from either end. This would mean that when on the first page, the previous and first page icon buttons would be disabled, and the same for the last page. This is represented in the design mockup in the description of this task.

Thanks all for the input. Derek and I chatted about pagination today and have some proposals:

Pagination: enable display on top of the table

We think we should make it possible, or possibly the default, to show pagination both above and below the table. Pagination arrows are displayed at both the top and bottom for TablePager tables, which is necessary since most of them contain large datasets showing many rows at once. We propose adding this to the Codex Table, either by default, or as a configuration option.

Items indicator: change phrasing

The current items indicator takes the format "X-Y of Z items". However, Z may not be known for some tables. To accommodate use cases where Z is known and Z is not known, we propose changing the phrasing to "Showing items X-Y of Z". That way, the "of Z" part can easily be omitted if Z isn't passed in.

Items per page selector: remove "all"

We propose removing "all" as an option for the items per page selector. This option doesn't exist in MediaWiki core's TablePager, and would not be recommended for larger datasets. Instead, we propose making the default highest option 100 instead of "all".


Would love to hear thoughts from others, especially @bmartinezcalvo!

El T360154#9856912, @AnneT escribió:

Thanks all for the input. Derek and I chatted about pagination today and have some proposals:

Pagination: enable display on top of the table

We think we should make it possible, or possibly the default, to show pagination both above and below the table. Pagination arrows are displayed at both the top and bottom for TablePager tables, which is necessary since most of them contain large datasets showing many rows at once. We propose adding this to the Codex Table, either by default, or as a configuration option.

@AnneT I'm okay with including the pagination as a customizable prop at the bottom or on the top of the Table. I just recommend including guidelines about this in Codex in case we allow both positions, explaining when to use the bottom or the top pagination.

Items indicator: change phrasing

The current items indicator takes the format "X-Y of Z items". However, Z may not be known for some tables. To accommodate use cases where Z is known and Z is not known, we propose changing the phrasing to "Showing items X-Y of Z". That way, the "of Z" part can easily be omitted if Z isn't passed in.

I wonder if this "Showing items X-Y of Z" (e.g. "Showing items 1-5 of 100" ) will be too long for small screens like mobile, especially if the "showing" is translated into a language with a longer word (e.g. "Visualizzazione 1-10 di 999" in Italian).

Items per page selector: remove "all"

We propose removing "all" as an option for the items per page selector. This option doesn't exist in MediaWiki core's TablePager, and would not be recommended for larger datasets. Instead, we propose making the default highest option 100 instead of "all".

I agree, let's remove the "all" as an option.

To @bmartinezcalvo 's second point, I imagine we might just omit this on smaller screens, as it is not necessarily interaction but potentially helpful information. Alternatively we could stack these elements, but that might make for a longer screen or content.

Additionally, really small detail, but let's ensure we are using an en dash (-) for number ranges. https://en.wikipedia.org/wiki/Dash

Thanks @bmartinezcalvo and @DTorsani-WMF for your responses! I've updated the task accordingly. There are a few more open questions, two of which are design related:

  1. What should be the default positioning of pagination - top, bottom, or both?
  2. Where should pagination go in relation to the header or footer slot content?
  1. I'm thinking both, as top only feels slightly odd to be a default, since traditionally we find pagination at the bottom of these elements, but bottom by default might lead to it going missed on very long tables. So both feels like a good option as it asks the implementor to opt out of top or bottom, but curious how others feel about this.
  2. I believe pagination should be the closest element to the table which it is paginating. So with everything included, I would suggest the order should be:
Title/header slot
Top pagination
Table content
Bottom pagination
Footer slot

I lean towards implementing Bottom Pagination as the default since it aligns with users' natural reading flow, allowing them to read the Table rows first. And I would reserve Top Pagination or Top+Bottom Pagination for long tables. Wdyt? @AnneT @DTorsani-WMF

Agree with @bmartinezcalvo - default pagination at the bottom and having both is a choice for folks that know that their table is going to be really long.

I've completed the Figma spec and Guidelines for the Table's pagination.

After some discussions, we have decided to implement the Table's pagination as follows:

  • Default pagination position: the default position will be at the bottom, with the top pagination being optional.
  • Items per page selector: for this MVP, the Select will always display all 10, 20, 50 as default. We will allow for more customization in future iterations.
    image.png (768×690 px, 34 KB)
  • Loading: we will implement the easier solution for the MVP, so we will use the Inline ProgressBar in the current Table's page while the next page is loading (with the buttons disabled while loading). A separate future task will be created to evaluate using the Skeleton when loading.
  • New paddings: in order to make the Table with pagination lighter, we will create a separate task to update the Table's header and footer paddings.
CCiufo-WMF set the point value for this task to 5.Jun 24 2024, 6:09 PM

Change #1050673 had a related patch set uploaded (by Eric Gardner; author: Eric Gardner):

[design/codex@main] [POC, DNM] WIP TablePager component

https://gerrit.wikimedia.org/r/1050673

egardner renamed this task from Table: Add pagination support to Table: Add basic pagination support.Thu, Jul 18, 5:56 PM
egardner updated the task description. (Show Details)
egardner updated the task description. (Show Details)

Change #1055461 had a related patch set uploaded (by Bmartinezcalvo; author: Bmartinezcalvo):

[design/codex@main] docs: include table pagination guidelines

https://gerrit.wikimedia.org/r/1055461

Change #1055461 merged by jenkins-bot:

[design/codex@main] docs: include table pagination guidelines

https://gerrit.wikimedia.org/r/1055461

@egardner I've reviewed the Table's pagination patch and there are some changes we need to fix:

  1. The divider between the last row and the pagination is missing.
    Captura de pantalla 2024-07-22 a las 20.14.56.png (686×681 px, 39 KB)
  2. The pagination selector is not functioning properly. When you are on the last page and switch the selector from 20 to 50 (or 10), the number of results per page does not change.
    Grabaciondepantalla2024-07-22alas20.17.00-ezgif.com-video-to-gif-converter.gif (673×600 px, 406 KB)

@egardner I've reviewed the Table's pagination patch and there are some changes we need to fix:

  1. The pagination selector is not functioning properly. When you are on the last page and switch the selector from 20 to 50 (or 10), the number of results per page does not change.
    Grabaciondepantalla2024-07-22alas20.17.00-ezgif.com-video-to-gif-converter.gif (673×600 px, 406 KB)

@bmartinezcalvo I think we need to specify the behavior more clearly if it's not inline with what you are expecting.

Let's imagine the following scenario. Say there is a paginated table with 43 rows. The table is configured to show 10 rows per page, and I navigate to the last page. Now the table shows 3 rows, because there are 4 previous pages of 10 and then the last page shows the remaining amount. In math terms, the number of items on the last page can be represented by this formula: lastPageSize = totalRows % rowsPerPage. (% is the "modulo" operator, it means "divide by this amount and return the remainder".)

If I now set the rows per page dropdown to 20, I would still see 3 results – now there are 2 previous pages of 20 rows, and the last row still has 3 (the remainder).

If I changed the dropdown to 50, then I would expect to see a change – in this case I'd see all rows at once.

The only other case where I would expect things to change would be in a case where the remainder is different. Say there is an option for 15 rows per page. If I was on the last page and I selected this option, then maybe the table should show 12 results – 2 pages of 15 rows, and then the remainder on the last page. 42 % 15 = 12.

Let me know if this behavior sounds correct to you, or if you have something different in mind.

Here's another scenario to think about: Say that I have another table of 43 rows total, showing 10 rows per page. I navigate one page forward (showing rows 11-20 now). Then I change the "items per page" dropdown to show 15 results per page instead of 10. What should happen? Clearly there should be 15 rows showing instead of 10 after I make the change, but which 15 rows should they be?

  • Should I see rows 1-15 (because this range is inclusive of row 11, which is the first one I had previously)?
  • Should I see rows 11-25 (15 rows starting at the same starting point from before, even though this is not a page you could naturally arrive at using only the arrow buttons)?
  • Should I see rows 16-30 (since I was on the second "page" before even though the size is different)?
  • Should I see something else not covered by the options above?

@bmartinezcalvo I've updated the deploy preview, you can see the updated table demo here: https://1050673--wikimedia-codex.netlify.app/components/demos/table.html#with-pagination

This demo implements the strategy of "ensure that the first row which was visible previously is still present" on whatever new page the user is presented with, while preserving "natural page boundaries". So you'll never see 11-25 if the table had 15 results per page; it would always be either 1-15 oder 16-30. Let me know if this behavior seems natural to you.

@bmartinezcalvo I've updated the deploy preview, you can see the updated table demo here: https://1050673--wikimedia-codex.netlify.app/components/demos/table.html#with-pagination

This demo implements the strategy of "ensure that the first row which was visible previously is still present" on whatever new page the user is presented with, while preserving "natural page boundaries". So you'll never see 11-25 if the table had 15 results per page; it would always be either 1-15 oder 16-30. Let me know if this behavior seems natural to you.

This behavior seems pretty intuitive to me, but I would still advocate for an approach that never changes the position of the rows currently in the table when the number of items per page is changed. Is it possible to provide an alternative implementation of the "preserve position of the first row" strategy to compare?

Ok, I've updated the demo to show the "preserve the first row in the same position" approach. You can see a live demo here: https://1050673--wikimedia-codex.netlify.app/sandbox/#cdx-table.

You can also see a demo for the alternate behavior (preserve natural page boundaries approach) here: https://1056600--wikimedia-codex.netlify.app/sandbox/#cdx-table

To see the difference, you can follow these steps:

  • Go through 1-2 pages with the page size set to 10 rows per page
  • Change the page size to 15 rows per page and compare the results to what you could see before the change

@egardner the updated approach definitely feels more intuitive to me.

@egardner the updated approach definitely feels more intuitive to me.

Same 👍🏼

One thing that folks should keep in mind if we stick with the approach of: "keep the first visible row at the top of the table when page size changes" – if the user is on the last page of items, and they increase the rows per page, they will not see any change. The table will not reach "backwards" to give them more data; it will keep the first row at the top and show up to X items until the data runs out.

If anyone is worried about the user not seeing any visible change in response to their action in such a scenario, we could consider making the table height always change based on the "items per page" selection, regardless of whether or not enough rows exist to fill the space.

One thing that folks should keep in mind if we stick with the approach of: "keep the first visible row at the top of the table when page size changes" – if the user is on the last page of items, and they increase the rows per page, they will not see any change. The table will not reach "backwards" to give them more data; it will keep the first row at the top and show up to X items until the data runs out.

If anyone is worried about the user not seeing any visible change in response to their action in such a scenario, we could consider making the table height always change based on the "items per page" selection, regardless of whether or not enough rows exist to fill the space.

I'm not really concerned about this. I'd argue the primary reason for increasing the number of items per page is to navigate between the pages faster. Once you're on the last page, you've probably already found what you're looking for or what your looking for is not in the table at all.

Change #1056999 had a related patch set uploaded (by LWatson; author: LWatson):

[mediawiki/core@master] Update Codex from v1.9.0 to v1.10.0

https://gerrit.wikimedia.org/r/1056999

One thing that folks should keep in mind if we stick with the approach of: "keep the first visible row at the top of the table when page size changes" – if the user is on the last page of items, and they increase the rows per page, they will not see any change. The table will not reach "backwards" to give them more data; it will keep the first row at the top and show up to X items until the data runs out.

If anyone is worried about the user not seeing any visible change in response to their action in such a scenario, we could consider making the table height always change based on the "items per page" selection, regardless of whether or not enough rows exist to fill the space.

@egardner I'm reviewing your last patch with the new approach implemented and it also seems reasonable to me. Regarding the user not seeing any visible changes when they are on the last page, as @CCiufo-WMF comments above, I also believe the main focus of the Table's pagination should be navigating through all the pages. Once the user completes this navigation and reaches the last page, they may not need to change the rows-per-page selector.

Anyway, since the rows-per-page selection will not change when the user is on the last page, what if we disable the selection on the last page to avoid confusion?

Captura de pantalla 2024-07-26 a las 12.25.52.png (518×1 px, 67 KB)

Anyway, since the rows-per-page selection will not change when the user is on the last page, what if we disable the selection on the last page to avoid confusion?

Captura de pantalla 2024-07-26 a las 12.25.52.png (518×1 px, 67 KB)

I think this is an idea we should hang on to for the future (especially if users complain about not seeing visible changes when results-per-page is changed while viewing the last page of the table). But for the introduction of the pagination feature I'd suggest that we hold off. I don't want to introduce too much complexity in the UX from the start, I'd rather add things incrementally when we are confident that we need them.

Right now I would argue that all the behavior follows reasonable expectations.

Change #1050673 merged by jenkins-bot:

[design/codex@main] Basic Table pagination and TablePager component

https://gerrit.wikimedia.org/r/1050673