Published on

How to Create an Airbnb Style Date Picker in your Tableau Dashboards

Authors

The objective here is to give our users the ability to filter dates as pleasantly as you can in the Airbnb app.

The experience of picking date on Airbnb's website

While you can achieve similar results in Tableau with sophisticated solutions like we see from Ludovic.

Ludovic's Dashboard with a Date Picker build in Tableau

While impressive, these solutions can come at a price and still leave us wanting the feel you get when interacting with UIs built in other applications.

Without a complex solution like Ludovic’s we’re left with the standard Tableau controls. These controls don’t look as stylish, nor are they aware of each other. For example, image the common scenario where you have a Start Date and End Date, and we did not want the user to be able to select an End Date that came before the Start Date. Currently, Tableaus’ parameters are not reactive to the values entered into another control.

By leveraging the Tableau JS API we can use the Airbnb Date Picker alongside our Tableau charts. This is the best of both worlds 💯


Prerequisites

This tutorial could be written using Tableau Public instead of Tableau Server, but I want to provide you with a walk through that’s more production ready. While the refactoring needed to use Tableau Public is minimal, it’s still there and I don’t want you to miss out because you didn’t get this POC working against your own Tableau Server instance or Tableau Online.

Tableau

We need the real deal Tableau Desktop and not Tableau Public. Reason being, you can’t publish to Tableau Server or Online with Tableau Public alone.

Tableau Server

If you don't have a Tableau Server to experiment with, you can get a free, development environment for yourself by signing up for the Data Dev Program.

If you do have a Tableau Server to experiment with, I suggest publishing the example dashboard from this post to the Default project or a Sandbox project to avoid confusing anyone.

Chrome

Sorry, I’m just going to push you to use Chrome here 😆 although Firefox also has great Dev Tools.

Visual Studio Code

VS Code is a fantastic text editor. There are heaps of benefits from working with VS Code! Burke Holland teaching a great course on Frontend Masters if you’re interesting in learning more about this editor.

Node

Node is what allows us to execute the scripts locally to see how our app is coming along. Node is also what allows us to install all of the dependencies for our application.

React Dev Tools

This is an optional install, but one you’ll want if you’re planning to do any future React development. The React Dev Tools allow you to see the State of your application from React’s perspective, which is different from how the Browser see it.


Setup

Tableau

To get the exact same results as I do, open up a fresh Tableau Workbook and download this Sample - Superstore data set.

All we need for this simple example are three calculations, two parameters, one view, and one dashboard. Let’s get into it!

The Date - Max and Date - Min calculations will be used as default values in our parameters.

// Date - Max
{ MAX([Date]) }
// Date - Min
{ MIN([Date]) }

These calculations will return the min and max date values in our workbook. That way our chart displays the full range of data everything it opens.

Start Date parameter set up

The End Date parameter should be set up the exact same way as the Start Date. Just use Date - Max instead of Date - Min as the default value.

By the way… these two calcs are no longer required in Tableau 2020.4. See Tim’s Video here to find out why.

Also, be sure to set the Data type of the parameters to Date. This is important as we need to pass values from the Airbnb Date Picker to Tableau and the data types must match.

Now we need to create that Date Filter.

// Date Filter
[Date] >= [Start Date] AND [Date] <= [End Date]

Place this calculation on the filter shelf and select the only option: True.

Now create a chart with a date on the x-axis and some value on the y-axis. A proper chart with x and y-axis isn’t required for this POC, but it makes the example more interesting. Go for a bar chart, line chart, area… whatever else suits your fancy.

This is what my setup looks like

Tableau view in Tableau Desktop

The last piece of the Tableau setup is creating a dashboard of our chart. Drag your chart onto the canvas and make sure the dashboard is set to Size: Automatic.

Creating the Tableau Dashboard

Once you’re done with this step, publish the dashboard to your Server.

JavaScript

I encourage to follow along, but also know having working code helps. Have the fully finished code base also does wonders to save you from frustration that will crop up when we inevitably fudge a common or letter somewhere. So, feel free to clone this repo.

As an additional aid, open this video in another window so you can watch me build app out while walking through the blog post.

What the final date picker will look like.

Enter Command + or Ctrl + to open the terminal within VS Code and run.

npx create-react-app date-picker
Create React App

Check out the Wes Bos Command Line Course if you’d like to deepen your skills.

After you see "Happy Hacking!" cd into the newly created date-picker directory.

Just to make sure all is good, run “npm start” to fire up the standard React App. This might take a minute, so be patient.

Start the React App

Now open your working directory (or the folder you’re in) within VS Code.

Open VS Code

If the “code .” command doesn’t work for you, you just need to install it.

Command + Shift + p or Ctrl + Shift + p will open the options list in VS Code so you can get that command working.

Install the CODE command

Now remove the unnecessary React boilerplate code. The video below will walk you through the steps.

Check the console in Google Chrome to make sure you don't have any errors.

Check for errors in the console

Add the Tableau JS API script to the index.html

<script src="https://us-east-1.online.tableau.com/javascripts/api/tableau-2.min.js"></script>

While you're here go ahead an update the Title tag of your index.html.

Add the Tableau JS API

Phew 😅 that was a lot of setup!

I’m probably going create a seperate post for the setup so we can get into the good parts more quickly in the future.

The next step is to create a components folder. Let’s also create a Viz component within that folder.

For now, our Viz component is just going to log the tableau API.

const { tableau } = window

export default function Viz() {
  console.log(tableau)

  return <div></div>
}

Now we need to import your Viz component into your App

import React from 'react'
import './App.css'
// Import component
import Viz from './components/Viz'

function App() {
  return <Viz />
}

export default App

Now we need to initialize the dashboard to make sure it’s showing up properly.

In order to see our dashboard, we need to grab the embed link and trim is up.

Grab the share link from Tableau Online.

Once you have the link, you can insert it into the code below and replace your current Viz component with this code.

import { useState, useRef, useEffect } from 'react'
const { tableau } = window

export default function Viz() {
  const [viz, setViz] = useState(null)

  // Set up the arguments to pass into the Tableau Viz function
  const ref = useRef(null)
  const url = 'INSERT YOUR CLEANED URL'
  const options = {
    hideTabs: true,
    hideToolbar: true,
    width: '900px',
    height: '540px',
    onFirstInteractive: function () {
      console.log('This viz is interactive.')
    },
  }

  // This function will be run on page load to initialize our viz.
  const initViz = () => {
    setViz(new tableau.Viz(ref.current, url, options))
  }

  // Initialize viz when the page loads
  useEffect(initViz, [])

  return <div ref={ref} />
}
Render the Tableau viz

Now let’s see what we have on the screen. You should see something like the image below.

The first look at the viz on our screen

Next, set up the Date Picker Class Component

A nice way to do this is by opening another terminal within VS Code. This way you can install packages while continuing to run the React App in another terminal.

Add another terminal window

In the new terminal window run the code below to install the DateRangePicker dependencies. In case you were wondering, the code was pulled straight from the Airbnb docs.

(
  export PKG=react-dates;
  npm info "$PKG" peerDependencies --json | command sed 's/[\,]//g ; s/: /@/g; s/ *//g' | xargs npm install --save "$PKG"
)
How to restart the node server

Update the App component to import the files we need and add the default Airbnb DateRangePicker.

import React, { Component } from 'react'
// Imports for the DateRangePicker
import 'react-dates/lib/css/_datepicker.css'
import 'react-dates/initialize'
import { DateRangePicker } from 'react-dates'
// Import component
import Viz from './components/Viz'

export default class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      startDate: null,
      endDate: null,
      focused: null,
    }
  }

  render() {
    return (
      <>
        <DateRangePicker
          startDate={this.state.startDate} // momentPropTypes.momentObj or null,
          startDateId="your_unique_start_date_id" // PropTypes.string.isRequired,
          endDate={this.state.endDate} // momentPropTypes.momentObj or null,
          endDateId="your_unique_end_date_id" // PropTypes.string.isRequired,
          onDatesChange={({ startDate, endDate }) => this.setState({ startDate, endDate })} // PropTypes.func.isRequired,
          focusedInput={this.state.focusedInput} // PropTypes.oneOf([START_DATE, END_DATE]) or null,
          onFocusChange={(focusedInput) => this.setState({ focusedInput })} // PropTypes.func.isRequired,
        />
        <Viz />
      </>
    )
  }
}

If you don’t see a Date Range component added to your UI, then you may need to restart node by entering Ctrl + c and run npm start again.

At this point you have a good looking date picker, but it is connected to the Viz.

Render the DateRangePicker

With the DateRangePicker on screen we next need to figure out how to capture the dates selected.

We will do this by passing the startDate and endDate props through to our Viz component.

<Viz startDate={this.state.startDate} endDate={this.state.endDate} />

To check that the values are coming through we first need to update the props that the Viz component receives.

Now within the Viz component let’s log startDate with a useEffect hook.

import { useState, useRef, useEffect } from "react"
const    = window

export default function Viz({ startDate, endDate }) {
  const [viz, setViz] = useState(null)

// Set up the arguments to pass into the Tableau Viz function
const ref = useRef(null)
const url =
"https://10ax.online.tableau.com/t/developmentonlydev595736/views/DatePicker/Dashboard"
const options = {
hideTabs: true,
hideToolbar: true,
width: "900px",
height: "540px",
onFirstInteractive: function() {
console.log("This viz is interactive.")
},
}

// This function will be run on page load to initialize our viz.
const initViz = () => {
setViz(new tableau.Viz(ref.current, url, options))
}

// This hook is listening to the startDate prop. When it's changed the
// changeStartDateParameterValue function will run with the new startDate
useEffect(() => {
console.log("Start Date ", startDate)
}, [startDate])

// Initialize viz when the page loads
useEffect(initViz, [])

return <div ref= />
}

With the console open (open the console by entering Command + option j or Cntl + option + j) select a date from the DateRangePicker component to see what’s logged.

Log the moment object

If all is well, you should see a moment object.

Once we know we’re able to get the Start Date we can set up a useEffect hook for the End Date just like we did for the Start Date.

Once you’ve updated your code to capture both selections we need write the code to update the parameter values within our workbook.

To do this we need to create a function that will update the value of our date parameters every time the values are updated (or a selection is made) in the DateRangePicker.

Will use the useEffect hooks we already have, but instead of console logging our result we'll run the needed function.

Lets also remove the moment import at the top of your file, since we’ve already tested it to made sure the selected dates are coming through.

Below are the functions we’ll be using inplace of console.log within our useEffect hook. Copy and paste the code above the useEffects for the startDate and endDate respectively.

Paste above useEffect for Start Date…

// This function will run with the startDate is changed
// update the parameter value in the workbook and update the viz
async function changeStartDateParameterValue(startDate) {
  let startYear, startMonth, startDay

  if (startDate === null) {
    startYear = 2016
    startMonth = 0
    startDay = 1
  } else {
    startYear = startDate.\_d.getUTCFullYear()
    startMonth = startDate.\_d.getUTCMonth()
    startDay = startDate.\_d.getUTCDate()
  }

  if (viz !== null) {
    const workbook = viz.getWorkbook()

    workbook.changeParameterValueAsync(
      'Start Date',
      new Date(Date.UTC(startYear, startMonth, startDay))
    )
  } else {
    console.log("We don't have a viz yet")
  }
}

Paste above useEffect for End Date…

// This function will run with the endDate is changed
// update the parameter value in the workbook and update the viz
async function changeEndDateParameterValue(endDate) {
  let endYear, endMonth, endDay

  if (endDate === null) {
    endYear = 2019
    endMonth = 11
    endDay = 31
  } else {
    endYear = endDate.\_d.getUTCFullYear()
    endMonth = endDate.\_d.getUTCMonth()
    endDay = endDate.\_d.getUTCDate()
  }

  if (viz !== null) {
    const workbook = viz.getWorkbook()

    workbook.changeParameterValueAsync('End Date', new Date(Date.UTC(endYear, endMonth, endDay)))
  } else {
    console.log("We don't have a viz yet")
  }
}

To test to see if it's working we need to select a date within the date range of our chart. Note that the latest month we have is December 2019.

Give it a shot…

Can't select past dates.

Oh no, we can’t select a date in the past!

To adjust the default we need to add this prop to our Date Picker.

isOutsideRange={() => false}

I picked up this trick on StackOverflow.

With that prop added you should be able to filter the chart to just show Dec 2019. Give it another shot.

Boom 💥 We. Are. Done!

You have successfully hooked up Airbnb’s date-picker to a Tableau dashboard with the Tableau JS API 🙌


CSS

Before we get going with the styles I want to say the approach I’m taking below is a simple one. Some would go so far as to call it lazy.

The truth is that there are dozens of preformatted date pickers you use in place of this and you can even take the date picker I’ve helped you set up here, but style it in wildly different ways.

For our purposes, I just want to give our components more space. We should make it responsive. And also control the size with CSS rather than what we’ve passed into the initViz function.

To do this I’m first going to install bootstrap

npm install --save bootstrap

Import bootstrap into your App

import 'bootstrap/dist/css/bootstrap.min.css'

Next we’ll put a container class on our App component

Container className

Now we need to create a styles folder within our src folder that contains an App.css and Viz.css file.

Update our App style

.container > \* {
  background: #efefef;
  margin: 20px;
}

Remove the height and width options from the Viz

Remove height and width.

Add a viz className to our div

Add viz className

Create a Viz.css file to set the height.

.viz {
  height: 540px;
}

In order to see these styles take effect we need to update our imports.

Add this import to your App.js file.

import './styles/App.css'

Add this import to your Viz.js file.

import '../styles/Viz.css'

That’s a wrap. With those styles in place, you have a decent looking layout with a slick data picker filtering your Tableau viz.

Bootstrap styling

For more examples check out this gallery and openbase which comparing several different date pickers.

CONCLUSION

In my opinion, this is a really cool compliment to Tableau and a great example of the potential we have when leveraging the JS API. Now, as for the date picker, I would have prefered to use a functional component, but the Airbnb date-picker is, unfortunately, a class component.

There are several other date pickers you can use in place of the date picker I used and many of those are functional components.

At least this example shows you how make this combo work the hard way, with a Class component. By making this combo work with a class component, it’ll be a breeze to refactor to use a functional component.

As always, if you have questions, don’t hesitate to reach out! Oh, and please please please let me know if you’ve found any mistakes in this tutorial. I really want it to work for you!

Last but not least, big should our to [Andre de Vries](Andre de Vries) and Matthew Hefferon for supporting me throughout this development 🙏