Http request volume/dropdown (#7016)
* init dropdown * add dropdown to storybook * move http requests components into container * add event handler for selecting new time window * no need for this. in the template * filter bar chart and table * add bar chart transitions * handle Last 12 Months in dropdown * don't use fake data * start tests * add jsdoc and notes for storybook * add container to storybook * compute filteredCounters when counters change * move static dropdown options to template * add tests * style the dropdown * use this.elementId * fix linting errors * use ember array extensions * use fillIn instead of page object and make dom assertions consistent * calculate the correct percent change between months * use data-test selector instead of id * show plus or minus next to percent change
This commit is contained in:
parent
19b67fc617
commit
5cd7e924fe
|
@ -4,6 +4,8 @@ import d3Scale from 'd3-scale';
|
|||
import d3Axis from 'd3-axis';
|
||||
import d3TimeFormat from 'd3-time-format';
|
||||
import d3Tip from 'd3-tip';
|
||||
import d3Transition from 'd3-transition';
|
||||
import d3Ease from 'd3-ease';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { computed } from '@ember/object';
|
||||
import { run } from '@ember/runloop';
|
||||
|
@ -29,6 +31,8 @@ import { task, waitForEvent } from 'ember-concurrency';
|
|||
|
||||
const HEIGHT = 240;
|
||||
const HOVER_PADDING = 12;
|
||||
const BASE_SPEED = 150;
|
||||
const DURATION = BASE_SPEED * 2;
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['http-requests-bar-chart-container'],
|
||||
|
@ -83,6 +87,10 @@ export default Component.extend({
|
|||
});
|
||||
},
|
||||
|
||||
didUpdateAttrs() {
|
||||
this.renderBarChart();
|
||||
},
|
||||
|
||||
renderBarChart() {
|
||||
const { margin, width, xScale, yScale, parsedCounters, elementId } = this;
|
||||
const height = this.height();
|
||||
|
@ -146,11 +154,22 @@ export default Component.extend({
|
|||
.append('rect')
|
||||
.attr('class', 'bar');
|
||||
|
||||
const t = d3Transition
|
||||
.transition()
|
||||
.duration(DURATION)
|
||||
.ease(d3Ease.easeQuad);
|
||||
|
||||
bars
|
||||
.merge(barsEnter)
|
||||
.attr('width', xScale.bandwidth())
|
||||
.attr('height', counter => height - yScale(counter.total))
|
||||
.attr('x', counter => xScale(counter.start_time))
|
||||
// set the initial y value to 0 so the bars animate upwards
|
||||
.attr('y', () => yScale(0))
|
||||
.attr('width', xScale.bandwidth())
|
||||
.transition(t)
|
||||
.delay(function(d, i) {
|
||||
return i * BASE_SPEED;
|
||||
})
|
||||
.attr('height', counter => height - yScale(counter.total))
|
||||
.attr('y', counter => yScale(counter.total));
|
||||
|
||||
bars.exit().remove();
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import Component from '@ember/component';
|
||||
import { computed } from '@ember/object';
|
||||
import isWithinRange from 'date-fns/is_within_range';
|
||||
import addMonths from 'date-fns/add_months';
|
||||
|
||||
/**
|
||||
* @module HttpRequestsContainer
|
||||
* The HttpRequestsContainer component is the parent component of the HttpRequestsDropdown, HttpRequestsBarChart, and HttpRequestsTable components. It is used to handle filtering the bar chart and table according to selected time window from the dropdown.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <HttpRequestsContainer @counters={counters}/>
|
||||
* ```
|
||||
*
|
||||
* @param counters=null {Array} - A list of objects containing the total number of HTTP Requests for each month. `counters` should be the response from the `/internal/counters/requests` endpoint which looks like:
|
||||
* COUNTERS = [
|
||||
* {
|
||||
* "start_time": "2019-05-01T00:00:00Z",
|
||||
* "total": 50
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['http-requests-container'],
|
||||
counters: null,
|
||||
timeWindow: 'All',
|
||||
filteredCounters: computed('counters', 'timeWindow', function() {
|
||||
const { counters, timeWindow } = this;
|
||||
if (timeWindow === 'All') {
|
||||
return counters;
|
||||
}
|
||||
|
||||
let filteredCounters = [];
|
||||
if (timeWindow === 'Last 12 Months') {
|
||||
const today = new Date();
|
||||
const TwelveMonthsAgo = addMonths(today, -12);
|
||||
filteredCounters = counters.filter(counter => {
|
||||
return isWithinRange(counter.start_time, TwelveMonthsAgo, today);
|
||||
});
|
||||
|
||||
return filteredCounters;
|
||||
}
|
||||
|
||||
filteredCounters = counters.filter(counter => {
|
||||
const year = counter.start_time.substr(0, 4);
|
||||
return year === timeWindow;
|
||||
});
|
||||
return filteredCounters;
|
||||
}),
|
||||
actions: {
|
||||
updateTimeWindow(newValue) {
|
||||
this.set('timeWindow', newValue);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import Component from '@ember/component';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
/**
|
||||
* @module HttpRequestsDropdown
|
||||
* HttpRequestsDropdown components are used to render a dropdown that filters the HttpRequestsBarChart.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <HttpRequestsDropdown @counters={counters} />
|
||||
* ```
|
||||
*
|
||||
* @param counters=null {Array} - A list of objects containing the total number of HTTP Requests for each month. `counters` should be the response from the `/internal/counters/requests` endpoint which looks like:
|
||||
* COUNTERS = [
|
||||
* {
|
||||
* "start_time": "2019-05-01T00:00:00Z",
|
||||
* "total": 50
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['http-requests-dropdown'],
|
||||
counters: null,
|
||||
timeWindow: 'All',
|
||||
options: computed('counters', function() {
|
||||
let counters = this.counters || [];
|
||||
let options = [];
|
||||
if (counters.length) {
|
||||
const years = counters.map(counter => counter.start_time.substr(0, 4)).uniq();
|
||||
years.sort().reverse();
|
||||
options = options.concat(years);
|
||||
}
|
||||
return options;
|
||||
}),
|
||||
onChange() {},
|
||||
actions: {
|
||||
onSelectTimeWindow(e) {
|
||||
const newValue = e.target.value;
|
||||
const { timeWindow } = this;
|
||||
if (newValue !== timeWindow) {
|
||||
this.onChange(newValue);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -30,21 +30,22 @@ export default Component.extend({
|
|||
|
||||
counters.forEach(month => {
|
||||
if (previousMonthVal) {
|
||||
let percentChange = (((month.total - previousMonthVal) / month.total) * 100).toFixed(1);
|
||||
let percentChange = (((previousMonthVal - month.total) / previousMonthVal) * 100).toFixed(1);
|
||||
// a negative value indicates a percentage increase, so we swap the value
|
||||
percentChange = -percentChange;
|
||||
let glyph;
|
||||
if (percentChange > 0) {
|
||||
glyph = 'arrow-up';
|
||||
} else if (percentChange < 0) {
|
||||
glyph = 'arrow-down';
|
||||
}
|
||||
percentChange = Math.abs(percentChange);
|
||||
const newCounter = assign({ percentChange, glyph }, month);
|
||||
countersWithPercentChange.push(newCounter);
|
||||
} else {
|
||||
// we're looking at the first counter in the list, so there is no % change value.
|
||||
countersWithPercentChange.push(month);
|
||||
previousMonthVal = month.total;
|
||||
}
|
||||
previousMonthVal = month.total;
|
||||
});
|
||||
return countersWithPercentChange;
|
||||
}),
|
||||
|
|
|
@ -95,6 +95,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.toolbar-label {
|
||||
padding: $spacing-xs;
|
||||
color: $grey;
|
||||
}
|
||||
|
||||
.toolbar-separator {
|
||||
border-right: $light-border;
|
||||
height: 32px;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{{#if (gt counters.length 1) }}
|
||||
<Toolbar>
|
||||
<HttpRequestsDropdown @counters={{counters}} @onChange={{action "updateTimeWindow"}} @timeWindow={{timeWindow}}/>
|
||||
</Toolbar>
|
||||
|
||||
<HttpRequestsBarChart @counters={{filteredCounters}} />
|
||||
{{/if}}
|
||||
|
||||
<AlertInline @type="info" @message="Metrics are recorded only for interactions that produce or use a Vault token." />
|
||||
<HttpRequestsTable @counters={{filteredCounters}} />
|
|
@ -0,0 +1,12 @@
|
|||
<label for="date-range" class="is-label toolbar-label">
|
||||
Date Range
|
||||
</label>
|
||||
<div class="select">
|
||||
<select class="select" id="date-range" data-test-date-range name="selectedTimeWindow" data-test-timewindow-select onchange={{action "onSelectTimeWindow"}}>
|
||||
<option value="All" selected={{eq timeWindow "All"}}>All</option>/>
|
||||
<option value="Last 12 Months" selected={{eq timeWindow "Last 12 Months"}}>Last 12 Months</option>
|
||||
{{#each options as |op|}}
|
||||
<option value={{op}} selected={{eq timeWindow op}}>{{op}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
|
@ -13,7 +13,7 @@
|
|||
<tbody>
|
||||
{{#each (reverse countersWithChange) as |c|}}
|
||||
<tr>
|
||||
<th scope="row" class="has-text-black">{{ format-utc c.start_time '%B %Y' }}</th>
|
||||
<th scope="row" class="start-time has-text-black">{{ format-utc c.start_time '%B %Y' }}</th>
|
||||
<td>{{format-number c.total}}</td>
|
||||
{{#if (gt counters.length 1)}}
|
||||
<td class="has-text-grey-dark percent-change" data-test-change="{{c.percentChange}}">
|
||||
|
|
|
@ -6,8 +6,4 @@
|
|||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
{{#if (gt model.counters.length 1) }}
|
||||
<HttpRequestsBarChart @counters={{model.counters}}/>
|
||||
{{/if}}
|
||||
<AlertInline @type="info" @message="Metrics are recorded only for interactions that produce or use a Vault token." />
|
||||
<HttpRequestsTable @counters={{model.counters}} />
|
||||
<HttpRequestsContainer @counters={{model.counters}}/>
|
||||
|
|
|
@ -54,10 +54,12 @@
|
|||
"columnify": "^1.5.4",
|
||||
"cool-checkboxes-for-bulma.io": "^1.1.0",
|
||||
"d3-axis": "^1.0.8",
|
||||
"d3-ease": "^1.0.5",
|
||||
"d3-scale": "^1.0.7",
|
||||
"d3-selection": "^1.3.0",
|
||||
"d3-time-format": "^2.1.1",
|
||||
"d3-tip": "^0.9.1",
|
||||
"d3-transition": "^1.2.0",
|
||||
"date-fns": "^1.29.0",
|
||||
"deepmerge": "^2.1.1",
|
||||
"doctoc": "^1.4.0",
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<!--THIS FILE IS AUTO GENERATED. This file is generated from JSDoc comments in app/components/http-requests-container.js. To make changes, first edit that file and run "yarn gen-story-md http-requests-container" to re-generate the content.-->
|
||||
|
||||
## HttpRequestsContainer
|
||||
The HttpRequestsContainer component is the parent component of the HttpRequestsDropdown, HttpRequestsBarChart, and HttpRequestsTable components. It is used to handle filtering the bar chart and table according to selected time window from the dropdown.
|
||||
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| counters | <code>Array</code> | <code></code> | A list of objects containing the total number of HTTP Requests for each month. `counters` should be the response from the `/internal/counters/requests` endpoint. |
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
<HttpRequestsContainer @counters={counters}/>
|
||||
```
|
||||
|
||||
**See**
|
||||
|
||||
- [Uses of HttpRequestsContainer](https://github.com/hashicorp/vault/search?l=Handlebars&q=HttpRequestsContainer+OR+http-requests-container)
|
||||
- [HttpRequestsContainer Source Code](https://github.com/hashicorp/vault/blob/master/ui/app/components/http-requests-container.js)
|
||||
|
||||
---
|
|
@ -0,0 +1,36 @@
|
|||
/* eslint-disable import/extensions */
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { storiesOf } from '@storybook/ember';
|
||||
import { withKnobs, object } from '@storybook/addon-knobs';
|
||||
import notes from './http-requests-container.md';
|
||||
|
||||
const COUNTERS = [
|
||||
{ start_time: '2017-04-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2018-06-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2018-07-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2018-08-01T00:00:00Z', total: 6500 },
|
||||
{ start_time: '2018-09-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2018-10-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2018-11-01T00:00:00Z', total: 6500 },
|
||||
{ start_time: '2018-12-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2019-01-01T00:00:00Z', total: 2500 },
|
||||
{ start_time: '2019-02-01T00:00:00Z', total: 3500 },
|
||||
{ start_time: '2019-03-01T00:00:00Z', total: 5000 },
|
||||
];
|
||||
|
||||
storiesOf('HttpRequests/Container/', module)
|
||||
.addParameters({ options: { showPanel: true } })
|
||||
.addDecorator(withKnobs())
|
||||
.add(
|
||||
`HttpRequestsContainer`,
|
||||
() => ({
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Http Requests Container</h5>
|
||||
<HttpRequestsContainer @counters={{counters}}/>
|
||||
`,
|
||||
context: {
|
||||
counters: object('counters', COUNTERS),
|
||||
},
|
||||
}),
|
||||
{ notes }
|
||||
);
|
|
@ -0,0 +1,22 @@
|
|||
<!--THIS FILE IS AUTO GENERATED. This file is generated from JSDoc comments in app/components/http-requests-dropdown.js. To make changes, first edit that file and run "yarn gen-story-md http-requests-dropdown" to re-generate the content.-->
|
||||
|
||||
## HttpRequestsDropdown
|
||||
HttpRequestsDropdown components are used to render a dropdown that filters the HttpRequestsBarChart.
|
||||
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| counters | <code>Array</code> | <code></code> | A list of objects containing the total number of HTTP Requests for each month. `counters` should be the response from the `/internal/counters/requests` endpoint. |
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
<HttpRequestsDropdown @counters={counters} />
|
||||
```
|
||||
|
||||
**See**
|
||||
|
||||
- [Uses of HttpRequestsDropdown](https://github.com/hashicorp/vault/search?l=Handlebars&q=HttpRequestsDropdown+OR+http-requests-dropdown)
|
||||
- [HttpRequestsDropdown Source Code](https://github.com/hashicorp/vault/blob/master/ui/app/components/http-requests-dropdown.js)
|
||||
|
||||
---
|
|
@ -0,0 +1,28 @@
|
|||
/* eslint-disable import/extensions */
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { storiesOf } from '@storybook/ember';
|
||||
import { withKnobs, object } from '@storybook/addon-knobs';
|
||||
import notes from './http-requests-dropdown.md';
|
||||
|
||||
const COUNTERS = [
|
||||
{ start_time: '2019-04-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2019-05-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2019-06-01T00:00:00Z', total: 5000 },
|
||||
];
|
||||
|
||||
storiesOf('HttpRequests/Dropdown/', module)
|
||||
.addParameters({ options: { showPanel: true } })
|
||||
.addDecorator(withKnobs())
|
||||
.add(
|
||||
`HttpRequestsDropdown`,
|
||||
() => ({
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Http Requests Dropdown</h5>
|
||||
<HttpRequestsDropdown @counters={{counters}}/>
|
||||
`,
|
||||
context: {
|
||||
counters: object('counters', COUNTERS),
|
||||
},
|
||||
}),
|
||||
{ notes }
|
||||
);
|
|
@ -5,24 +5,25 @@ import { withKnobs, object } from '@storybook/addon-knobs';
|
|||
import notes from './http-requests-table.md';
|
||||
|
||||
const COUNTERS = [
|
||||
{ start_time: '2019-04-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2019-05-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2019-06-01T00:00:00Z', total: 5000 },
|
||||
{ start_time: '2018-12-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2019-01-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2019-02-01T00:00:00Z', total: 5000 },
|
||||
{ start_time: '2019-03-01T00:00:00Z', total: 5000 },
|
||||
];
|
||||
|
||||
storiesOf('HttpRequests/Table/', module)
|
||||
.addParameters({ options: { showPanel: true } })
|
||||
.addDecorator(
|
||||
withKnobs()
|
||||
)
|
||||
.add(`HttpRequestsTable`, () => ({
|
||||
template: hbs`
|
||||
.addDecorator(withKnobs())
|
||||
.add(
|
||||
`HttpRequestsTable`,
|
||||
() => ({
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Http Requests Table</h5>
|
||||
<HttpRequestsTable @counters={{counters}}/>
|
||||
`,
|
||||
context: {
|
||||
counters: object('counters', COUNTERS),
|
||||
}
|
||||
}),
|
||||
{ notes }
|
||||
);
|
||||
context: {
|
||||
counters: object('counters', COUNTERS),
|
||||
},
|
||||
}),
|
||||
{ notes }
|
||||
);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, fillIn } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
const COUNTERS = [
|
||||
{ start_time: '2018-12-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2019-01-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2019-02-01T00:00:00Z', total: 5000 },
|
||||
{ start_time: '2019-03-01T00:00:00Z', total: 5000 },
|
||||
];
|
||||
|
||||
module('Integration | Component | http-requests-container', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
this.set('counters', COUNTERS);
|
||||
});
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
await render(hbs`<HttpRequestsContainer @counters={{counters}}/>`);
|
||||
|
||||
assert.dom('.http-requests-container').exists();
|
||||
assert.dom('.http-requests-dropdown').exists();
|
||||
assert.dom('.http-requests-bar-chart-container').exists();
|
||||
assert.dom('.http-requests-table').exists();
|
||||
});
|
||||
|
||||
test('it does not render a bar chart for less than one month of data', async function(assert) {
|
||||
const one_month_counter = [
|
||||
{
|
||||
start_time: '2019-05-01T00:00:00Z',
|
||||
total: 50,
|
||||
},
|
||||
];
|
||||
this.set('one_month_counter', one_month_counter);
|
||||
|
||||
await render(hbs`<HttpRequestsContainer @counters={{one_month_counter}}/>`);
|
||||
|
||||
assert.dom('.http-requests-table').exists();
|
||||
assert.dom('.http-requests-bar-chart-container').doesNotExist();
|
||||
});
|
||||
|
||||
test('it filters the data according to the dropdown', async function(assert) {
|
||||
await render(hbs`<HttpRequestsContainer @counters={{counters}}/>`);
|
||||
await fillIn('[data-test-timewindow-select]', '2018');
|
||||
|
||||
assert.dom('.shadow-bars> .bar').exists({ count: 1 }, 'filters the bar chart to the selected year');
|
||||
assert.dom('.start-time').exists({ count: 1 }, 'filters the table to the selected year');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
const COUNTERS = [
|
||||
{ start_time: '2018-04-01T00:00:00Z', total: 5500 },
|
||||
{ start_time: '2019-05-01T00:00:00Z', total: 4500 },
|
||||
{ start_time: '2019-06-01T00:00:00Z', total: 5000 },
|
||||
];
|
||||
|
||||
module('Integration | Component | http-requests-dropdown', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
this.set('counters', COUNTERS);
|
||||
});
|
||||
|
||||
test('it renders with options', async function(assert) {
|
||||
await render(hbs`<HttpRequestsDropdown @counters={{counters}} />`);
|
||||
|
||||
assert.dom('[data-test-date-range]').hasValue('All', 'shows all data by default');
|
||||
|
||||
assert.equal(
|
||||
this.element.querySelector('[data-test-date-range]').options.length,
|
||||
4,
|
||||
'it adds an option for each year in the data set'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -44,11 +44,21 @@ module('Integration | Component | http-requests-table', function(hooks) {
|
|||
});
|
||||
|
||||
test('it shows the percent change between each time window', async function(assert) {
|
||||
await render(hbs`<HttpRequestsTable @counters={{counters}}/>`);
|
||||
const expected = Math.abs(
|
||||
(((COUNTERS[1].total - COUNTERS[0].total) / COUNTERS[1].total) * 100).toFixed(1)
|
||||
);
|
||||
const simple_counters = [
|
||||
{ start_time: '2019-04-01T00:00:00Z', total: 1 },
|
||||
{ start_time: '2019-05-01T00:00:00Z', total: 2 },
|
||||
{ start_time: '2019-06-01T00:00:00Z', total: 1 },
|
||||
{ start_time: '2019-07-01T00:00:00Z', total: 1 },
|
||||
];
|
||||
this.set('counters', simple_counters);
|
||||
|
||||
assert.ok(this.element.textContent.includes(expected));
|
||||
await render(hbs`<HttpRequestsTable @counters={{counters}}/>`);
|
||||
// the expectedValues are in reverse chronological order because that is the order
|
||||
// that the table shows its data.
|
||||
let expectedValues = ['', '-50%', '100%', ''];
|
||||
|
||||
this.element.querySelectorAll('[data-test-change]').forEach((td, i) => {
|
||||
return assert.equal(td.textContent.trim(), expectedValues[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
29
ui/yarn.lock
29
ui/yarn.lock
|
@ -6503,6 +6503,16 @@ d3-color@1:
|
|||
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.3.tgz#6c67bb2af6df3cc8d79efcc4d3a3e83e28c8048f"
|
||||
integrity sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==
|
||||
|
||||
d3-dispatch@1:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.5.tgz#e25c10a186517cd6c82dd19ea018f07e01e39015"
|
||||
integrity sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==
|
||||
|
||||
d3-ease@1, d3-ease@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.5.tgz#8ce59276d81241b1b72042d6af2d40e76d936ffb"
|
||||
integrity sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==
|
||||
|
||||
d3-format@1:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.2.tgz#6a96b5e31bcb98122a30863f7d92365c00603562"
|
||||
|
@ -6528,7 +6538,7 @@ d3-scale@^1.0.7:
|
|||
d3-time "1"
|
||||
d3-time-format "2"
|
||||
|
||||
d3-selection@^1.3.0:
|
||||
d3-selection@^1.1.0, d3-selection@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.0.tgz#ab9ac1e664cf967ebf1b479cc07e28ce9908c474"
|
||||
integrity sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg==
|
||||
|
@ -6545,6 +6555,11 @@ d3-time@1:
|
|||
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.11.tgz#1d831a3e25cd189eb256c17770a666368762bbce"
|
||||
integrity sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw==
|
||||
|
||||
d3-timer@1:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.9.tgz#f7bb8c0d597d792ff7131e1c24a36dd471a471ba"
|
||||
integrity sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==
|
||||
|
||||
d3-tip@^0.9.1:
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-tip/-/d3-tip-0.9.1.tgz#84e6d331c4e6650d80c5228a07e41820609ab64b"
|
||||
|
@ -6553,6 +6568,18 @@ d3-tip@^0.9.1:
|
|||
d3-collection "^1.0.4"
|
||||
d3-selection "^1.3.0"
|
||||
|
||||
d3-transition@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.2.0.tgz#f538c0e21b2aa1f05f3e965f8567e81284b3b2b8"
|
||||
integrity sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==
|
||||
dependencies:
|
||||
d3-color "1"
|
||||
d3-dispatch "1"
|
||||
d3-ease "1"
|
||||
d3-interpolate "1"
|
||||
d3-selection "^1.1.0"
|
||||
d3-timer "1"
|
||||
|
||||
dag-map@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/dag-map/-/dag-map-2.0.2.tgz#9714b472de82a1843de2fba9b6876938cab44c68"
|
||||
|
|
Loading…
Reference in New Issue