From 3fa19528d9c4d871ae0bd22861ee017b2bb0283b Mon Sep 17 00:00:00 2001 From: David Miculit Date: Tue, 7 Jul 2026 14:38:18 +0300 Subject: [PATCH] feat: highlight the last clicked 'open graph' button --- ui/css/perf.css | 5 ++++ ui/perfherder/alerts/AlertTable.jsx | 6 +++++ ui/perfherder/alerts/AlertTableRow.jsx | 8 ++++-- ui/perfherder/alerts/AlertsViewControls.jsx | 27 +++++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/ui/css/perf.css b/ui/css/perf.css index 12ee0d855de..81f559a0100 100644 --- a/ui/css/perf.css +++ b/ui/css/perf.css @@ -639,6 +639,11 @@ li.pagination-active.active > button { padding: 3px; } +.graph-link-active { + background-color: #d3d3d3; + border-radius: 3px; +} + .due-date-container { width: 50px; } diff --git a/ui/perfherder/alerts/AlertTable.jsx b/ui/perfherder/alerts/AlertTable.jsx index 9099fa1c859..312fcfbe05c 100644 --- a/ui/perfherder/alerts/AlertTable.jsx +++ b/ui/perfherder/alerts/AlertTable.jsx @@ -312,6 +312,8 @@ export default class AlertTable extends React.Component { updateViewState, modifyAlert = undefined, performanceTags, + lastClickedGraphAlertId, + setLastClickedGraphAlertId, } = this.props; const { alertSummary, @@ -448,6 +450,8 @@ export default class AlertTable extends React.Component { updateViewState={updateViewState} modifyAlert={modifyAlert} fetchAlertSummaries={fetchAlertSummaries} + lastClickedGraphAlertId={lastClickedGraphAlertId} + setLastClickedGraphAlertId={setLastClickedGraphAlertId} /> ))} {filteredAndSortedAlerts.length > @@ -462,6 +466,8 @@ export default class AlertTable extends React.Component { updateViewState={updateViewState} modifyAlert={modifyAlert} fetchAlertSummaries={fetchAlertSummaries} + lastClickedGraphAlertId={lastClickedGraphAlertId} + setLastClickedGraphAlertId={setLastClickedGraphAlertId} /> )} {downstreamIdsLength > 0 && ( diff --git a/ui/perfherder/alerts/AlertTableRow.jsx b/ui/perfherder/alerts/AlertTableRow.jsx index 08e76c4769e..a9b20ebc95f 100644 --- a/ui/perfherder/alerts/AlertTableRow.jsx +++ b/ui/perfherder/alerts/AlertTableRow.jsx @@ -362,7 +362,7 @@ export default class AlertTableRow extends React.Component { } render() { - const { user = null, alert, alertSummary } = this.props; + const { user = null, alert, alertSummary, lastClickedGraphAlertId, setLastClickedGraphAlertId } = this.props; const { starred, checkboxSelected, icons } = this.state; const { repository, framework, revision } = alertSummary; @@ -384,6 +384,7 @@ export default class AlertTableRow extends React.Component { ? `Classified by ${alert.classifier_email}` : 'Classified automatically'; const bookmarkClass = starred ? 'visible' : ''; + const graphActive = lastClickedGraphAlertId != null && lastClickedGraphAlertId === alert.id; const noiseProfile = alert.noise_profile || 'N\\A'; const noiseProfileTooltip = alert.noise_profile ? noiseProfiles[alert.noise_profile.replace('/', '')] @@ -465,7 +466,10 @@ export default class AlertTableRow extends React.Component { )} target="_blank" rel="noopener noreferrer" - className="text-dark button btn border p-0 border-0 bg-transparent" + onClick={() => setLastClickedGraphAlertId(alert.id)} + className={`text-dark button btn border p-0 border-0 + ${graphActive ? 'graph-link-active' : 'bg-transparent'}` + } aria-label="graph-link" > diff --git a/ui/perfherder/alerts/AlertsViewControls.jsx b/ui/perfherder/alerts/AlertsViewControls.jsx index b5c17e024b3..10ea8d1a4b0 100644 --- a/ui/perfherder/alerts/AlertsViewControls.jsx +++ b/ui/perfherder/alerts/AlertsViewControls.jsx @@ -17,6 +17,7 @@ export default class AlertsViewControls extends React.Component { disableHideDownstream: ['invalid', 'reassigned', 'downstream', 'infra'].includes( filters.status, ), + lastClickedGraphAlertId: null, }; } @@ -56,6 +57,30 @@ export default class AlertsViewControls extends React.Component { setFiltersState({ framework }); }; + setLastClickedGraphAlertId = (alertId) => { + this.setState({ lastClickedGraphAlertId: alertId }); + }; + + componentDidMount() { + document.addEventListener('visibilitychange', this.handleVisibilityChange); + } + + componentWillUnmount() { + document.removeEventListener('visibilitychange', this.handleVisibilityChange); + clearTimeout(this.graphHighlightTimer); + } + + handleVisibilityChange = () => { + if (document.visibilityState === 'hidden') { + clearTimeout(this.graphHighlightTimer); + } else if (this.state.lastClickedGraphAlertId != null) { + clearTimeout(this.graphHighlightTimer); + this.graphHighlightTimer = setTimeout( + () => this.setState({ lastClickedGraphAlertId: null }), 3000, + ); + } + }; + render() { const { alertSummaries = [], @@ -167,6 +192,8 @@ export default class AlertsViewControls extends React.Component { }} alertSummary={alertSummary} fetchAlertSummaries={fetchAlertSummaries} + lastClickedGraphAlertId={this.state.lastClickedGraphAlertId} + setLastClickedGraphAlertId={this.setLastClickedGraphAlertId} user={user} {...this.props} />