Building A Trello Power-Up

Published by Bentley Cook on May 23, 2017 •

In this post, we’ll walk you through building a Power-Up for Trello. We’ll build a Power-Up that gives teams the power to add size estimates to Trello cards.

Don’t know what a Power-Up is? You can read about them here, check out popular Power-Ups here, and read the developer documentation here. We’ll start from the very beginning and hopefully end up with a Power-Up you and your team can customize and use internally!

Imagine the scenario: You and your team use Trello to manage projects. You keep a list of ideas for projects that you might want to tackle one day. Some of them are great ideas but might take the team a while to complete. You want to use t-shirt sizing as a way of quickly ball-parking the size of projects so that you and the team can better understand the list at a glance.

Getting Started

We will need a server hosting all of our front-end code. For this tutorial I’m going to be using Glitch. It is a great tool for getting started. If you want to follow along in Glitch, I’ve put together a skeleton project to get you started: https://glitch.com/edit/#!/trello-power-up-skeleton.

Power-Ups require that the assets be served over HTTPS. Trello loads the assets into the web client on an iframe, so you’ll want to update your CORS settings to allow Trello.com to make requests to receive your files. For example, using express in node.js we have:

var cors = require('cors');
var express = require('express');
var app = express();

app.use(cors({ origin: 'https://trello.com' }));

Firstly, we’re going to want to register our Power-Up with Trello. To add a custom Power-Up to a team, you need to be an admin on a team. If you want, you can create a new team to add the Power-Up to. Login to Trello and visit the custom Power-Up admin portal at https://trello.com/power-ups/admin.

You should see a list of the teams for which you are an admin. Choose the team you’d like to add the Power-Up to. Then click the “Create New Power-Up” button. You will be presented with a form to fill out.

For the sake of brevity, we’ll skip over what all of the fields are used for and focus on the ones you need to get started. You can read more about them at Managing Power-Ups.

We want to turn on the correct capabilities that our Power-Up will need. To start, we’re only going to make use of the card-buttons and callback capabilities. These capabilities will allow us to add a button onto the back of a card. We’ll use that button to load a form with a drop-down input that contains estimation sizes a user can select. Turn on the green switches for the card-buttons and callback capabilities:

The other most important field is the iframe connector field. This field is used to point to a HTML page that Trello will load onto the page as a hidden iframe and then Trello will use it to communicate to our Power-Up via window.postMessage. If you’re following along on Glitch, you should click the “Show Live” button and copy and paste the URL of the page into this field. If you’re using a different hosting option, you’re going to want to create the HTML page below and, once it is live, put the URL to access it into the iframe connector field.

Let’s take a look at what our index.html file looks like:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script src="https://p.trellocdn.com/power-up.min.js"></script>
    <script src="/client.js"></script>
  </body>
</html>

Not a whole lot to see here, huh? 🤔 We’re including the Power-Up client library as we’ll need it to interact with Trello. Additionally, we pull in our own client.js file.

Before we dive into the contents of client.js let’s talk a little bit more about the capabilities we turned on above. When you turn on a capability, you’re saying to Trello, “Hey, I want to do something here!” And when the time comes, Trello will reach out to you and say, “You told us you wanted to do something for card-buttons! What do you want to do?” This conversation takes place via the connector html page we included above. Your response for each capability should be included in the initialization of the Trello client library on the connector. Here, we’re using client.js to cover this:

TrelloPowerUp.initialize({
  'card-buttons': function(t, options){
    return [{
      icon: 'https://cdn.glitch.com/1b42d7fe-bda8-4af8-a6c8-eff0cea9e08a%2Frocket-ship.png?1494946700421',
      text: 'Estimate Size',
    }];
  },
});

When we initialize the Trello client library, for each of the capabilities we’ve turned on, we must include a function that Trello will call at the point in time that they should be invoked. In the case of card-buttons, when a user views the back of a card, Trello will call the function we have defined above.

The card-buttons capability expects the end-result of the function call to be an array of objects with each object representing a button to include on the card back. In our case, we’re only showing a single button with an icon and text.

And just like that, we’ve got the bare minimum needed for a Power-Up! 🚀

If you haven’t yet, save the custom Power-Up admin form. Now when you open the Power-Ups directory, you will see your custom Power-Up listed under the Custom section on the left. 🎉 🎊

Check it out: Initial Power-Up Work Screenshot

Adding Functionality

Now that our Power-Up “works” we want to add some functionality. Let’s start by using the Power-Up client library’s t.popup method to provide some functionality to our button via the callback capability.

When we return the list of buttons that we want to show the user, each button object can include a callback parameter in addition to the text and icon that we’re already using. The callback parameter expects a function that will be called when the user clicks the button. For our callback, we just want to show the user an HTML page that includes our drop-down form. We’ll use the t.popup method to render the page. It will be displayed adjacent to the element in the current context. Pop-ups are designed for lists of capabilities or content that a user is expected to click on.

Here’s our updated client.js:

TrelloPowerUp.initialize({
  'card-buttons': function(t, options){
    return [{
      icon: 'https://cdn.glitch.com/1b42d7fe-bda8-4af8-a6c8-eff0cea9e08a%2Frocket-ship.png?1494946700421',
      text: 'Estimate Size',
      callback: function(t){
        return t.popup({
          title: "Estimation",
          url: 'estimate.html'
        });
      }
    }];
  }
});

Now that we’re trying to show an html page called estimate.html we should probably add it to the project as well… It should contain a form with drop-down items for our sizing scheme.

Check out estimate.html:

<html>
  <head>
    <link rel="stylesheet" href="https://p.trellocdn.com/power-up.min.css">
    <style>
      select { height: 30px; }
    </style>
    <script src="https://p.trellocdn.com/power-up.min.js"></script>
  </head>
  <body>
    <form id="estimate">
      <label for="estimateSize">Estimate:</label>
      <select name="size" id="estimateSize">
        <option value="small">Small 👕</option>
        <option value="medium">Medium 👚</option>
        <option value="large">Large 👔</option>
        <option value="x-large">Extra Large 👖</option>
      </select>
      <button type="submit" class="mod-primary">Save</button>
    </form>
  </body>
</html>

We’re including Power-Ups’ styles here so that our Power-Up looks and feels like Trello does. No need to add any extra styling!

If we go refresh Trello and take a look at our Power-Up, we can see that we’ve got the drop-down showing!

Power-Up Popup With Correct Height

But, nothing happens when we click the save button! Let’s fix that by including some JavaScript in our iframe. We’ll create a new file called /js/estimate.js and include it on estimate.html:

<button type="submit" class="mod-primary">Save</button>
    </form>
    <script src="./js/estimate.js"></script>
  </body>
</html>

We want to make use of the methods provided by the client library so we want to instantiate an instance of it for our use:

var t = TrelloPowerUp.iframe();

You may have noticed the gap of space between our save button and the bottom of the popup. That’s doesn’t look very Trello-y, so let’s fix it!

The client library provides a method t.render() that expects a function to be called by the web client when there are updates. We can use it along with t.sizeTo() to resize our popup on load:

t.render(function(){
  t.sizeTo('#estimate').done();
});

After reloading, that looks much better!

Now we need to handle what happens when the user clicks on our button. We’ll want to save our data so that we can display it in a badge later on. The client library provides a number of data setting and getting methods. We only care about using this estimate within the context of the card, so we will save it to the context of the card using t.set(). We’ll trigger setting the data by adding an event listener to our button and then use t.closePopup() to close our popup since the user has nothing left to do. Now our js/estimate.js should look like this:

var t = TrelloPowerUp.iframe();

window.estimate.addEventListener('submit', function(event){
  // Stop the browser trying to submit the form itself.
  event.preventDefault();
  return t.set('card', 'shared', 'estimate', window.estimateSize.value)
  .then(function(){
    t.closePopup();
  });
});

t.render(function(){
  t.sizeTo('#estimate').done();
});

We can test that our data has been set properly by checking the plugindata field for our card via Trello’s API. You can make a GET request to https://api.trello.com/1/cards/{yourCardId}/plugindata (documentation here) with your API key and token and you will see the data stored on the card. It should look something like:

[{
  "id": "591b06d48d971c4c59be0b72",
  "idPlugin": "5914735007cb5508b5064001",
  "scope": "card",
  "idModel": "591affde9068cae91542b976",
  "value": "{\"estimate\":\"small\"}",
  "access": "shared"
}]

Don’t 🎊 just yet! If you open the popup via the card button, select a size, hit the button, and then re-open the popup, you’ll notice that your previous selection isn’t initially selected. When the iframe loads, we’ll want to check to see if there is a value that has already been set, and if so pre-select it for our list. Adding this leaves our js/estimate.js looking like:

/* global TrelloPowerUp */

var t = TrelloPowerUp.iframe();

window.estimate.addEventListener('submit', function(event){
  event.preventDefault();
  return t.set('card', 'shared', 'estimate', window.estimateSize.value)
  .then(function(){
    t.closePopup();
  });
});

t.render(function(){
  return t.get('card', 'shared', 'estimate')
  .then(function(estimate){
    window.estimateSize.value = estimate;
  })
  .then(function(){
    t.sizeTo('#estimate').done();
  });
});

Now we can reload Trello, click on our card button, and see that the size we selected last time is already selected for us! You can see all of the code for this project here.

Next Steps

We’ve added the ability for a user to select a size, but you can only see the size when the card button is clicked. This doesn’t provide a lot of perspective to the Trello board. In part two we will use the card-badges and card-details-badges to surface the data!


Read Part Two


What Should We Do Next?

The fate of part three is in your hands! We’ve got a lot of things we could cover but we want to make sure we walk you through the things you most want to hear about. Some possible options include:

  • Settings: Allow users to change the estimation points system via the Power-Ups settings. We’ll add the ability to let them chose between t-shirt sizes and fibonacci numbers.
  • Trello’s RESTful API: Allow users to sort lists by the estimation sizes via a board button.
  • Integrate Third-Party Service: Allow users to authorize with a third-party service to provide extra data to the estimation asynchronously. We’ll setup a fake endpoint in our Node server for this.
  • Your Idea!: Give us another user story to build!

Head over to this Trello board to vote for an idea, submit a new one, leave a comment about the walkthrough, ask a question, and check out the Power-Up we just built, live on a board.