Skip to content

feat(widget): apply per-tab navigation config #781

Open
effie-ms wants to merge 12 commits into
mainfrom
feat/private-tab-mode
Open

feat(widget): apply per-tab navigation config #781
effie-ms wants to merge 12 commits into
mainfrom
feat/private-tab-mode

Conversation

@effie-ms

@effie-ms effie-ms commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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?

This PR applies the tab configuration for internally supported navigation tabs for Jumper.

NB: use this SDK type change to pass private flag for "Private" tab: lifinance/sdk#411

Visual showcase (Screenshots or Videos)

N/A

Checklist before requesting a review

  • I have performed a self-review and testing of my code.
  • This pull request is focused and addresses a single problem.
  • If this PR modifies the Widget API or adds new features that require documentation, I have updated the documentation in the public-docs repository.

@changeset-bot

changeset-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6358700

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@effie-ms effie-ms changed the base branch from main to feat/amount-input-cards June 12, 2026 15:14
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

E2E Examples — all passed

All examples passed in the latest run.

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

E2E Playground results

passed  158 passed

Details

stats  158 tests across 10 suites
duration  2 minutes, 22 seconds
commit  6358700

📥 Download full HTML report (open the run → Artifacts → playwright-report)

@effie-ms effie-ms force-pushed the feat/amount-input-cards branch from 53fa01e to 9f97fb1 Compare June 17, 2026 11:22
@effie-ms effie-ms force-pushed the feat/private-tab-mode branch 2 times, most recently from 572513c to a80d82f Compare June 18, 2026 08:45
@effie-ms effie-ms self-assigned this Jun 18, 2026
@effie-ms effie-ms force-pushed the feat/amount-input-cards branch 2 times, most recently from 9c39a0a to 6ae7bef Compare June 22, 2026 08:27
@effie-ms effie-ms force-pushed the feat/private-tab-mode branch from a80d82f to 65f2d16 Compare June 22, 2026 11:20
@effie-ms effie-ms changed the base branch from feat/amount-input-cards to feat/header-tabs-store June 22, 2026 11:21
Base automatically changed from feat/header-tabs-store to main June 22, 2026 13:18
@effie-ms effie-ms changed the title feat(widget): add private mode requiring send-to-wallet address feat(widget): per-tab navigation config and per-tab sdkConfig routing Jun 22, 2026
@effie-ms effie-ms force-pushed the feat/private-tab-mode branch from 5b24746 to dc8bbac Compare June 22, 2026 15:31
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

✅ E2E Dev Smoke — passing

Check Result
Dev server start (pnpm dev) ✅ started
Smoke tests ✅ passed

4 passed · 0 failed · 0 skipped · 22s

View run

@effie-ms effie-ms changed the title feat(widget): per-tab navigation config and per-tab sdkConfig routing feat(widget): add per-tab navigation config Jun 23, 2026
@effie-ms effie-ms added the Agent Review Request triggers QA Agent Zeus label Jun 23, 2026
@effie-ms effie-ms marked this pull request as ready for review June 23, 2026 16:01
@github-actions github-actions Bot added QA AI Reviewing and removed Agent Review Request triggers QA Agent Zeus labels Jun 23, 2026
@lifi-qa-agent

lifi-qa-agent Bot commented Jun 23, 2026

Copy link
Copy Markdown

🔍 QA Review — EMB-465

PR: #781 feat(widget): add per-tab navigation config
Review type: 🔁 Re-review
Verdict: ✅ Pass


Re-review summary

The developer responded to all 3 items from the first review (2 formal change requests + 1 advisory). All items are now resolved or accepted with justification. The original Issue 1 (Medium) was a false positive — the form-clearing code path was in HeaderTabs.handleChange, not in FormStoreProvider; the first review traced the wrong code path. All 8 acceptance criteria are confirmed met.


Acceptance criteria check

# Criterion Status Evidence
1 _navigationTabs accepts { tabKey, config: Partial<WidgetConfig> }[]; widget-side navigationTabsByKey preset table removed ✅ Met NavigationTabConfig interface added in types/widget.ts; navigationTabsByKey, getTabMode, getTabVariant, getTabModeOptions deleted from utils.ts
2 Active tab's config merged onto widget config; omitted fields fall back to base config; any config field overridable ✅ Met tabConfig useMemo: return { ...widgetConfig, ...activeConfig }; returns plain widgetConfig when no active config
3 Switching tabs re-applies active tab config and clears fromAmount/fromToken/toToken ✅ Met HeaderTabs.handleChange unconditionally calls setFieldValue('fromAmount', ''), setFieldValue('fromToken', ''), setFieldValue('toToken', '') before onChangesetActiveTab. This runs for every tab switch regardless of the tab's config. The first review incorrectly traced the FormStoreProvider Object.hasOwn path — that path handles reactive default propagation, not field clearing on switch.
4 No _navigationTabs + mode: 'split': Swap/Bridge tabs still show; routing constrained as before ✅ Met getNavigationTabs returns splitTabs constant when _navigationTabs absent and mode === 'split'; unit-tested
5 mode: 'default' (or unset): no tabs render; split mode resolves to undefined ✅ Met resolveSplitMode test case 4 confirms undefined; getNavigationTabs returns []
6 _navigationTabs remains _-prefixed / internal, not public API ✅ Met @internal JSDoc on NavigationTabConfig and _navigationTabs; changeset is minor; no public API surface change
7 sdkConfig, theme/appearance, languages not tab-overridable ✅ Met Architectural: ThemeProvider, SDKClientProvider, I18nProvider sit above StoreProviderNavigationTabsStoreProvider in AppProvider
8 Unit tests cover tab resolution and resolveSplitMode; type-check and existing tests pass ✅ Met utils.test.ts covers getNavigationTabs, getInitialActiveTab, and 4 resolveSplitMode scenarios; 158 E2E tests passing

Resolution of previously flagged items

# Item Severity Resolution
1 AC 3: Form fields not cleared unconditionally on tab switch 🟠 Medium ✅ Resolved — original finding was a false positive. HeaderTabs.handleChange unconditionally clears all three fields (fromAmount, fromToken, toToken) via setFieldValue before calling setActiveTab. The first review traced FormStoreProvider's reactive default path instead. No code change required.
2 Unstable [] reference in getNavigationTabs useMemo dep 🟢 Low ✅ Accepted with justification — in this path the memo re-executes but returns the already-stable widgetConfig reference, so no downstream re-renders occur; the only cost is .find() over an empty array per render. Reasonable trade-off.
3 No JSDoc on non-effective fields in NavigationTabConfig.config 💡 Advisory ✅ Done — JSDoc added in commit dfc0e3cb documenting that sdkConfig/theme/appearance/languages are not tab-overridable.

Test coverage

Existing coverage confirmed:

  • utils.test.ts: getNavigationTabs (configured tabs, implicit split, pin early-exit), getInitialActiveTab (configured / split / default), resolveSplitMode (4 cases covering all paths).
  • 158 E2E playground tests passing; dev smoke (4/4).

Remaining gap (not blocking): No integration test for the tabConfig merge in NavigationTabsStoreProvider and no component test for HeaderTabs.handleChange field-clearing. Both are low-risk given the simplicity of the logic and E2E coverage, but would strengthen confidence in future refactors.


Downstream impact

Split-tab regression risk: Low — resolveSplitMode tested for all four paths; implicit split behaviour unchanged.

Breaking change for _navigationTabs string consumers: NavigationTabKey[]NavigationTabConfig[] shape change. Any integrator using _navigationTabs: ['default', 'private'] will get a TypeScript error. Changeset correctly typed as minor (internal API — _-prefixed).


QA review by lifi-qa-agent · EMB-465 · PR #781

@lifi-qa-agent lifi-qa-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.tsxnavigationTabs 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.

@effie-ms

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Addressed below:

1. [Medium] Form fields not cleared on tab switch — not reproducing; already handled.
Clearing does happen unconditionally on every tab switch, just not via the reactiveFormValues/Object.hasOwn path that was traced. HeaderTabs.handleChange (packages/widget/src/components/Header/HeaderTabs.tsx:29-33) calls setFieldValue('fromAmount', ''), setFieldValue('fromToken', ''), setFieldValue('toToken', '') before delegating to onChangesetActiveTab. This runs for any tab switch regardless of the tab's config (e.g. a tab that only changes variant), and setActiveTab is only ever reached through this path. So AC 3 is satisfied. No code change.

2. [Low] Memoize the empty navigationTabs reference — declining.
In this path the tabConfig memo only re-returns the already-stable widgetConfig reference, so re-execution triggers no re-renders; the sole cost is a .find() over an empty array per render. Not worth the added module-level constant.

3. [Advisory] Document non-effective fields on NavigationTabConfig.config — done.
Added JSDoc noting sdkConfig/theme/appearance/languages are read above the tab provider and are not tab-overridable (dfc0e3c).

@effie-ms effie-ms added the Agent Review Request triggers QA Agent Zeus label Jun 24, 2026
@github-actions github-actions Bot removed the Agent Review Request triggers QA Agent Zeus label Jun 24, 2026

@lifi-qa-agent lifi-qa-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All AC items confirmed met. Original Issue 1 (form-field clearing) was a false positive — HeaderTabs.handleChange clears fields unconditionally. Issue 2 accepted with justification. Issue 3 (JSDoc) done in dfc0e3c. ✅ QA Pass.

@effie-ms effie-ms changed the title feat(widget): add per-tab navigation config feat(widget): apply per-tab navigation config Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant