Code Monkey home page Code Monkey logo

search-ui's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

search-ui's Issues

Add a Live Search feature

Live Search is a search that is performed as a user types in a search box.

As part of this change, I believe that instead of tracking the SearchBox's "value" in local state, we should use the global searchTerm value from the provider state.

This has a couple of of advantages:

  • The SearchBox can be cleared from elsewhere in the app, using the "reset" action, for instance.
  • We can key off changes to the search term to trigger query suggestions / autocomplete.

The interface I had in mind requires a change to setSearchTerm. It looks something like this:

setSearchTerm(searchTerm, {refresh = true, debounce = 200, suggest = false} = {})

To do a live search, you'd pass refresh: true. To perform a regular search, you would pass false, or omit the option.

the debounce option would let you throttle requests a bit, as live search tends to generate many thrashing requests.

I won't be implementing suggest yet, it is just there for reference.

Implemented by #79

Numeric filter fails from query parameters

I have a dataset which includes a numeric field difficulty.

I want to filter on this, so I add a filter with addFilter("difficulty", 2). This works great and filters my result set with the difficulty of 2. It also adds ?fv-difficulty=2 to my query parameters. If I then refresh my page I get the following error:

Error: [400] Filters contains invalid value for field: difficulty; must be a number, an array of numbers, or a range object with `to` and/or `from` keys
    at handleErrorResponse (swiftype_app_search.umd.js:362)

Support for NONE facet selections

Currently, all facets are treated as AND filters.

I believe that there should be an option on certain Facet component types to make it a NONE filter.

Ex:

<Facet field="state" type="none" />

When using dynamic date range filters, reloading the page will not correctly select a Date Range in Facet options.

For example, if you select a "Last 30 Minutes" filter, and reload the page, the option that is generate for the last 30 days will not match what is stored in the URL. This causes the filter go be applied, but not show up as a selection in the Facet Filter.

Possible solutions:

  • Have a new component, called "Filter Breadcrumb", which gives you breadcrumbs for all applied filters and lets you selectively remove them.
  • If a value is applied, but no option is found in a Facet filter, then generate a new option and render it, for the currently applied value.

Provide managed mode

A benefit of this library is the fact that it manages state for you, and converts that state to API calls automatically.

If a user chooses not to do that, either because they have a different backend they'd like to support, or they're storing their state elsewhere, we should provide support for that.

An example:
https://github.com/react-tools/react-table#fully-controlled-component

In manage mode:

When parameter state is changed in the driver, instead of storing that state, it simply notifies of that state change. It would then also accept updated state after that state change is handled. It makes no API calls.

Change mapContextToProps / mapViewProps recommendation

Currently, we make the following recommendation: https://github.com/elastic/search-ui/blob/master/packages/react-search-ui/README.md#customizebehavior.

I believe, for simplicity, we can actually remove this recommendation, along with the underlying mapContextToProps function (mapViewProps doesn't actually exist yet).

Mads was able to accomplish exactly what he needed by using wrapper components.

For ex, overriding props in containers:

<SearchProvider>
  {({filters, ...rest}) => (
     <SomeComponent filters={filters} {...rest} />
  )}
</SearchProvider>

For ex, overriding props in views:

<SomeComponent view={
  {({color, ...rest}) => (
       <SomeComponentView color="red" {...rest}/>
  )}
}/>

Create component styles

Currently, we pull in the Reference UI CSS and Markup. This is not ideal because the terms "reference-ui" are used all throughout it.

We should factor out common styles that are tied to individual components and have a separate stylesheet for react-search-components.

I also recommend setting up Storybook for styling these components individually.

Have a "Default" state as well as an "Initial" state.

When initializing a SearchProvider, there are two different scenarios for passing in state.

Default sate: The default state of the application. The default state is the state that is displayed if no request has been made, and the state that the application is returned to after a "reset" action is performed. An example would be setting a default sort on data.

Initial State: The initial state would be used for something like SSR, or pre-loading a previous search state. It would have priority over the Default state, but would be ephemeral, in that if a "reset" action was performed, it would return to the Default State, NOT the Initial State. Another way of thinking about this is the same way we read values from the query string, the query string would be another means of providing an Initial State.

Error on SearchProvider Unmount

If I do clearFilters and setSearchTerm("") in the SearchBox’s componentWillUnmount() I get the following warning: Can't perform a React state update on an unmounted component.

Basically, we need to make sure we unsubscribe from the driver on unmount of SearchProvider.

Normalize the Data Model

Come up with domain specific state shape that works best for the app, and not necessarily derived from API syntax.

The current syntax is derived from the App Search API syntax. It is highly nested and difficult to work with, especially when try to add or remove filters. This is because the API syntax is geared towards providing a DSL for querying, it was not designing with the intention of state management.

For this reason, a new state shape for managing UI state is would be better.

This will also push the connectors in a slightly different direction. Rather than converting to/from the App Search API syntax, they'll be converting to / from Search UI state syntax.

Design

Note: Not modeling value types
- Date, Text, Geo, Number

The App Search API does not distinguish between the Geo, Date, Text, and Number types. So likewise, the UI does not distinguish, because there is no way to tell what the schema is by looking at values in an API response. We just pass values through and let the API figure out what is what. This means that no code in Search UI can automatically detect a value as being a Date, for instance and format it or treat it as a date explicitly. Components will need to have optional flags that tell a component that they're dealing with Dates if they need to render values as dates, etc.

This is an overall positive approach I believe, because it's entirely possible that different backends will have different value types. For instance, we don't support a Boolean type, but other APIs may. This way, we just pass the value through and don't worry about the particular type, so it should "just work"

Filter

  • field: String - Which field is this filter associated with
  • type: FilterType - "all", "or", "none"
    - values: [FilterValue] - An array represents "or" filters

Facet
- field: String - Which field is this facet associated with
- type: String - "range" or "value"
- data:** [FacetValue]

FacetValue
- count: Number
- value: FilterValue

FilterType: String["all" | "none" | "or"]

** FieldValue: <String, Boolean, Number> **

FilterValue > FilterValueRange<T:FieldValue>
- to: <T>
- from: <T>
- name: String - We'll need this to correlate applied dynamic Date Range filters with options in corresponding range Facets on a page reload. This will not be an optional field.

FilterValue > FilterValueValue: FieldValue

Cases to consider:

  • Detecting if any filter value is applied to a particular field
  • Not applying the same filter twice / uniqueness
  • Switching between "all" and "or" for a field
  • Deleting all filters values for a field
  • Deleting an individual filter field
  • Adding a new filter
  • Creating specific types of filters via components, like a RangeFilter component with "from" and "to" fields.
  • Facet filters

CSS import error on rc-pagination.min.css

This CSS file need to be bundled in our CSS build, otherwise the consuming application needs to have a CSS bundler enabled.

Quote:

I try to use the import "@elastic/react-search-ui-views/lib/styles/styles.css";, I get an error from webpack Error: Failed to find '~rc-pagination/dist/rc-pagination.min.css'

Error on CSS build:

To replicate in Rails project:
rails new test-app --webpack=react --database=sqlite3
cd test-app
bundle exec rake db:create
rails g controller home index
change routes.rb to "root 'home#index'"
add "<%= javascript_pack_tag 'application' %>" to application.html.erb
add example code from "https://github.com/elastic/search-ui/tree/master/packages/react-search-ui" to application.js in app/javascript/packs
npm install
npm install --save @elastic/react-search-ui
npm install --save @elastic/search-ui-app-search-connector
./bin/webpack-dev-server

Support for OR facet selections

Currently, all facets are treated as AND filters.

I believe that there should be an option on certain Facet component types to make it an OR filter.

I propose the following API:

// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }

<Facet field="test" label="Test" filterType="any" />

Test
  [ ] value
  [ ] value 1
  [x] value2 // "value2" is selected, pull from "any" list

<Facet field="test" label="Test" filterType="all" />

Test
  [x] value // "value" selected, pull from "all" list
  [] value 1
  [x] value2 // "value2" selected, pull from "all" list
addFilter(name, value, type = "all")

ex.

// Defaults to 'all'
addFilter("test", "value")
// { field: "test", values: ["value"], type: "all" }

// If 'all' list for 'field' already exists, it will add to that list
addFilter("test", "value2", "all")
// { field: "test", values: ["value", "value2"], type: "all" }

// Will treat 'any' list for 'field' separately
addFilter("test", "value2", "any")
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
removeFilter(name, value, type)

ex.

// Removes from all lists if none specified
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
removeFilter("test", "value2")
// { field: "test", values: ["value2"], type: "all" }

// Removes just specified type of filter
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
removeFilter("test", "value", "all")
// { field: "test", values: ["value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
setFilter(name, value, type)

ex.

// Defaults to 'all'
setFilter("test", "value2")
// { field: "test", values: ["value2"], type: "all" }

// Sets specified type of filter otherwise
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
setFilter("test", "new", type: "all")
// { field: "test", values: ["new"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }

New URL filter scheme

How do we represent search parameters from the App Search API in a query string?

Challenge: need to flatten highly nested structure and maintain typing.

Reference: https://swiftype.com/documentation/app-search/api/search/filters

API Schema

{
  “all”: [{
     // Range with type Number
     {"visitors": {
        "from": 1,
        "to": 100
     }},
     // Range with type Date
     {"date_established": {
       "from": "1900-01-01T12:00:00+00:00",
       "to": "1950-01-01T00:00:00+00:00"
     }},
     // Geo with type Geolocation
     {"location": {
       "center": "37.386483, -122.083842",
       "distance": 300,
       "unit": "km"
     }},
     // Value with type Text
     {"world_heritage_site": "true"},
     // Value with type Date
     {“created_date”: "1900-01-01T12:00:00+00:00"},
     // Value with Type Number
     {“visitors”: 100},
     // Combining with "all"
     {“category”: ”fun”},
     {“category”: ”healthy”}
     // Nested "or"s
     {“category”: [”gradeA”, “gradeB”]} 
  }],
  // Combining with "any"
  “or”: { … },
  // Combining with "none"
   “none”: { … },
}

Proposed Query String Schema

f_{filterType}{valueType}{andAnyNone}{sequence}{fieldName}{[]}={value}

  • filterType:
    • “v” for Value
    • “r” for Range
    • “g” for Geo.
  • valueType:
    • “t” for Text”
    • “d” for Date
    • “n” for Number
    • “g” for Geolocation
  • andAnyNone:
    • “a” for “and”
    • “o” for “any”
    • “n” for “none”
  • _sequence: Nothing present if there is only one value, “_1” for the second, then “_2”, etc.
  • fieldName
  • value:
    • Value filters - just the value
    • Range filter - {low}_{high}
    • Geo filter - {center}{distance}{unit}

Examples

Range with type Number

{"visitors": {
   "from": 1,
  "to": 100
}}
     
f_rna_visitors=1_100

Range with type Date

{"date_established": {
  "from": "1900-01-01T12:00:00+00:00",
  "to": "1950-01-01T00:00:00+00:00"
}}

f_rda_date_established=1900-01-01T12:00:00+00:00_1950-01-01T00:00:00+00:00

Geo with type Geolocation

{"location": {
 "center": "37.386483, -122.083842",
  "distance": 300,
  "unit": "km"
}}

f_gga_location=37.386483, -122.083842_300_km

Value with type Text

{"world_heritage_site": "true"}

f_vta_world_heritage_site=true

Value with type Date

{“created_date”: "1900-01-01T12:00:00+00:00"}
     
f_vda_created_date=1900-01-01T12:00:00+00:00

Value with Type Number

{“visitors”: 100},
 
f_vna_visitors=100

Combining with "all"

{"all": [
  {“category”: ”fun”},
  {“category”: ”healthy”}
]}

f_vta_category=fun&f_vta_category_1=healthy

Nested "or"s

{"all": [
  {“category”: ”fun”},
  {“category”: ”healthy”},
  {“category”: [”gradeA”, “gradeB”]} 
]}

f_vta_category=fun&f_vta_category_2=healthy&f_vta_category_3[]=gradeA&f_vta_category_3[]=gradeB

Combining with "any"

{"any": [
  {“category”: ”fun”},
  {“category”: ”healthy”}
]}


f_vto_category=fun&f_vto_category_1=healthy

None filters

{"none": [
  {“category”: ”fun”},
  {“category”: ”healthy”}
]}

f_vtn_category=fun&f_vtn_category_1=healthy

Add Rollup build

This currently simply uses babel to compile our source from /src to /lib.

We should pull in Rollup, for two things:

  • Include umd builds
  • Tree shaking. I believe this will be important, because people may pull down our library just for one component, like an autocomplete.

EDIT:

  • UMD build will not really help us for a React library. Users are 99% guaranteed to be using a tool like webpack or Rollup, so this should be unnecessary.
  • Tree shaking is unimportant for this library. Again, because users will likely be using webpack or Rollup, the tree shaking will be performed on their end. We should just need to add "sideEffects": false to our package.json so that webpack knows that it can do tree shaking. https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free

It seems reasonable to just not use Rollup for this particular package. We'll punt on this until it's clear we have a use for it.

Config should match App Search API config

When configuring Search UI, we should use the App Search API as the format for configuration.

For example, right now, we have a facetConfig configuration option. It has additional properties that do not match App Search. Those should be pulled out into different configuration, to alleviate any confusion.

Add Layout Components to react-search-components

It would be neat to add basic layout components to react-search-components.

You'd get layout that basically allows you to implement a simple 2 column, facets on left, search bar on top.

This would make it easy for a quick an easy implementation.

Rework Facet component types

Facets currently have two problems:

  1. Facet views are tied to a specific type of facet, "range" or "value". They should be able to work with either.
  2. The Facet container should have a default view, rather than requiring one to be passed.

BREAKING CHANGES:
Facet views have been renamed.

All components should render pass through props

View components should provide a means to allow arbitrary properties like "className" and "data-" properties to be passed through.

Container components should pass through all additional properties to View components, so users can customize View properties through the container.

I.e.,

<SearchBox
  onSelectAutoCompleteResult={console.log}
/>

onSelectAutoCompleteResult is a property of the view, not the container, so it get passed through.

However, because of this, we don't want to pass ALL properties from the View to the underlying HTML, because you could end up with some weird attributes on your html. What we should DO is destructure props with all KNOWN, needed properties, and pass through everything else.

const {onSelectAutoCompleteResult, data, ...rest} = props;
return <input {...rest} />;

Convert to Typescript

We deal with a number of types in this Project. Making concrete types that could be shared across the various projects and externally to consumers would clean things up a lot.

Clean up file name casing

Files names, across the entire project use a mix of camel and snake casing. We should pick one and be consistent about it.

Bad error handling In the Site Search Connector

If an invalid page type is passed, the following error message is triggered:

index.js:1446 TypeError: Cannot read property 'map' of undefined
    at getResults (responseAdapters.js:180)
    at toResultList (responseAdapters.js:111)
    at SiteSearchAPIConnector.js:258

Additionally, we need to make it clear in our App.js example AND the documentation where you can find your documentType, and also, if it is a crawler based engine that your documentType is page.

Add CircleCI configuration

Initially, we should just add CircleCI to run unit tests.

This does not need to include comprehensive integration and browser level tests.

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.