Skip to content

Commit 6fdfd8a

Browse files
feat: update claims management ui and navigation (#38380)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Updated claims navigation and UI https://www.figma.com/design/HTAO1SrmixV4ppv7qIvLoa/Metamask-Transaction-Shield?node-id=14873-117456&t=Vl0Gf1X669HvXRaY-4 <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/38380?quickstart=1) ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Updated claims navigation and UI ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** **Empty list** <img width="404" height="606" alt="Screenshot 2025-12-04 at 2 10 26 AM" src="https://github.com/user-attachments/assets/291c8647-3a9c-4499-8042-a5c953cb03fb" /> **Navigation and UI changes** <video src="https://github.com/user-attachments/assets/341d4106-c45d-458a-a560-fa0a9facfb1c" /> <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Revamps Transaction Shield claims with a tabbed list (pending/history), new per‑state groupings, split view routes, updated navigation/tests, and refreshed i18n; removes legacy status labels. > > - **Transaction Shield – Claims UX overhaul** > - **UI/Navigation**: > - New tabbed `ClaimsList` with `pending` and `history` tabs; groups: `shieldClaimGroupActive`, `shieldClaimGroupCompleted`, `shieldClaimGroupRejected`, with empty-state UIs and submit buttons. > - Split view routes in `ui/helpers/constants/routes.ts`: `TRANSACTION_SHIELD_CLAIM_ROUTES.VIEW_PENDING` and `...VIEW_HISTORY`; `ClaimsArea` routes updated accordingly. > - `TransactionShield` “Submit a claim” now navigates to `TRANSACTION_SHIELD_CLAIM_ROUTES.BASE` (claims list) instead of `/new-claim`. > - `settings.container`: dynamic tab title via `shieldClaimsListTitle`; back-navigation uses `?tab=pending|history` when returning from view pages. > - **Claims state**: > - `ui/contexts/claims/claims.tsx`: derive `pendingClaims`, `completedClaims` (`APPROVED`), `rejectedClaims`; remove ad‑hoc `claimNumber` mapping. > - **Claims form**: > - View route handling via `isView`; removes previous pending-claims banner; uses `refetchClaims` and returns to claims base after submit. > - **E2E tests & page objects**: > - New selectors/actions for claims list (`clickSubmitClaimButton`, `clickEmptyNewClaimButton`); remove `checkClaimStatus`. > - Tests updated to navigate through `claims` list, click “Submit a claim”, verify created claim, and open via new view route. > - **i18n**: > - Add strings for claim groups/tabs and empty-state copy; update `shieldClaimsListTitle` to “`Claims`”; add `shieldClaimsTabPending`/`shieldClaimsTabHistory`; remove old per-status label keys. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 474bb11. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Chaitanya Potti <[email protected]>
1 parent 2e8d577 commit 6fdfd8a

File tree

12 files changed

+372
-227
lines changed

12 files changed

+372
-227
lines changed

app/_locales/en/messages.json

Lines changed: 30 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/_locales/en_GB/messages.json

Lines changed: 30 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/e2e/page-objects/pages/settings/shield/shield-claims-list-page.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { Driver } from '../../../../webdriver/driver';
33
export default class ShieldClaimsListPage {
44
private readonly driver: Driver;
55

6+
private readonly emptyNewClaimButton =
7+
'[data-testid="claims-list-empty-new-claim-button"]';
8+
9+
private readonly submitClaimButton =
10+
'[data-testid="claims-list-submit-claim-button"]';
11+
612
private readonly claimItem = (claimId: string) =>
713
`[data-testid="claim-item-${claimId}"]`;
814

@@ -22,17 +28,18 @@ export default class ShieldClaimsListPage {
2228
await this.driver.waitForSelector(this.claimItem(claimId));
2329
}
2430

25-
async checkClaimStatus(claimId: string, statusText: string): Promise<void> {
26-
console.log(`Checking if claim ${claimId} has status: ${statusText}`);
27-
const claimItemSelector = this.claimItem(claimId);
28-
await this.driver.waitForSelector({
29-
css: claimItemSelector,
30-
text: statusText,
31-
});
32-
}
33-
3431
async clickClaimItem(claimId: string): Promise<void> {
3532
console.log(`Clicking on claim ${claimId} to view details`);
3633
await this.driver.clickElement(this.claimItem(claimId));
3734
}
35+
36+
async clickEmptyNewClaimButton(): Promise<void> {
37+
console.log('Clicking on empty new claim button');
38+
await this.driver.clickElement(this.emptyNewClaimButton);
39+
}
40+
41+
async clickSubmitClaimButton(): Promise<void> {
42+
console.log('Clicking on submit claim button');
43+
await this.driver.clickElement(this.submitClaimButton);
44+
}
3845
}

test/e2e/tests/shield/shield-subscription-management.spec.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ describe('Shield Plan Stripe Integration', function () {
7474

7575
await shieldDetailPage.clickSubmitCaseButton();
7676

77+
const shieldClaimsListPage = new ShieldClaimsListPage(driver);
78+
await shieldClaimsListPage.checkPageIsLoaded();
79+
await shieldClaimsListPage.clickSubmitClaimButton();
80+
7781
const shieldClaimPage = new ShieldClaimPage(driver);
7882
await shieldClaimPage.checkPageIsLoaded();
7983

@@ -91,13 +95,10 @@ describe('Shield Plan Stripe Integration', function () {
9195

9296
await shieldClaimPage.checkSuccessMessageDisplayed();
9397

94-
const shieldClaimsListPage = new ShieldClaimsListPage(driver);
9598
await shieldClaimsListPage.checkPageIsLoaded();
9699

97100
const { claimId } = SUBMIT_CLAIMS_RESPONSE;
98101
await shieldClaimsListPage.checkClaimExists(claimId);
99-
await shieldClaimsListPage.checkClaimStatus(claimId, 'Created');
100-
101102
await shieldClaimsListPage.clickClaimItem(claimId);
102103

103104
await shieldClaimPage.checkPageIsLoadedInViewMode();
@@ -142,6 +143,10 @@ describe('Shield Plan Stripe Integration', function () {
142143

143144
await shieldDetailPage.clickSubmitCaseButton();
144145

146+
const shieldClaimsListPage = new ShieldClaimsListPage(driver);
147+
await shieldClaimsListPage.checkPageIsLoaded();
148+
await shieldClaimsListPage.clickSubmitClaimButton();
149+
145150
const shieldClaimPage = new ShieldClaimPage(driver);
146151
await shieldClaimPage.checkPageIsLoaded();
147152

@@ -194,6 +199,10 @@ describe('Shield Plan Stripe Integration', function () {
194199

195200
await shieldDetailPage.clickSubmitCaseButton();
196201

202+
const shieldClaimsListPage = new ShieldClaimsListPage(driver);
203+
await shieldClaimsListPage.checkPageIsLoaded();
204+
await shieldClaimsListPage.clickSubmitClaimButton();
205+
197206
const shieldClaimPage = new ShieldClaimPage(driver);
198207
await shieldClaimPage.checkPageIsLoaded();
199208

ui/contexts/claims/claims.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import { getShieldClaims } from '../../store/actions';
1414
type ClaimsContextType = {
1515
claims: Claim[];
1616
pendingClaims: Claim[];
17-
historyClaims: Claim[];
17+
completedClaims: Claim[];
18+
rejectedClaims: Claim[];
1819
isLoading: boolean;
1920
error: Error | null;
2021
refetchClaims: () => Promise<void>;
@@ -50,13 +51,11 @@ export const ClaimsProvider: React.FC<ClaimsProviderProps> = ({ children }) => {
5051
const dateB = new Date(b.createdAt).getTime();
5152
return dateB - dateA;
5253
})
53-
.map((claim: Claim, index: number) => {
54+
.map((claim: Claim) => {
5455
const numberChain = Number(claim.chainId);
5556
const chainId = isNaN(numberChain) ? '' : numberToHex(numberChain);
5657
return {
5758
...claim,
58-
// used for displaying list of claims
59-
claimNumber: index + 1,
6059
chainId,
6160
};
6261
});
@@ -78,22 +77,33 @@ export const ClaimsProvider: React.FC<ClaimsProviderProps> = ({ children }) => {
7877
);
7978
}, [claims]);
8079

81-
const historyClaims = useMemo(() => {
82-
return claims.filter(
83-
(claim) => !PENDING_CLAIM_STATUSES.includes(claim.status),
84-
);
80+
const completedClaims = useMemo(() => {
81+
return claims.filter((claim) => claim.status === ClaimStatusEnum.APPROVED);
82+
}, [claims]);
83+
84+
const rejectedClaims = useMemo(() => {
85+
return claims.filter((claim) => claim.status === ClaimStatusEnum.REJECTED);
8586
}, [claims]);
8687

8788
const value = useMemo(
8889
() => ({
8990
claims,
9091
pendingClaims,
91-
historyClaims,
92+
completedClaims,
93+
rejectedClaims,
9294
isLoading,
9395
error,
9496
refetchClaims: fetchClaims,
9597
}),
96-
[claims, pendingClaims, historyClaims, isLoading, error, fetchClaims],
98+
[
99+
claims,
100+
pendingClaims,
101+
completedClaims,
102+
rejectedClaims,
103+
isLoading,
104+
error,
105+
fetchClaims,
106+
],
97107
);
98108

99109
return (

ui/helpers/constants/routes.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ export const TRANSACTION_SHIELD_CLAIM_ROUTES = {
2525
FULL: `${TRANSACTION_SHIELD_CLAIMS}/new-claim`,
2626
RELATIVE: '/new-claim',
2727
},
28-
VIEW: {
29-
FULL: `${TRANSACTION_SHIELD_CLAIMS}/view-claim`,
30-
RELATIVE: '/view-claim',
28+
VIEW_PENDING: {
29+
FULL: `${TRANSACTION_SHIELD_CLAIMS}/view-pending-claim`,
30+
RELATIVE: '/view-pending-claim',
31+
},
32+
VIEW_HISTORY: {
33+
FULL: `${TRANSACTION_SHIELD_CLAIMS}/view-history-claim`,
34+
RELATIVE: '/view-history-claim',
3135
},
3236
} as const;
3337
export const SECURITY_ROUTE = '/settings/security';

ui/pages/settings/settings.container.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { getIsMetaMaskShieldFeatureEnabled } from '../../../shared/modules/envir
5252
import { getHasSubscribedToShield } from '../../selectors/subscription/subscription';
5353
import { SHIELD_QUERY_PARAMS } from '../../../shared/lib/deep-links/routes/shield';
5454
import Settings from './settings.component';
55+
import { CLAIMS_TAB_KEYS } from './transaction-shield-tab/types';
5556

5657
const ROUTES_TO_I18N_KEYS = {
5758
[ABOUT_US_ROUTE]: 'about',
@@ -114,8 +115,11 @@ const mapStateToProps = (state, ownProps) => {
114115
const isShieldClaimNewPage = Boolean(
115116
pathname.match(TRANSACTION_SHIELD_CLAIM_ROUTES.NEW.FULL),
116117
);
117-
const isShieldClaimViewPage = Boolean(
118-
pathname.startsWith(TRANSACTION_SHIELD_CLAIM_ROUTES.VIEW.FULL),
118+
const isShieldClaimViewActivePage = Boolean(
119+
pathname.startsWith(TRANSACTION_SHIELD_CLAIM_ROUTES.VIEW_PENDING.FULL),
120+
);
121+
const isShieldClaimViewCompletedPage = Boolean(
122+
pathname.startsWith(TRANSACTION_SHIELD_CLAIM_ROUTES.VIEW_HISTORY.FULL),
119123
);
120124
const isShieldClaimBasePage = Boolean(
121125
pathname.startsWith(TRANSACTION_SHIELD_CLAIM_ROUTES.BASE),
@@ -134,8 +138,8 @@ const mapStateToProps = (state, ownProps) => {
134138
pathnameI18nKey = 'securitySrpWalletRecovery';
135139
}
136140

137-
// If pathname is `TRANSACTION_SHIELD_CLAIM_VIEW_ROUTE` rename the tab title to "Claim details"
138-
if (pathname.startsWith(TRANSACTION_SHIELD_CLAIM_ROUTES.VIEW.FULL)) {
141+
// If pathname is view claim route rename the tab title to "Claim details"
142+
if (isShieldClaimViewActivePage || isShieldClaimViewCompletedPage) {
139143
pathnameI18nKey = 'shieldClaimsListTitle';
140144
}
141145

@@ -151,11 +155,13 @@ const mapStateToProps = (state, ownProps) => {
151155
} else if (isRevealSrpListPage || isPasswordChangePage) {
152156
backRoute = SECURITY_ROUTE;
153157
} else if (isShieldClaimNewPage) {
154-
backRoute = TRANSACTION_SHIELD_ROUTE;
155-
} else if (isShieldClaimViewPage) {
156158
backRoute = TRANSACTION_SHIELD_CLAIM_ROUTES.BASE;
159+
} else if (isShieldClaimViewActivePage) {
160+
backRoute = `${TRANSACTION_SHIELD_CLAIM_ROUTES.BASE}?tab=${CLAIMS_TAB_KEYS.PENDING}`;
161+
} else if (isShieldClaimViewCompletedPage) {
162+
backRoute = `${TRANSACTION_SHIELD_CLAIM_ROUTES.BASE}?tab=${CLAIMS_TAB_KEYS.HISTORY}`;
157163
} else if (isShieldClaimBasePage) {
158-
backRoute = TRANSACTION_SHIELD_CLAIM_ROUTES.NEW.FULL;
164+
backRoute = TRANSACTION_SHIELD_ROUTE;
159165
}
160166

161167
const addressName = getAddressBookEntryOrAccountName(

0 commit comments

Comments
 (0)