{"action":"created","comment":{"url":"https://api.github.com/repos/Addepar/ember-charts/pulls/comments/77735671","id":77735671,"diff_hunk":"@@ -0,0 +1,792 @@\n+import Ember from \"ember\";\n+import { test, moduleForComponent } from 'ember-qunit';\n+\n+\n+var three_ranges = [{\n+   sliceLabel: \"Label 1\",\n+   barLabel: \"Group One\",\n+   value: 20\n+}, {\n+   sliceLabel: \"Label 1\",\n+   barLabel: \"Group Two\",\n+   value: 32\n+}, {\n+   sliceLabel: \"Label 1\",\n+   barLabel: \"Group Three\",\n+   value: 4\n+}, {\n+   sliceLabel: \"Label 2\",\n+   barLabel: \"Group One\",\n+   value: 16\n+}, {\n+   sliceLabel: \"Label 2\",\n+   barLabel: \"Group Two\",\n+   value: 17\n+}, {\n+   sliceLabel: \"Label 2\",\n+   barLabel: \"Group Three\",\n+   value: -18\n+}, {\n+   sliceLabel: \"Label 3\",\n+   barLabel: \"Group One\",\n+   value: -18\n+}, {\n+   sliceLabel: \"Label 3\",\n+   barLabel: \"Group Two\",\n+   value: 18\n+}, {\n+   sliceLabel: \"Label 3\",\n+   barLabel: \"Group Three\",\n+   value: -19\n+}];\n+\n+// Adds additional slice types to the three_ranges data for higher granularity\n+// and more thorough testing of slice sorting mechanics.\n+var sliceSortingData = _.cloneDeep(three_ranges).concat([{\n+  sliceLabel: \"Label 4\",\n+  barLabel: \"Group One\",\n+  value: -10\n+}, {\n+  sliceLabel: \"Label 4\",\n+  barLabel: \"Group Two\",\n+  value: -10\n+}, {\n+  sliceLabel: \"Label 4\",\n+  barLabel: \"Group Three\",\n+  value: 15\n+}, {\n+  sliceLabel: \"Label 5\",\n+  barLabel: \"Group One\",\n+  value: 10\n+}, {\n+  sliceLabel: \"Label 5\",\n+  barLabel: \"Group Three\",\n+  value: 10\n+}, {\n+  sliceLabel: \"Label 6\",\n+  barLabel: \"Group One\",\n+  value: 20\n+}, {\n+  sliceLabel: \"Label 6\",\n+  barLabel: \"Group Three\",\n+  value: 20\n+}]);\n+\n+moduleForComponent('stacked-vertical-bar-chart', '[Unit] Stacked bar component', {\n+  needs: [ 'template:components/chart-component'],\n+  beforeEach: function () {},\n+  afterEach: function () {}\n+});\n+\n+test('Margins are the right size', function(assert) {\n+  assert.expect(3);\n+  var component = this.subject();\n+\n+  assert.equal(component.get('marginLeft'), 0, 'no left margin');\n+  assert.equal(component.get('marginRight'), 0, 'no right margin');\n+  assert.equal(component.get('marginBottom'), 30, 'has top margin (because it has a legend)');\n+});\n+\n+test('In total, the correct number of stacking slices is displayed', function(assert) {\n+  assert.expect(1);\n+  this.subject({data: three_ranges});\n+  this.render();\n+\n+  assert.equal(this.$('svg rect').length, three_ranges.length,\n+    \"For \" + three_ranges.length + \" data points, that many stacking slices are shown\");\n+});\n+\n+// Passed in to Array.prototype.sort(), which has the bogus default behavior of\n+// coercing every value in the array to a string and sorting alphabetically\n+const diff = function(x, y) {\n+  return (x - y);\n+};\n+\n+const getFloatAttrValueOfSvgSlice = function(slice, attributeName) {\n+  return parseFloat(slice.attributes[attributeName].value);\n+};\n+\n+const getHeightOfSvgSlice = function(slice) {\n+  return getFloatAttrValueOfSvgSlice(slice, 'height');\n+};\n+\n+/**\n+ * Helper function to extract transform values from an HTML element.\n+ * These values are stored inside a string in the 'transform' attribute, so\n+ * some massaging must be done.\n+ *\n+ * For example, if the HTML looks as follows:\n+ *   <g class='tick' transform='translate(0.0, 42.0)' ... />\n+ * then calling this helper with this element will return either 0.0 or 42.0,\n+ * depending on whether index is 0 or 1.\n+ * @function\n+ * @param  {jQuery} $element - HTML element wrapped in jQuery\n+ * @param  {number} index - Either 0 or 1, whether to extract the x transform\n+ *                          value or y transform value\n+ * @return {float}\n+ */\n+const getFloatTransformValue = function($element, index) {\n+  return parseFloat($element.attr('transform').match(/\\d+(\\.\\d+)?/g)[index]);\n+};\n+\n+const PERCENT_TOLERANCE = 0.0001;\n+\n+const equalsWithTolerance = function(actual, expected) {\n+  var tolerance = Math.abs(expected * PERCENT_TOLERANCE);\n+  return Math.abs(actual - expected) < tolerance;\n+};\n+\n+test('Within each bar, the stacking slices have the correct heights relative to each other',\n+    function(assert) {\n+  var dataByBarLabel, barData, svgBarsByBarLabel, barLabel, svgBar, slices,\n+    barHeight, sliceHeights, iSlice, expectedSliceHeights, barDatum;\n+  assert.expect(three_ranges.length);\n+\n+  dataByBarLabel = _.groupBy(_.cloneDeep(three_ranges), 'barLabel');\n+\n+  // Compute the expected heights of each slice as a percentage\n+  // of the height of the whole bar.\n+  _.forEach(dataByBarLabel, function(barData) {\n+    var iDatum;\n+    var grossBarSum = 0.0;\n+    for (iDatum = 0; iDatum < barData.length; iDatum++) {\n+      grossBarSum += Math.abs(barData[iDatum].value);\n+    }\n+    for (iDatum = 0; iDatum < barData.length; iDatum++) {\n+      barData[iDatum].percentOfBar = Math.abs(barData[iDatum].value) / grossBarSum;\n+    }\n+  });\n+\n+  this.subject({data: three_ranges});\n+  this.render();\n+\n+  // Find all the SVG bars.\n+  svgBarsByBarLabel = {};\n+  this.$('svg g.bars').each(function() {\n+    svgBarsByBarLabel[$(this).text()] = this;\n+  });\n+\n+  // For each SVG bar:\n+  for (barLabel in svgBarsByBarLabel) {\n+    svgBar = svgBarsByBarLabel[barLabel];\n+    slices = $('rect', svgBar);\n+\n+    // Find the actual heights of each slice in the bar,\n+    // and the overall height of the bar.\n+    sliceHeights = _.map(slices, getHeightOfSvgSlice);\n+    barHeight = _.sum(sliceHeights);\n+    sliceHeights.sort(diff);\n+\n+    // Find the absolute expected heights of each slice\n+    // using the actual overall height of the bar.\n+    expectedSliceHeights = [];\n+    for (iSlice = 0; iSlice < slices.length; iSlice++) {\n+      barDatum = dataByBarLabel[barLabel][iSlice];\n+      expectedSliceHeights.push(barHeight * barDatum.percentOfBar);\n+    }\n+    expectedSliceHeights.sort(diff);\n+\n+    // Check that each slice has the correct height.\n+    // Note: we can't check that the slice also maps to the correct slice label,\n+    // because the SVG doesn't carry this information.\n+    //\n+    // FIXME(SBC): is there a workaround for the noted limitation?\n+    // E.g. by looking at the hover tooltip or color palette?\n+    //\n+    for (iSlice = 0; iSlice < slices.length; iSlice++) {","path":"tests/unit/stacked-vertical-bar-test.js","position":196,"original_position":196,"commit_id":"6f99dff4a3b04e27f242d49117a4495ead23ecd9","original_commit_id":"6f99dff4a3b04e27f242d49117a4495ead23ecd9","user":{"login":"billy-addepar","id":11281370,"avatar_url":"https://avatars.githubusercontent.com/u/11281370?v=3","gravatar_id":"","url":"https://api.github.com/users/billy-addepar","html_url":"https://github.com/billy-addepar","followers_url":"https://api.github.com/users/billy-addepar/followers","following_url":"https://api.github.com/users/billy-addepar/following{/other_user}","gists_url":"https://api.github.com/users/billy-addepar/gists{/gist_id}","starred_url":"https://api.github.com/users/billy-addepar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/billy-addepar/subscriptions","organizations_url":"https://api.github.com/users/billy-addepar/orgs","repos_url":"https://api.github.com/users/billy-addepar/repos","events_url":"https://api.github.com/users/billy-addepar/events{/privacy}","received_events_url":"https://api.github.com/users/billy-addepar/received_events","type":"User","site_admin":false},"body":"use `i`","created_at":"2016-09-06T23:13:12Z","updated_at":"2016-09-06T23:13:12Z","html_url":"https://github.com/Addepar/ember-charts/pull/171#discussion_r77735671","pull_request_url":"https://api.github.com/repos/Addepar/ember-charts/pulls/171","_links":{"self":{"href":"https://api.github.com/repos/Addepar/ember-charts/pulls/comments/77735671"},"html":{"href":"https://github.com/Addepar/ember-charts/pull/171#discussion_r77735671"},"pull_request":{"href":"https://api.github.com/repos/Addepar/ember-charts/pulls/171"}}},"pull_request":{"url":"https://api.github.com/repos/Addepar/ember-charts/pulls/171","id":79817258,"html_url":"https://github.com/Addepar/ember-charts/pull/171","diff_url":"https://github.com/Addepar/ember-charts/pull/171.diff","patch_url":"https://github.com/Addepar/ember-charts/pull/171.patch","issue_url":"https://api.github.com/repos/Addepar/ember-charts/issues/171","number":171,"state":"open","locked":false,"title":"Implement Stacked Vertical Bar Chart Component","user":{"login":"billylittlefield","id":13475895,"avatar_url":"https://avatars.githubusercontent.com/u/13475895?v=3","gravatar_id":"","url":"https://api.github.com/users/billylittlefield","html_url":"https://github.com/billylittlefield","followers_url":"https://api.github.com/users/billylittlefield/followers","following_url":"https://api.github.com/users/billylittlefield/following{/other_user}","gists_url":"https://api.github.com/users/billylittlefield/gists{/gist_id}","starred_url":"https://api.github.com/users/billylittlefield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/billylittlefield/subscriptions","organizations_url":"https://api.github.com/users/billylittlefield/orgs","repos_url":"https://api.github.com/users/billylittlefield/repos","events_url":"https://api.github.com/users/billylittlefield/events{/privacy}","received_events_url":"https://api.github.com/users/billylittlefield/received_events","type":"User","site_admin":false},"body":"## **NOTE:**\r\n\r\nThis PR is against Addepar's own feature branch _Addepar_, and not off _master_. However, if and when it is merged to _Addepar_, equivalent changes will be landed in _master_ as well.\r\n\r\n# **Background:**\r\n\r\nCurrently, the only support for stacked bar charts is a boolean toggle (`stackBars`) on the `VerticalBarChartComponent`. This feature does not support slices with negative values, not does it offer any property to outline the slices within a bar to provide visual separation. It also overloads certain properties from Vertical Bar Chart which can lead to confusion (for example: `betweenGroupPadding` & `withinGroupPadding`). \r\n\r\n# **Feature:**\r\n\r\nThis PR implements a new chart type _Stacked Vertical Bar Chart_ as its own component,  `StackedVerticalBarChartComponent`, which addresses the above concerns. This component is largely similar to the existing Vertical Bar Chart with several notable differences:\r\n\r\n* Added support for slices with negative values, which will be displayed beneath the X-axis.  \r\n* Added new property called `strokeWidth` in the `StackedVerticalBarChartComponent`, which can be set to control the width of the white border surrounding each slice, in pixels. \r\n* If a given slice has a value of 0, it will _no longer_ be represented in the DOM (previously it would exist with a height of zero).  \r\n* If all slices with a given `sliceLabel` have a value of 0, that `sliceLabel` will _no longer_ appear in the chart legend or have a color assigned to it at all (the latter means that in this case, there will no longer be irregular jumps in color between adjacent slices).\r\n* Any slices without a specified `barLabel` will be aggregated into a separate bar, whose label by default is 'Other'.\r\n* To prevent bars from being overcrowded with slices and the legend containing too many labels, we now introduce the ability to group small slices together into a single 'Other' slice for each bar (not to be confused with the 'Other' bar discussed previously). There are 3 properties of the chart that determine how the ‘Other’ slice is calculated:\r\n  * `minSlicePercent` - The minimum size for a slice label to not be aggregated into the  ‘Other’ slice, as a percentage of the tallest bar in the chart. By default, this is 2%. We want to ensure the slices that are shown in the chart are at least large enough to be visible. This way, tiny slivers that are hard to identify from the legend will be lumped together for ease of interpretation. To determine which slices these are (if any), we first find the tallest bar (gross value) in the chart, as well as the largest slice for each unique slice label. We then compare these largest slices as a percentage of the tallest bar against the minSlicePercent value. If any slice label fails to meet the minSlicePercent threshold with its largest slice, all slicess with that label will be aggregated into ‘Other’. This is the first filter applied to determine what slice labels go into ‘Other’. If you do not wish to set a size threshold for slices, this can be set to 0.\r\n  * `maxNumberOfSlices` - Limits the maximum number of unique slice labels to display in the chart and legend. By default, this is 10. After filtering the smallest slices, we still may have more unique slice labels than we can reasonably fit in the legend (and/or that can be readable on the chart itself). If there are more unique slice labels remaining (after filtering by `minSlicePercent`) than `maxNumberOfSlices`, the smallest slices will be aggregated into an ‘Other’ slice. For example, if the `maxNumberOfSlices` is set to 10 and we have 15 unique slice labels, at least the smallest 6 slice labels will be aggregated into ‘Other’ (giving us 9 unique slice labels, and 1 slice for ‘Other’ that is comprised of the remaining 6 slice labels). To determine which slices are aggregated into ‘Other’, we find the largest slice for each unique slice type and compare the absolute values of these slices, taking the largest (N - 1) slices (where N = `maxNumberOfSlices`). If you do not wish to set a count threshold for the number of slices in a chart, this can be any high number larger than the number of unique slice types (or, to guarantee no threshold is set, it can be turned into a computed property dependent on the length of input data, ie `Ember.computed.readOnly(‘data.length’)`).\r\n  * `otherSliceLabel` - String value for the aggregate slice. By default, this is ‘Other’.\r\n  * There is no explicit flag to ‘turn off’ the ‘Other’ slice, but it can easily be turned off by overriding the default values for `minSlicePercent` and `maxNumberOfSlices`as described in the above comments.\r\n* Bar sorting within the chart can also be manipulated and/or customized. Bar sorting is performed using two properties: `barSortKey` and `barSortAscending`. The `barSortKey` string dictates which bar sorting function to use, and can be set to either `’value’`, `’original’`, or `’custom’` (described in detail below). The `barSortAscending` boolean determines whether the bars sorted with `barSortKey` are in ascending or descending order.\r\n  * `barSortKey: 'original'` - Bars will not be sorted at all, i.e. the original order of bar labels from the input data is preserved. The first bar label to appear in the array of slice data in `data` will be the first bar in the chart, the second bar label in the slice data will be the second bar, and so on. If `barSortKey` is `null` or `undefined`, the chart will default to this sorting order.\r\n  * `barSortKey: 'value'` - Bars will be sorted by their \"net value\", i.e. the height of the bar above the y=0 axis minus the height below the y=0 axis. This criterion was already available.\r\n  * `barSortKey: 'custom'` - If you wish to sort bars in a separate manner, you can define a comparison function called `customBarSortingFn` used to compare two bar data points. This callback will be used to sort the bars when `barSortKey` is set to `‘custom’`. By default, the `customBarSortingFn` exists and will sort bars alphabetically by their bar label. The data objects being passed into these comparison functions only contain two fields: `barLabel` and `value`. Tip: If you wish to use your own custom bar sort with this method, it may be easiest to create an array of bar labels in the desired order and then override `customBarSortingFn` to compare the indices of each `comparisonObject.barLabel` within this array of labels.\r\n* Slice sorting within the chart can also be manipulated and/or customized. Slice sorting is performed using one property: `sliceSortKey` -- this can be set to either `’value’`, `’original’`, or `’custom’`. Because slices can have either positive or negative values, sorting implies the order in which bars will stack above and below the x-axis. A slice is sorted “before” another slice if it is closer to the x-axis. We cannot compare negative slices against positive slices, so a bar with negative and positive slices is effectively treated as two bars, each branching out from the x-axis. Regardless of which `sliceSortKey` is used, the aggregate ‘Other’ slice will ALWAYS be end-aligned within a bar (ie, it will always either be the top-most or bottom-most slice in the bar, depending on whether its net value is positive or negative).\r\n  * `sliceSortKey: ‘original’` - Slices will not be sorted at all, i.e. the original order of slices from the input `data` is preserved. If `sliceSortKey` is `null` or `undefined`, the chart will default to this sorting order.\r\n  * `sliceSortKey: ‘value’` - Establishes a slice sorting order and applies the same order to every bar, so slices are sorted the same relative to each other within each bar. Sorting slices by value is tricky because not every bar necessarily has every slice, and slices have different values in different bars. In order to accommodate this, the ‘value’ slice sorting is weighted by bar sizes. Starting with the largest net-value bar and moving down, we sort each slice by absolute value (descending) and add to an array. We then look at the second largest net-value bar, sort the slices by absolute value again, and add any slice labels that have not already been accounted for. This is continued down to the smallest net-value bar, at which point every slice label must have been accounted for. The end result gives a `sliceOrderByValue`, that is used for the comparison function when `sliceSortKey` is value.\r\n  * `sliceSortKey: ‘custom’` - If you wish to sort slices in a separate manner, you can define a comparison function called `customSliceSortingFn` used to compare two slice objects. This callback will be used to sort slices when `sliceSortKey` is set to `custom`. By default, the `customSliceSortingFn` exists and will sort slices alphabetically by their slice label. The data objects being passed into these comparison functions are the actual slice objects containing three fields: `sliceLabel`, `barLabel`, and `value`.\r\n* Changes the expected data input to be more representative of a stacked bar chart, changing `label` to `sliceLabel` and `group` to `barLabel`:\r\n\r\n**BEFORE:**\r\n```javascript\r\n[{\r\n    label: \"Label 1\",\r\n    group: \"Group One\",\r\n    value: 20\r\n}, {\r\n    label: \"Label 1\",\r\n    group: \"Group Two\",\r\n    value: 32\r\n}]\r\n```\r\n\r\n**NOW:**\r\n```javascript\r\n[{\r\n    sliceLabel: \"Label 1\",\r\n    barLabel: \"Group One\",\r\n    value: 20\r\n}, {\r\n    sliceLabel: \"Label 1\",\r\n    barLabel: \"Group Two\",\r\n    value: 32\r\n}]\r\n```\r\n\r\nThe existing `stackBars` toggle on the `VerticalBarChartComponent` will be deprecated in favor of this new component. A new page has been added to the sample app to showcase this new chart type.\r\n\r\n## **Sample images:**\r\n\r\n**BEFORE:**\r\n![screen shot 2016-08-02 at 3 01 40 pm](https://cloud.githubusercontent.com/assets/13475895/17346965/1b3edcf8-58c2-11e6-9477-6feb8dd729d4.png)\r\n\r\n**AFTER:**\r\n![screen shot 2016-08-02 at 3 01 22 pm](https://cloud.githubusercontent.com/assets/13475895/17346964/1b2f2740-58c2-11e6-81c0-9dceb5e26415.png)\r\n\r\n## **Path from input data to finished data**\r\n\r\n1) `data` - This is an array of slice objects, where each slice object has a value, sliceLabel, and barLabel. This is the raw input, so no filtering has been done. For example, there might be more types of slices than we are allowed to render (set by  `maxNumberOfSlices`), and/or some of the slices might not meet the `minSlicePercent` threshold. This data needs to be massaged and sorted before it can be used to render the chart.\r\n\r\n2) `dataGroupedByBar` - In order to determine which slice types get aggregated into the ‘Other’ slice, we first need to sort the slices into their respective bars. This is an object whose keys are barLabels and values are an array of slices with that barLabel.\r\n\r\n3) `largestSliceData` - Once the slices are sorted by bar, we can find the largest gross bar value. This is the tallest bar from bottom to top (aka, without ‘netting out’ positive and negative portions). Then we go through each slice label, find the largest slice of that specific label, and see what % of the tallest bar it is. This returns an array of objects (one object for each slice label), where each object contains the sliceLabel and the `percentOfBar` (compared to the tallest bar).\r\n\r\n4) `nonOtherSliceTypes` - Using the largestSliceData, we can determine what slices get aggregated into other and which ones don’t make the cut. First we filter out any slice labels that don’t meet the minSlicePercent. Of the remaining ones, we filter out the smallest ones until we have less than maxNumberOfSlices slices. All these filtered out ones will get aggregated into the ‘Other’ slice. If there’s only one slice label that gets filtered out, we don’t use an ‘Other’ slice, because we might as well just explicitly show it.\r\n\r\n5) `sortedData` - Now that we know which slices are grouped into ‘Other’, we can apply this knowledge to our previously calculated `dataGroupedByBar`. For each bar, we go through all slices in the bar. If it is on the `nonOtherSliceTypes` list, we just keep the slice, but if it isn’t, we add its value to a new ‘Other’ slice, and at the end add that new ‘Other’ slice instead. Because the ‘Other’ slice is netted out, we don’t need to worry about having a positive and negative ‘Other’. Once we’ve added all our slices, we sort them using the `sliceSortingFn`. The end result of this is once again an object mapping barLabels to slices, but now the slices are sorted in order and every slice will actually be rendered on the page. Bar sorting is handled in `barNames`, outside of this flow.\r\n\r\n6) `finishedData` - This is what d3 actually uses as its data to render the chart. Using the `sortedData`, it goes through each bar, and then each slice in the bar, and determines the y positioning for the top and bottom of each slice. This is done through a `map`, so the order of slices will determine where they actually are positioned in the bar. This is why we must sort the slices prior to this step. Slices are rolled up into a `stackedValues` array for each bar. Bar sorting is done differently, through the `barNames` property. D3 uses this to set the x domain, A bars order in the `finishedData` array does not necessarily indicate its position on the graph. \r\n\r\n## **Structure of The Code Changes:**\r\n\r\n- `addon/` and `app/` - The actually added product code, including `addon/components/stacked-vertical-bar-chart.js` which was copied from `addon/components/vertical-bar-chart.js` and extensively modified.\r\nAs noted in a comment below, it may be helpful to just look at the changes _after_ the copy:\r\nhttps://github.com/Addepar/ember-charts/pull/171/files/94d9e2e25310218572fc77ef1cca8cbc7d399534..91bed1cd24fcf6e888c5a751687f4376beffa538\r\n- `dist/` - Auto-generated files (globals-based version of Ember Charts). Can be ignored.\r\n- `packaging/globals.js` - Code to build the globals-based version of Ember Charts. Changed to add stacked bar charts to the globals-based version.\r\n- `tests/dummy/` - The demo application for Ember Charts, including a new demo page for stacked bar charts, copied and modified from the existing one for vertical bar charts. Also includes documentation updates.\r\n- `tests/unit/` - New tests for stacked bar charts.\r\n\r\nWe have not added any acceptance tests in `tests/acceptance/` for the new pages in the demo application.\r\n\r\n## **DEPRECATION of Old Code / Bug Fix for #167**\r\n\r\nAs noted above, the existing `stackBars` toggle on the `VerticalBarChartComponent` will be deprecated in favor of the new component `StackedVerticalBarChartComponent`.\r\n\r\nWe have made this change in order to avoid unintentionally or intentionally breaking existing users;  we have already made a breaking change to the data format as per above, and more breaking changes should be expected as we make further improvements to the `StackedVerticalBarChartComponent`.\r\n\r\nWe are sorry for any inconvenience the deprecation/migration may cause you, and we ask that you please understand.\r\n\r\nThe deprecated stacked bar chart code has been mostly untouched. The sole exception is the fix for bug #167 - Hover value shows NaN for chart with one bar.\r\nThis bug has been fixed in the new `StackedVerticalBarChartComponent` _and_ in the old stacked bar chart code in `VerticalBarChartComponent`.\r\n\r\n## **TODO**\r\n\r\n- [x] Take a screenshot of the stacked bar chart that we can use on the Ember Charts sample website's homepage for the link to the stacked bar chart example. (Currently, we reuse another image for that purpose which is rather lame.)\r\n- [ ] Find and hopefully remove all FIXMEs and TODOs in new code.\r\n\r\nDevelopers: @metathinker, @billylittlefield\r\nNominated Reviewers: @Addepar/fire ","created_at":"2016-08-02T22:04:25Z","updated_at":"2016-09-06T23:13:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"39f3224d19da93bd0fd995fb4d3151cf8924e934","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/Addepar/ember-charts/pulls/171/commits","review_comments_url":"https://api.github.com/repos/Addepar/ember-charts/pulls/171/comments","review_comment_url":"https://api.github.com/repos/Addepar/ember-charts/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Addepar/ember-charts/issues/171/comments","statuses_url":"https://api.github.com/repos/Addepar/ember-charts/statuses/6f99dff4a3b04e27f242d49117a4495ead23ecd9","head":{"label":"Addepar:working/stacked-bar-charts","ref":"working/stacked-bar-charts","sha":"6f99dff4a3b04e27f242d49117a4495ead23ecd9","user":{"login":"Addepar","id":847867,"avatar_url":"https://avatars.githubusercontent.com/u/847867?v=3","gravatar_id":"","url":"https://api.github.com/users/Addepar","html_url":"https://github.com/Addepar","followers_url":"https://api.github.com/users/Addepar/followers","following_url":"https://api.github.com/users/Addepar/following{/other_user}","gists_url":"https://api.github.com/users/Addepar/gists{/gist_id}","starred_url":"https://api.github.com/users/Addepar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Addepar/subscriptions","organizations_url":"https://api.github.com/users/Addepar/orgs","repos_url":"https://api.github.com/users/Addepar/repos","events_url":"https://api.github.com/users/Addepar/events{/privacy}","received_events_url":"https://api.github.com/users/Addepar/received_events","type":"Organization","site_admin":false},"repo":{"id":13790555,"name":"ember-charts","full_name":"Addepar/ember-charts","owner":{"login":"Addepar","id":847867,"avatar_url":"https://avatars.githubusercontent.com/u/847867?v=3","gravatar_id":"","url":"https://api.github.com/users/Addepar","html_url":"https://github.com/Addepar","followers_url":"https://api.github.com/users/Addepar/followers","following_url":"https://api.github.com/users/Addepar/following{/other_user}","gists_url":"https://api.github.com/users/Addepar/gists{/gist_id}","starred_url":"https://api.github.com/users/Addepar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Addepar/subscriptions","organizations_url":"https://api.github.com/users/Addepar/orgs","repos_url":"https://api.github.com/users/Addepar/repos","events_url":"https://api.github.com/users/Addepar/events{/privacy}","received_events_url":"https://api.github.com/users/Addepar/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Addepar/ember-charts","description":"","fork":false,"url":"https://api.github.com/repos/Addepar/ember-charts","forks_url":"https://api.github.com/repos/Addepar/ember-charts/forks","keys_url":"https://api.github.com/repos/Addepar/ember-charts/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Addepar/ember-charts/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Addepar/ember-charts/teams","hooks_url":"https://api.github.com/repos/Addepar/ember-charts/hooks","issue_events_url":"https://api.github.com/repos/Addepar/ember-charts/issues/events{/number}","events_url":"https://api.github.com/repos/Addepar/ember-charts/events","assignees_url":"https://api.github.com/repos/Addepar/ember-charts/assignees{/user}","branches_url":"https://api.github.com/repos/Addepar/ember-charts/branches{/branch}","tags_url":"https://api.github.com/repos/Addepar/ember-charts/tags","blobs_url":"https://api.github.com/repos/Addepar/ember-charts/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Addepar/ember-charts/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Addepar/ember-charts/git/refs{/sha}","trees_url":"https://api.github.com/repos/Addepar/ember-charts/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Addepar/ember-charts/statuses/{sha}","languages_url":"https://api.github.com/repos/Addepar/ember-charts/languages","stargazers_url":"https://api.github.com/repos/Addepar/ember-charts/stargazers","contributors_url":"https://api.github.com/repos/Addepar/ember-charts/contributors","subscribers_url":"https://api.github.com/repos/Addepar/ember-charts/subscribers","subscription_url":"https://api.github.com/repos/Addepar/ember-charts/subscription","commits_url":"https://api.github.com/repos/Addepar/ember-charts/commits{/sha}","git_commits_url":"https://api.github.com/repos/Addepar/ember-charts/git/commits{/sha}","comments_url":"https://api.github.com/repos/Addepar/ember-charts/comments{/number}","issue_comment_url":"https://api.github.com/repos/Addepar/ember-charts/issues/comments{/number}","contents_url":"https://api.github.com/repos/Addepar/ember-charts/contents/{+path}","compare_url":"https://api.github.com/repos/Addepar/ember-charts/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Addepar/ember-charts/merges","archive_url":"https://api.github.com/repos/Addepar/ember-charts/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Addepar/ember-charts/downloads","issues_url":"https://api.github.com/repos/Addepar/ember-charts/issues{/number}","pulls_url":"https://api.github.com/repos/Addepar/ember-charts/pulls{/number}","milestones_url":"https://api.github.com/repos/Addepar/ember-charts/milestones{/number}","notifications_url":"https://api.github.com/repos/Addepar/ember-charts/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Addepar/ember-charts/labels{/name}","releases_url":"https://api.github.com/repos/Addepar/ember-charts/releases{/id}","deployments_url":"https://api.github.com/repos/Addepar/ember-charts/deployments","created_at":"2013-10-23T01:27:05Z","updated_at":"2016-08-20T22:53:59Z","pushed_at":"2016-09-03T00:50:17Z","git_url":"git://github.com/Addepar/ember-charts.git","ssh_url":"git@github.com:Addepar/ember-charts.git","clone_url":"https://github.com/Addepar/ember-charts.git","svn_url":"https://github.com/Addepar/ember-charts","homepage":null,"size":11250,"stargazers_count":732,"watchers_count":732,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":119,"mirror_url":null,"open_issues_count":25,"forks":119,"open_issues":25,"watchers":732,"default_branch":"master"}},"base":{"label":"Addepar:Addepar","ref":"Addepar","sha":"809f4e4a82ba64382d03c2dabb3e2cc0a7192d52","user":{"login":"Addepar","id":847867,"avatar_url":"https://avatars.githubusercontent.com/u/847867?v=3","gravatar_id":"","url":"https://api.github.com/users/Addepar","html_url":"https://github.com/Addepar","followers_url":"https://api.github.com/users/Addepar/followers","following_url":"https://api.github.com/users/Addepar/following{/other_user}","gists_url":"https://api.github.com/users/Addepar/gists{/gist_id}","starred_url":"https://api.github.com/users/Addepar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Addepar/subscriptions","organizations_url":"https://api.github.com/users/Addepar/orgs","repos_url":"https://api.github.com/users/Addepar/repos","events_url":"https://api.github.com/users/Addepar/events{/privacy}","received_events_url":"https://api.github.com/users/Addepar/received_events","type":"Organization","site_admin":false},"repo":{"id":13790555,"name":"ember-charts","full_name":"Addepar/ember-charts","owner":{"login":"Addepar","id":847867,"avatar_url":"https://avatars.githubusercontent.com/u/847867?v=3","gravatar_id":"","url":"https://api.github.com/users/Addepar","html_url":"https://github.com/Addepar","followers_url":"https://api.github.com/users/Addepar/followers","following_url":"https://api.github.com/users/Addepar/following{/other_user}","gists_url":"https://api.github.com/users/Addepar/gists{/gist_id}","starred_url":"https://api.github.com/users/Addepar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Addepar/subscriptions","organizations_url":"https://api.github.com/users/Addepar/orgs","repos_url":"https://api.github.com/users/Addepar/repos","events_url":"https://api.github.com/users/Addepar/events{/privacy}","received_events_url":"https://api.github.com/users/Addepar/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Addepar/ember-charts","description":"","fork":false,"url":"https://api.github.com/repos/Addepar/ember-charts","forks_url":"https://api.github.com/repos/Addepar/ember-charts/forks","keys_url":"https://api.github.com/repos/Addepar/ember-charts/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Addepar/ember-charts/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Addepar/ember-charts/teams","hooks_url":"https://api.github.com/repos/Addepar/ember-charts/hooks","issue_events_url":"https://api.github.com/repos/Addepar/ember-charts/issues/events{/number}","events_url":"https://api.github.com/repos/Addepar/ember-charts/events","assignees_url":"https://api.github.com/repos/Addepar/ember-charts/assignees{/user}","branches_url":"https://api.github.com/repos/Addepar/ember-charts/branches{/branch}","tags_url":"https://api.github.com/repos/Addepar/ember-charts/tags","blobs_url":"https://api.github.com/repos/Addepar/ember-charts/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Addepar/ember-charts/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Addepar/ember-charts/git/refs{/sha}","trees_url":"https://api.github.com/repos/Addepar/ember-charts/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Addepar/ember-charts/statuses/{sha}","languages_url":"https://api.github.com/repos/Addepar/ember-charts/languages","stargazers_url":"https://api.github.com/repos/Addepar/ember-charts/stargazers","contributors_url":"https://api.github.com/repos/Addepar/ember-charts/contributors","subscribers_url":"https://api.github.com/repos/Addepar/ember-charts/subscribers","subscription_url":"https://api.github.com/repos/Addepar/ember-charts/subscription","commits_url":"https://api.github.com/repos/Addepar/ember-charts/commits{/sha}","git_commits_url":"https://api.github.com/repos/Addepar/ember-charts/git/commits{/sha}","comments_url":"https://api.github.com/repos/Addepar/ember-charts/comments{/number}","issue_comment_url":"https://api.github.com/repos/Addepar/ember-charts/issues/comments{/number}","contents_url":"https://api.github.com/repos/Addepar/ember-charts/contents/{+path}","compare_url":"https://api.github.com/repos/Addepar/ember-charts/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Addepar/ember-charts/merges","archive_url":"https://api.github.com/repos/Addepar/ember-charts/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Addepar/ember-charts/downloads","issues_url":"https://api.github.com/repos/Addepar/ember-charts/issues{/number}","pulls_url":"https://api.github.com/repos/Addepar/ember-charts/pulls{/number}","milestones_url":"https://api.github.com/repos/Addepar/ember-charts/milestones{/number}","notifications_url":"https://api.github.com/repos/Addepar/ember-charts/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Addepar/ember-charts/labels{/name}","releases_url":"https://api.github.com/repos/Addepar/ember-charts/releases{/id}","deployments_url":"https://api.github.com/repos/Addepar/ember-charts/deployments","created_at":"2013-10-23T01:27:05Z","updated_at":"2016-08-20T22:53:59Z","pushed_at":"2016-09-03T00:50:17Z","git_url":"git://github.com/Addepar/ember-charts.git","ssh_url":"git@github.com:Addepar/ember-charts.git","clone_url":"https://github.com/Addepar/ember-charts.git","svn_url":"https://github.com/Addepar/ember-charts","homepage":null,"size":11250,"stargazers_count":732,"watchers_count":732,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":119,"mirror_url":null,"open_issues_count":25,"forks":119,"open_issues":25,"watchers":732,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Addepar/ember-charts/pulls/171"},"html":{"href":"https://github.com/Addepar/ember-charts/pull/171"},"issue":{"href":"https://api.github.com/repos/Addepar/ember-charts/issues/171"},"comments":{"href":"https://api.github.com/repos/Addepar/ember-charts/issues/171/comments"},"review_comments":{"href":"https://api.github.com/repos/Addepar/ember-charts/pulls/171/comments"},"review_comment":{"href":"https://api.github.com/repos/Addepar/ember-charts/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Addepar/ember-charts/pulls/171/commits"},"statuses":{"href":"https://api.github.com/repos/Addepar/ember-charts/statuses/6f99dff4a3b04e27f242d49117a4495ead23ecd9"}}}}
{"actor":{"display_login":"billy-addepar"}}