Coder Social home page Coder Social logo

hepa's Introduction

Hepa

Because it filters, get it? This is a set of compound components to make filtering data easier.

NPM badge Discord badge CircleCI build

Problem:

When iterating on different filter controls for data, a big impediment is wiring up the controls to where the actual data is available. Maybe the filter controls are in a sidebar, and the data is being used in the main page, and there are several components in between them in the tree.

Solution:

Because these components use context to communicate, they can be used in any combination without any manual wiring. Put a FilterProvider at the top, add some custom FilterControls (or one of the built in controls), and pass data into a Filter component.

API Reference

FilterProvider

The top level component that owns filter state from controls below it in the hierarchy. Accepts no props, silently communicates with child controls via context.

Filter

A component that expects data to filter as a prop, and passes the filtered list to a render prop. This component consumes the filter predicate from FilterProvider.

WithFilteredData

Same as the Filter component, but a higher order component. Expects data as a prop, passes filtered data into the wrapped component.

FilterControl

A generic control. This is what's used to implement the rest of the filter controls, and allows for custom controls to be implemented. This is very powerful, making this library very extensible within your codebase. It takes 2 props which it will call to build a predicate, mapValuesToComparison and compare. It will handle its own state when just handling a simple string, but it also allows for external control by accepting value and onChange props. By default it renders a simple DOM input and passes all props through, but it allows allows for a render prop for customized inputs.

Required Props

mapValuesToComparison: datum => intermediateValue

A function that receives each item in the data array passed to Filter or WithFilteredData and returns the value that should be passed into compare.

compare: valueFromControl => intermediateValue => boolean

A curried function that expects the value from within FilterControl (either from its own state if uncontrolled, or from a value prop if controlled) and the value returned from mapValuesToComparison.

For example, in Exact, these two functions are very simple. When mapping the data, it pulls off a single key that gets compared to the current filter value.

mapValuesToComparison = 
  dataValue => dataValue[this.props.name]

compare = (filterValue /* a string value typed by the user */) => 
  (intermediateValue /* the string returned from `mapValuesToComparison` */) =>
    dataValue === filterValue;

Search provides an example of why this combination can be very powerful. It looks at multiple keys to see if any contain the filter value as a substring.

mapValuesToComparison = (datum) => {
  const { keys } = this.props;

  const values = keys.reduce((out, key) => {
    out.push(datum[key].toString().toLowerCase());
    return out;
  }, []);

  return values;
};
compare = (filterValue /* a string value typed by the user */) => 
  (dataValues /* the array returned from `mapValuesToComparison` */) =>
    dataValues.some(value => value.includes(filterValue));

Optional Props

onChange
value

These can be passed to make the input controlled. If you need more complex behavior, you can extract the filter state while still leaning on FilterControl to build a predicate and communicate it to the FilterProvider.

render

Some filters are more complex than a simple text input. Maybe you want to render an HTML select, or use an existing component. All props other than mapValuesToComparison and compare are passed into this function as an argument. You'll likely only need to use value and onChange in here.

Exact

This performs an exact string comparison (===) on what is entered into the filter with the value on the data keyed of what's passed in as the name prop. For instance, a data value with the shape of { id: 1, username: 'vcarl' } would match <Exact name="username"> with "vcarl" entered.

Fuzzy

This performs a "fuzzy" match akin to Sublime Text's cmd-p menu on what is entered into the filter with the value on the data key passed in as the name prop. For instance, a data value with the shape of { id: 1, username: 'vcarl' } would match <Fuzzy name="username"> with "val" entered, because vcarl

Search

This checks if multiple keys contain what is entered into the filter as a substring, with the value on the data keys passed in as the name prop. For instance, a data value with the shape of { id: 1, username: 'vcarl', email: '[email protected]' } would match <Search keys={["username", "email"]}> with "@me.com" entered.

Example usage

<FilterProvider>
  <div>
    <Exact name="name" />
    <Search keys={["name", "email", "id"]}>
    <Fuzzy name="email" />
  </div>
  <ul>
    <Filter
      data={rawData}
      render={filteredData =>
        filteredData.map(datum => (
          <li>
            {datum.name}, {datum.email}
          </li>
        ))
      }
    />
  </ul>
</FilterProvider>

Custom filter controls

For instance, we could reuse the Exact control to create a select of possible options.

import { Exact } from "hepa";

export default const SelectFilter = ({ children }) =>
  <Exact
    name={this.props.name}
    render={({ onChange, value }) => (
      <select onChange={onChange} value={value}>
        {this.props.children}
      </select>
    )}
  />

/**
 * Used like:
 * <SelectFilter name="customerId">
 *  <option value="1">Google</option>
 *  <option value="2">Facebook</option>
 *  <option value="3">Apple</option>
 *  <option value="4">Microsoft</option>
 * </SelectFilter>
 */

hepa's People

Contributors

vcarl avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.