Skip to content

Instantly share code, notes, and snippets.

@adamwathan
Created June 19, 2017 23:25
Show Gist options
  • Save adamwathan/ad0e5fe6c78f8239cf809b8153e7c274 to your computer and use it in GitHub Desktop.
Save adamwathan/ad0e5fe6c78f8239cf809b8153e7c274 to your computer and use it in GitHub Desktop.
Building KiteTail #6: New logo, cracking UI problems, and testing tricks

Building KiteTail #6: New logo, cracking UI problems, and testing tricks

Hey, turns out it's hard to get these done on Sundays, so expect them on Mondays going forward 😊

It Has a Tail!

Steve's been working on some new logo ideas for a while now but think we finally have something we are happy with, so thought I'd share it here first!

Neither of us really disliked the original logo idea, but it sort of bugged us that a product called KiteTail had no tail on it's kite logo! So here's what Steve came up with:

kitetail-mark-new

I really like that it has a recognizable identity but is still really easy for anyone to draw on a napkin. Should look cool on some stickers this summer! :)

Solving UI Challenges

This week Steve and I spent a lot of time fine-tuning some UI decisions for the clickable prototype we've been working on to demo at the ConvertKit conference. I think I've mentioned this before, but man is it ever crazy how much effort goes into solving even the (seemingly) simplest interface problems.

One of the things we worked on this week was "publishing" a product; making it live and available for purchase.

Up until this week we just assumed (naively) that a simple "publish" button in the corner of the screen would be ok:

publish-1

But as we got deeper into it we realized this had few issues:

  1. It didn't tell you what the current state was in a clear, explicit way. You had to figure out that you were in "draft" mode by working backwards from the fact that the button was labelled "Publish" and not "Unpublish".

  2. Publishing requires some validation checks. If the product wasn't currently publishable (missing price for example), where do we tell them that? If the button is disabled because of a missing field, how does the user find out why?

  3. Publishing and unpublishing are somewhat "high consequence" actions; the sort of thing you wouldn't want to do by mistake. It's not a huge deal if you publish by mistake, and the odds of doing it are of course low, but having a button that you just click once to publish without any confirmation step puts just a tiny nugget of anxiety in the back of your mind on this page.

Here's a few of the ideas we tried to solve these problems...

Idea #1: Adding a confirmation modal

One idea we had was to add a confirmation modal dialog. This meant if you accidentally clicked the button and didn't mean to, it would be easy to cancel.

It also meant if you tried to publish but your product didn't have all of the required information, we could show you those issues in the modal.

We decided against this quickly for a few reasons:

  1. It doesn't solve the problem of telling you the current product state explicitly.

  2. It's not obvious that there's going to be a confirmation modal when you click the button, so it doesn't solve that nagging anxiety problem.

  3. You don't know that your product has errors until after clicking the "Publish" button and seeing them in the modal. It felt silly to make it look like you could publish even if you couldn't.

Idea #2: Using a select menu

Another idea was to use a fairly standard <select> dropdown, like this:

publish-2

This solved the issue of showing the current state, but had it's own issues:

  1. It was even more ambiguous about a confirmation step. Will changing the value instantly publish the product with an ajax call, or will I be asked to confirm? Can't know.

  2. Still no way to show errors.

Idea #3: Custom popover menu

After an hour or so of brainstorming and weighing the pros and cons of the ideas we had so far, Steve came up with the idea of using a custom popover/dropdown menu, similar to a Bootstrap dropdown menu:

publish-3

This felt like a breakthrough for a few reasons:

  1. It was obvious to the user that just clicking the dropdown wouldn't immediately change the state of the product. Anxiety gone!

  2. It was pretty clear that clicking the dropdown to open it, then clicking the blue highlighted "Published" option would publish the product right away; it acted sort of like a built-in confirmation step without needing an ugly modal or separate button.

  3. If the product wasn't ready to be published, we could grey out the "Published" option in the dropdown and explain why:

publish-4

This whole thing probably took 2.5 hours of screensharing and brainstorming to really nail, and no one would ever know that seeing the finished product! I heard a quote once that "good design is invisible," truly thankless work πŸ˜„

Live Stream: Test-Driving Views without the HTML Headaches

In the first stream this week, I drove out the backend implementation of the "orders" list view using TDD:

Watch "Test-Driving Views without the HTML Headaches"

πŸ”₯ Tip of the Stream

Asserting against view data instead of rendered HTML

I generally find making assertions against rendered HTML to be painful and brittle. So instead I usually make my assertions about the data that was bound to my view, rather than how that data gets rendered.

Laravel makes this possible by giving you access to the original response returned from your controller through the original property on the returned $response.

Here's the test I worked on in the stream that shows how I do this, including some handy macros I've been using that make the assertions more expressive:

See an example on GitHub

Live Stream: Logging Fulfillment Attempts

In the second stream, I worked on taking webhook attempts that get logged every time KiteTail tries to fulfill a purchase and displaying them on the order details page:

Watch "Logging Fulfillment Attempts"

πŸ”₯ Tip of the Stream

Eager-loading sorted relationships

You probably know that Laravel lets you define relationships like this:

public function fulfillmentAttempts()
{
    return $this->hasMany(FulfillmentAttempt::class);
}

...and eager load them like this:

$purchase = Purchase::with('fulfillmentAttempts')->get();

...but did you know that you can create multiple versions of the same relationship that build off of each other, applying different modifiers like sort order?

public function latestFulfillmentAttempts()
{
    return $this->fulfillmentAttempts()->latest();
}

Even though that method never calls hasMany, it's still returning an instance of the HasMany class by building off of the initial relationship, so since it's still a relationship, you can actually eager load it:

$purchase = Purchase::with('latestFulfillmentAttempts')->get();

Cool huh? Here's a link to a more fleshed out example:

See an example on GitHub

That's all I've got this week! I'm doing another stream tomorrow at 3pm EST as usual, so be sure to come say hi if you're interested.

Thanks as always for being interested in what I'm working on and hope your week is off to a good start!

– Adam

@JacobBennett
Copy link

first πŸ˜„

@JunaidQadirB
Copy link

Cool!

2nd btw ;)

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