Skip to content
Snippets Groups Projects
Commit 66a7bd05 authored by Williams, Darius Shamar's avatar Williams, Darius Shamar
Browse files

Merge branch 'wdfn-767' into 'main'

Wdfn 767 - Convert cursor slider to vue

See merge request !413
parents bd788978 c208dbd8
No related branches found
No related tags found
1 merge request!413Wdfn 767 - Convert cursor slider to vue
Showing
with 170 additions and 163 deletions
......@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased](https://code.usgs.gov/wma/iow/waterdataui/-/compare/waterdataui-1.4.0...main)
### Changed
- The cursor slider component was converted to vue.
## [1.4.0](https://code.usgs.gov/wma/iow/waterdataui/-/compare/waterdataui-1.4.0...waterdataui-1.3.0) - 2022-07-21
### Fixed
......
<template>
<GraphBrush />
<div>
<CursorSlider />
<GraphBrush />
</div>
</template>
<script>
import GraphBrush from './vue-components/graph-brush.vue';
import CursorSlider from './vue-components/cursor-slider.vue';
export default {
name: 'HydrographApp',
components: {
GraphBrush
GraphBrush,
CursorSlider
}
};
</script>
\ No newline at end of file
import {sliderTop} from 'd3-simple-slider';
import {createStructuredSelector} from 'reselect';
import {link} from 'ui/lib/d3-redux';
import {getGraphCursorOffset} from 'ml/selectors/hydrograph-state-selector';
import {setGraphCursorOffset} from 'ml/store/hydrograph-state';
import {getMainXScale} from './selectors/scales';
import {getMainLayout} from './selectors/layout';
/*
* Renders the cursor slider and sets up the redux event handlers to update the slider
* when needed.
* @param {D3 selection} svg
* @param {Redux store} store
*/
export const drawCursorSlider = function(svg, store) {
const slider = sliderTop()
.displayValue(false)
.ticks(0);
svg.append('g')
.attr('class', 'cursor-slider-group')
.attr('ga-on', 'click')
.attr('ga-event-category', 'hydrograph-interaction')
.attr('ga-event-action', 'clickOnSlider')
.call(link(store, (group, {xScale, layout}, store ) => {
const [startMillis, endMillis] = xScale.domain();
const [startX, endX] = xScale.range();
const cursorOffset = getGraphCursorOffset(store.getState());
slider
.min(startMillis)
.max(endMillis)
.width(endX - startX);
slider.silentValue(cursorOffset ? slider.min() + cursorOffset : slider.max());
group
.attr('transform', `translate(${layout.margin.left},15)`)
.call(slider);
slider.on('onchange', (val) => {
store.dispatch(setGraphCursorOffset(val - startMillis));
});
}, createStructuredSelector({
xScale: getMainXScale('current'),
layout: getMainLayout
}), store))
.call(link(store, (group, cursorOffset) => {
slider.silentValue(cursorOffset ? slider.min() + cursorOffset : slider.max());
}, getGraphCursorOffset));
};
import {select} from 'd3-selection';
import config from 'ui/config';
import * as utils from 'ui/utils';
import {configureStore} from 'ml/store';
import {TEST_PRIMARY_IV_DATA, TEST_CURRENT_TIME_RANGE} from './mock-hydrograph-state';
import {drawCursorSlider} from './cursor-slider';
describe('monitoring-location/components/hydrograph/cursor-slider', () => {
utils.mediaQuery = jest.fn().mockReturnValue(true);
config.locationTimeZone = 'America/Chicago';
const TEST_STATE = {
hydrographData: {
primaryIVData: TEST_PRIMARY_IV_DATA,
currentTimeRAnge: TEST_CURRENT_TIME_RANGE
},
hydrographState: {
graphCursorOffset: 500000
},
ui: {
windowWidth: 1300,
width: 990
}
};
describe('drawCursorSlider', () => {
let svg;
let store;
beforeEach(() => {
svg = select('body').append('svg');
store = configureStore(TEST_STATE);
});
afterEach(() => {
svg.remove();
});
it('Creates a cursor slider', () => {
drawCursorSlider(svg, store);
const sliderGroup = svg.selectAll('.cursor-slider-group');
expect(sliderGroup.size()).toBe(1);
expect(sliderGroup.selectAll('.slider').size()).toBe(1);
});
});
});
\ No newline at end of file
......@@ -32,7 +32,6 @@ import {showDataIndicators} from './data-indicator';
import {drawTimeSeriesLegend} from './legend';
import {initializeTimeSeriesGraph, drawTimeSeriesGraphData} from './time-series-graph';
import {initializeTooltipCursorSlider, drawTooltipCursorSlider} from './tooltip';
import DataTablesApp from './DataTablesApp.vue';
import GraphControlsApp from './GraphControlsApp.vue';
......@@ -142,7 +141,7 @@ export const attachToNode = function(store,
graphContainer.call(initializeTimeSeriesGraph, store, siteno, agencyCd, sitename, thisShowMLName, !thisShowOnlyGraph);
showDataIndicators(true, store);
if (!showOnlyGraph) {
initializeTooltipCursorSlider(graphContainer, store);
//TODO: The tooltips, legend and the main hydrograph can be added to the HydrographApp.
// The main hydrograph should be converted to a Vue component last. As part of that task we
// will figure out how to handle the loading indicator and the no data overlay
......@@ -196,10 +195,6 @@ export const attachToNode = function(store,
showDataIndicators(false, store);
graphContainer.call(drawTimeSeriesGraphData, store, !thisShowOnlyGraph);
if (!thisShowOnlyGraph) {
graphContainer
.call(drawTooltipCursorSlider, store);
}
legendControlsContainer.call(drawTimeSeriesLegend, store);
if (!thisShowOnlyGraph) {
......
......@@ -15,7 +15,6 @@ import {getCursorTime, getIVDataCursorPoint, getIVDataTooltipPoint, getGroundwat
import {getMainLayout} from './selectors/layout';
import {getMainXScale, getMainYScale} from './selectors/scales';
import {getPrimaryParameter} from './selectors/time-series-data';
import {drawCursorSlider} from './cursor-slider';
const getIVDataTooltipTextInfo = function(point, dataKind, unitCode) {
......@@ -165,28 +164,3 @@ export const drawTooltipFocus = function(elem, store) {
setGraphCursorOffset)
);
};
/*
* Initial rendering of the tooltip slider
* @param {D3 selection} elem
* @param {Redux store} store
*/
export const initializeTooltipCursorSlider = function(elem, store) {
elem.append('svg')
.classed('cursor-slider-svg', true)
.attr('xmlns', 'http://www.w3.org/2000/svg')
.call(link(store, (elem, layout) => {
elem.attr('viewBox', `0 0 ${layout.width + layout.margin.left + layout.margin.right} 25`);
}, getMainLayout));
};
/*
* Renders the cursor slider used to move the tooltip focus and sets up handlers
* to update the slider as the tooltip focus changes.
* @param {D3 selection} elem
* @param {Redux store} store
*/
export const drawTooltipCursorSlider = function(elem, store) {
elem.select('.cursor-slider-svg')
.call(drawCursorSlider, store);
};
......@@ -6,7 +6,7 @@ import * as utils from 'ui/utils';
import {configureStore} from 'ml/store';
import {TEST_GW_LEVELS, TEST_PRIMARY_IV_DATA, TEST_SECONDARY_IV_DATA, TEST_CURRENT_TIME_RANGE} from './mock-hydrograph-state';
import {drawTooltipText, drawTooltipFocus, drawTooltipCursorSlider, initializeTooltipCursorSlider} from './tooltip';
import {drawTooltipText, drawTooltipFocus} from './tooltip';
describe('monitoring-location/components/hydrograph/tooltip module', () => {
utils.mediaQuery = jest.fn().mockReturnValue(true);
......@@ -146,27 +146,4 @@ describe('monitoring-location/components/hydrograph/tooltip module', () => {
expect(svg.select('.focus-overlay').size()).toBe(1);
});
});
describe('initializeTooltipCursorSlider and drawTooltipCursorSlider', () => {
let div;
beforeEach(() => {
div = select('body').append('div');
});
afterEach(() => {
div.remove();
});
it('should render the cursor slider', () => {
let store = configureStore(TEST_STATE);
initializeTooltipCursorSlider(div, store);
drawTooltipCursorSlider(div, store);
const sliderSvg = div.selectAll('.cursor-slider-svg');
const slider = sliderSvg.selectAll('.slider');
expect(sliderSvg.size()).toBe(1);
expect(slider.size()).toBe(1);
});
});
});
import {mount} from '@vue/test-utils';
import {bindActionCreators} from 'redux';
import ReduxConnectVue from 'redux-connect-vue';
import {createStructuredSelector} from 'reselect';
import {configureStore} from 'ml/store';
import * as utils from 'ui/utils';
import {Actions as uiActions} from 'ml/store/ui-state';
import {TEST_PRIMARY_IV_DATA, TEST_CURRENT_TIME_RANGE} from '../mock-hydrograph-state';
import CursorSlider from './cursor-slider.vue';
describe('monitoring-location/components/hydrograph/cursor-slider', () => {
const TEST_STATE = {
hydrographData: {
primaryIVData: TEST_PRIMARY_IV_DATA,
currentTimeRAnge: TEST_CURRENT_TIME_RANGE
},
hydrographState: {
graphCursorOffset: 500000
},
ui: {
windowWidth: 1300,
width: 990
}
};
let store;
let wrapper;
describe('drawCursorSlider', () => {
utils.mediaQuery = jest.fn().mockReturnValue(true);
beforeEach(() => {
store = configureStore(TEST_STATE);
wrapper = mount(CursorSlider, {
global: {
plugins: [
[ReduxConnectVue, {
store,
mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch),
mapStateToPropsFactory: createStructuredSelector
}]
],
provide: {
store: store
}
}
});
});
it('Creates a cursor slider', async() => {
const sliderGroup = wrapper.findAll('.cursor-slider-group');
expect(sliderGroup).toHaveLength(1);
expect(wrapper.vm.group.getAttribute('transform')).toContain('translate');
});
it('Expects that changing the ui width will change the viewBox and transforms', async() => {
const viewBox = wrapper.find('.cursor-slider-svg').attributes('viewBox');
const sliderGroup = wrapper.find('.cursor-slider-group');
utils.mediaQuery.mockReturnValue(false);
store.dispatch(uiActions.resizeUI(400, 350));
await wrapper.vm.$nextTick();
expect(wrapper.find('.cursor-slider-svg').attributes('viewBox')).not.toEqual(viewBox);
expect(wrapper.find('.cursor-slider-group').attributes('translate')).not.toEqual(sliderGroup);
});
});
});
\ No newline at end of file
<template>
<svg
class="cursor-slider-svg"
xmlns="http://www.w3.org/2000/svg"
:viewBox="viewBox"
>
<g
ref="group"
class="cursor-slider-group"
ga-on="click"
ga-event-category="hydrograph-interaction"
ga-event-action="clickOnSlider"
:transform="transform"
/>
</svg>
</template>
<script>
import {useActions, useState} from 'redux-connect-vue';
import {computed, ref, watchEffect} from 'vue';
import {sliderTop} from 'd3-simple-slider';
import {select} from 'd3-selection';
import {getGraphCursorOffset} from 'ml/selectors/hydrograph-state-selector';
import {setGraphCursorOffset} from 'ml/store/hydrograph-state';
import {getMainXScale} from '../selectors/scales';
import {getMainLayout} from '../selectors/layout';
export default {
name: 'CursorSlider',
setup() {
const state = useState({
layout: getMainLayout,
xScale: getMainXScale('current'),
cursorOffset: getGraphCursorOffset
});
const actions = useActions({
setGraphCursorOffset
});
const group = ref(null);
const transform = computed(() => {
return `translate(${state.layout.value.margin.left},15)`;
});
const viewBox = computed(() => {
return `0 0 ${state.layout.value.width + state.layout.value.margin.left + state.layout.value.margin.right} 25`;
});
const slider = sliderTop()
.displayValue(false)
.ticks(0);
watchEffect(() => {
const [startMillis, endMillis] = state.xScale.value.domain();
const [startX, endX] = state.xScale.value.range();
slider
.min(startMillis)
.max(endMillis)
.width(endX - startX);
slider.silentValue(state.cursorOffset.value ? slider.min() + state.cursorOffset.value : slider.max());
if (group.value) {
select(group.value).call(slider);
}
slider.on('onchange', (val) => {
actions.setGraphCursorOffset(val - startMillis);
});
});
watchEffect(() => {
slider.silentValue(state.cursorOffset.value ? slider.min() + state.cursorOffset.value : slider.max());
});
return {
...state,
viewBox,
group,
transform
};
}
};
</script>
\ No newline at end of file
......@@ -686,4 +686,4 @@ class TestGetDefaultParameterCode(TestCase):
self.assertEqual(get_default_parameter_code({}, {
'72019': {},
'65536': {}
}), '72019')
}), '72019')
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment