{"action":"created","issue":{"url":"https://api.github.com/repos/algolia/algolia-ui-kit/issues/34","labels_url":"https://api.github.com/repos/algolia/algolia-ui-kit/issues/34/labels{/name}","comments_url":"https://api.github.com/repos/algolia/algolia-ui-kit/issues/34/comments","events_url":"https://api.github.com/repos/algolia/algolia-ui-kit/issues/34/events","html_url":"https://github.com/algolia/algolia-ui-kit/issues/34","id":95526410,"number":34,"title":"InstantSearch.js","user":{"login":"bobylito","id":393765,"avatar_url":"https://avatars.githubusercontent.com/u/393765?v=3","gravatar_id":"","url":"https://api.github.com/users/bobylito","html_url":"https://github.com/bobylito","followers_url":"https://api.github.com/users/bobylito/followers","following_url":"https://api.github.com/users/bobylito/following{/other_user}","gists_url":"https://api.github.com/users/bobylito/gists{/gist_id}","starred_url":"https://api.github.com/users/bobylito/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bobylito/subscriptions","organizations_url":"https://api.github.com/users/bobylito/orgs","repos_url":"https://api.github.com/users/bobylito/repos","events_url":"https://api.github.com/users/bobylito/events{/privacy}","received_events_url":"https://api.github.com/users/bobylito/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":{"login":"bobylito","id":393765,"avatar_url":"https://avatars.githubusercontent.com/u/393765?v=3","gravatar_id":"","url":"https://api.github.com/users/bobylito","html_url":"https://github.com/bobylito","followers_url":"https://api.github.com/users/bobylito/followers","following_url":"https://api.github.com/users/bobylito/following{/other_user}","gists_url":"https://api.github.com/users/bobylito/gists{/gist_id}","starred_url":"https://api.github.com/users/bobylito/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bobylito/subscriptions","organizations_url":"https://api.github.com/users/bobylito/orgs","repos_url":"https://api.github.com/users/bobylito/repos","events_url":"https://api.github.com/users/bobylito/events{/privacy}","received_events_url":"https://api.github.com/users/bobylito/received_events","type":"User","site_admin":false},"milestone":null,"comments":11,"created_at":"2015-07-16T20:55:00Z","updated_at":"2015-07-18T02:55:17Z","closed_at":null,"body":"Replace the dom interface by a programmatic interface to the UI-Kit and rename the project to instantsearch.js\r\n\r\n# Motivation\r\n\r\nThe  current implementation is very easy to use but once you hit one of its limits, the path to customisation is very steep. In order to reduce the gap between ease of use and extensibility, we need to rethink the API and at least provide a programmatic way to interact with the current architecture with hooks, data filters/transformers and much more template possibilities.\r\n\r\n# Expose the current flow of initialisation\r\n\r\nRight now, the initialisation flow is as follow (this is now done in a IIFE):\r\n 1. Read the DOM and create a parameters object\r\n 2. Helper instanciation and event listener setup\r\n 3. First query (that will then trigger the first rendering)\r\n \r\nThe third step triggers the feedback flow between user input and new renderings.\r\n\r\nFrom there, we could easily expose the process from the second step as function or an object. The function would be simpler but the object could let us add hooks to our API. \r\n\r\nThe API could look like (similiar to the intermediate config currently used) : \r\n```javascript\r\ninstantsearch( {\r\n  applicationID : \"\",\r\n  apiKey : \"\",\r\n  searchbox : {\r\n    nodeSelector : \"#my-search-box\",\r\n    placeholder : \"My super search box\"\r\n  },\r\n  hits : {\r\n    nodeSelector : \"#hits\",\r\n    resultTemplate : \"<div class='results'>{name} - {followers}</div>\"\r\n  }\r\n} ); \r\n```\r\n\r\nThis API differs from the current implementation with the use of selector instead of DomElements.\r\n\r\n# Creating new widgets\r\n\r\nEach block represent a specific widget configuration. This version would be very bound to the existing scope of supported widget. One way we could avoid that would be to have widget represented with real object responding to a wider contract that could also contain the rendering of the widget itself :\r\n```javascript\r\nvar search = instantSearch( {\r\n  applicationID : \"\",\r\n  apiKey : \"\"\r\n} );\r\n\r\nsearch.addWidget(searchBox({\r\n  node-selector : \"#my-search-box\"\r\n}));\r\n```\r\n\r\nsearchbox instanciate a new object that follows the contract : \r\n```javascript\r\n{\r\n  render : function(){ /* changes the dom */ }, \r\n}\r\n```\r\n\r\nThe render function could use any sort of library. And we could also be able to use plain objects (for singleton and rapid prototyping)\r\n\r\n# Transforming data\r\n\r\nThe object returned by the widget could also bear some hook function from the lifecycle. For example we could have a `transformData` function that transform the data in a new object. As long as this function exists, it will be called before the rendering and its result will be passed to the render function.\r\n\r\n```javascript\r\n{\r\n  render : function( data ){\r\n    React.render( <div>{data.name} - {data.followers}</div> );\r\n  },\r\n  transformData : function( rawData ){\r\n    return {\r\n      name : rawData.name.capitalize(),\r\n      followers : rawData.followers\r\n    };\r\n  }\r\n}\r\n```\r\n\r\n# Other ideas\r\n\r\n - path to data\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/algolia/algolia-ui-kit/issues/comments/122476851","html_url":"https://github.com/algolia/algolia-ui-kit/issues/34#issuecomment-122476851","issue_url":"https://api.github.com/repos/algolia/algolia-ui-kit/issues/34","id":122476851,"user":{"login":"redox","id":29529,"avatar_url":"https://avatars.githubusercontent.com/u/29529?v=3","gravatar_id":"","url":"https://api.github.com/users/redox","html_url":"https://github.com/redox","followers_url":"https://api.github.com/users/redox/followers","following_url":"https://api.github.com/users/redox/following{/other_user}","gists_url":"https://api.github.com/users/redox/gists{/gist_id}","starred_url":"https://api.github.com/users/redox/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/redox/subscriptions","organizations_url":"https://api.github.com/users/redox/orgs","repos_url":"https://api.github.com/users/redox/repos","events_url":"https://api.github.com/users/redox/events{/privacy}","received_events_url":"https://api.github.com/users/redox/received_events","type":"User","site_admin":false},"created_at":"2015-07-18T02:55:17Z","updated_at":"2015-07-18T02:55:17Z","body":"I'm almost convinced we should not make React.js visible (like the `render` method).\r\n\r\nAlso I wasn't sure you wanted to have the whole/final API now, so let me show you what I had in mind:\r\n\r\n```html\r\n<input type=\"text\" id=\"search-input\" />\r\n\r\n<!-- [...] -->\r\n\r\n<div class=\"row\">\r\n  <div class=\"col-sm-3\" id=\"facets\"></div>\r\n  <div class=\"col-sm-9\">\r\n    <div id=\"stats\"></div>\r\n    <div id=\"hits\"></div>\r\n    <div id=\"pagination\"></div>\r\n  </div>\r\n</div>\r\n\r\n<!-- [...] -->\r\n\r\n<script>\r\ninstantsearch(\"ApplicationID\", \"SearchOnlyAPIKey\", {\r\n  index: 'YourIndex',\r\n  input: {\r\n    selector: \"#search-input\",\r\n    placeholder: \"Search for products by name, categories, description...\"\r\n  },\r\n  hits: {\r\n    selector: \"#hits\",\r\n    onHit: function(hit, content) { return hit; }, // optional function to override/enrich a hit before display\r\n    template: /* function(hit) {} or string */,\r\n    sortOrders: [\r\n      { label: \"Relevance\", index: \"YourIndex\" },\r\n      { label: \"Highest price\", index: \"YourIndex_price_desc\" },\r\n      { label: \"Lowest price\", index: \"YourIndex_price_asc\" }\r\n    ]\r\n  },\r\n  pagination: {\r\n     selector: \"#pagination\",\r\n     nbHitsPerPage: 12\r\n  },\r\n  facets: {\r\n    selector: \"#facets\",\r\n    onFacets: function(facets, content) { return facets; },  // optional function to override/enrich a facet before display\r\n    configuration: [\r\n      {\r\n        attribute: 'category',\r\n        sortBy: 'count',\r\n        onFacet: function(facet, content) { return facet; } // optional function to override/enrich a facet value before display\r\n      },\r\n      {\r\n        attribute: 'price',\r\n        type: 'slider',\r\n        onFacet: function(facet, content) { return facet; } // optional function to override/enrich a facet value before display\r\n      },\r\n      {\r\n        attribute: 'price',\r\n        type: 'disjunctive',\r\n        sortBy: 'value',\r\n        onFacet: function(facet, content) { return facet; } // optional function to override/enrich a facet value before display\r\n      }\r\n    ]\r\n  },\r\n  stats: {\r\n    selector: \"#stats\",\r\n    displayProcessingTime: true\r\n  }\r\n});\r\n</script>\r\n```\r\n\r\nI would not allow any templating for the facets, the sorts and the pagination for now: only customizable with CSS.\r\n\r\nI'm not sure about the `on{Hit,Facet,Facets}` naming, but couldn't find something smarter (didn't like the `filter/map` options)."}}