diff --git a/README.md b/README.md index 3e679a322..ce1e0ca0d 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,16 @@ If you do not want to host the JSON file publicly, you can follow [these steps]( **_Note:_** The JSON file parsing is using D3 library, so consult the [D3 documentation](https://github.com/d3/d3-request/blob/master/README.md#json) for the data format details. +### Deep linking + +BYOR supports two ways to deep link + +1 - You can use the optional query string parameter `blipId` to specify the blip to view. + +2 - You can use the optional query string parameter `quadrant` to specify the quadrant to view. Supported values are: `first`, `second`, `third`, `fourth`. + +_Note: blip id takes precedence when both are present_ + ### Building the radar Paste the URL in the input field on the home page. diff --git a/spec/util/urlUtils-spec.js b/spec/util/urlUtils-spec.js index f5737f589..1b5cec666 100644 --- a/spec/util/urlUtils-spec.js +++ b/spec/util/urlUtils-spec.js @@ -1,4 +1,10 @@ -const { constructSheetUrl, getDocumentOrSheetId, getSheetName } = require('../../src/util/urlUtils') +const { + constructSheetUrl, + getDocumentOrSheetId, + getSheetName, + getBlipIdFromUrl, + getQuadrantFromURL, +} = require('../../src/util/urlUtils') const queryParams = require('../../src/util/queryParamProcessor') jest.mock('../../src/util/queryParamProcessor') @@ -74,4 +80,48 @@ describe('Url Utils', () => { expect(sheetName).toEqual('sheetName') }) + + it('should return all if no blip id found in url', () => { + queryParams.mockReturnValue({ some: 'param' }) + delete window.location + window.location = Object.create(window) + window.location.href = 'https://thoughtworks.com/radar?sheet=radar' + window.location.search = '?' + const quadrant = getBlipIdFromUrl() + + expect(quadrant).toBeNull() + }) + + it('should return blip id if found in url', () => { + queryParams.mockReturnValue({ blipId: '50' }) + delete window.location + window.location = Object.create(window) + window.location.href = 'https://thoughtworks.com/radar?sheet=radar' + window.location.search = '?' + const quadrant = getBlipIdFromUrl() + + expect(quadrant).toBe(50) + }) + + it('should return all if no quadrant found in url', () => { + queryParams.mockReturnValue({ some: 'param' }) + delete window.location + window.location = Object.create(window) + window.location.href = 'https://thoughtworks.com/radar?sheet=radar' + window.location.search = '?' + const quadrant = getQuadrantFromURL() + + expect(quadrant).toBe('all') + }) + + it('should return quadrant if found in url', () => { + queryParams.mockReturnValue({ quadrant: 'FIRST' }) + delete window.location + window.location = Object.create(window) + window.location.href = 'https://thoughtworks.com/radar?sheet=radar' + window.location.search = '?' + const quadrant = getQuadrantFromURL() + + expect(quadrant).toBe('first') + }) }) diff --git a/src/graphing/components/quadrantTables.js b/src/graphing/components/quadrantTables.js index 5b10346ef..00557acce 100644 --- a/src/graphing/components/quadrantTables.js +++ b/src/graphing/components/quadrantTables.js @@ -115,31 +115,7 @@ function renderBlipDescription(blip, ring, quadrant, tip, groupBlipTooltipText) e.stopPropagation() } - const blipId = d3.select(targetElement).attr('data-blip-id') - highlightBlipInGraph(blipId) - - d3.selectAll('.blip-list__item-container.expand').classed('expand', false) - - let selectedBlipContainer = d3.select(`.blip-list__item-container[data-blip-id="${blipId}"`) - selectedBlipContainer.classed('expand', true) - - setTimeout( - () => { - if (window.innerWidth >= uiConfig.tabletViewWidth) { - stickQuadrantOnScroll() - } - - const isGroupBlip = isNaN(parseInt(blipId)) - if (isGroupBlip) { - selectedBlipContainer = d3.select(`.blip-list__item-container[data-group-id="${blipId}"`) - } - const elementToFocus = selectedBlipContainer.select('button.blip-list__item-container__name') - elementToFocus.node()?.scrollIntoView({ - behavior: 'smooth', - }) - }, - isQuadrantView ? 0 : 1500, - ) + return performBlipClick(targetElement) } !groupBlipTooltipText && @@ -204,7 +180,38 @@ function renderQuadrantTables(quadrants, rings) { }) } +function performBlipClick(targetElement) { + const isQuadrantView = d3.select('svg#radar-plot').classed('quadrant-view') + + const blipId = d3.select(targetElement).attr('data-blip-id') + highlightBlipInGraph(blipId) + + d3.selectAll('.blip-list__item-container.expand').classed('expand', false) + + let selectedBlipContainer = d3.select(`.blip-list__item-container[data-blip-id="${blipId}"`) + selectedBlipContainer.classed('expand', true) + + setTimeout( + () => { + if (window.innerWidth >= uiConfig.tabletViewWidth) { + stickQuadrantOnScroll() + } + + const isGroupBlip = isNaN(parseInt(blipId)) + if (isGroupBlip) { + selectedBlipContainer = d3.select(`.blip-list__item-container[data-group-id="${blipId}"`) + } + const elementToFocus = selectedBlipContainer.select('button.blip-list__item-container__name') + elementToFocus.node()?.scrollIntoView({ + behavior: 'smooth', + }) + }, + isQuadrantView ? 0 : 1500, + ) +} + module.exports = { renderQuadrantTables, renderBlipDescription, + performBlipClick, } diff --git a/src/graphing/radar.js b/src/graphing/radar.js index e4d791525..2f09ca2a9 100644 --- a/src/graphing/radar.js +++ b/src/graphing/radar.js @@ -20,11 +20,12 @@ const { renderMobileView, renderRadarLegends, removeScrollListener, + selectRadarQuadrant, } = require('./components/quadrants') -const { renderQuadrantTables } = require('./components/quadrantTables') +const { renderQuadrantTables, performBlipClick } = require('./components/quadrantTables') const { addQuadrantNameInPdfView, addRadarLinkInPdfView } = require('./pdfPage') -const { constructSheetUrl } = require('../util/urlUtils') +const { constructSheetUrl, getBlipIdFromUrl, getQuadrantFromURL } = require('../util/urlUtils') const { toRadian } = require('../util/mathUtils') const MIN_BLIP_WIDTH = 12 @@ -832,9 +833,46 @@ const Radar = function (size, radar) { hideTooltipOnScroll(tip) addRadarLinkInPdfView() } + + renderDeepLinkViewIfPresent(quadrants) } return self } +function renderDeepLinkViewIfPresent(quadrants) { + let quadrant + + // Deep link by blip id + const blipIdToShow = getBlipIdFromUrl() + if (blipIdToShow) { + for (const q of quadrants) { + for (const b of q.quadrant.blips()) { + if (b.id() === blipIdToShow) { + quadrant = q + } + } + } + } + + // Deep link by quadrant order + const quadrantToShow = getQuadrantFromURL() + if (quadrantToShow && !blipIdToShow) { + for (const q of quadrants) { + if (q.order === quadrantToShow) { + quadrant = q + } + } + } + + if (quadrant) { + selectRadarQuadrant(quadrant.order, quadrant.startAngle, quadrant.quadrant.name()) + + if (blipIdToShow) { + const blipLink = document.getElementById(`blip-link-${blipIdToShow}`) + performBlipClick(blipLink) + } + } +} + module.exports = Radar diff --git a/src/util/urlUtils.js b/src/util/urlUtils.js index cb46d2267..311c13a14 100644 --- a/src/util/urlUtils.js +++ b/src/util/urlUtils.js @@ -24,8 +24,26 @@ function getSheetName() { return queryParams.sheetName } +function getBlipIdFromUrl() { + const queryParams = QueryParams(window.location.search.substring(1)) + const blipQueryString = queryParams.blipId + + const blipId = parseInt(blipQueryString) + + return isNaN(blipId) ? null : blipId +} + +function getQuadrantFromURL() { + const queryParams = QueryParams(window.location.search.substring(1)) + const quadrantQueryString = queryParams.quadrant + + return quadrantQueryString?.toLowerCase() ?? 'all' +} + module.exports = { constructSheetUrl, getDocumentOrSheetId, getSheetName, + getBlipIdFromUrl, + getQuadrantFromURL, }