-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2476 from govuk-one-login/pyic-7304
PYIC-7304: add journeyContext visualisation
- Loading branch information
Showing
5 changed files
with
239 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
const addDefinitionOptions = (definition, disabledOptions, featureFlagOptions) => { | ||
Object.entries(definition.checkIfDisabled || {}).forEach(([opt, def]) => { | ||
if (!disabledOptions.includes(opt)) { | ||
disabledOptions.push(opt); | ||
} | ||
addDefinitionOptions(def, disabledOptions, featureFlagOptions); | ||
}); | ||
Object.entries(definition.checkFeatureFlag || {}).forEach(([opt, def]) => { | ||
if (!featureFlagOptions.includes(opt)) { | ||
featureFlagOptions.push(opt); | ||
} | ||
addDefinitionOptions(def, disabledOptions, featureFlagOptions); | ||
}); | ||
}; | ||
|
||
// Traverse the journey map to collect the available 'disabled' and 'featureFlag' options | ||
export const getOptions = (journeyMaps, nestedJourneys) => { | ||
const disabledOptions = ['ticf']; | ||
const featureFlagOptions = []; | ||
|
||
const states = [ | ||
...Object.values(journeyMaps) | ||
.flatMap(journeyMap => Object.values(journeyMap.states)), | ||
...Object.values(nestedJourneys) | ||
.flatMap(nestedJourney => Object.values(nestedJourney.nestedJourneyStates)), | ||
]; | ||
|
||
states.forEach((definition) => { | ||
const events = definition.events || definition.exitEvents || {}; | ||
Object.values(events).forEach((def) => { | ||
addDefinitionOptions(def, disabledOptions, featureFlagOptions); | ||
}); | ||
}); | ||
|
||
Object.values(nestedJourneys).forEach(nestedJourney => { | ||
Object.values(nestedJourney.entryEvents).forEach((def) => { | ||
addDefinitionOptions(def, disabledOptions, featureFlagOptions); | ||
}); | ||
}); | ||
|
||
disabledOptions.sort(); | ||
featureFlagOptions.sort(); | ||
|
||
return { disabledOptions, featureFlagOptions }; | ||
}; | ||
|
||
export const resolveEventTargets = (definition, formData, resolvedEventTargets) => { | ||
const resolvedTargets = resolvedEventTargets || []; | ||
|
||
// Look for an override for disabled CRIs | ||
const disabledCris = formData.getAll('disabledCri'); | ||
const disabledResolution = Object.keys(definition.checkIfDisabled || {}).find((k) => disabledCris.includes(k)); | ||
if (disabledResolution) { | ||
return resolveEventTargets( | ||
{...definition.checkIfDisabled[disabledResolution], journeyContext: definition.journeyContext}, | ||
formData, | ||
resolvedTargets); | ||
} | ||
|
||
Object.keys(definition.checkJourneyContext || {}).forEach(journeyContext => { | ||
const targets = resolveEventTargets( | ||
{...definition.checkJourneyContext[journeyContext], journeyContext: journeyContext}, | ||
formData) | ||
resolvedTargets.push(...targets); | ||
}) | ||
|
||
// Look for an override for feature flags | ||
const featureFlags = formData.getAll('featureFlag'); | ||
const featureFlagResolution = Object.keys(definition.checkFeatureFlag || {}).find((k) => featureFlags.includes(k)); | ||
if (featureFlagResolution) { | ||
return resolveEventTargets( | ||
{...definition.checkFeatureFlag[featureFlagResolution], journeyContext: definition.journeyContext}, | ||
formData, | ||
resolvedTargets); | ||
} | ||
|
||
return [...resolvedTargets, definition]; | ||
} | ||
|
||
const addJourneyContextFromDefinition = (definition, journeyContexts) => { | ||
Object.entries(definition.checkIfDisabled || {}).forEach(([_, def]) => { | ||
addJourneyContextFromDefinition(def, journeyContexts); | ||
}) | ||
|
||
Object.entries(definition.checkJourneyContext || {}).forEach(([ctx, def]) => { | ||
if (!journeyContexts.includes(ctx)) { | ||
journeyContexts.push(ctx); | ||
} | ||
addJourneyContextFromDefinition(def, journeyContexts); | ||
}) | ||
|
||
Object.entries(definition.checkFeatureFlag || {}).forEach(([_, def]) => { | ||
addJourneyContextFromDefinition(def, journeyContexts); | ||
}) | ||
} | ||
|
||
export const getJourneyContexts = (journeyStates) => { | ||
const checkedJourneyContexts = []; | ||
Object.values(journeyStates).forEach((definition) => { | ||
const events = definition.events || definition.exitEvents || {}; | ||
Object.values(events).forEach((def) => { | ||
addJourneyContextFromDefinition(def, checkedJourneyContexts); | ||
}); | ||
}); | ||
return checkedJourneyContexts; | ||
} | ||
|
||
export const getNestedJourneyStates = (nestedJourney) => ({ | ||
...nestedJourney.nestedJourneyStates, | ||
// Create an entry state for each entry event | ||
...Object.fromEntries( | ||
Object.entries(nestedJourney.entryEvents) | ||
.map(([event, def]) => [ | ||
`entry_${event}`.toUpperCase(), | ||
{ | ||
entryEvent: event, | ||
events: { [event]: def } | ||
} | ||
]), | ||
), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; | ||
import svgPanZoom from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'; | ||
import yaml from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'; | ||
import { getOptions, render } from './render.mjs'; | ||
import { render } from './render.mjs'; | ||
import { getJourneyContexts, getNestedJourneyStates, getOptions } from "./helpers.mjs"; | ||
|
||
const DEFAULT_JOURNEY_TYPE = 'INITIAL_JOURNEY_SELECTION'; | ||
const NESTED_JOURNEY_TYPE_SEARCH_PARAM = 'nestedJourneyType'; | ||
|
@@ -208,6 +209,26 @@ const setupOtherOptions = () => { | |
} | ||
} | ||
|
||
const displayJourneyContextInfo = (ctxOptions) => { | ||
journeyContextsList.innerText = ''; | ||
const ctxHeader = document.createElement('h3'); | ||
ctxHeader.innerText = "Journey Contexts" | ||
journeyContextsList.append(ctxHeader); | ||
|
||
const ctxDesc = document.createElement('p'); | ||
ctxDesc.innerText = "This journey checks for the following contexts:" | ||
journeyContextsList.append(ctxDesc); | ||
|
||
const list = document.createElement('ul'); | ||
list.setAttribute("class", "journeyCtxList") | ||
journeyContextsList.append(list); | ||
ctxOptions.forEach(ctx => { | ||
const bulletPoint = document.createElement('li'); | ||
bulletPoint.innerText = ctx; | ||
list.append(bulletPoint) | ||
}); | ||
} | ||
|
||
const updateView = async () => { | ||
const formData = new FormData(form); | ||
const selectedNestedJourney = new URLSearchParams(window.location.search).get(NESTED_JOURNEY_TYPE_SEARCH_PARAM); | ||
|
@@ -219,14 +240,20 @@ const updateView = async () => { | |
journeyDesc.innerText = nestedJourneys[selectedNestedJourney].description; | ||
} else { | ||
journeyName.innerText = journeyMaps[selectedJourney].name || 'Details'; | ||
|
||
const desc = journeyMaps[selectedJourney].description; | ||
|
||
journeySelect.value = selectedJourney; | ||
journeyDesc.innerText = desc || ''; | ||
} | ||
|
||
return renderSvg(selectedJourney, selectedNestedJourney, formData); | ||
const ctxOptions = getJourneyContexts(selectedNestedJourney ? getNestedJourneyStates(nestedJourneys[selectedNestedJourney]) : journeyMaps[selectedJourney].states); | ||
if (ctxOptions.length > 0) { | ||
displayJourneyContextInfo(ctxOptions); | ||
} else { | ||
journeyContextsList.innerText = ''; | ||
} | ||
|
||
await renderSvg(selectedJourney, selectedNestedJourney, formData); | ||
}; | ||
|
||
// Render the journey map SVG | ||
|
Oops, something went wrong.