npm installnpm run start:haapi-react-appnpm run build:haapi-react-app
+
=6.9.0"
@@ -365,13 +365,13 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.29.1",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
- "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.29.0",
- "@babel/types": "^7.29.0",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -381,13 +381,13 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
- "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.28.6",
- "@babel/helper-validator-option": "^7.27.1",
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
@@ -397,36 +397,36 @@
}
},
"node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
- "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.28.6",
- "@babel/types": "^7.28.6"
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
- "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.28.6",
- "@babel/helper-validator-identifier": "^7.28.5",
- "@babel/traverse": "^7.28.6"
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -436,36 +436,36 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
- "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -485,12 +485,12 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.29.2",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
- "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.29.0"
+ "@babel/types": "^7.29.7"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -500,13 +500,13 @@
}
},
"node_modules/@babel/plugin-syntax-import-assertions": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz",
- "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz",
+ "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
+ "@babel/helper-plugin-utils": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -555,31 +555,31 @@
}
},
"node_modules/@babel/template": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
- "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.28.6",
- "@babel/parser": "^7.28.6",
- "@babel/types": "^7.28.6"
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
- "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.29.0",
- "@babel/generator": "^7.29.0",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.29.0",
- "@babel/template": "^7.28.6",
- "@babel/types": "^7.29.0",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
"debug": "^4.3.1"
},
"engines": {
@@ -587,13 +587,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
- "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.28.5"
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -3849,6 +3849,21 @@
"node": ">=18"
}
},
+ "node_modules/@noble/hashes": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
+ "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -7186,9 +7201,9 @@
}
},
"node_modules/baseline-browser-mapping": {
- "version": "2.10.12",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz",
- "integrity": "sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ==",
+ "version": "2.10.38",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+ "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.cjs"
@@ -7394,9 +7409,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
- "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "version": "4.28.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
+ "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
"funding": [
{
"type": "opencollective",
@@ -7413,11 +7428,11 @@
],
"license": "MIT",
"dependencies": {
- "baseline-browser-mapping": "^2.9.0",
- "caniuse-lite": "^1.0.30001759",
- "electron-to-chromium": "^1.5.263",
- "node-releases": "^2.0.27",
- "update-browserslist-db": "^1.2.0"
+ "baseline-browser-mapping": "^2.10.38",
+ "caniuse-lite": "^1.0.30001799",
+ "electron-to-chromium": "^1.5.376",
+ "node-releases": "^2.0.48",
+ "update-browserslist-db": "^1.2.3"
},
"bin": {
"browserslist": "cli.js"
@@ -7600,9 +7615,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001782",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001782.tgz",
- "integrity": "sha512-dZcaJLJeDMh4rELYFw1tvSn1bhZWYFOt468FcbHHxx/Z/dFidd1I6ciyFdi3iwfQCyOjqo9upF6lGQYtMiJWxw==",
+ "version": "1.0.30001799",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+ "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
"funding": [
{
"type": "opencollective",
@@ -9012,9 +9027,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.328",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz",
- "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==",
+ "version": "1.5.378",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz",
+ "integrity": "sha512-VinvOAuuPmdD1guEgGv5f2Qp7/vlfqOrUOMYNnOD4wj3pit8kRsQHzfIf6teyUGWo15Tg5+bOJaRunvyltpVWQ==",
"license": "ISC"
},
"node_modules/emoji-regex": {
@@ -14955,10 +14970,13 @@
"license": "MIT"
},
"node_modules/node-releases": {
- "version": "2.0.36",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
- "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
- "license": "MIT"
+ "version": "2.0.49",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.49.tgz",
+ "integrity": "sha512-f06bl1D+8ZDkn2oOQQKAh5/otFWqVnM1Q5oerA8Pex7UfT66Tx4IPHIqVVFKqFT3FUtaDstdgkM7yT7JWhqxfw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/normalize-package-data": {
"version": "2.5.0",
@@ -16203,21 +16221,42 @@
}
},
"node_modules/raw-body": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
- "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
+ "node_modules/raw-body/node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/raw-body/node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -16231,6 +16270,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/raw-body/node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/react": {
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
@@ -16862,12 +16911,13 @@
"license": "MIT"
},
"node_modules/resolve": {
- "version": "1.22.10",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
- "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
"license": "MIT",
"dependencies": {
- "is-core-module": "^2.16.0",
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
diff --git a/package.json b/package.json
index 422c8764..81a682a0 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
"start:component-library": "npm run -w src/common/component-library watch",
"start:identity-server": "npm run -w src/identity-server start",
"build:identity-server": "npm run -w src/identity-server build",
- "start:haapi-react-app": "npm run -w src/haapi-react-app start",
+ "start:haapi-react-app": "npm run -w src/haapi-react-app previewer:serve",
"build:haapi-react-app": "npm run -w src/haapi-react-app build",
"build:ssp": "npm run -w src/self-service-portal/app build",
"build:css": "npm run -w src/common/css build",
@@ -49,4 +49,4 @@
"concurrently": "^9.1.2",
"cross-env": "^7.0.3"
}
-}
\ No newline at end of file
+}
diff --git a/src/haapi-react-app/README.md b/src/haapi-react-app/README.md
index 495c0c6b..bf02bec1 100644
--- a/src/haapi-react-app/README.md
+++ b/src/haapi-react-app/README.md
@@ -2,7 +2,11 @@
## Project & tools
-Install packages with `npm install`.
+Install all workspaces from the repo root:
+
+```shell
+npm i --prefix "$(git rev-parse --show-toplevel)"
+```
- React
- Built with Vite
@@ -13,14 +17,32 @@ Install packages with `npm install`.
- IDEs usually have Prettier integrated with the "format code" commands.
- IntelliJ has a [plugin](https://plugins.jetbrains.com/plugin/10456-prettier). Additional settings in `Languages & Frameworks | JavaScript | Prettier`.
+## SDK documentation
+
+This app is built on the [HAAPI React SDK](../haapi-react-sdk/README.md).
+
+> 📖 **[Read the HAAPI React SDK docs online](https://curity.io/docs/identity-server/developer-guide/haapi-sdks/web-sdk)**
+> (Curity Docs → Developer Guide → HAAPI SDKs → Web SDK). The site is generated from the SDK package —
+> see the [SDK README](../haapi-react-sdk/README.md) for how the documentation works.
+
+## Previewer
+
+A standalone playground for browsing the SDK's UI against mocked HAAPI steps — pick an example to see how the
+components render every authentication step, without a running Identity Server. Run it from this directory:
+
+```shell
+npm run previewer
+```
+
## Development setup
The Vite development server is used both to serve the application and as a proxy for specific Identity Server endpoints.
This allows running the application as a type-0 client (which is the realistic scenario) and still get all the Vite/React development facilities.
Follow these steps to get started:
+
1. Start Identity Server locally.
- - The server should have a minimum setup to allow running OAuth authorization flows with user interaction.
+ - The server should have a minimum setup to allow running OAuth authorization flows with user interaction.
2. In this directory, run `IDSVR_HOME= ./configure-idsvr-dev.sh`.
- `IDSVR_HOME` should contain the path to the home directory of the running Identity Server instance.
3. Run `npm run dev`.
@@ -37,9 +59,11 @@ To test the updates, enable the API-driven UI in the Identity Server instance an
## Error Handling
### ErrorBoundary
+
**Purpose**: Global error boundary that catches unhandled React errors and displays fallback UI.
**Example Usage**:
+
```tsx
@@ -49,47 +73,53 @@ To test the updates, enable the API-driven UI in the Identity Server instance an
```
**Features:**
+
- Catches JavaScript errors anywhere in the component tree
- Displays user-friendly error messages with retry functionality
- Prevents entire application crashes
- Includes error reporting for debugging
-
## Folder Structure
Inspired by the Domain-Driven Design approach, this project's folder structure is organized by 2 dimensions/levels:
### **Dimension 1: Subdomain/Feature**
+
- **First level folders** represent different **subdomains or features** the app implements
- Each subdomain encapsulates related functionality
#### **Shared Code Organization**
+
- **`shared/` folder**: Contains libraries used by multiple subdomains/features
### **Dimension 2: Technical Layer Types**
+
Within each subdomain, code is organized by these technical layer types:
#### **`feature/`**
+
- **Purpose**: Smart UI components with data access
- **Contains**: Business logic components that connect to data sources. "Smart" components that manage state and side effects
- **Examples**: Components that handle authentication flows, form submissions, API calls
#### **`ui/`**
+
- **Purpose**: Presentational components only
- **Contains**: "Dumb" components focused on rendering. No business logic, just UI rendering based on props
- **Examples**: Buttons, input fields, layout components, styled elements
#### **`data-access/`**
+
- **Purpose**: Backend interaction and state management
- **Contains**: API clients, state management code, data transformation. Handles all external data operations
- **Examples**: HTTP service functions, Redux stores
#### **`util/`**
+
- **Purpose**: Low-level shared utilities
- **Contains**: Helper functions, constants, type definitions. Reusable across multiple components/features
- **Examples**: Date formatters, validation functions, common types
-
### **Example Schema**
```
@@ -106,6 +136,7 @@ src/
```
This structure promotes:
+
- **Discoverability**: Clear separation makes code easy to find
- **Maintainability**: Similar responsibilities are grouped together
- **Reusability**: Shared components can be easily identified and reused
diff --git a/src/haapi-react-app/package.json b/src/haapi-react-app/package.json
index 36d95c1f..101fb2e0 100644
--- a/src/haapi-react-app/package.json
+++ b/src/haapi-react-app/package.json
@@ -25,6 +25,7 @@
"lint": "eslint .",
"prettier-check": "prettier --check .",
"previewer": "vite serve previewer --open",
+ "previewer:serve": "vite serve previewer",
"watch:css-lib": "npm run watch --workspace @curity/ui-kit-css-lib"
},
"dependencies": {
diff --git a/src/haapi-react-sdk/.prettierignore b/src/haapi-react-sdk/.prettierignore
index 47663139..b7892598 100644
--- a/src/haapi-react-sdk/.prettierignore
+++ b/src/haapi-react-sdk/.prettierignore
@@ -1,3 +1,6 @@
.prettierrc
tsconfig.*
README.md
+
+# The docs are their own workspace with its own prettier-check (run via `-w src/haapi-react-sdk`).
+docs
diff --git a/src/haapi-react-sdk/README.md b/src/haapi-react-sdk/README.md
new file mode 100644
index 00000000..ffe3d8fa
--- /dev/null
+++ b/src/haapi-react-sdk/README.md
@@ -0,0 +1,353 @@
+# Overview
+
+The HAAPI React SDK is a set of React components that fully manages HAAPI authentication
+flows in the frontend. It works out of the box with minimal setup and lets you customize the UI only as far as you need.
+
+[Browse the HAAPI step catalog](./docs/sections/00-overview/HaapiStepperPreviewHaapiReactSDKPlaygroundExample.tsx)
+
+
+
+## Documentation
+
+The docs website is generated **from this package**. Edit the files here and the documentation website follows.
+
+> 📖 **[Read the docs online](https://curity.io/docs/identity-server/developer-guide/haapi-sdks/web-sdk)**
+> (Curity Docs → Developer Guide → HAAPI SDKs → Web SDK).
+> The generator lives in the `identity-server` repo, at `docs/haapi-react-sdk-docgen` — its README
+> explains how it works and how to preview the site locally.
+
+### One convention for the whole tree
+
+The `docs/sections/` tree **is** the site's menu. At every level, a `NN-name` folder is a menu entry:
+- the number is its position
+- the name is its title and URL (readers never see the prefix)
+- its `README.md` declares its description and the **ordered** list of the library's exports documented in
+ the section, as a plain numbered list (page order = list order; child folders follow, by prefix).
+- a folder named like one of the exports documents that export: `01-form-ui/` is `HaapiStepperFormUI`'s
+ own page, and its list documents the component's parts.
+
+Renumber or rename a folder and the site follows. `_`-prefixed folders (the harness) are never menu entries.
+
+### What becomes what
+
+Sections differ only in where their page **content** comes from:
+
+| You edit… | The website shows… |
+|---|---|
+| This README | The **Overview** pages — one page per `##` section. |
+| TSDoc comments on the library's exports | The **API Reference** section — one page per export listed in a section README's numbered list. Every public export must be listed somewhere, or tagged `@docsIgnore`, or generation fails. |
+| `.tsx` files in `docs/` | The code examples — Live, editable **playgrounds**. |
+
+### How an example shows up on the site
+
+An example is one `.tsx` file in `docs/`. It can appear in three places:
+
+- **On an Overview page** — add a markdown link to the file from this README. On GitHub it stays a normal link; on the
+ website it becomes a playground, right where the link is.
+- **On an API Reference page** — put `{@see_example ./docs/…/.tsx}` in that
+ export's TSDoc comment. The playground appears where you put the marker.
+- **In the Examples menu** — every `.tsx` in the examples section gets its own page, in its folder's
+ place in the menu (the one convention above).
+
+Every example lives in the folder of the section that shows it: `docs/sections/00-overview/` (embedded via a README
+link), `docs/sections/01-api-reference/` (embedded via a `{@see_example}` marker), or `docs/sections/02-examples/…` (its own
+page in the menu).
+
+The `_harness/` folder is different: it holds the helpers the playgrounds need (`ExamplePreviewer`,
+the step catalog, …). Nothing in it ever becomes a page.
+
+### Adding an example, step by step
+
+1. Create the file in the folder of the section that will show it: `docs/sections/00-overview/` (embedded on an
+ Overview page), `docs/sections/01-api-reference/` (embedded on an API Reference page), or the menu-group folder
+ (e.g. `docs/sections/02-examples/01-render-interceptors/`) for its own page in the Examples menu.
+2. Name it ending in `HaapiReactSDKPlaygroundExample` — for example
+ `StepLinksHaapiReactSDKPlaygroundExample.tsx`. The file name (minus `.tsx`) is the example's id.
+3. Write a small app with a `default` `App` export, usually wrapped in ``. Every
+ example is typechecked, linted and smoke-rendered (`docs/DocsExamples.spec.tsx`) by this repo's CI.
+4. Link it from this README or add a `{@see_example}` marker to the TSDoc of the component (see above). If you placed it under `examples/`, it gets its own page in the Examples menu automatically.
+5. Re-run the generator to see the result.
+
+
+
+## Previewer
+
+Browse the default UI for every HAAPI authentication step: pick a step to see how `HaapiStepperStepUI`
+renders it out of the box, then edit the code to see your changes live.
+
+[Browse the HAAPI step catalog](./docs/sections/00-overview/HaapiStepperPreviewHaapiReactSDKPlaygroundExample.tsx)
+
+## Glossary
+
+- **Flow**: sequence of steps that results in either a successful authentication (`HAAPI_STEPS.COMPLETED_WITH_SUCCESS`) or an error/failure (`HAAPI_PROBLEM_STEPS.COMPLETED_WITH_ERROR`).
+- **Step**: A single stage in the authentication flow, often represented as a screen (e.g., a login page). A step can be composed of actions, links, and messages.
+ - [Step types](./haapi-stepper/data-access/types/haapi-step.types.ts)
+- **Action**: instructions about how to progress to the next step in the authentication flow. Actions often require specific user input and change the state of the authentication (e.g., submitting a form). There are three kinds of action: **form** (e.g. a username/password login form), **client operation** (e.g. a BankID or WebAuthn operation) and **selector** (e.g. choosing an authenticator).
+ - [Action types](./haapi-stepper/data-access/types/haapi-action.types.ts)
+- **Link**: instructions about how to navigate to an alternative but related path (e.g. starting a password reset flow from the main authentication step)
+ - [Link](./haapi-stepper/data-access/types/haapi-step.types.ts#L335)
+- **Message**: Text that provides context to the user about the state of the authentication flow and possible interaction options (e.g., validation errors, warnings, or instructions).
+ - [Message](./haapi-stepper/data-access/types/haapi-step.types.ts#L324)
+
+Check out the following HAAPI documentation for in-depth technical details:
+
+* [Browserless Login Solution](https://curity.io/product/user-journey-orchestration/browserless-login/)
+* [What is Hypermedia Authentication API](https://curity.io/resources/learn/what-is-hypermedia-authentication-api/)
+* [HAAPI Data Model](https://curity.io/docs/haapi-data-model/latest/).
+
+
+## Main actors
+
+`HaapiStepper` **runs the flow**; everything else is about **how you render it**.
+
+### `HaapiStepper` — runs the flow
+
+A headless provider that manages multi-step HAAPI authentication workflows. Wrap your app in it.
+
+```tsx
+
+ {/* your UI goes here */}
+
+```
+
+[Read the HaapiStepper reference](/api-reference/stepper)
+
+### `useHaapiStepper()` — read & advance the flow
+
+A hook that exposes the ongoing `HaapiStepper` authentication flow: its current step and state
+(`currentStep`, `loading`, `error`), the `history` of steps taken so far, and a `nextStep`
+function to advance it.
+
+```tsx
+const { currentStep, loading, error, history, nextStep } = useHaapiStepper();
+```
+
+[Read the useHaapiStepper reference](/api-reference/use-haapi-stepper)
+
+### `HaapiStepperStepUI` — the default UI
+
+Renders any HAAPI flow step out of the box, providing a complete default **opinionated** login UI. It is the fastest way to get HAAPI flows running, and the starting point you customize from.
+
+```tsx
+
+
+
+```
+
+[See example](./docs/sections/00-overview/DefaultRenderingHaapiReactSDKPlaygroundExample.tsx)
+
+[Read the HaapiStepperStepUI reference](/api-reference/step-ui)
+
+### HAAPI stepper UI components — the building blocks
+
+The UI representation of the HAAPI entities (`HaapiStep` → `HaapiStepperStepUI`, `HaapiUserMessage` → `HaapiStepperMessageUI`…). These are the building blocks `HaapiStepperStepUI` is made of, and what you compose your own UI from.
+
+```tsx
+function Step() {
+ const { currentStep, nextStep } = useHaapiStepper();
+ if (!currentStep) return null;
+
+ const { actions, messages, links } = currentStep.dataHelpers;
+
+ return (
+ <>
+
+
+
+ >
+ );
+}
+
+
+
+
+```
+
+[See example](./docs/sections/00-overview/StepBuildingBlocksHaapiReactSDKPlaygroundExample.tsx)
+
+[Read the UI components reference](/api-reference/ui-components)
+
+## Customization
+
+Start with the zero-effort default and adopt customization **only as far as you need**.
+
+| | Effort | Control | What you use | Best for |
+|---|--------|---------|--------------|----------|
+| Default | None | Low | `HaapiStepper` + `HaapiStepperStepUI` | getting HAAPI flows running out of the box |
+| Styles customization | Very low | Look only | CSS classes (`.haapi-stepper-*`) | restyling the default UI |
+| Render interceptors | Low | Medium | `HaapiStepperStepUI` + interceptor props | tweaking the default UI |
+| UI composition | High | Full | `HaapiStepper` + `useHaapiStepper` hook + UI components | custom layout, grouping, complex/behaviour |
+| Mixed | Mixed | Full | a combination of the above | the default UI with localized custom parts |
+
+### Default — works from scratch
+
+Renders the complete HAAPI flow UI.
+
+```tsx
+
+
+
+```
+
+[See example](./docs/sections/00-overview/DefaultRenderingHaapiReactSDKPlaygroundExample.tsx)
+
+### Styles customization
+
+The UI components emit plain `.haapi-stepper-*` CSS class names — restyle the default UI just by
+overriding those classes in your own stylesheet, no code changes needed.
+
+```css
+.haapi-stepper-button {
+ background: #6200ee;
+ border-radius: 8px;
+}
+```
+
+[Restyle the button with CSS](./docs/sections/00-overview/StylesButtonCustomizationHaapiReactSDKPlaygroundExample.tsx)
+
+### CSS customization
+
+The HAAPI UI components are styled via plain CSS class names — no CSS-in-JS, no inline styles. The components only emit class names; the actual rules live in a stylesheet shipped alongside the host application's global stylesheet. For example, in the case of the `haapi-react-app`, in `haapi-react-app/src/shared/util/css/styles.css`.
+
+#### Importing CSS styles
+
+Import the stylesheet once from the consuming application's entry point (e.g. `main.tsx`):
+
+```ts
+import './shared/util/css/styles.css';
+```
+
+By default, the rules in `styles.css` compose utility classes from [Curity CSS Library](https://github.com/curityio/ui-kit/tree/main/src/common/css) (imported at the top of the file) using PostCSS `@extend` — e.g. `.haapi-stepper-button { @extend .button, .button-medium, .button-primary, .w100, .mt2; }`. The components themselves only know about the `.haapi-stepper-*` class names, so consumers are free to back those classes with anything they like.
+
+#### Overriding or extending the defaults
+
+Because the components emit static class names, consumers can:
+
+- **Override / Extend**: define rules for the same class names — or append additional CSS — in a separate stylesheet imported after `styles.css`.
+- **Replace**: skip the default import entirely and provide your own definitions for the classes listed below — written in plain CSS, or composed from any third-party library, for example Tailwind CSS.
+
+The Curity utility composition shown above is just how *this* project chose to implement the defaults; it is not a contract. Nothing in the components requires `@curity/ui-kit-css`, PostCSS, or `@extend`.
+
+
+Available CSS classes
+
+| Class | Used by | Purpose |
+|-------|---------|---------|
+| `.haapi-stepper-selector` | `HaapiStepperSelectorUI` | Selector action container |
+| `.haapi-stepper-authenticator-button` | `HaapiStepperFormSubmitButton` | Authenticator-selector option button (applied automatically when the action carries `authenticatorType`); combine with `.button-` (e.g. `.button-google`) to get the per-authenticator icon color |
+| `.haapi-stepper-messages` | `HaapiStepperMessagesUI` | Messages container |
+| `.haapi-stepper-form-field-text-input` | `HaapiStepperTextFormFieldUI` | Text input fields |
+| `.haapi-stepper-form-field-text-label` | `HaapiStepperTextFormFieldUI` | Form field labels |
+| `.haapi-stepper-form-field-checkbox-input` | `HaapiStepperCheckboxFormFieldUI` | Checkbox inputs |
+| `.haapi-stepper-form-field-checkbox-label` | `HaapiStepperCheckboxFormFieldUI` | Checkbox-specific labels |
+| `.haapi-stepper-form-field-select-input` | `HaapiStepperSelectFormFieldUI` | Select inputs |
+| `.haapi-stepper-form-field-select-label` | `HaapiStepperSelectFormFieldUI` | Select-specific labels |
+| `.haapi-stepper-form-field-password-wrapper` | `HaapiStepperPasswordFormFieldUI` | Password input container |
+| `.haapi-stepper-form-field-password-label` | `HaapiStepperPasswordFormFieldUI` | Password label |
+| `.haapi-stepper-form-field-password-input` | `HaapiStepperPasswordFormFieldUI` | Password input |
+| `.haapi-stepper-form-field-password-visibility-toggle` | `HaapiStepperPasswordFormFieldUI` | Password visibility toggle button |
+| `.haapi-stepper-button` | `HaapiStepperFormUI` | Primary submit buttons |
+| `.haapi-stepper-button-outline` | `HaapiStepperFormUI` | Outline/cancel buttons |
+| `.haapi-stepper-well` | `Well` | Styled content container |
+| `.haapi-stepper-links` | `HaapiStepperLinksUI` | Links container |
+| `.haapi-stepper-link` | `HaapiStepperLinkUI` | Link element |
+| `.haapi-stepper-link-qr-code` | `HaapiStepperLinkUI` | QR code link figure wrapper |
+| `.haapi-stepper-link-qr-code-title` | `HaapiStepperLinkUI` | QR code link figcaption |
+| `.haapi-stepper-link-qr-code-button` | `HaapiStepperLinkUI` | QR code link expand button |
+| `.haapi-stepper-link-qr-code-dialog` | `HaapiStepperQrCodeLinkDialog` | Fullscreen QR code dialog |
+| `.haapi-stepper-link-qr-code-dialog-close-button` | `HaapiStepperQrCodeLinkDialog` | Button wrapping the expanded QR code image; closes the dialog when clicked |
+| `.haapi-stepper-link-qr-code-dialog-image` | `HaapiStepperQrCodeLinkDialog` | Fullscreen QR code dialog image |
+| `.haapi-stepper-actions` | `HaapiStepperActionsUI` | Actions container |
+| `.haapi-stepper-heading` | `HaapiStepperMessagesUI` | Heading messages |
+| `.haapi-stepper-userName` | `HaapiStepperMessagesUI` | User name display |
+| `.haapi-stepper-userCode` | `HaapiStepperMessagesUI` | User code display (e.g. recovery codes) |
+| `.haapi-stepper-polling-progress` | `HaapiStepperClientOperationUI` | Remaining polling time indicator (e.g. recovery codes) |
+| `.haapi-stepper-webauthn-registration-attachment-icon` | `HaapiStepperWebAuthnRegistrationAttachmentCard` | Attachment card icon |
+| `.haapi-stepper-webauthn-registration-attachment-title` | `HaapiStepperWebAuthnRegistrationAttachmentCard` | Attachment card option label |
+| `.haapi-stepper-webauthn-registration-attachment-description` | `HaapiStepperWebAuthnRegistrationAttachmentCard` | Attachment card option description |
+| `.haapi-stepper-consent-logos` | `UserConsentViewNameBuiltInUI` | Container for user consent logos |
+| `.haapi-stepper-error-boundary-fallback` | `DefaultErrorFallback` | Error boundary fallback container |
+| `.haapi-validation-errors-container` | `HaapiStepperFormValidationErrorInputWrapper` | Wrapper around a form field that has validation errors. Receives the `.has-errors` modifier class while errors are visible |
+| `.haapi-validation-errors` | `HaapiStepperFormValidationErrorInputWrapper` | Inner container that holds the list of validation error messages |
+| `.haapi-validation-error` | `HaapiStepperFormValidationErrorInputWrapper` | A single validation error entry (also gets the utility classes `.red .py1`) |
+| `.haapi-validation-error-description` | `HaapiStepperFormValidationErrorInputWrapper` | Validation error message text |
+
+
+
+### Customize with render interceptors
+
+Render interceptors are the programmatic way to customize the default step UI elements — loader, error, step, actions (form, client operation, selector), links, messages, and form fields.
+
+Each is a function that receives the `HaapiStepper` API data for the target UI element (`currentStep`, `loading`, `error`, `nextStep`…) and returns either a React element to replace the default UI element, the API data to render the default UI element, or `null` to skip the element from being rendered:
+
+```tsx
+
+
📨 {message.text}
}
+ />
+
+```
+
+[See example](./docs/sections/02-examples/01-render-interceptors/MessageRenderInterceptorHaapiReactSDKPlaygroundExample.tsx)
+
+> 💡 **Design pattern note**: always return or pass through the API data.
+>
+> - **To override**: return your custom element.
+> - **To delegate to the default renderer**: return the API data (`{ currentStep, history, loading, error, nextStep }`), optionally modified.
+> - **To remove an element**: return `null`.
+
+### Customize with UI composition
+
+The declarative path to build UIs from scratch. Use it for what the API doesn't expose as elements — grouping (fieldsets/tabs), cross-element layouts, inserting your own elements, and behaviour customizations (tabs, multi-step wizards). Best for layout and complex customizations.
+
+Each HAAPI entity has a corresponding UI component (`HaapiStepperActionsUI`, `HaapiStepperMessagesUI`, `HaapiStepperLinksUI`…). `HaapiStepper` still runs the flow:
+
+```tsx
+function LoginPage() {
+ const { currentStep, loading, nextStep } = useHaapiStepper();
+ if (loading || !currentStep) return
+
+
+
+
+ );
+}
+
+
+
+
+```
+
+[See example](./docs/sections/01-api-reference/BuildingBlocksUICompositionHaapiReactSDKPlaygroundExample.tsx)
+
+### Mixed — combine the default with your own UI
+
+Render interceptors and UI composition aren't exclusive — mix them wherever it helps. For
+example, return UI building blocks from a render interceptor to restructure *part* of the
+default UI while keeping everything else: a form render interceptor that re-lays-out the same
+fields, a step interceptor that swaps one step's UI, or the headless `HaapiStepper` driving a
+mix of building blocks, plain HTML and third-party components.
+
+```tsx
+// Keep the default everywhere, but give the form your own layout built from the building blocks.
+ {
+ const action = currentStep.dataHelpers.actions.form[0];
+ return (
+
+
+
+ );
+ }}
+/>
+```
+
+[See example](./docs/sections/01-api-reference/01-ui-components/01-form-ui/FormUICompositionHaapiReactSDKPlaygroundExample.tsx)
diff --git a/src/haapi-react-sdk/docs/DocsExamples.spec.tsx b/src/haapi-react-sdk/docs/DocsExamples.spec.tsx
new file mode 100644
index 00000000..257d75cc
--- /dev/null
+++ b/src/haapi-react-sdk/docs/DocsExamples.spec.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2026 Curity AB. All rights reserved.
+ *
+ * The contents of this file are the property of Curity AB.
+ * You may not copy or use this file, in either source code
+ * or executable form, except in compliance with terms
+ * set by Curity AB.
+ *
+ * For further information, please contact Curity AB.
+ */
+
+import type { ComponentType } from 'react';
+import { render, cleanup } from '@testing-library/react';
+import { afterEach, describe, expect, test, vi } from 'vitest';
+
+/*
+ * Smoke test for every runnable docs example: each `*HaapiReactSDKPlaygroundExample.tsx` must mount
+ * without crashing. The examples are typechecked by `tsc -b`, but only this spec executes them, so a
+ * runtime break (bad hook usage, a step the harness data doesn't cover) fails CI here instead of on the
+ * published docs page. Sandbox-only packages the examples import (antd, …) are aliased to
+ * `_harness/sandbox-package-stub.ts` in `vitest.config.ts`.
+ */
+
+// The examples normally run against the playground's mocked web driver (docgen-side). Here the driver
+// never resolves, so each example renders its initial UI — enough to catch mount-time crashes.
+vi.mock('@curity/identityserver-haapi-web-driver', () => ({
+ createHaapiFetch: () => () => new Promise(() => undefined),
+}));
+
+const examples = import.meta.glob('./sections/**/*HaapiReactSDKPlaygroundExample.tsx');
+
+describe('docs examples', () => {
+ afterEach(cleanup);
+
+ test('the glob finds the examples', () => {
+ expect(Object.keys(examples).length).toBeGreaterThan(0);
+ });
+
+ for (const [file, load] of Object.entries(examples)) {
+ test(`${file} renders without crashing`, async () => {
+ const { default: App } = (await load()) as { default: ComponentType };
+ expect(() => render()).not.toThrow();
+ });
+ }
+});
diff --git a/src/haapi-react-sdk/docs/_harness/AutoSubmitForm.tsx b/src/haapi-react-sdk/docs/_harness/AutoSubmitForm.tsx
new file mode 100644
index 00000000..c455e42d
--- /dev/null
+++ b/src/haapi-react-sdk/docs/_harness/AutoSubmitForm.tsx
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2026 Curity AB. All rights reserved.
+ *
+ * The contents of this file are the property of Curity AB.
+ * You may not copy or use this file, in either source code
+ * or executable form, except in compliance with terms
+ * set by Curity AB.
+ *
+ * For further information, please contact Curity AB.
+ */
+
+import { ReactNode, useEffect, useRef } from 'react';
+
+/**
+ * Previewer helper: clicks the rendered form's submit button once, as soon as it appears.
+ *
+ * Used so error examples surface their error without manual input — a HAAPI error only exists as the
+ * response to a submitted action. This stays *outside* the example so the example's own code remains
+ * a clean, documentation-grade ``; the "force" lives here, not in the example.
+ *
+ * The stepper boots asynchronously (it fetches the step before rendering the form), so a fixed delay is
+ * unreliable — a short one fires before the form exists, a long one adds visible lag. Instead we poll for
+ * the submit button and act the moment it renders, giving up after a few seconds.
+ */
+const POLL_INTERVAL_MS = 50;
+const MAX_WAIT_MS = 5000;
+
+export function AutoSubmitForm({ children }: { children: ReactNode }) {
+ const containerRef = useRef(null);
+
+ useEffect(() => {
+ let cancelled = false;
+ let waited = 0;
+ // One handle for the initial kick-off AND every retry, so cleanup always clears the pending timer.
+ let timeoutId: ReturnType;
+
+ const trySubmit = () => {
+ if (cancelled) {
+ return;
+ }
+ const container = containerRef.current;
+ // Prefer the SDK's submit button; fall back to a plain submit button for examples with a custom form.
+ const submit =
+ container?.querySelector('[data-testid="form-submit-button"]') ??
+ container?.querySelector('button[type="submit"]');
+
+ if (!submit) {
+ if ((waited += POLL_INTERVAL_MS) <= MAX_WAIT_MS) {
+ timeoutId = setTimeout(trySubmit, POLL_INTERVAL_MS);
+ }
+ return;
+ }
+
+ // Fill text-like inputs first, otherwise the browser's required-field validation blocks the submit
+ // and no request is sent. Values are irrelevant — the mock returns its canned error regardless. Set
+ // via the native setter + an `input` event so React's controlled state updates too.
+ // eslint-disable-next-line @typescript-eslint/unbound-method -- always invoked via `.call(input, …)` below
+ const setNativeValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
+ const fillableTypes = ['text', 'email', 'password', 'tel', 'url', 'number'];
+ container?.querySelectorAll('input').forEach(input => {
+ if (fillableTypes.includes(input.type) && !input.value) {
+ // Password fields get a unique filler: submitting a common word like "preview" as a password
+ // trips Chrome's data-breach warning dialog over the docs page.
+ setNativeValue?.call(input, input.type === 'password' ? 'preview-docs-playground-42!' : 'preview');
+ input.dispatchEvent(new Event('input', { bubbles: true }));
+ }
+ });
+
+ submit.click();
+ };
+
+ timeoutId = setTimeout(trySubmit, 0);
+ return () => {
+ cancelled = true;
+ clearTimeout(timeoutId);
+ };
+ }, []);
+
+ return
{children}
;
+}
diff --git a/src/haapi-react-sdk/docs/_harness/ExamplePreviewer.tsx b/src/haapi-react-sdk/docs/_harness/ExamplePreviewer.tsx
new file mode 100644
index 00000000..f6602d9a
--- /dev/null
+++ b/src/haapi-react-sdk/docs/_harness/ExamplePreviewer.tsx
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2026 Curity AB. All rights reserved.
+ *
+ * The contents of this file are the property of Curity AB.
+ * You may not copy or use this file, in either source code
+ * or executable form, except in compliance with terms
+ * set by Curity AB.
+ *
+ * For further information, please contact Curity AB.
+ */
+
+import { cloneElement, Fragment, ReactElement, useState } from 'react';
+import type { HaapiStepperConfig } from '@curity/haapi-react-sdk/haapi-stepper/feature/stepper/haapi-stepper.types';
+import { DEFAULT_EXAMPLE, EXAMPLES, HAAPI_EXAMPLE } from './catalog';
+import { StepSelect } from './StepSelect';
+import { StepDataDetails } from './StepDataDetails';
+import { bootstrapForStep } from './config';
+import { AutoSubmitForm } from './AutoSubmitForm';
+
+/**
+ * Wraps a HAAPI example in the docs preview chrome — a step selector and a collapsed step-data view — and
+ * provides **served-mode** config: it points `window.__CONFIG__` at the selected step's bootstrap (the
+ * host app's job in production) before the wrapped `` mounts, so the example itself stays
+ * clean served code (``, no `config` prop).
+ *
+ * `defaultStep` sets which step the preview opens on; the reader can switch to any other from the
+ * selector.
+ */
+export function ExamplePreviewer({
+ children,
+ defaultStep = DEFAULT_EXAMPLE,
+ autoSubmit = false,
+ showStepSelect = false,
+}: {
+ children: ReactElement<{ config?: Partial }>;
+ /**
+ * Which example the preview opens on — a {@link HAAPI_EXAMPLE} key (a customization-pinned step for
+ * single-step examples, or a showcase step). Defaults to the first browsable showcase entry.
+ */
+ defaultStep?: string;
+ /** Submit the step on mount so its post-submit error shows by default (e.g. error-component examples). */
+ autoSubmit?: boolean;
+ /**
+ * Show the "Step to display" selector. Opt-in: only for examples meant to be browsed across steps
+ * (default UI, whole-step customizations). Examples pinned to one step omit it — switching would render
+ * nothing custom.
+ */
+ showStepSelect?: boolean;
+}) {
+ const [step, setStep] = useState(defaultStep);
+
+ // Runs during render, before the child mounts, so it's set in time — an effect would be too late.
+ window.__CONFIG__ = bootstrapForStep(step);
+
+ // Sandbox-safe config injected into every example at runtime (the visible snippet stays clean served
+ // code): autostart off (never fire a real WebAuthn/BankID ceremony on mount, which would render nothing
+ // while the browser prompts) and auto-redirect off (a completed flow shows the completed step instead of
+ // trying to follow an authorization-response link to a real redirect URI the mock doesn't have).
+ // The previewer wraps an arbitrary example element it doesn't render itself, so injecting the sandbox
+ // config via cloneElement is the point here.
+ // eslint-disable-next-line @eslint-react/no-clone-element
+ const example = cloneElement(children, { config: SANDBOX_CONFIG });
+
+ // Auto-submit when the prop opts in, or when the catalog entry marks the example as one whose whole
+ // point is the post-submit state (e.g. an authentication or validation error).
+ const shouldAutoSubmit = autoSubmit || EXAMPLES[step as HAAPI_EXAMPLE].autoSubmit === true;
+
+ return (
+ <>
+ {showStepSelect && }
+
+ {/* key={step} remounts the example on change so the stepper re-reads window.__CONFIG__. */}
+ {shouldAutoSubmit ? {example} : example}
+
+ {/* The selected step's HAAPI data, collapsed below the rendered UI. */}
+
+ >
+ );
+}
+
+const SANDBOX_CONFIG: Partial = {
+ webAuthnAutostart: false,
+ bankIdAutostart: false,
+ autoRedirectOnAuthenticationComplete: false,
+};
diff --git a/src/haapi-react-sdk/docs/_harness/StepDataDetails.tsx b/src/haapi-react-sdk/docs/_harness/StepDataDetails.tsx
new file mode 100644
index 00000000..f5459fa7
--- /dev/null
+++ b/src/haapi-react-sdk/docs/_harness/StepDataDetails.tsx
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2026 Curity AB. All rights reserved.
+ *
+ * The contents of this file are the property of Curity AB.
+ * You may not copy or use this file, in either source code
+ * or executable form, except in compliance with terms
+ * set by Curity AB.
+ *
+ * For further information, please contact Curity AB.
+ */
+
+import type { HaapiStep } from '@curity/haapi-react-sdk/haapi-stepper/data-access/types/haapi-step.types';
+
+// Minimal JSON token colours, readable on the translucent panel in both light and dark.
+const JSON_COLORS = { key: '#9d174d', string: '#0b7285', number: '#b45309', keyword: '#6d28d9' };
+
+/** Render a value as syntax-highlighted JSON HTML (keys, strings, numbers and keywords coloured). */
+function highlightJson(value: unknown): string {
+ const json = JSON.stringify(value, null, 2);
+ const escaped = json.replace(/&/g, '&').replace(//g, '>');
+ return escaped.replace(
+ /("(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*"(?:\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g,
+ token => {
+ let color: string = JSON_COLORS.number;
+ if (token.startsWith('"')) {
+ color = token.endsWith(':') ? JSON_COLORS.key : JSON_COLORS.string;
+ } else if (token === 'true' || token === 'false' || token === 'null') {
+ color = JSON_COLORS.keyword;
+ }
+ return `${token}`;
+ }
+ );
+}
+
+/**
+ * A collapsed "See HAAPI step data" view of the raw HAAPI step the preview is rendering — a
+ * syntax-highlighted JSON panel shown below the form so readers can correlate the UI with its step data.
+ */
+export function StepDataDetails({ step }: { step: HaapiStep }) {
+ // Inline styles override the Curity stylesheet's global `details`/`summary` box so this docs panel
+ // reads as a deliberate, neutral card rather than a login-form field.
+ return (
+
+
+ See HAAPI step data
+
+
s, so the markup
+ // below is entirely self-generated.
+ // eslint-disable-next-line @eslint-react/dom/no-dangerously-set-innerhtml
+ dangerouslySetInnerHTML={{ __html: highlightJson(step) }}
+ />
+
+ );
+}
diff --git a/src/haapi-react-sdk/docs/_harness/StepSelect.tsx b/src/haapi-react-sdk/docs/_harness/StepSelect.tsx
new file mode 100644
index 00000000..8f9436a0
--- /dev/null
+++ b/src/haapi-react-sdk/docs/_harness/StepSelect.tsx
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2026 Curity AB. All rights reserved.
+ *
+ * The contents of this file are the property of Curity AB.
+ * You may not copy or use this file, in either source code
+ * or executable form, except in compliance with terms
+ * set by Curity AB.
+ *
+ * For further information, please contact Curity AB.
+ */
+
+import { EXAMPLES, HAAPI_EXAMPLE } from './catalog';
+
+// The selector options, grouped by catalog section (preserving the catalog's declaration order). Only
+// browsable `kind: 'step'` entries are listed. Each group becomes an
* ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/ClientOperationUiUsageHaapiReactSDKPlaygroundExample.tsx}
*/
export function HaapiStepperClientOperationUI({ action, onAction }: HaapiStepperClientOperationUIProps) {
const isAvailable = useIsClientOperationAvailable(action);
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/client-operation/operations/webauthn/HaapiStepperWebAuthnRegistrationAttachmentCard.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/client-operation/operations/webauthn/HaapiStepperWebAuthnRegistrationAttachmentCard.tsx
index 888bc549..c46f001d 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/client-operation/operations/webauthn/HaapiStepperWebAuthnRegistrationAttachmentCard.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/client-operation/operations/webauthn/HaapiStepperWebAuthnRegistrationAttachmentCard.tsx
@@ -27,6 +27,8 @@ export interface HaapiStepperWebAuthnRegistrationAttachmentCardProps {
* (icon + bold title + description).
*
* Must be rendered within `HaapiStepper`, for the current active step's action.
+ *
+ * @docsIgnore Not published in the Curity docs.
*/
export const HaapiStepperWebAuthnRegistrationAttachmentCard = ({
action,
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormContext.ts b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormContext.ts
index 14f3f665..9b054986 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormContext.ts
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormContext.ts
@@ -19,8 +19,10 @@ export interface HaapiStepperFormContextValue {
submit: () => void;
}
+/** Not published in the Curity docs. @docsIgnore */
export const HaapiStepperFormContext = createContext(null);
+/** Not published in the Curity docs. @docsIgnore */
export function useHaapiStepperForm(): HaapiStepperFormContextValue {
const context = use(HaapiStepperFormContext);
if (!context) {
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormHook.ts b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormHook.ts
index 29642c1f..14525f7c 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormHook.ts
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormHook.ts
@@ -17,6 +17,14 @@ import { HaapiStepperFormField, HaapiStepperFormState } from '../../stepper/haap
* Hook to manage form state. Returns an array with two values:
* 1. a convenience {@link HaapiStepperFormState} object that can be used to get and set field values
* 2. a map of the current form values that can be used to submit the form
+ *
+ * ```tsx
+ * const formState = useHaapiStepperFormState(action.model.fields ?? []);
+ *
+ * formState.set(field, event.target.value)} />;
+ * // Submit the action with the current values: nextStep(action, formState.values)
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/FormStateHookUsageHaapiReactSDKPlaygroundExample.tsx}
*/
export function useHaapiStepperFormState(fields: HaapiStepperFormField[]): HaapiStepperFormState {
// State to hold values of form fields. Initial value is calculated once, lazily.
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormSubmitButton.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormSubmitButton.tsx
index 50f9dcff..b7da6c4f 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormSubmitButton.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormSubmitButton.tsx
@@ -20,6 +20,29 @@ interface HaapiStepperFormSubmitButtonProps extends ComponentPropsWithRef<'butto
icon?: ReactNode;
}
+/**
+ * Renders the form's submit button with the SDK defaults: the label comes from the HAAPI action, the
+ * icon and styling from the action's authenticator type (cancel actions get the outline style).
+ *
+ * Must be rendered inside a `HaapiStepperFormUI` (it reads the action from the form context, so it
+ * throws outside one). Use it in a `children` render interceptor to keep the default submit button
+ * while composing your own form layout, and customize it via `label`, `icon`, `children` or any native
+ * `` prop:
+ *
+ * ```tsx
+ *
+ * {({ fields }) => (
+ * <>
+ * {fields.map(field => (
+ *
+ * ))}
+ *
+ * >
+ * )}
+ *
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/SubmitButtonCustomizationHaapiReactSDKPlaygroundExample.tsx}
+ */
export function HaapiStepperFormSubmitButton({
label,
icon,
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormUI.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormUI.tsx
index 3ae141a2..c0459b6c 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormUI.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormUI.tsx
@@ -35,14 +35,13 @@ interface HaapiStepperFormUIProps {
/**
* @description
- * # HAAPI STEPPER FORM FEATURES
+ * # Features
*
- * ## BUILT-IN HAAPI FORM ACTION SUPPORT
+ * ## Built-in HAAPI form action support
*
* Renders a HAAPI form action inside the stepper. Tests in
* `haapi-stepper/feature/actions/form/HaapiStepperFormUI.spec.tsx` cover the supported usage patterns:
*
- * @example
* ```tsx
* const onSubmit: HaapiStepperNextStep = (action, payload) => nextStep(action, payload);
*
@@ -55,19 +54,17 @@ interface HaapiStepperFormUIProps {
* - Keeps hidden fields in the submission payload without rendering them.
* - Submits the original action together with the current form values as payload (`Map`).
*
- * ## CUSTOMIZATION
+ * ## Customization
*
- * ### CUSTOMIZATION VIA INTERCEPTORS
+ * ### Customization via interceptors
*
* Use {@link HaapiStepperFormFieldRenderInterceptor} to adjust data, swap components, or omit fields while still
- * leveraging the built-in state management. The interceptor mirrors the “Data customization” and “UI
- * customization” tests.
+ * leveraging the built-in state management.
*
* The `formFieldRenderInterceptor` is better suited for customization of form fields. For form-level customization
* (e.g. adding elements between fields, field group customizations), consider using the `children` render interceptor
* as described in the next section.
*
- * @example
* ```tsx
* const formFieldRenderInterceptor: FormFieldRenderInterceptor = (field, formState) => {
* if (field.type === HAAPI_FORM_FIELDS.USERNAME) {
@@ -105,12 +102,14 @@ interface HaapiStepperFormUIProps {
* formFieldRenderInterceptor={formFieldRenderInterceptor}
* />
* ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/FormFieldRenderInterceptorHaapiReactSDKPlaygroundExample.tsx}
*
- * ### CUSTOMIZATION VIA COMPOSITION (CHILDREN RENDER INTERCEPTOR)
+ * See more examples in `HaapiStepperFormUI.spec.tsx` (`describe('Via Interceptors')` → `Data customization` / `UI customization`).
+ *
+ * ### Customization via composition (children render interceptor)
*
* Passing `children` to the `HaapiStepperFormUI` component disables the default renderer. Provide a render function
- * that receives the visible form `fields`, and the current `formState`. This pattern mirrors the scenarios covered
- * under “Via Composition (children render interceptor)” in the tests.
+ * that receives the visible form `fields`, and the current `formState`.
*
* This approach provides full control over the form content, while still leveraging the built-in state management
* and submission handling. It is better suited for complex customizations, such as adding elements between fields,
@@ -120,7 +119,6 @@ interface HaapiStepperFormUIProps {
* will prevent the form content from rendering. Also, returning the `HaapiStepperFormAPI` data allows you to
* delegate back to the default form, optionally with customized data.
*
- * @example
* ```tsx
*
* {({ fields, formState }) => {
@@ -157,14 +155,15 @@ interface HaapiStepperFormUIProps {
* }}
*
* ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/FormUICompositionHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * See more examples in `HaapiStepperFormUI.spec.tsx` (`describe('Via Composition (children render interceptor)')`).
*
- * ### BEHAVIOUR OVERRIDES AROUND SUBMISSION
+ * ### Behaviour overrides around submission
*
* Because `submit` is exposed through context, you can layer on additional behaviour (confirmation
- * prompts, analytics, pre-submit validation) before delegating to the incoming `onSubmit`. Tests under
- * “Behavior customization” illustrate this pattern.
+ * prompts, analytics, pre-submit validation) before delegating to the incoming `onSubmit`.
*
- * @example
* ```tsx
* const handleSubmit: HaapiStepperNextStep = (action, payload) => {
* if (!window.confirm('Submit the form?')) {
@@ -175,6 +174,9 @@ interface HaapiStepperFormUIProps {
*
*
* ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/FormSubmitBehaviorHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * See more examples in `HaapiStepperFormUI.spec.tsx` (the `Behavior customization` describe blocks).
*/
export function HaapiStepperFormUI({
action,
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormValidationErrorInputWrapper.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormValidationErrorInputWrapper.tsx
index 93e8127f..5c626f66 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormValidationErrorInputWrapper.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/HaapiStepperFormValidationErrorInputWrapper.tsx
@@ -18,6 +18,23 @@ export interface HaapiStepperFormValidationErrorInputWrapperProps {
fieldName: string;
}
+/**
+ * @description
+ *
+ * Field-level display for HAAPI validation `InputError`s. Wrap an input so its validation messages render
+ * beneath it and the field gets the error styling, letting the user correct and resubmit in place.
+ *
+ * ```tsx
+ *
+ *
+ *
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/FormValidationErrorWrapperHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * **Features**
+ * - Shows `InputValidationProblemStep` errors below the corresponding input field.
+ * - Applies the `haapi-validation-error` CSS classes for styling.
+ */
export function HaapiStepperFormValidationErrorInputWrapper({
children,
fieldName,
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperCheckboxFormFieldUI.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperCheckboxFormFieldUI.tsx
index b9766aa2..aac8ca7f 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperCheckboxFormFieldUI.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperCheckboxFormFieldUI.tsx
@@ -14,6 +14,34 @@ import type { ReactElement } from 'react';
import type { HaapiStepperCheckboxFormField } from '../../../stepper/haapi-stepper.types';
import { useHaapiStepperForm } from '../HaapiStepperFormContext';
+/**
+ * Renders the built-in checkbox for a HAAPI checkbox `field`, wired to the form's state.
+ *
+ * Must be rendered inside a `HaapiStepperFormUI` (it reads `formState` from the form context, so it throws
+ * outside one). Normally {@link HaapiStepperFormFieldUI} picks it automatically — use it directly only to place
+ * the checkbox yourself in a custom layout:
+ *
+ * ```tsx
+ * // Pair the checkbox with a terms description, keep the default rendering for every other field.
+ *
+ * {({ fields }) => (
+ * <>
+ * {fields.map(field =>
+ * field.type === HAAPI_FORM_FIELDS.CHECKBOX ? (
+ *
+ *
+ * Read our terms and conditions.
+ *
+ * ) : (
+ *
+ * )
+ * )}
+ * >
+ * )}
+ *
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/CheckboxFieldRenderingHaapiReactSDKPlaygroundExample.tsx}
+ */
export function HaapiStepperCheckboxFormFieldUI({ field }: { field: HaapiStepperCheckboxFormField }): ReactElement {
const { formState, action } = useHaapiStepperForm();
const inputId = `${action.id}-${field.name}-input`;
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperFormFieldUI.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperFormFieldUI.tsx
index bcc8a880..2ea5a4d7 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperFormFieldUI.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperFormFieldUI.tsx
@@ -18,6 +18,32 @@ import { HaapiStepperSelectFormFieldUI } from './HaapiStepperSelectFormFieldUI';
import { HaapiStepperTextFormFieldUI } from './HaapiStepperTextFormFieldUI';
import { HaapiStepperPasswordFormFieldUI } from './HaapiStepperPasswordFormFieldUI';
+/**
+ * Renders the built-in UI for a single HAAPI form `field`, automatically choosing the right input for the
+ * field type (text, password, checkbox or select).
+ *
+ * Must be used inside a `HaapiStepperFormUI` `children` render interceptor — it reads the field's value and
+ * submission state from that form's context, so rendering it anywhere else throws. Reach for it when you want a
+ * custom form layout but still want the default, state-managed inputs for the fields:
+ *
+ * ```tsx
+ *
+ * {({ fields }) => (
+ *
+ *
+ * {fields.map(field => (
+ *
+ * ))}
+ *
+ * )}
+ *
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/FormUICompositionHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * To customize a single field, render its specific component ({@link HaapiStepperTextFormFieldUI},
+ * {@link HaapiStepperPasswordFormFieldUI}, {@link HaapiStepperCheckboxFormFieldUI},
+ * {@link HaapiStepperSelectFormFieldUI}) or your own input wired to the form's `formState`.
+ */
export function HaapiStepperFormFieldUI({ field }: { field: HaapiStepperVisibleFormField }): ReactElement {
switch (field.type) {
case HAAPI_FORM_FIELDS.SELECT:
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperPasswordFormFieldUI.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperPasswordFormFieldUI.tsx
index 13e7efd5..fa08d8ac 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperPasswordFormFieldUI.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperPasswordFormFieldUI.tsx
@@ -21,6 +21,35 @@ import { HAAPI_FORM_FIELDS } from '../../../../data-access/types/haapi-form.type
import type { HaapiStepperPasswordFormField } from '../../../stepper/haapi-stepper.types';
import { useHaapiStepperForm } from '../HaapiStepperFormContext';
+/**
+ * Renders the built-in password input (with a show/hide toggle) for a HAAPI password `field`, wired to the
+ * form's state.
+ *
+ * Must be rendered inside a `HaapiStepperFormUI` (it reads `formState` from the form context, so it throws
+ * outside one). Normally {@link HaapiStepperFormFieldUI} picks it automatically — use it directly only to place
+ * the password field yourself in a custom layout:
+ *
+ * ```tsx
+ * // Add a "forgot password?" link under the password field, keep the default rendering for every other field.
+ *
+ * {({ fields }) => (
+ * <>
+ * {fields.map(field =>
+ * field.type === HAAPI_FORM_FIELDS.PASSWORD ? (
+ *
+ *
+ * Forgot your password?
+ *
+ * ) : (
+ *
+ * )
+ * )}
+ * >
+ * )}
+ *
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/01-form-ui/PasswordFieldRenderingHaapiReactSDKPlaygroundExample.tsx}
+ */
export function HaapiStepperPasswordFormFieldUI({ field }: { field: HaapiStepperPasswordFormField }): ReactElement {
const { formState, action } = useHaapiStepperForm();
const [isVisible, setIsVisible] = useState(false);
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperSelectFormFieldUI.tsx b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperSelectFormFieldUI.tsx
index 85825374..957cf194 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperSelectFormFieldUI.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/actions/form/fields/HaapiStepperSelectFormFieldUI.tsx
@@ -14,6 +14,34 @@ import type { ReactElement } from 'react';
import type { HaapiStepperSelectFormField } from '../../../stepper/haapi-stepper.types';
import { useHaapiStepperForm } from '../HaapiStepperFormContext';
+/**
+ * Renders the built-in `
* ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/SelectorUiUsageHaapiReactSDKPlaygroundExample.tsx}
*/
export function HaapiStepperSelectorUI({ action, onSubmit }: HaapiStepperSelectorUIProps) {
const options = action.model.options;
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepper.tsx b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepper.tsx
index bcde029e..a707452f 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepper.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepper.tsx
@@ -59,11 +59,9 @@ type SetCurrentStepAndUpdateHistoryFn = (
/**
* @description
*
- * # HAAPI STEPPER FEATURES
+ * `HaapiStepper` is a React UI-less component designed to handle complex, multi-step authentication HAAPI workflows. It provides a declarative way to manage HAAPI (HTTP Authentication API) flows, abstracting away the complexity of step-by-step user interactions, HTTP requests, and state transitions.
*
- * The HAAPI Stepper is a React UI-less component designed to handle complex, multi-step authentication HAAPI workflows. It provides a declarative way to manage HAAPI (HTTP Authentication API) flows, abstracting away the complexity of step-by-step user interactions, HTTP requests, and state transitions.
- *
- * ## Key Features
+ * ## Key features
*
* - **Step Management**: Automatically handles navigation between authentication steps
* - **Automatic Redirections**: Seamlessly handles server-driven redirections without exposing them to consumers
@@ -76,37 +74,52 @@ type SetCurrentStepAndUpdateHistoryFn = (
*
* ## Configuration modes
*
- * The HaapiStepper supports two ways of receiving its bootstrap configuration
- * (`initialUrl`, HAAPI driver config, theme):
+ * The HaapiStepper needs a bootstrap configuration — an `initialUrl` (where the
+ * flow starts), a `haapi` driver config, and a `theme` (branding: the company
+ * logo and per-view page symbols; pass `{}` for none) — supplied in one of two ways:
*
- * 1. **Served mode (default)** — the stepper runs inside a server-rendered
- * shell (e.g. the Curity HAAPI React App) that injects the config onto
+ * 1. **Served mode (default)** — the stepper runs inside a server-rendered shell
+ * (e.g. the Curity HAAPI React App) that injects the config onto
* `window.__CONFIG__` before the SPA boots. No prop is required:
*
* ```tsx
* ...
* ```
*
- * 2. **Standalone (library) mode** — when consumed as a library or in any
+ * 2. **Standalone (library) mode** — when consumed as a library, or in any
* context without `window.__CONFIG__`, the consumer supplies the bootstrap
- * explicitly via `config.bootstrap`.
+ * explicitly via `config.bootstrap`:
*
* ```tsx
+ * import type { HaapiStepperBootstrapConfig } from './haapi-stepper.types';
+ *
+ * const bootstrap: HaapiStepperBootstrapConfig = {
+ * initialUrl: 'https://idsvr.example.com/oauth/v2/oauth-authorize/...',
+ * haapi: { ... }, // HAAPI web-driver config
+ * theme: {}, // optionally a company logo and page symbols
+ * };
+ *
* ...
* ```
*
- * See the [HAAPI Stepper README](../../README.md#basic-setup) for the full
- * configuration reference.
+ * > Only one HAAPI configuration is supported per page load — the underlying
+ * > driver is a process-global singleton; switching `bootstrap.haapi` mid-page
+ * > throws (see {@link useHaapiFetch}).
*
- * ## HAAPI Stepper API
+ * Both modes can be combined with `config` overrides for other tunables
+ * (e.g. `pollingInterval`, `bankIdAutostart`); see {@link HaapiStepperConfig}
+ * for the full set.
+ *
+ * ## HAAPI stepper API
*
* Child components can access the following API via the `useHaapiStepper()` hook:
*
- * - `currentStep: HaapiProviderStep | null` - The current authentication step (null during initial load)
+ * - `currentStep: HaapiStepperStep | null` - The current authentication step (null during initial load)
* - `history: HaapiStepperHistoryEntry[]` - Complete history of all steps and actions taken, accessible via `history[index]`
* - `loading: boolean` - Whether the stepper is currently loading (initial load or transitioning between steps)
* - `error: HaapiStepperError | null` - Current error state (app errors or input validation errors)
* - `nextStep: HaapiStepperAPINextStep` - Function to navigate to the next step by submitting an action or link
+ * - `config: HaapiStepperConfig` - The resolved stepper configuration (its `bootstrap` holds the initial URL, HAAPI driver config, and theme)
*
* ## Usage
*
@@ -114,7 +127,6 @@ type SetCurrentStepAndUpdateHistoryFn = (
*
* Built-in HAAPI flow example using HaapiStepperStepUI:
*
- * @example
* ```tsx
* import { HaapiStepper } from './HaapiStepper';
* import { HaapiStepperStepUI } from '../steps/HaapiStepperStepUI';
@@ -123,10 +135,10 @@ type SetCurrentStepAndUpdateHistoryFn = (
*
*
* ```
+ * {@see_example ./docs/sections/00-overview/DefaultRenderingHaapiReactSDKPlaygroundExample.tsx}
*
- * Partial customization example with custom links and default [HAAPI UI components](../../README.MD#haapi-ui-components) for the rest:
+ * Partial customization example with custom links and default [HAAPI UI components](../../../README.md#haapi-stepper-ui-components--the-building-blocks) for the rest:
*
- * @example
* ```tsx
* import { HaapiStepper } from './HaapiStepper';
* import { useHaapiStepper } from './useHaapiStepper';
@@ -165,10 +177,10 @@ type SetCurrentStepAndUpdateHistoryFn = (
*
*
* ```
+ * {@see_example ./docs/sections/01-api-reference/BuildingBlocksUICompositionHaapiReactSDKPlaygroundExample.tsx}
*
* Full customization example:
*
- * @example
* ```tsx
* import { HaapiStepper } from './HaapiStepper';
* import { useHaapiStepper } from './useHaapiStepper';
@@ -187,39 +199,41 @@ type SetCurrentStepAndUpdateHistoryFn = (
* const { actions, links } = currentStep.dataHelpers;
*
* return (
- *
@@ -270,14 +284,80 @@ type SetCurrentStepAndUpdateHistoryFn = (
*
*
* ```
+ * {@see_example ./docs/sections/01-api-reference/ConditionalCustomizationHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * ## Error handling
+ *
+ * The `HaapiStepper` implements a comprehensive error-handling strategy with multiple layers to ensure
+ * robust error management and an optimal user experience.
+ *
+ * ### Error state management
+ *
+ * The HAAPI stepper manages errors according to two categories: HAAPI errors and non-HAAPI errors.
+ *
+ * #### HAAPI errors
+ *
+ * HAAPI errors are HAAPI `ProblemStep`s (HAAPI flow steps of type `HAAPI_PROBLEM_STEPS`).
+ *
+ * HAAPI errors are classified into two groups:
+ *
+ * ```text
+ * HaapiStepperError
+ * ├── app (Unrecoverable)
+ * │ ├── UnrecoverableProblemStep
+ * │ ├── UnexpectedProblemStep
+ * │ └── CompletedWithErrorStep
+ * └── input (Recoverable)
+ * ├── ValidationProblemStep
+ * └── IncorrectCredentialsProblemStep
+ * ```
+ *
+ * **`AppError` (Unrecoverable)**
+ * - **Description**: Errors that cannot be resolved in the step (action form) where they originated,
+ * so they need to be handled at the application level (e.g., show a dedicated error page) and/or
+ * require restarting the stepper flow.
+ * - Like any other problem, they might include `UserMessages` and `Links` that need to be displayed
+ * to the user.
+ * - **Types**: `UnrecoverableProblemStep`, `UnexpectedProblemStep`, `CompletedWithErrorStep`.
+ * - **Examples**: Authentication failed, too many attempts, session mismatches.
+ * - **Handling**: Displayed as toast notifications and/or a problem step UI.
+ *
+ * **`InputError` (Recoverable)**
+ * - **Description**: Errors that can be resolved in the step (form) where they originated.
+ * - They should be handled while keeping the step's UI, providing the problem's `UserMessages` and
+ * `Links`, and allowing the user to correct the input and resubmit.
+ * - **Types**: `ValidationProblemStep`, `IncorrectCredentialsProblemStep`.
+ * - **Examples**: Invalid form fields, incorrect credentials.
+ * - **Handling**: Displayed below relevant input fields for immediate correction.
+ *
+ * **`HaapiStepperError` interface**:
+ *
+ * ```tsx
+ * interface HaapiStepperError {
+ * app?: AppError | null;
+ * input?: InputError | null;
+ * }
+ * ```
+ *
+ * HAAPI errors are provided by the `useHaapiStepper` hook:
+ *
+ * ```tsx
+ * const { error } = useHaapiStepper();
+ * const { app, input } = error || {};
+ * ```
+ *
+ * ##### HAAPI error utils
+ *
+ * Two UI components render these errors — documented under **API Reference → UI Components**:
+ * `HaapiStepperErrorNotifier` (toast notifications for `AppError`s, and optionally `InputError`s) and
+ * `HaapiStepperFormValidationErrorInputWrapper` (field-level display of validation `InputError`s).
*
- * ## Error Handling
+ * #### Non-HAAPI errors
*
- * The HaapiStepper distinguishes between:
- * - **App errors** (`error.app`): Unexpected problems or system errors that prevent flow continuation
- * - **Input errors** (`error.input`): Validation errors on user input that allow the user to retry
+ * Non-HAAPI errors are network, backend, and frontend errors that are not handled at lower levels.
*
- * Critical errors are thrown to the app's error boundary for proper error UI rendering.
+ * The `HaapiStepper` throws them as JavaScript errors so they can be caught by the nearest React error
+ * boundary.
*
*/
export function HaapiStepper({ children, config }: HaapiStepperProps) {
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperContext.tsx b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperContext.tsx
index 0beaabbb..5b38d743 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperContext.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperContext.tsx
@@ -12,4 +12,5 @@
import { createContext } from 'react';
import type { HaapiStepperAPI } from './haapi-stepper.types';
+/** Not published in the Curity docs. @docsIgnore */
export const HaapiStepperContext = createContext(null);
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperErrorNotifier.tsx b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperErrorNotifier.tsx
index 23ed821e..a8d885d3 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperErrorNotifier.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperErrorNotifier.tsx
@@ -22,6 +22,23 @@ interface HaapiErrorNotifierProps {
errorFormatter?: (error: HaapiStepperAppError | HaapiStepperInputError) => string;
}
+/**
+ * @description
+ *
+ * Toast-based notification UI for HAAPI `AppError`s (and, optionally, `InputError`s). Wrap your app so
+ * unrecoverable problems surface as dismissible toasts without each step having to handle them.
+ *
+ * ```tsx
+ *
+ *
+ *
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/01-ui-components/ErrorNotifierHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * **Features**
+ * - Automatically shows notifications for `AppError` and, optionally, `InputError`.
+ * - Auto-dismisses after a timeout, and manually via a close button.
+ */
export function HaapiStepperErrorNotifier({
children,
showInputErrorNotifications = true,
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperHook.ts b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperHook.ts
index b2d7802f..e60ea7a4 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperHook.ts
+++ b/src/haapi-react-sdk/haapi-stepper/feature/stepper/HaapiStepperHook.ts
@@ -21,10 +21,10 @@ import { HaapiStepperContext } from './HaapiStepperContext';
* - `loading`: Loading state during transitions
* - `error`: Current error state (app or input validation errors)
* - `nextStep`: Function to navigate to the next step
+ * - `config`: The resolved stepper configuration
*
* @throws {Error} If used outside of HaapiStepper
*
- * @example
* ```tsx
* function MyComponent() {
* const { currentStep, history, loading, error, nextStep } = useHaapiStepper();
@@ -40,6 +40,7 @@ import { HaapiStepperContext } from './HaapiStepperContext';
* );
* }
* ```
+ * {@see_example ./docs/sections/01-api-reference/UseHaapiStepperHookHaapiReactSDKPlaygroundExample.tsx}
*/
export function useHaapiStepper() {
const haapiStepperContext = use(HaapiStepperContext);
diff --git a/src/haapi-react-sdk/haapi-stepper/feature/steps/HaapiStepperStepUI.tsx b/src/haapi-react-sdk/haapi-stepper/feature/steps/HaapiStepperStepUI.tsx
index 4d781a08..c6cd30f9 100644
--- a/src/haapi-react-sdk/haapi-stepper/feature/steps/HaapiStepperStepUI.tsx
+++ b/src/haapi-react-sdk/haapi-stepper/feature/steps/HaapiStepperStepUI.tsx
@@ -26,19 +26,21 @@ import type { HaapiStepperStepUIProps } from './typings';
/**
* @description
- * # HAAPI UI STEP FEATURES
*
- * ## BUILT-IN HAAPI AUTHENTICATION FLOW SUPPORT
+ * `HaapiStepperStepUI` is the default UI for HAAPI authentication flows: drop it inside a `HaapiStepper` and it renders every step — out of the box and ready to customize.
*
- * In combination with the HaapiStepper, the HaapiStepperStepUI component provides a seamless way to implement
- * complete HAAPI authentication flows in your application, allowing extensive customization, with minimal setup.
+ * ## Built-in HAAPI authentication flow support
+ *
+ * Used together with the `HaapiStepper`, the `HaapiStepperStepUI` renders a proper user interface (UI) for every
+ * HAAPI authentication flow out of the box — making it the fastest and easiest way to get HAAPI up and running
+ * in your application, with minimal setup.
*
- * @example
* ```tsx
*
*
*
* ```
+ * {@see_example ./docs/sections/00-overview/DefaultRenderingHaapiReactSDKPlaygroundExample.tsx}
*
* By default, it covers all HAAPI authentication flow steps out-of-the-box including:
* - Authentication, Registration, User Consent, Consentor, Polling, Redirection, Continue same, Completed and,
@@ -49,16 +51,20 @@ import type { HaapiStepperStepUIProps } from './typings';
* Note: Redirection, and Continue Same steps are handled automatically by the HaapiStepper and never
* reach this component
*
- * ### VIEW NAME BUILT-IN UIs
+ * ### View name built-in UIs
*
* The HaapiStepperStepUI ships built-in UIs for specific HAAPI `viewName`s (`step.metadata.viewName`) that need a
- * more tailored UI than the generic step shell can provide (e.g. the BankID requires the QR link to be lifted
- * above the actions). They are displayed by default and can be customized like any other step by using render
- * interceptors.
+ * more tailored UI than the generic step shell can provide: BankID (the QR link is lifted above the actions) and
+ * User Consent (custom logos). They are displayed by default and can be customized like any other step
+ * by using render interceptors.
+ *
+ * ## Customization
*
- * ## CUSTOMIZATION
+ * The `HaapiStepperStepUI` is highly customizable and granular: you can customize some aspects (via render
+ * interceptors) while keeping the defaults for the rest, making it the best way to apply partial customizations
+ * to the default UI.
*
- * ### CUSTOMIZATION DIMENSIONS
+ * ### Customization dimensions
*
* The `HaapiStepperStepUI` component allows the customization of the HAAPI Authentication flows in 3 dimensions:
* 1. Data (e.g., modify action titles)
@@ -68,7 +74,7 @@ import type { HaapiStepperStepUIProps } from './typings';
* 2. UI (e.g., show custom spinner when loading)
* 3. Behaviour/Logic (e.g., trigger a confirmation dialog on cancel actions)
*
- * ### CUSTOMIZABLE ELEMENTS (COMPONENTS)
+ * ### Customizable elements (components)
*
* Those 3 dimensions can be customized on the following elements:
* - Loading
@@ -82,7 +88,7 @@ import type { HaapiStepperStepUIProps } from './typings';
* - Message
* - Step
*
- * ### RENDER INTERCEPTORS
+ * ### Render interceptors
*
* Customization (data, ui and behaviour) is managed through render interceptors, which allow to intercept the
* default rendering for the customizable element to provide custom UX while maintaining the underlying HAAPI
@@ -116,7 +122,7 @@ import type { HaapiStepperStepUIProps } from './typings';
* For example, if only a Loading render interceptor is passed to the `HaapiStepperStepUI`, the step will show the default step UI
* with a custom loading element when loading.
*
- * #### RENDER INTERCEPTORS TYPES
+ * #### Render interceptors types
*
* - Loading (loadingRenderInterceptor): customizes only loading states (e.g., custom spinner or progress bar)
* - Error (errorRenderInterceptor): customizes only error displays (e.g., detailed error information)
@@ -129,18 +135,22 @@ import type { HaapiStepperStepUIProps } from './typings';
* - Message (messageRenderInterceptor): customizes only message rendering (e.g., custom login message display)
* - Step (stepRenderInterceptor): customizes entire step rendering (e.g., custom authentication step UI)
*
- * ### CUSTOMIZATION EXAMPLES
+ * ### Customization examples
+ *
+ * #### Loading element customization example
*
- * #### Loading Element Customization Example
+ * Data customization: show loading only for specific template areas.
*
- * @example
* ```tsx
- * // Data Customization: show loading only for specific template areas
* const loadingRenderInterceptor: HaapiStepperStepUILoadingRenderInterceptor = ({ loading, currentStep, ...rest }) => {
* return { loading: loading && currentStep?.metadata?.templateArea !== 'lwa', currentStep, ...rest };
* };
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/LoadingDataRenderInterceptorHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * UI customization: show a custom loading component for the select authenticator step and delegate to default rendering otherwise.
*
- * // UI Customization: show custom loading component for the select authenticator step and delegate to default rendering otherwise
+ * ```tsx
* const customLoadingRenderInterceptor: HaapiStepperStepUILoadingRenderInterceptor = ({ loading, currentStep, ...rest }) => {
* if (loading && currentStep?.metadata?.viewName?.includes('select-authenticator')) {
* return
Authenticating...
;
@@ -148,8 +158,12 @@ import type { HaapiStepperStepUIProps } from './typings';
*
* return { loading, currentStep, ...rest };
* };
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/LoadingRenderInterceptorHaapiReactSDKPlaygroundExample.tsx}
*
- * // Behavior Customization: trigger analytics event when loading starts
+ * Behaviour customization: trigger an analytics event when loading starts.
+ *
+ * ```tsx
* const loadingRenderInterceptorWithAnalytics: HaapiStepperStepUILoadingRenderInterceptor = ({ loading, currentStep, ...rest }) => {
* if (loading) {
* analyticsTracker('loading_started', { hasStep: !!currentStep });
@@ -157,37 +171,50 @@ import type { HaapiStepperStepUIProps } from './typings';
* return { loading, currentStep, ...rest };
* };
* ```
+ * {@see_example ./docs/sections/01-api-reference/LoadingBehaviorRenderInterceptorHaapiReactSDKPlaygroundExample.tsx}
*
- * #### Step Element Customization Example
+ * #### Step element customization example
+ *
+ * Data customization: modify the step's message and link data before default rendering.
*
- * @example
* ```tsx
- * // Data customization: modify step's action, message and link data before default rendering
* const customStepRenderInterceptor: HaapiStepperStepUIStepRenderInterceptor = ({ currentStep, ...rest }) => ({
* currentStep: {
* ...currentStep,
- * actions: currentStep.dataHelpers?.actions?.all?.map(action => ({ ...action, title: `Modified ${action.title}` })),
* messages: currentStep.dataHelpers?.messages?.map(message => ({ ...message, text: `Modified ${message.text}` })),
* links: currentStep.dataHelpers?.links?.map(link => ({ ...link, title: `Modified ${link.title}` })),
* },
* ...rest,
*});
+ * ```
+ * {@see_example ./docs/sections/01-api-reference/StepDataRenderInterceptorHaapiReactSDKPlaygroundExample.tsx}
+ *
+ * UI customization: display the select-authenticator step as a custom card grid, and delegate every other step to
+ * default rendering.
*
- * // UI customization: show custom component for the select authenticator step and delegate to default rendering otherwise
- * const customStepRenderInterceptor: HaapiStepperStepUIStepRenderInterceptor = ({ currentStep, ...rest }) => {
- * if (currentStep.metadata?.viewName === 'views/select-authenticator/index') {
- * return (
- *