[Workaround] An alternate to displaying more than 100 widgets/sections.

Working around the 100-widget limit in Card Services, used when building Workspace Add-ons.

[Workaround] An alternate to displaying more than 100 widgets/sections.
[Workaround] An alternate to displaying more than 100 widgets/sections.

TL;DR: Switch to adding all the data inside a Grid instead of individual widgets inside a section.

Context

If you've build add-ons on the latest Workspace platform using CardServices, you may've noticed that at the moment, the maximum number of widgets that you could add to a section is 100 —

100-widget limitation
100-widget limitation

For the most part, I think its a reasonable limit to have as you'd definitely not want a ton to browser through on your add-on — not to mention, having more components would just make the add-on heavy, hard to navigate and very time-consuming (something you may not have given the 30-second timeout).

However, I started working on another add-on that might — depending on how successful it gets 😎 — require it to go beyond that threshold.

Problem statement

To not ruin your day with spoilers about my next release (sly!), I've created the following scenario which comes unreasonably close to what I'm building next.

I wanted to be able to list all the functions that are supported by Google Sheets – a list that you can easily find here. As of today (21st Nov, 2022), they've got ~503 functions listed and I wanted to be able to list the same inside an add-on.

We can't do this by simply adding new widgets to an existing section given the limitation and so, here are some of the things that I tried and finally found something that got me to where I needed to be.

Architecture

I first tried — what I thought would be — "tricking" the system. The documentation led me to believe that inside a section, there can be no more than 100 widgets. To which I though, maybe I could try creating multiple sections instead. The logic to do that would be guided by the first letter of the function (i.e., everything that starts with an A goes into the 1st section, B would be 2nd and so on).

While I wasn't able to programatically do it, I still wanted to try if this were even possible and so, I manually created these groupings (with some help of my slick text editor) but as it turned out — folks at the Apps Script team may've already had this one in the bag.

Learning: Even if we group less than 100 widgets inside a single section and create multiple sections without breaching the limit, the add-on would only consider the first 100.

I almost gave-up and figured I could just create HTML content inside decorated-text and that's when RTFMing really kicked-in. Stumbled-upon making use of Grid as the primary widget and everything fell in place.

Codebase

you can access the entire code from my github repository here or make a copy of this script instead.

The major pieces of code to look for are —

Creating a new grid

const functionList = CardService.newCardSection();

...
const functionGrid = CardService.newGrid()
    .setTitle(" ")
    .setBorderStyle(CardService.newBorderStyle()
      .setType(CardService.BorderType.STROKE)
      .setCornerRadius(8)
      .setStrokeColor("#D3D3D3"))
    .setNumColumns(1)
    .setOnClickAction(CardService.newAction()
      .setFunctionName("functionClick_"));
...

Adding widgets to the grid

...
if (userFiltered?.length > 0) {
    userFiltered.forEach(func => functionGrid.addItem(funcGridItem(func)));
  } else {
    functionList.addWidget(CardService.newDecoratedText()
      .setText(`<font color="#FF0000">No function matched: "<b>${userInput}</b>"</font>`)
      .setWrapText(true))
    newData.forEach(func => functionGrid.addItem(funcGridItem(func)));
  }
  functionList.addWidget(functionGrid);
...

Bonus

The script from above also contains the logic to create a searchable grid and then filtering the results every time someone tries to search using different keyword.

0:00
/

Demo

Here's the one where you can see all the functions listed and each of them have their own set of property that can be viewed when clicked (observe the toast at the bottom) —

0:00
/