Mocking React/Apollo Data Tables for UI Development

Recently, I was tasked with creating a new screen containing a table of data for a project using React and Apollo. In the past, we would typically start by defining the database table, other access layers in between that and a GraphQL query schema, and finally the query on the front end using Apollo. This has been quite tedious, and often, we ended up tweaking the schema many times until we ended up with precisely what the front-end UI needed.

This time, I thought I would start from the front-end UI mockup and work downwards instead. I started developing the front end using React and thought it would be useful to have a sample set of data that I could use with our data table component.

Our data tables support pagination, so we’ll use a simple interface (defined elsewhere in our project) to specify this.

interface TableProps {
  tableDimensions: {
    rowsPerPage: number;
    pageNumber: number;
  };
}

Next, we’ll define our table rows builder function, which simply injects the rows and props them down to the components below. This works in place of the props function in a query higher-order component you’d see with Apollo. Later, after we’ve implemented the back end, we’ll swap out the higher-order component with our Apollo query.

export function BuildTableRows(args: { propName: string, rows: T[], isPaginated: boolean }) {
  return (WrappedComponent) => (props: TableProps) => {
    // support simple pagination
    const queryLimit = props.tableDimensions.rowsPerPage;
    const queryOffset = props.tableDimensions.rowsPerPage ? props.tableDimensions.rowsPerPage * props.tableDimensions.pageNumber : 0;

    const propData = args.isPaginated
      ? args.rows.slice(queryOffset, queryOffset + queryLimit)
      : args.rows;

    // propName is the name you'd normally use in the Apollo query props() function.
    const injectedProps = {
      [args.propName]: propData,
      totalRows: args.rows.length,
      loading: false,
    };

    return wrap(WrappedComponent, { ...injectedProps, ...props });
  };
}

Now, we can call our new table rows builder function to make a higher-order component. This component contains dummy data that we’ll use to wrap our UI component.

interface DeliveryTableRow {
  scheduledDate: Date;
  origin: string;
  destination: string;
  carrier: string;
}

const WithDeliveryTableRows = BuildTableRows({
  propName: 'routePlans',
  rows: [
    {
      origin: 'Grand Rapids, MI',
      destination: 'Ann Arbor, MI',
      carrier: 'FedEx',
      scheduledDate: new Date('2018-01-01'),
    },
    {
      origin: 'Allendale, MI',
      destination: 'Ada, MI',
      carrier: 'UPS',
      scheduledDate: new Date('2018-09-01'),
    },
    {
      origin: 'Kalamazoo, MI',
      destination: 'Los Angeles, CA',
      carrier: 'USPS',
      scheduledDate: new Date('2018-10-01'),
    },
  ],
  isPaginated: true,
});

Finally, we’ll use the new higher-order component. (We have typically been using flowRight from Lodash to stack our components.)

export const DataTableWithDeliveryTableRows = flowRight(
  WithDeliveryTableRows,
  // include other data and other wrappers
)(DataTable);

This method has helped me speed up the process of determining the minimum required data to drive the front end. This, in turn, has directly driven the back-end code, including GraphQL schema and database schema. As a result, it’s reduced refactoring time and prevented unnecessary fields in our database.