diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-07-05 15:55:14 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-07-13 14:34:17 +0200 |
commit | bd28f67e3f5250a5e4ac5706eeb555f416e15782 (patch) | |
tree | be42afce0d9be47b147fda149bb66f4cd7e44f5d /server | |
parent | 223c274b837e855af0dc8c6fa7be3ea591f34feb (diff) | |
download | sonarqube-bd28f67e3f5250a5e4ac5706eeb555f416e15782.tar.gz sonarqube-bd28f67e3f5250a5e4ac5706eeb555f416e15782.zip |
SONAR-9415 Click on the graph or on the list to see the matching analysis on project activity page
Diffstat (limited to 'server')
10 files changed, 212 insertions, 109 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js index a2aa8084c66..9c98102a95e 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js @@ -25,7 +25,12 @@ import { throttle } from 'lodash'; import ProjectActivityAnalysis from './ProjectActivityAnalysis'; import FormattedDate from '../../../components/ui/FormattedDate'; import { translate } from '../../../helpers/l10n'; -import { activityQueryChanged, getAnalysesByVersionByDay } from '../utils'; +import { + activityQueryChanged, + getAnalysesByVersionByDay, + selectedDateQueryChanged +} from '../utils'; +import type { RawQuery } from '../../../helpers/query'; import type { Analysis, Query } from '../types'; type Props = { @@ -39,13 +44,15 @@ type Props = { deleteAnalysis: (analysis: string) => Promise<*>, deleteEvent: (analysis: string, event: string) => Promise<*>, loading: boolean, - query: Query + query: Query, + updateQuery: RawQuery => void }; export default class ProjectActivityAnalysesList extends React.PureComponent { - scrollContainer: HTMLElement; + analyses: HTMLCollection<HTMLElement>; badges: HTMLCollection<HTMLElement>; props: Props; + scrollContainer: HTMLElement; constructor(props: Props) { super(props); @@ -54,22 +61,36 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { componentDidMount() { this.badges = document.getElementsByClassName('project-activity-version-badge'); + this.analyses = document.getElementsByClassName('project-activity-analysis'); } componentDidUpdate(prevProps: Props) { - if (prevProps.analysis !== this.props.analyses && this.scrollContainer) { - if (activityQueryChanged(prevProps.query, this.props.query)) { - this.scrollContainer.scrollTop = 0; - } - for (let i = 1; i < this.badges.length; i++) { - this.badges[i].removeAttribute('originOffsetTop'); - this.badges[i].classList.remove('sticky'); + if (this.scrollContainer) { + const selectedDateChanged = selectedDateQueryChanged(prevProps.query, this.props.query); + if (selectedDateChanged || prevProps.analysis !== this.props.analyses) { + if (selectedDateChanged && this.props.query.selectedDate) { + const selectedDate = this.props.query.selectedDate.valueOf(); + for (let i = 1; i < this.analyses.length; i++) { + if (Number(this.analyses[i].getAttribute('data-date')) === selectedDate) { + const containerHeight = this.scrollContainer.offsetHeight - 100; + const scrollDiff = Math.abs( + this.scrollContainer.scrollTop - this.analyses[i].offsetTop + ); + // Center only the extremities and the ones outside of the container + if (scrollDiff > containerHeight || scrollDiff < 100) { + this.resetScrollTop(this.analyses[i].offsetTop - containerHeight / 2); + } + break; + } + } + } else if (activityQueryChanged(prevProps.query, this.props.query)) { + this.resetScrollTop(0, true); + } } - this.handleScroll(); } } - handleScroll = () => { + updateStickyBadges = (forceBadgeAlignement?: boolean) => { if (this.scrollContainer && this.badges) { const scrollTop = this.scrollContainer.scrollTop; if (scrollTop != null) { @@ -78,11 +99,12 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { const badge = this.badges[i]; let originOffsetTop = badge.getAttribute('originOffsetTop'); if (originOffsetTop == null) { + // Set the originOffsetTop attribute, to avoid using getBoundingClientRect originOffsetTop = badge.offsetTop; badge.setAttribute('originOffsetTop', originOffsetTop.toString()); } if (Number(originOffsetTop) < scrollTop + 18 + i * 2) { - if (!badge.classList.contains('sticky')) { + if (forceBadgeAlignement && !badge.classList.contains('sticky')) { newScrollTop = originOffsetTop; } badge.classList.add('sticky'); @@ -90,12 +112,26 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { badge.classList.remove('sticky'); } } - if (newScrollTop != null) { + if (forceBadgeAlignement && newScrollTop != null) { this.scrollContainer.scrollTop = newScrollTop - 6; } } } }; + handleScroll = () => this.updateStickyBadges(true); + + resetScrollTop = (newScrollTop: number, forceBadgeAlignement?: boolean) => { + this.scrollContainer.scrollTop = newScrollTop; + for (let i = 1; i < this.badges.length; i++) { + this.badges[i].removeAttribute('originOffsetTop'); + this.badges[i].classList.remove('sticky'); + } + this.updateStickyBadges(forceBadgeAlignement); + }; + + updateSelectedDate = (date: Date) => { + this.props.updateQuery({ selectedDate: date }); + }; render() { if (this.props.analyses.length === 0) { @@ -110,6 +146,9 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { const firstAnalysisKey = this.props.analyses[0].key; const byVersionByDay = getAnalysesByVersionByDay(this.props.analyses); + const selectedDate = this.props.query.selectedDate + ? this.props.query.selectedDate.valueOf() + : null; return ( <ul className={classNames('project-activity-versions-list', this.props.className)} @@ -145,6 +184,8 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { deleteEvent={this.props.deleteEvent} isFirst={analysis.key === firstAnalysisKey} key={analysis.key} + selected={analysis.date.valueOf() === selectedDate} + updateSelectedDate={this.updateSelectedDate} /> ))} </ul> diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js index 1cd17b22bb9..4ccb7cb9718 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js @@ -19,6 +19,7 @@ */ // @flow import React from 'react'; +import classNames from 'classnames'; import Events from './Events'; import AddEventForm from './forms/AddEventForm'; import RemoveAnalysisForm from './forms/RemoveAnalysisForm'; @@ -34,72 +35,87 @@ type Props = { changeEvent: (event: string, name: string) => Promise<*>, deleteAnalysis: (analysis: string) => Promise<*>, deleteEvent: (analysis: string, event: string) => Promise<*>, - isFirst: boolean + isFirst: boolean, + selected: boolean, + updateSelectedDate: Date => void }; -export default function ProjectActivityAnalysis(props: Props) { - const { date, events } = props.analysis; - const { isFirst, canAdmin } = props; - const analysisTitle = translate('project_activity.analysis'); - const hasVersion = events.find(event => event.category === 'VERSION') != null; - return ( - <li className="project-activity-analysis clearfix"> - <div className="project-activity-time spacer-right"> - <FormattedDate date={date} format="LT" tooltipFormat="LTS" /> - </div> - <div - className="project-activity-analysis-icon little-spacer-top big-spacer-right" - title={analysisTitle} - /> +export default class ProjectActivityAnalysis extends React.PureComponent { + props: Props; - {canAdmin && - <div className="project-activity-analysis-actions spacer-left"> - <div className="dropdown display-inline-block"> - <button - className="js-analysis-actions button-small button-compact dropdown-toggle" - data-toggle="dropdown"> - <i className="icon-settings" /> - {' '} - <i className="icon-dropdown" /> - </button> - <ul className="dropdown-menu dropdown-menu-right"> - {!hasVersion && + handleClick = () => this.props.updateSelectedDate(this.props.analysis.date); + + render() { + const { analysis, isFirst, canAdmin } = this.props; + const { date, events } = analysis; + const analysisTitle = translate('project_activity.analysis'); + const hasVersion = events.find(event => event.category === 'VERSION') != null; + return ( + <li + className={classNames('project-activity-analysis clearfix', { + selected: this.props.selected + })} + data-date={date.valueOf()} + onClick={this.handleClick} + role="listitem" + tabIndex="0"> + <div className="project-activity-time spacer-right"> + <FormattedDate date={date} format="LT" tooltipFormat="LTS" /> + </div> + <div + className="project-activity-analysis-icon little-spacer-top big-spacer-right" + title={analysisTitle} + /> + + {canAdmin && + <div className="project-activity-analysis-actions spacer-left"> + <div className="dropdown display-inline-block"> + <button + className="js-analysis-actions button-small button-compact dropdown-toggle" + data-toggle="dropdown"> + <i className="icon-settings" /> + {' '} + <i className="icon-dropdown" /> + </button> + <ul className="dropdown-menu dropdown-menu-right"> + {!hasVersion && + <li> + <AddEventForm + addEvent={this.props.addVersion} + analysis={analysis} + addEventButtonText="project_activity.add_version" + /> + </li>} <li> <AddEventForm - addEvent={props.addVersion} - analysis={props.analysis} - addEventButtonText="project_activity.add_version" - /> - </li>} - <li> - <AddEventForm - addEvent={props.addCustomEvent} - analysis={props.analysis} - addEventButtonText="project_activity.add_custom_event" - /> - </li> - {!isFirst && <li role="separator" className="divider" />} - {!isFirst && - <li> - <RemoveAnalysisForm - analysis={props.analysis} - deleteAnalysis={props.deleteAnalysis} + addEvent={this.props.addCustomEvent} + analysis={analysis} + addEventButtonText="project_activity.add_custom_event" /> - </li>} - </ul> - </div> - </div>} + </li> + {!isFirst && <li role="separator" className="divider" />} + {!isFirst && + <li> + <RemoveAnalysisForm + analysis={analysis} + deleteAnalysis={this.props.deleteAnalysis} + /> + </li>} + </ul> + </div> + </div>} - {events.length > 0 && - <Events - analysis={props.analysis.key} - canAdmin={canAdmin} - changeEvent={props.changeEvent} - deleteEvent={props.deleteEvent} - events={events} - isFirst={props.isFirst} - />} + {events.length > 0 && + <Events + analysis={analysis.key} + canAdmin={canAdmin} + changeEvent={this.props.changeEvent} + deleteEvent={this.props.deleteEvent} + events={events} + isFirst={this.props.isFirst} + />} - </li> - ); + </li> + ); + } } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js index dc7d213f30f..46b6dd4fe53 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js @@ -112,6 +112,7 @@ export default class ProjectActivityApp extends React.PureComponent { deleteEvent={this.props.deleteEvent} loading={this.props.loading} query={this.props.query} + updateQuery={this.props.updateQuery} /> </div> <div className="project-activity-layout-page-main"> diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js index 4baa9a82dba..03605c8114c 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js @@ -40,7 +40,6 @@ type Props = { }; type State = { - selectedDate?: ?Date, graphStartDate: ?Date, graphEndDate: ?Date, series: Array<Serie> @@ -97,7 +96,7 @@ export default class ProjectActivityGraphs extends React.PureComponent { } }; - updateSelectedDate = (selectedDate: ?Date) => this.setState({ selectedDate }); + updateSelectedDate = (selectedDate: ?Date) => this.props.updateQuery({ selectedDate }); updateGraphZoom = (graphStartDate: ?Date, graphEndDate: ?Date) => { if (graphEndDate != null && graphStartDate != null) { @@ -138,7 +137,7 @@ export default class ProjectActivityGraphs extends React.PureComponent { measuresHistory={this.props.measuresHistory} metricsType={metricsType} project={this.props.project} - selectedDate={this.state.selectedDate} + selectedDate={this.props.query.selectedDate} series={series} updateGraphZoom={this.updateGraphZoom} updateSelectedDate={this.updateSelectedDate} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js b/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js index fbea7a4d2c8..d07737c8d02 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js @@ -47,6 +47,7 @@ type Props = { }; type State = { + selectedDate?: ?Date, tooltipIdx: ?number, tooltipXPos: ?number }; @@ -86,8 +87,8 @@ export default class StaticGraphs extends React.PureComponent { hasSeriesData = () => some(this.props.series, serie => serie.data && serie.data.length > 2); - updateTooltipPos = (tooltipXPos: ?number, tooltipIdx: ?number) => - this.setState({ tooltipXPos, tooltipIdx }); + updateTooltip = (selectedDate: ?Date, tooltipXPos: ?number, tooltipIdx: ?number) => + this.setState({ selectedDate, tooltipXPos, tooltipIdx }); render() { const { loading } = this.props; @@ -111,8 +112,8 @@ export default class StaticGraphs extends React.PureComponent { </div> ); } - - const { graph, selectedDate, series } = this.props; + const { selectedDate, tooltipIdx, tooltipXPos } = this.state; + const { graph, series } = this.props; return ( <div className="project-activity-graph-container"> <StaticGraphsLegend series={series} /> @@ -129,16 +130,16 @@ export default class StaticGraphs extends React.PureComponent { formatYTick={this.formatValue} leakPeriodDate={this.props.leakPeriodDate} metricType={this.props.metricsType} - selectedDate={selectedDate} + selectedDate={this.props.selectedDate} series={series} showAreas={['coverage', 'duplications'].includes(graph)} startDate={this.props.graphStartDate} updateSelectedDate={this.props.updateSelectedDate} - updateTooltipPos={this.updateTooltipPos} + updateTooltip={this.updateTooltip} updateZoom={this.props.updateGraphZoom} /> {selectedDate != null && - this.state.tooltipXPos != null && + tooltipXPos != null && <GraphsTooltips formatValue={this.formatValue} graph={graph} @@ -146,8 +147,8 @@ export default class StaticGraphs extends React.PureComponent { measuresHistory={this.props.measuresHistory} selectedDate={selectedDate} series={series} - tooltipIdx={this.state.tooltipIdx} - tooltipPos={this.state.tooltipXPos} + tooltipIdx={tooltipIdx} + tooltipPos={tooltipXPos} />} </div> )} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap index b97c9aa9054..91f1dcdc0e4 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap @@ -117,6 +117,7 @@ exports[`should render correctly 1`] = ` "project": "org.sonarsource.sonarqube:sonarqube", } } + updateQuery={[Function]} /> </div> <div diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css index 1702b791a09..903bd9ff4f9 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css @@ -119,6 +119,14 @@ cursor: pointer; } +.project-activity-analysis.selected { + background-color: #ecf6fe; +} + +.project-activity-analysis:focus { + outline: none; +} + .project-activity-analysis:hover { background-color: #ecf6fe; } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/types.js b/server/sonar-web/src/main/js/apps/projectActivity/types.js index 106fb632866..f3c75fa8535 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/types.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/types.js @@ -53,5 +53,6 @@ export type Query = { from?: Date, graph: string, project: string, - to?: Date + to?: Date, + selectedDate?: Date }; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.js b/server/sonar-web/src/main/js/apps/projectActivity/utils.js index 50b3ab32929..5e04dcc5436 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/utils.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.js @@ -49,16 +49,25 @@ export const GRAPHS_METRICS = { }; export const activityQueryChanged = (prevQuery: Query, nextQuery: Query): boolean => - prevQuery.category !== nextQuery.category || - prevQuery.from !== nextQuery.from || - prevQuery.to !== nextQuery.to; + prevQuery.category !== nextQuery.category || datesQueryChanged(prevQuery, nextQuery); -export const datesQueryChanged = (prevQuery: Query, nextQuery: Query): boolean => - prevQuery.from !== nextQuery.from || prevQuery.to !== nextQuery.to; +export const datesQueryChanged = (prevQuery: Query, nextQuery: Query): boolean => { + const nextFrom = nextQuery.from ? nextQuery.from.valueOf() : null; + const previousFrom = prevQuery.from ? prevQuery.from.valueOf() : null; + const nextTo = nextQuery.to ? nextQuery.to.valueOf() : null; + const previousTo = prevQuery.to ? prevQuery.to.valueOf() : null; + return previousFrom !== nextFrom || previousTo !== nextTo; +}; export const historyQueryChanged = (prevQuery: Query, nextQuery: Query): boolean => prevQuery.graph !== nextQuery.graph; +export const selectedDateQueryChanged = (prevQuery: Query, nextQuery: Query): boolean => { + const nextSelectedDate = nextQuery.selectedDate ? nextQuery.selectedDate.valueOf() : null; + const previousSelectedDate = prevQuery.selectedDate ? prevQuery.selectedDate.valueOf() : null; + return nextSelectedDate !== previousSelectedDate; +}; + export const generateCoveredLinesMetric = ( uncoveredLines: MeasureHistory, measuresHistory: Array<MeasureHistory>, @@ -143,7 +152,8 @@ export const parseQuery = (urlQuery: RawQuery): Query => ({ from: parseAsDate(urlQuery['from']), graph: parseGraph(urlQuery['graph']), project: parseAsString(urlQuery['id']), - to: parseAsDate(urlQuery['to']) + to: parseAsDate(urlQuery['to']), + selectedDate: parseAsDate(urlQuery['selected_date']) }); export const serializeQuery = (query: Query): RawQuery => @@ -160,6 +170,7 @@ export const serializeUrlQuery = (query: Query): RawQuery => { from: serializeDate(query.from), graph: serializeGraph(query.graph), id: serializeString(query.project), - to: serializeDate(query.to) + to: serializeDate(query.to), + selected_date: serializeDate(query.selectedDate) }); }; diff --git a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js index fc2123d1fe4..413a53a78d3 100644 --- a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js +++ b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js @@ -50,7 +50,7 @@ type Props = { showEventMarkers?: boolean, startDate: ?Date, updateSelectedDate?: (selectedDate: ?Date) => void, - updateTooltipPos?: (tooltipXPos: ?number, tooltipIdx: ?number) => void, + updateTooltip?: (selectedDate: ?Date, tooltipXPos: ?number, tooltipIdx: ?number) => void, updateZoom?: (start: ?Date, endDate: ?Date) => void, zoomSpeed: number }; @@ -59,6 +59,7 @@ type State = { maxXRange: Array<number>, mouseOver?: boolean, mouseOverlayPos?: { [string]: number }, + selectedDate: ?Date, selectedDateXPos: ?number, selectedDateIdx: ?number, yScale: Scale, @@ -78,8 +79,9 @@ export default class AdvancedTimeline extends React.PureComponent { constructor(props: Props) { super(props); const scales = this.getScales(props); - this.state = { ...scales, ...this.getSelectedDatePos(scales.xScale, props.selectedDate) }; - this.updateSelectedDate = throttle(this.updateSelectedDate, 40); + const selectedDatePos = this.getSelectedDatePos(scales.xScale, props.selectedDate); + this.state = { ...scales, ...selectedDatePos }; + this.updateTooltipPos = throttle(this.updateTooltipPos, 40); } componentWillReceiveProps(nextProps: Props) { @@ -100,8 +102,9 @@ export default class AdvancedTimeline extends React.PureComponent { const xScale = scales ? scales.xScale : this.state.xScale; const selectedDatePos = this.getSelectedDatePos(xScale, nextProps.selectedDate); this.setState({ ...scales, ...selectedDatePos }); - if (nextProps.updateTooltipPos) { - nextProps.updateTooltipPos( + if (nextProps.updateTooltip) { + nextProps.updateTooltip( + selectedDatePos.selectedDate, selectedDatePos.selectedDateXPos, selectedDatePos.selectedDateIdx ); @@ -158,12 +161,13 @@ export default class AdvancedTimeline extends React.PureComponent { this.props.series.some(serie => serie.data[idx].y || serie.data[idx].y === 0) ) { return { + selectedDate, selectedDateXPos: xScale(selectedDate), selectedDateIdx: idx }; } } - return { selectedDateXPos: null, selectedDateIdx: null }; + return { selectedDate: null, selectedDateXPos: null, selectedDateIdx: null }; }; getEventMarker = (size: number) => { @@ -197,31 +201,43 @@ export default class AdvancedTimeline extends React.PureComponent { handleMouseMove = (evt: MouseEvent & { target: HTMLElement }) => { const parentBbox = this.getMouseOverlayPos(evt.target); - this.updateSelectedDate(evt.pageX - parentBbox.left); + this.updateTooltipPos(evt.pageX - parentBbox.left); }; handleMouseEnter = () => this.setState({ mouseOver: true }); handleMouseOut = (evt: Event & { relatedTarget: HTMLElement }) => { - const { updateSelectedDate } = this.props; + const { updateTooltip } = this.props; const targetClass = evt.relatedTarget && typeof evt.relatedTarget.className === 'string' ? evt.relatedTarget.className : ''; if ( - !updateSelectedDate || + !updateTooltip || targetClass.includes('bubble-popup') || targetClass.includes('graph-tooltip') ) { return; } - this.setState({ mouseOver: false }); - updateSelectedDate(null); + this.setState({ + mouseOver: false, + selectedDate: null, + selectedDateXPos: null, + selectedDateIdx: null + }); + updateTooltip(null, null, null); }; - updateSelectedDate = (xPos: number) => { + handleClick = () => { const { updateSelectedDate } = this.props; + if (updateSelectedDate) { + updateSelectedDate(this.state.selectedDate); + } + }; + + updateTooltipPos = (xPos: number) => { const firstSerie = this.props.series[0]; - if (this.state.mouseOver && firstSerie && updateSelectedDate) { + if (this.state.mouseOver && firstSerie) { + const { updateTooltip } = this.props; const date = this.state.xScale.invert(xPos); const bisectX = bisector(d => d.x).right; let idx = bisectX(firstSerie.data, date); @@ -231,7 +247,12 @@ export default class AdvancedTimeline extends React.PureComponent { if (!nextPoint || (previousPoint && date - previousPoint.x <= nextPoint.x - date)) { idx--; } - updateSelectedDate(firstSerie.data[idx].x); + const selectedDate = firstSerie.data[idx].x; + const xPos = this.state.xScale(selectedDate); + this.setState({ selectedDate, selectedDateXPos: xPos, selectedDateIdx: idx }); + if (updateTooltip) { + updateTooltip(selectedDate, xPos, idx); + } } } }; @@ -428,11 +449,14 @@ export default class AdvancedTimeline extends React.PureComponent { if (zoomEnabled) { mouseEvents.onWheel = this.handleWheel; } - if (this.props.updateSelectedDate) { + if (this.props.updateTooltip) { mouseEvents.onMouseEnter = this.handleMouseEnter; mouseEvents.onMouseMove = this.handleMouseMove; mouseEvents.onMouseOut = this.handleMouseOut; } + if (this.props.updateSelectedDate) { + mouseEvents.onClick = this.handleClick; + } return ( <rect className="chart-mouse-events-overlay" |