feat(widget): add per-tab navigation config#781
Conversation
🦋 Changeset detectedLatest commit: 8df9a50 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
E2E Examples — all passedAll examples passed in the latest run. |
E2E Playground resultsDetails
📥 Download full HTML report (open the run → Artifacts → |
53fa01e to
9f97fb1
Compare
572513c to
a80d82f
Compare
9c39a0a to
6ae7bef
Compare
a80d82f to
65f2d16
Compare
5b24746 to
dc8bbac
Compare
✅ E2E Dev Smoke — passing
4 passed · 0 failed · 0 skipped · 23s |
🔍 QA Review — EMB-465PR: #781 feat(widget): add per-tab navigation config What this PR doesReplaces the widget-internal Acceptance criteria check
Issues1. [Medium] AC 3 — Form fields not cleared unconditionally on tab switchFile: The AC states: "Switching tabs re-applies the active tab's config and clears the form fields ( The implementation relies on To clear fields under the current implementation, an integrator must explicitly include Suggested fix: In 2. [Low]
|
There was a problem hiding this comment.
Requesting changes on 2 items — each requires either a code fix or an explicit acceptance comment with justification before this review is considered complete.
| # | Severity | Type | Issue / File |
|---|---|---|---|
| 1 | 🟠 Medium | Code | AC 3 not met: form fields (fromAmount/fromToken/toToken) not cleared unconditionally on tab switch |
| 2 | 🟢 Low | Code | packages/widget/src/stores/navigationTabs/useNavigationTabsStore.tsx — navigationTabs not memoized, unstable reference in useMemo deps |
1. [Medium] AC 3 — Form fields not cleared unconditionally on tab switch
The AC states: "Switching tabs re-applies the active tab's config and clears the form fields (fromAmount / fromToken / toToken)."
The current implementation does not do this unconditionally. FormStoreProvider only includes form fields in reactiveFormValues (and therefore in setUserAndDefaultValues) when Object.hasOwn(tabConfig, '<field>') is true. For a typical tab config such as { mode: 'default', variant: 'wide' }, none of fromAmount, fromToken, or toToken are own properties of the merged tabConfig, so FormUpdater never resets them. The user's field selections persist unchanged after switching tabs.
For clearing to work under the current implementation, an integrator must explicitly include { fromToken: undefined, fromAmount: undefined, toToken: undefined } in every tab config — this is unintuitive, undocumented, and directly contradicts the AC.
Suggested fix: Add a useEffect in NavigationTabsStoreProvider (or HeaderNavigationTabs) that fires when activeTab changes and unconditionally resets those three fields. Alternatively, if the intended design is integrator opt-in, update the AC/documentation to clarify this and add a JSDoc example to NavigationTabConfig.
2. [Low] Unstable navigationTabs reference in tabConfig useMemo deps
In useNavigationTabsStore.tsx, navigationTabs is computed inline on every render via getNavigationTabs(config). When this returns [] (no tabs configured, non-split mode), each call produces a new array reference, which causes the tabConfig useMemo to re-execute on every render even when nothing has changed. The correctness impact is zero (the memo always returns the same stable widgetConfig reference in this path), but it is unnecessary work on every render of NavigationTabsStoreProvider.
Fix: Return a module-level empty-array constant from getNavigationTabs when returning [] (analogous to the existing splitTabs constant), or wrap the call: const navigationTabs = useMemo(() => getNavigationTabs(config), [config]).
💡 Once you've addressed the items above, re-apply the "Agent Review Request" label to trigger an automated re-review.
Which Linear task is linked to this PR?
https://linear.app/lifi-linear/issue/EMB-465/per-tab-config-for-header-navigation-tabs
Why was it implemented this way?
Generalises a header navigation tab's overrides from a fixed, widget-side preset to integrator-supplied config.
Per-tab config on
_navigationTabs. Changes the entry shape fromNavigationTabKey[]to{ tabKey, config: Partial<WidgetConfig> }[]. The widget-side preset table (navigationTabsByKey, which bakedvariant/mode/modeOptionsper key) is removed — each tab's config is now supplied by the integrator and layered onto the widget config ({ ...widgetConfig, ...activeConfig }) while the tab is active, with omitted fields falling back to the base config. A tab can therefore override any config field, not just the three preset ones.useSplitModederives the effective split side from the active tab's resolved config (resolveSplitMode); existingmode: 'split'andmode: 'default'behaviour is unchanged.Scope / trade-offs.
NavigationTabsStoreProvidersits insideStoreProvider— belowSDKClientProvider,ThemeProviderandI18nProvider— so tab overrides apply to the widget body only.sdkConfig,theme/appearanceandlanguagesare read above this provider and are not tab-overridable.Visual showcase (Screenshots or Videos)
N/A
Checklist before requesting a review