jmromer / columns-as-a-service

Display a CSV string as a columnar table

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Columns as a Service

CircleCI

Display a string of comma-separated values as a columnar table with a user-selectable number of columns.

The app responds to input dynamically, validating input and generating the columnar table on the fly.

Dependencies

To install dependencies locally, issue yarn install.

To start a server locally, issue yarn start.

Screenshots

Dynamic re-sorting

demo1

demo2

Error validation

error-validation

Architecture

Components

  • <CSVForm />: The CSV string submission form
  • <ResultsTable />: The table generated upon changes to the input
  • <CSVTransformer />: The "controller" mediating interactions between the child components and the modules below.
// src/CSVTransformer.js L21-L38 (4ddf0c9f)

  render () {
    const table = ColumnarTable.fromValues({
      valuesList: this.state.inputValues,
      numberOfColumns: this.state.selectedNumberOfColumns
    })

    return (
      <div className='App'>
        <CSVForm
          minColumns={this.MIN_COLS}
          maxColumns={this.MAX_COLS}
          maxEntries={this.MAX_CSV_ENTRIES}
          updateSharedState={this.setState.bind(this)} />

        <ResultsTable tableBody={table} />
      </div>
    )
  }
src/CSVTransformer.js#L21-L38 (4ddf0c9f)

Modules

  • ColumnarTable
// src/ColumnarTable.js L6-L27 (d36ac344)

  //
  // Generate a columnar table (as an Array of Arrays) of width
  // `numberOfColumns` (an Integer) from `valuesList`, expected to be a
  // 1-dimensional Array.
  //
  fromValues: ({ valuesList, numberOfColumns }) => {
    const sliceLength = Math.ceil(valuesList.length / numberOfColumns)

    if (sliceLength < 1 || isNaN(sliceLength)) { return [] }

    // partition values list into slices of length sliceLength
    const slices = Enum.eachSlice(valuesList, sliceLength)

    // transpose to make each slice into a column
    const tableBody = _.zip(...slices)

    // right-pad each row to the specified number of columns
    // (so a <td> will be rendered for empty cells, for valid HTML)
    tableBody.forEach(row => { row.length = numberOfColumns })

    return tableBody
  }
src/ColumnarTable.js#L6-L27 (d36ac344)
  • Enum
// src/Enum.js L1-L35 (d36ac344)

// Slice an enumerable `collection` into a list of lists, each sub-list of
// length at least `sliceLength`.
//
// Signature:
//
//   Enum.eachSlice(obj, sliceLength)
//
// Example:
//
//   >>> Enum.eachSlice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 4)
//   [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
//
const Enum = {
  eachSlice: (collection, sliceLength) => {
    if (sliceLength < 1) {
      throw new InvalidSliceSizeException(sliceLength)
    }

    const slicedList = []
    const list = collection.map(e => e)
    if (typeof list.slice === 'undefined') { return slicedList }

    const numberOfSlices = Math.ceil(list.length / sliceLength)

    for (let i = 0; i < numberOfSlices; i++) {
      const startIdx = i * sliceLength
      const endIdx = startIdx + sliceLength
      const slice = list.slice(startIdx, endIdx)

      slicedList.push(slice)
    }

    return slicedList
  }
}
src/Enum.js#L1-L35 (d36ac344)

Tests

Tests are written using Jest and co-located with their implementation files.

To run tests locally, issue yarn test from the project root.

// src/Enum.test.js L5-L15 (4ddf0c9f)

describe('Enum.eachSlice', () => {
  describe('given a slice size that divides into the collection', () => {
    it('slices into evenly sized sub-arrays', () => {
      const originalArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      const expectedArray = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

      const slicedArray = Enum.eachSlice(originalArray, 2)

      assert.deepEqual(slicedArray, expectedArray)
    })
  })
src/Enum.test.js#L5-L15 (4ddf0c9f)

About

Display a CSV string as a columnar table


Languages

Language:JavaScript 91.9%Language:HTML 4.5%Language:CSS 3.5%