-
Notifications
You must be signed in to change notification settings - Fork 1
IS-11275 LWA: viewName built-in UIs (first: BankID wait UI) #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: integration/IS-5161/login-web-app
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,6 +114,25 @@ Because the `HaapiStepperStepUI` handles all possible HAAPI authentication flows | |
|
|
||
| Check out [the HaapiStepperStepUI documentation and usage examples](./feature/steps/HaapiStepperStepUI.tsx). | ||
|
|
||
| ### ViewName built-in UIs | ||
|
|
||
| Some HAAPI viewNames (`step.metadata.viewName`) need a UI that the generic step rendering can't deliver well. For example, the **BankID** screen needs to render a spinner while the polling status is `pending`, not only while `loading` is true, and lifts the QR code above the actions. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw: I think that "render a spinner while the polling status is pending" should be the default behavior for any polling step. I see you considered it out of scope of this ticket - and I recall there was more to be discussed about loading indicators - but please don't forget about that. |
||
|
|
||
| To handle this kind of view, the library ships **viewName built-in UIs** that automatically take over when the matching `step.metadata.viewName` arrives from the server. | ||
|
|
||
| #### The `enableViewNameBuiltInUIs` prop | ||
|
|
||
| `<HaapiStepperStepUI>` accepts a | ||
| `enableViewNameBuiltInUIs?: HaapiStepperViewNameBuiltInUI[] | boolean` prop that opts in to which viewName built-in UIs are active. It is **opt-in**: when the prop is omitted (or `false`), no viewName built-in UIs are applied and every step renders through the generic render pipeline. Pass `true` to enable all known built-ins, or an array of built-in view names (`HaapiStepperViewNameBuiltInUI[]`) to pin a specific subset. | ||
|
|
||
| #### Current set | ||
|
|
||
| | `metadata.viewName` | Enum member | What it delivers | | ||
| | --------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------ | | ||
| | `authenticator/bankid/wait/index` | `HaapiStepperViewNameBuiltInUI.BANKID` | Spinner while polling `status === pending`; QR code link rendered above the actions. | | ||
|
|
||
| Check out documentation and usage examples in [`HaapiStepperStepUI`](./feature/steps/HaapiStepperStepUI.tsx), and the test use cases in [`HaapiStepperStepUI.spec.tsx`](./feature/steps/HaapiStepperStepUI.spec.tsx) (`describe('ViewName built-in UIs Rendering')`) for more details. | ||
|
|
||
|
|
||
|
|
||
| ## HAAPI Stepper UI Components | ||
|
|
@@ -132,6 +151,7 @@ Check out documentation and usage examples in the links below: | |
| * [HaapiStepperSelectorUI](./feature/actions/selector/HaapiStepperSelectorUI.tsx) | ||
| * [HaapiStepperClientOperationUI](./feature/actions/client-operation/HaapiStepperClientOperationUI.tsx) | ||
| * [HaapiStepperMessagesUI](./ui/messages/HaapiStepperMessagesUI.tsx) | ||
| * [HaapiStepperMessageUI](./ui/messages/HaapiStepperMessageUI.tsx) | ||
| * [HaapiStepperLinksUI](./ui/links/HaapiStepperLinksUI.tsx) | ||
| * [HaapiStepperLinkUI](./ui/links/HaapiStepperLinkUI.tsx) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ import { HaapiStepperMessagesUI } from '../../ui/messages/HaapiStepperMessagesUI | |
| import { Well } from '../../ui/well/Well'; | ||
| import { applyRenderInterceptor } from '../../util/generic-render-interceptor'; | ||
| import { formatNextStepData } from '../stepper/data-formatters/format-next-step-data'; | ||
| import { HaapiStepperViewNameBuiltInUI, getViewNameBuiltInUI } from '../viewnames'; | ||
| import type { | ||
| HaapiStepperAPI, | ||
| HaapiStepperAPIWithRequiredCurrentStep, | ||
|
|
@@ -52,6 +53,7 @@ interface HaapiStepperStepUIProps { | |
| clientOperationActionRenderInterceptor?: HaapiStepperStepUIClientOperationActionRenderInterceptor; | ||
| linkRenderInterceptor?: HaapiStepperStepUILinkRenderInterceptor; | ||
| messageRenderInterceptor?: HaapiStepperStepUIMessageRenderInterceptor; | ||
| enableViewNameBuiltInUIs?: HaapiStepperViewNameBuiltInUI[] | boolean; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -79,6 +81,56 @@ interface HaapiStepperStepUIProps { | |
| * Note: Redirection, and Continue Same steps are handled automatically by the HaapiStepper and never | ||
| * reach this component | ||
| * | ||
| * ### VIEW NAME BUILT-IN UIs | ||
| * | ||
| * The HaapiStepperStepUI component also provides built-in UIs for specific HAAPI `viewName`s that require a more | ||
| * tailored UI than the generic step shell can provide (e.g. the BankID QR code step, which requires lifting | ||
| * the QR code up and showing a spinner while polling). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I don't think this is needed as it makes it look like the default is flawed. |
||
| * | ||
| * The viewName built-in UIs are opt-in: `enableViewNameBuiltInUIs` defaults to `undefined` (no built-ins active). | ||
| * Pass: | ||
| * | ||
| * - `true` (or the JSX shorthand `enableViewNameBuiltInUIs`) to enable all known built-ins. This | ||
| * stays in sync with the library — if a new built-in is added in a future release, it is | ||
| * activated automatically. | ||
| * - An array of `HaapiStepperViewNameBuiltInUI` values to enable only specific built-ins. | ||
| * This pins the active set, so adding a new built-in to the library is a purely additive | ||
| * change that doesn't affect existing rendering. | ||
| * - `false` or `undefined` to keep all built-ins disabled (every view renders through the | ||
| * generic shell). | ||
| * | ||
| * Composition: the matching viewName built-in UI is rendered after the `stepRenderInterceptor` has processed the | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| * step, and before any of the per-element render interceptors (actions, messages, links…). It is only rendered | ||
| * when `stepRenderInterceptor` was not provided or if it returns the stepper API data (pass-through) — the same | ||
| * rule that governs every other render interceptor. | ||
| * | ||
| * #### ViewName Built-in UIs Example | ||
| * | ||
| * @example | ||
| * ```tsx | ||
| * import { HaapiStepperViewNameBuiltInUI } from '...'; | ||
| * | ||
| * // No prop = no built-ins active. The component renders every view through the generic shell. | ||
| * <HaapiStepperStepUI /> | ||
| * | ||
| * // Boolean shorthand: opt in to all known built-ins (current and future). | ||
| * <HaapiStepperStepUI enableViewNameBuiltInUIs /> | ||
| * | ||
| * // Pin to a specific subset. | ||
| * <HaapiStepperStepUI enableViewNameBuiltInUIs={[HaapiStepperViewNameBuiltInUI.BANKID]} /> | ||
| * | ||
| * // Override a viewName built-in UI with a `stepRenderInterceptor` | ||
| * const customBankIdUI: HaapiStepperStepUIStepRenderInterceptor = ({ currentStep, ...rest }) => { | ||
| * if (currentStep.metadata?.viewName === 'authenticator/bankid/wait/index') { | ||
| * return <MyBankId step={currentStep} />; | ||
| * } | ||
| * return { currentStep, ...rest }; | ||
| * }; | ||
| * | ||
| * // MyBankId will be rendered instead of the built-in UI for the BankID | ||
| * <HaapiStepperStepUI stepRenderInterceptor={customBankIdUI} enableViewNameBuiltInUIs /> | ||
| * ``` | ||
| * | ||
| * ## CUSTOMIZATION | ||
| * | ||
| * ### CUSTOMIZATION DIMENSIONS | ||
|
|
@@ -241,6 +293,7 @@ export const HaapiStepperStepUI = ({ | |
| clientOperationActionRenderInterceptor, | ||
| linkRenderInterceptor, | ||
| messageRenderInterceptor, | ||
| enableViewNameBuiltInUIs, | ||
| }: HaapiStepperStepUIProps) => { | ||
| const haapiStepperAPI = useHaapiStepper(); | ||
| const loadingElement: ReactElement | null = getLoadingElement(haapiStepperAPI, loadingRenderInterceptor); | ||
|
|
@@ -249,38 +302,46 @@ export const HaapiStepperStepUI = ({ | |
| return loadingElement; | ||
| } | ||
|
|
||
| let haapiUIStepperAPI = haapiStepperAPI as HaapiStepperAPIWithRequiredCurrentStep; | ||
| let haapiStepperUiAPI = haapiStepperAPI as HaapiStepperAPIWithRequiredCurrentStep; | ||
|
|
||
| if (stepRenderInterceptor) { | ||
| const customStepRenderInterceptorResult = stepRenderInterceptor(haapiUIStepperAPI); | ||
| const stepRenderInterceptorResult = stepRenderInterceptor(haapiStepperUiAPI); | ||
|
|
||
| if (isValidElement(customStepRenderInterceptorResult)) { | ||
| return customStepRenderInterceptorResult; | ||
| } else if (customStepRenderInterceptorResult === null || customStepRenderInterceptorResult === undefined) { | ||
| if (isValidElement(stepRenderInterceptorResult)) { | ||
| return stepRenderInterceptorResult; | ||
| } | ||
|
|
||
| if (stepRenderInterceptorResult === null || stepRenderInterceptorResult === undefined) { | ||
| return null; | ||
| } else { | ||
| haapiUIStepperAPI = { | ||
| ...customStepRenderInterceptorResult, | ||
| currentStep: formatNextStepData(customStepRenderInterceptorResult.currentStep), | ||
| }; | ||
| } | ||
|
|
||
| haapiStepperUiAPI = { | ||
| ...stepRenderInterceptorResult, | ||
| currentStep: formatNextStepData(stepRenderInterceptorResult.currentStep), | ||
| }; | ||
| } | ||
|
|
||
| const ViewNameBuiltInUI = getViewNameBuiltInUI(haapiStepperUiAPI, enableViewNameBuiltInUIs); | ||
|
|
||
| if (ViewNameBuiltInUI) { | ||
| return <ViewNameBuiltInUI {...haapiStepperUiAPI} />; | ||
| } | ||
|
aleixsuau marked this conversation as resolved.
|
||
|
|
||
| const { error, currentStep } = haapiUIStepperAPI; | ||
| const errorElement: ReactElement | null = getErrorElement(haapiUIStepperAPI, errorRenderInterceptor); | ||
| const { error, currentStep } = haapiStepperUiAPI; | ||
| const errorElement: ReactElement | null = getErrorElement(haapiStepperUiAPI, errorRenderInterceptor); | ||
| const linksToDisplay = getLinksToDisplay(error, currentStep); | ||
| const messagesToDisplay = error?.input ? error.input.dataHelpers.messages : currentStep.dataHelpers.messages; | ||
|
|
||
| const messagesElement = getMessagesElement(haapiUIStepperAPI, messagesToDisplay, messageRenderInterceptor); | ||
| const messagesElement = getMessagesElement(haapiStepperUiAPI, messagesToDisplay, messageRenderInterceptor); | ||
| const actionsElement = getActionsElement( | ||
| haapiUIStepperAPI, | ||
| haapiStepperUiAPI, | ||
| actionsRenderInterceptor, | ||
| formActionRenderInterceptor, | ||
| formFieldRenderInterceptor, | ||
| selectorActionRenderInterceptor, | ||
| clientOperationActionRenderInterceptor | ||
| ); | ||
| const linksElement = getLinksElement(haapiUIStepperAPI, linksToDisplay, linkRenderInterceptor); | ||
| const linksElement = getLinksElement(haapiStepperUiAPI, linksToDisplay, linkRenderInterceptor); | ||
|
|
||
| return ( | ||
| <Well> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need to be so specific here. Maybe just say some UIs need more tailored behavior or so.
Also no need to have such details in the table below.