diff --git a/app/components/FlowNavigation.tsx b/app/components/FlowNavigation.tsx index 0fa8a547f..3792f9cd4 100644 --- a/app/components/FlowNavigation.tsx +++ b/app/components/FlowNavigation.tsx @@ -1,5 +1,10 @@ import CheckCircleOutlineIcon from "@digitalservicebund/icons/CheckCircleOutline"; import CircleOutlinedIcon from "@digitalservicebund/icons/CircleOutlined"; +import React from "react"; +import ExpandLessIcon from "@digitalservicebund/icons/ExpandLess"; +import ExpandMoreIcon from "@digitalservicebund/icons/ExpandMore"; + +import { useCollapse } from "react-collapsed"; export enum NavState { DoneDisabled, @@ -29,19 +34,30 @@ export default function FlowNavigation({ }) { return ( ); } -function navItem( - destination: string, - state: NavState, - label: string, - subflows = [] as NavItem[], -) { +function NavItem({ + destination, + label, + state, + subflows, +}: { + readonly destination: string; + readonly state: NavState; + readonly label: string; + readonly subflows: NavItem[]; +}) { const relevantSubflows = subflows.filter((subflow, index) => { const isCurrentState = subflow.state === NavState.Current; const isDoneState = subflow.state === NavState.Done; @@ -62,6 +78,11 @@ function navItem( ); }); + // eslint-disable-next-line @typescript-eslint/unbound-method + const { getCollapseProps, getToggleProps, isExpanded } = useCollapse({ + defaultExpanded: state === NavState.Current, + }); + const isDisabled = [NavState.DoneDisabled, NavState.OpenDisabled].includes( state, ); @@ -78,9 +99,9 @@ function navItem( key={destination} className={"list-none border-t-2 border-white first:border-t-0"} > - - - {label} - + aria-disabled={[ + NavState.DoneDisabled, + NavState.OpenDisabled, + ].includes(state)} + > + + + ) : ( + getNavRootItem(destination, state, isDisabled, hasActiveSubflows, label) + )} {hasActiveSubflows && ( - +
+ +
)} ); } +function SubflowNavigation({ + subflows, + destination, +}: { + readonly subflows: NavItem[]; + readonly destination: string; +}) { + return ( + + ); +} + +function getNavRootItem( + destination: string, + state: NavState, + isDisabled: boolean, + hasActiveSubflows: boolean, + label: string, +) { + return ( + + + {label} + + ); +} + function navSubflowItem(destination: string, state: NavState, label: string) { return ( -
  • +
  • { const subflows = @@ -37,6 +36,34 @@ export function navItemsFromFlowSpecifics( }`; const rootLabel = translation[rootStateName] ?? flowEntry.key ?? "No key"; + const subflowSpecifics = + subflows.length > 0 + ? _.flatten( + subflows.map((entry) => { + const subflow = entry[1] ?? {}; + const subflowKey = entry[0] ?? "No key"; + const subflowRoot = `${rootStateName}/${subflow?.id ?? ""}`; + const subflowDestionationStepId = `${subflowRoot}/${ + typeof subflow?.initial === "string" ? subflow?.initial : "" + }`; + const subflowLabel = translation[subflowRoot] ?? subflowKey; + + return { + destination: flowRoot + subflowDestionationStepId, + label: subflowLabel ?? subflowKey, + state: navState({ + isCurrent: currentStepId.startsWith(subflowRoot), + isReachable: true, + isDone: flowController.isSubflowDone( + rootStateName, + subflowKey, + ), + isUneditable: flowController.isUneditable(subflowRoot), + }), + }; + }), + ) + : []; return { destination: flowRoot + destinationStepId, @@ -47,34 +74,7 @@ export function navItemsFromFlowSpecifics( isDone: flowController.isDone(rootStateName), isUneditable: flowController.isUneditable(rootStateName), }), - subflows: - subflows.length > 0 - ? _.flatten( - subflows.map((entry) => { - const subflow = entry[1] ?? {}; - const subflowKey = entry[0] ?? "No key"; - const subflowRoot = `${rootStateName}/${subflow?.id ?? ""}`; - const subflowDestionationStepId = `${subflowRoot}/${ - typeof subflow?.initial === "string" ? subflow?.initial : "" - }`; - const subflowLabel = translation[subflowRoot] ?? subflowKey; - - return { - destination: flowRoot + subflowDestionationStepId, - label: subflowLabel ?? subflowKey, - state: navState({ - isCurrent: currentStepId.startsWith(subflowRoot), - isReachable: true, - isDone: flowController.isSubflowDone( - rootStateName, - subflowKey, - ), - isUneditable: flowController.isUneditable(subflowRoot), - }), - }; - }), - ) - : [], + subflows: subflowSpecifics, }; }); } diff --git a/package-lock.json b/package-lock.json index 7ad0e7ba1..7f056b848 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@remix-run/serve": "^2.5.1", "@remix-validated-form/with-zod": "^2.0.7", "@sentry/remix": "^7.98.0", + "@types/react-collapse": "^5.0.4", "@xstate/graph": "^1.4.2", "axios": "^1.6.7", "classnames": "^2.5.1", @@ -32,6 +33,7 @@ "posthog-node": "^3.6.0", "quicktype-core": "^23.0.81", "react": "^18.2.0", + "react-collapsed": "^4.1.2", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-imask": "^7.3.0", @@ -9264,8 +9266,7 @@ "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "devOptional": true + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/qs": { "version": "6.9.11", @@ -9283,13 +9284,20 @@ "version": "18.2.48", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-collapse": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/react-collapse/-/react-collapse-5.0.4.tgz", + "integrity": "sha512-tM5cVB6skGLneNYnRK2E3R56VOHguSeJQHslGPTIMC58ytL3oelT8L/l1onkwHGn5vSEs2BEq2Olzrur+YdliA==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "18.2.18", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", @@ -9308,8 +9316,7 @@ "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "devOptional": true + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@types/semver": { "version": "7.5.6", @@ -12455,8 +12462,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -23478,6 +23484,18 @@ "node": ">=0.10.0" } }, + "node_modules/react-collapsed": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-collapsed/-/react-collapsed-4.1.2.tgz", + "integrity": "sha512-vusqVnW/VbFCKJx29KNTnjJrwCbV7k3bB/FiO9/0Fj7JNoNarkU1xU7yK4FZHqPW0Q2izB7o6fYzG16zgdDNMQ==", + "dependencies": { + "tiny-warning": "^1.0.3" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, "node_modules/react-colorful": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", @@ -26165,6 +26183,11 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 415a49215..67a6d954f 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "posthog-node": "^3.6.0", "quicktype-core": "^23.0.81", "react": "^18.2.0", + "react-collapsed": "^4.1.2", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-imask": "^7.3.0",