Tutorial! Asana Chrome Extension: Filter Kanban Board Cards by Assignee

ChunYu Shi
Jul 24, 2017 · 14 min read

Summary: Easily make a personal chrome extension to filter your Asana kanban board cards by assignee.

Click here to skip to the implementation instructions

Oh, how I love my productivity tools. I’ve been a fan of Asana for years, and when their Kanban boards feature came out, I was extremely excited because they were now going into direct competition with another one of my favorite tools, Trello.

Overview of the Asana Kanban Board

Asana came into the Kanban scene pretty late, but I really pretty pleased to see that Asana boards were as beautiful as the rest of their interface. The drag and drop functionality of the cards feel as smooth as the Trello experience, and they’ve upheld much of the same minimalist UI choices as Trello: color tabs for labels, picture covers and details shown only on the “back” of the card (click on the card to open it and see the full details).

Asana’s team is also one of the best at integrating “delighters” into the product; in the traditional list view, rainbows cross off your tasks when you complete them, and in the case of the Kanban boards, I’ve seen monsters pop out as I move a card from one list to another.

Here’s an image from the Asana blog on the Kanban board release

Functionality Lacking

Both Asana & Trello are relatively light in their core Kanban functionality. This is a good thing, because feature bloat kills products. (Here’s praying Atlassian doesn’t turn Trello into a JIRA). However, as of today, Trello has some solid advantages over Asana in terms of functionality, and it’s actually not because the Trello team built any of them, but they came from the huge rabid lovestruck fanbase that they’ve built over the years.

When humans are filled with passion, they’ll do work and build amazing things to share with others for free. This is true, and explains the extensive list of free Trello chrome extensions that exists today.

Do a quick search for “trello” on the Chrome webstore and be blessed with a playground of toys for all productivity freaks.

A search for “Asana” will not bring up nearly as many extensions, free or otherwise.

Asana vs Trello Chrome Extensions

I’m not sure if part of the reason has anything to do with how the two products are built, but I have noticed in my dabblings with building extensions for Trello vs Asana that Asana is much harder to build extensions for. For example, every Trello card has a unique URL link per card. When you open up a Trello card, you are effectively navigating to a new URL.

See the lower left corner, which shows the URL for each card, on mouse hover. FYI, this is a public board I made for the Scrum Training Series. And yes, I use a lot of Trello Chrome extensions. The ones you see here are “Trelabels”, “Planyway” and “List Layouts for Trello” (not active).

Trello board URLs follow the convention as follows, where “b” stands for board and “c” stands for card.

https://trello.com/b/<board id>/<board name>
https://trello.com/c/<card id>/<card name>

This immediately points at the possibility of parsing a URL in order to access various information about a board or a card.

Similarly, Trello has some silly hidden secrets I discovered while playing around with building chrome extensions on my own.

For example, Although Trello labels are simply color pills, they have hidden the actual name of the label inside the <div> code, so that if you were to expand the color pill height using CSS, the text would reveal itself.

Using Chrome, if you right click on any color label and choose “inspect,” you can see that in the <div> for that label, there is actually a <span> which contains the label name. In this case, I named the label “Label Name” to illustrate this idea. FYI, this is a public board I made with a collection of Product Management resources for anyone looking to get into the field. Credits for resources go to Brainstation, a school in Toronto at which I took an intro course to Product Management.

If the CSS for the <div> that contains label is adjusted in height… magically, the name of the label appears!

In this situation, I changed the height = 20px, width = 80px, and line-height = 15pt.

Not the most beautiful label, but this is essentially how some of my favorite Trello chrome extensions, like “Trelabels” works, by modifying the front-end layer of the we application — changing the CSS styles, modifying HTML elements likes <div> or <span>, and using Javascript to tie it all together.

Asana Hack: Filter by Assignee

Asana, unlike Trello, does not seem to provide as much to work with. Or at least it’s harder, at least for people like me, who are not professional developers, but rather hobbyists with an interest in hacking stuff to make them better.

However, I did manage to create a useful, although rather hacky, Asana Crome Extension which I’d love to share with everyone. Comments for improvement is always appreciated.

Step 0: Setting up a Chrome Extension

For anyone who’s not familiar with Chrome extensions, they’re incredible add-on pieces of software that you can install directly onto your Chrome browser, instead of on your computer. There’s a whole store of Chrome extensions to check out to make your life better.

I’m not an expert in creating Chrome extensions, so I’ve set up a very simple one. A Chrome extension consists of a few different parts:

  • A JSON file called manifest.json
  • A CSS file we’ll call style.css
  • A Javascript file we’ll call script.js
  • A popup HTML file (we’re going to skip this, because unnecessary)
  • Icons (we’re going to skip this, because lazy)

In addition to those standard files above, we’re going to use a Javascript library called JQuery which makes coding a bit faster and funner 😊

To use JQuery, one must download a file called jquery-3.2.1.min.js from the JQuery website here. If you want the exact one I used, download using the link Download the compressed, production jQuery x.x.x My version number is 3.2.1 but your version number could be different if you come from the future (or past, I guess).

Lastly, remember to put all those files into the SAME folder on your computer. And you’re ready to go.

I suggest using a text editor like Sublime Text if you don’t have one yet to work with. Sublime is free to use and takes donations!

First, let’s set up our manifest.json file. Open up the file and start typing.

{
"manifest_version": 2,
"name": "Asana Kanban Assignee Filter",
"short_name": "Filter Assignee",
"version": "0.1",
"description": "Filter kanban cards by me, Coworker & Cat",
"content_scripts": [
{
"matches": ["https://app.asana.com/*"],
"js": ["jquery-3.2.1.min.js", "script.js"],
"css": ["style.css"],
"run_at": "document_end"
}
]
}

The manifest file is in JSON format, which looks like the above. It is basically a list that describes the Chrome extension. For example, what is the name of the extension? What is the description? How about version? Does it use any scripts? (yes it does).

In this case, we’ve named it Asana Kanban Assignee Filter (yeah, it’s long, but better than "Bob" right?), the version is 0.1 (this doesn’t affect anything, other than helping you keep track of versioning) and so on.

It’s important to note the content_scripts section though. We’re telling Chrome a few things here:

  • There are two JS files to use: jquery-3.2.1.min.js and script.js
  • The above scripts should be run when the website URL matches https://app.asana.com/* The star indicates that anything afterwards is fine, so the extension will still work for something like https://app.asana.com/randomtexthere
  • The script should be executed (run_at) document_end when the page has been fully loaded. This is important because if the page hasn’t loaded fully, there’s nothing we can work with.
  • The CSS file to use is style.css

Next, we’ll be working on the script.js and style.css files. We’ll first start with script.js in the next steps below. The Javascript is what makes the magic happen! The CSS just makes things look nice.

Step 1: Selecting cards that have your face on it

When you assign yourself, or anyone else to an Asana card on the Kanban board, your profile picture appears on the front of the card. This profile picture has a static image URL which can be used to create the filter. Note: this only works if you have actually uploaded a profile picture!

Here is where you would copy the URL. Double-click on the URL in order to select it and copy it.

Having grabbed that image URL, you can now insert it into the Javascript code to look up all cards that have your face on it, and hide everything that doesn’t.

Notice how the URL in the image above ends with 27x27. This number could change depending on the computer monitor’s screen resolution. If your computer’s resolution is different from mine, you may find that the URL ends with 60x60. That’s completely fine — by providing 2 images of different sizes, Asana makes sure that your profile image always looks crystal clear, no matter the resolution of your monitor screen. I have yet to find images ending with anything other than 27 or 60, although it’s possible there could be more.

The next step is to write some code in the script.js file that will hide any cards that doesn’t have your face on it and show any that does:

//hide everything
$('div.BoardCardMetadata-left').closest('.SortableItem').hide();
//show me
$('[style*="http://www.someURLendingin27x27"]').closest('.SortableItem').show();
$('[style*="http://www.someURLendingin60x60"]').closest('.SortableItem').show();

The code above works in a 2 step process. Anything next to // is a “comment” which means that line of code is not for the computer, but for a human to read; it’s just descriptive information for illustrative purposes and so shouldn’t actually do anything.

The way that Asana has structured with code is that anything with the class SortableItem is an item that can be sorted, which basically identifies the existence of an object called a “card” on the Kanban board.

Using Chrome, if you right click on a card and select “inspect” you can see that if you hover the different “div” elements, it highlights different parts of card. The div that contains “SortableItem” highlights the entire card. See how the mouse first clicks on one div with class=”sortable Item” and another div below it with the class=”BoardCard-metadata”
This shows the mouse selecting the “SortableItem” div, which represents a card.
This shows the mouse selecting the “BoardCard — metadata” div, which represents a small area of a card.

The code under //hide everything basically says “select any <div> elements that have the class BoardCardMetadata-left. Then, take that <div> and find the closest <div> that has the class SortableItem and hide it. This effectively will hide every single card on the Kanban board.

Why does it work?

Using SortableItem by itself is not enough though. For some reason, we have to be very very specific; first find a div with the class BoardCardMetadata-left, and then find something with the class SortableItem closest to it.

If you just write the code to hideSortableItem directly (like below), everything disappears and gets screwy.

//hide everything
$('div.SortableItem').hide();

Why doesn’t the above work? Although it makes intuitive sense to just hide the card, which seems to be represented by SortableItem, it doesn’t work. This could be because SortableItem is a class being used elsewhere by Asana. So, in order to be very specific about how we only want to hide the cards (not anything else), it was necessary to first say “find any div with a class of BoardCardMetadata-left and then find the closest SortableItem to it, and hide that SortableItem. A lot of this was simply figured out through trial and error. There could be a better way ¯\_(ツ)_/¯

The next section //show me contains two lines of code that are the same, but one is for the 27x27 picture, and one is for the 60x60 picture. If you’re like me and switch between computers with different resolutions frequently, it’s useful to have both lines of code, so that the Chrome extension works on any computer.

The code essentially says, “find any elements on the page that have an style attribute value that matches the following URL. Once that element is found, find the closest element on the page that has the class SortableItem and show it.” This effectively will show every single card on the Kanban board that has a picture of your face on it.

Why does it work?

The first piece of the code told the browser to please hide all the cards. The next section finds anything with your picture on it (meaning you’re assigned to it) by matching the exact URL of the image. Pretty cool right? 😎

Step 2: A button to filter my cards

Now that the code is going to filter your cards, you need to create something that will let you toggle this functionality on and off. After all, you do want to see more cards than just your own!

Naturally, what we need, is a big red shiny button to press. 👈

var menuButton = `
<button type="button" id="me" class="kanbanFilter">Me</button>
<button type="button" id="Coworker" class="kanbanFilter">Coworker</button>
<button type="button" id="Cat" class="kanbanFilter">Cat</button>
<button type="button" id="showAll" class="kanbanFilter">All</button>
`;
function addMenuButton() {
$(menuButton).prependTo('div.BoardHeader.Board-header');
}

The Javascript above will create buttons on the Asana menu bar. There is a button for you, your coworker, your cat, and lastly, one to reset the page to show all the cards.

The variable menuButton that is declared is basically a big block of HTML text that creates 4 buttons with four unique IDs.

The function right below it called addMenuButton takes the variablemenuButton and now prepends the big block of HTML text it represents, into the webpage. “Prepend” means to put before something. In this case, the script is looking for a div that has 2 classes, BoardHeader and “Board-header.” These two classes are classes that Asana uses, and we are taking advantage of them to insert our HTML code. When the script finds any div that matches, it will put our menuButton HTML code right before it.

Finding a good place to insert the beautiful buttons…
If we inspect the text “View: all Tasks” we can find the div with the class “BoardHeader.” Notice how it contains the “View: All Tasks” menu link. As you can tell from the highlight, this div contains the “head” area across the top of the board.
If we inspect the div with the “BoardHeader” class after running the script, now we see that all the buttons have been added below, when there weren’t there before!

Lastly, the class kanbanFilter on each button serves for styling purposes. The CSS for the entire Chrome extension is as follows:

button.kanbanFilter {
width: auto;
height: auto;
border-width: 0px;
margin: 0px 10px 0px 0px;
}
button.kanbanFilter:focus {
outline: none;
}

The first CSS section finds any buttons on the page with the kanbanFilter class we created and sets the width & height to just the size of the text on the label using “auto.” It also ensures the button has no border (cuz that would be ugly) and creates a margin of 10px on the right (the code for margin goes top, right, down, left for each number), which helps space the buttons out a bit.

The second CSS section ensures that when a button is clicked, it doesn’t have an ugly outline around it, as tends to happen to buttons. Buttons are ugly before you put makeup on them. That’s life.

The size of the text is inherited from Asana’s styling, which I like and so am not going to change. The result is the small text buttons as shown below.

Self explanatory.

Step 3: Tying it all together

So, now we have the buttons and the script to show cards assigned to us, but it’s not really going to work, because the script is missing other essential elements.

First, the script has to run when the page loads, so that the buttons will appear.

Second, when the buttons are clicked, they have to trigger the function to show and hide the right cards.

This one is rather simple. Using JQuery, we can use the $(function(){}); code to tell the browser to execute anything when the page has finished loading.

$(function() {
console.log("ready!");
addMenuButton();
});

The above code will make the console display ready! after the page loads, and also execute our addMenuButton function. This will make the four buttons appear on the page. Magick!

Although the buttons are showing up, they don’t do anything when you click on them. Other than look pretty that is (now that they’re all done up with CSS).

The next bit of code can get long.

$(function() {
console.log("ready!");
addMenuButton();
//me
$('#me').click(function() {
console.log('filtered Me!');
//hide everything
$('div.BoardCardMetadata-left').closest('.SortableItem').hide();
//show me
$('[style*="http://www.someURLendingin27x27"]').closest('.SortableItem').show();
$('[style*="http://www.someURLendingin60x60"]').closest('.SortableItem').show();
})

In the above code, you can see that there are 3 new sections: //me , //hide everything, and show //me.

The code inside though, should look familiar — it’s from before! Yes, there is the 27x27 URL and the 60x60 URL. The only difference is that they are now inside another piece of code, the the //me section.

The //me section says that when something with the ID of #me is clicked on, perform the following actions:

  1. have the console say “filtered Me!”
  2. hide everything (hide all the cards)
  3. show me (show only my cards)

By the way, what has an ID of #me? It’s the button that we created earlier that says id=me😄

Ok, here’s the final Javascript to filter my cards

$(function() {
console.log("ready!");
addMenuButton();
//me
$('#me').click(function() {
console.log('filtered Me!');
//hide everything
$('div.BoardCardMetadata-left').closest('.SortableItem').hide();
//show me $('[style*="https://s3.amazonaws.com/profile_photos/391122297822876.3249ZQE4jHR92CDU5IaA_27x27.png"]').closest('.SortableItem').show();
$('[style*="https://s3.amazonaws.com/profile_photos/391122297822876.3249ZQE4jHR92CDU5IaA_60x60.png"]').closest('.SortableItem').show();
})});

As you can see, I have actually put in the real URL for the my profile image for both the 27x27 image and the 60x60 image. If you don’t have both URLs, it’s ok, just leave one of them blank. If you ever change monitors and the extension no longer works, then it could be because it’s using the 60x60 image, in which case you’d have to find the URL and add it into the code.

Alright, so now, when you click on the “me” button in the Kanban board, it should only show cards assigned to you (which is “me” — was that confusing?)

Click on “Me” and voila! Only my cards are now showing!

Awesome, it’s working! But what if I want to see all the cards again? Well, you could refresh the page, but that’s bad UX. So we should make the “all” button start working.

What we need to do is to add this snippet of code into our function:

//showAll
$('#showAll').click(function() {
console.log('showing all!');
$('div.BoardCardMetadata-left').closest('.SortableItem').show();
})

This code is the opposite of the .hide() code we had before. Instead of hiding all the cards, we now want to show them all. This code can be added to the rest of the code like so:

$(function() {
console.log("ready!");
addMenuButton();
//me
$('#me').click(function() {
console.log('filtered Me!');
//hide everything
$('div.BoardCardMetadata-left').closest('.SortableItem').hide();
//show me $('[style*="https://s3.amazonaws.com/profile_photos/391122297822876.3249ZQE4jHR92CDU5IaA_27x27.png"]').closest('.SortableItem').show();
$('[style*="https://s3.amazonaws.com/profile_photos/391122297822876.3249ZQE4jHR92CDU5IaA_60x60.png"]').closest('.SortableItem').show();
})//showAll
$('#showAll').click(function() {
console.log('showing all!');
$('div.BoardCardMetadata-left').closest('.SortableItem').show();
})
});

The new //showAll section now shows all the cards whenever a button with the #id of showAll is clicked!

It works! It’s aliiiiiive!!!

Adding more Assignees

The last step is to add more humans (or animals) to filter by. We haven’t gotten “Coworker” or “Cat” working, but it’s as simple as pulling up their picture and inserting the URLs into this code.

Here’s how the final Javascript code would look with everything. Don’t forget to replace the URLs for 27x27 and 60x60 of course!

Create the Chrome Extension

After you have all the files ready, make sure they’re all in the same folder, and install the chrome extension as follows.

Click on the preferences menu (3 dots on the top right of the browser), select More Tools > Extensions
Click on “Load Unpacked Extension” and choose the folder with your files in it.

Voila! And that should be it.🎉

Conclusion

While making this Chrome extension, I learned so much about HTML, CSS and Javascript, and had a really fun time. I was really inspired by the fact that a code newbie like me could make something that can be immediately useful in this world. I still haven’t figured out how to make more complex extensions like show the subtasks directly on the card (ala Next Steps for Trello), or even simple ones, like show the damn label name (ala Trelabels). One day I’d love to be able to create something like Planyway for Trello, but on Asana, which integrates a calendar onto your kanban board and allows you to drag and drop cards onto it, and even integrate with Gcal.

However, it was a great first step and something that I’m proud of enough to share. If you are reading this and are just starting to learn HTML, CSS or Javascript and found it informative (or not), let me know your feedback. Or, if you’re a veteran programmer, I’d love to hear your thoughts on how to make this better, and how I can improve.

Until next time, ciao!

Bonus for anyone who made it this far. Another awesome Chrome extension: a new kitty on every new tab!

ChunYu Shi

Written by

Business. Tech. Design. Enlightenment

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade