refactor: migrate BadgeCount to union and shared types#942
Conversation
📖 Storybook Preview |
9ead452 to
a4c2e1b
Compare
📖 Storybook Preview |
📖 Storybook Preview |
📖 Storybook Preview |
Removed duplicate export of BadgeCountSize from React Native BadgeCount.types.ts to match React package and BadgeStatus golden path pattern. Enum is exported only from index.ts, ensuring cross-platform consistency and 100% test coverage. Addresses cursor bot feedback: #942 (comment)
📖 Storybook Preview |
Establishes foundational architectural patterns for components: - ADR-0003: Const objects over TypeScript enums - ADR-0004: Centralized types in shared package - Layered architecture with one-way dependencies - Two-file export pattern preventing test coverage loss This rule provides the foundation that other cursor rules reference. Validated through BadgeCount migration (PR #942) which demonstrated the coverage issue when duplicate exports exist. References BadgeStatus as the golden path proof-of-concept for these patterns.
Establishes foundational architectural patterns for components: - ADR-0003: Const objects over TypeScript enums - ADR-0004: Centralized types in shared package - Layered architecture with one-way dependencies - Two-file export pattern preventing test coverage loss This rule provides the foundation that other cursor rules reference. Validated through BadgeCount migration (PR #942) which demonstrated the coverage issue when duplicate exports exist. References BadgeStatus as the golden path proof-of-concept for these patterns.
Provides step-by-step HOW-TO guide for creating components following ADR-0003 and ADR-0004 patterns. This rule is the technical implementation guide referenced by component-migration.md workflow. **Key guidance:** - Using create-component scripts (never manual file creation) - Two-file export pattern to prevent test coverage loss - Shared types in @metamask/design-system-shared - Cross-platform consistency between React and React Native - Transforming generated templates to ADR-compliant code **Critical anti-patterns documented:** - Re-exporting const objects from .types.ts files - Using TypeScript enums instead of const objects - Not creating shared types (violates ADR-0004) - Including platform props in shared types Validated through BadgeCount migration (PR #942) which exposed the duplicate export coverage issue this rule prevents. References BadgeStatus as the golden path proof-of-concept.
Provides workflow for refactoring existing monorepo components to ADR-0003 (const objects) and ADR-0004 (centralized types) patterns. This is specifically for internal cleanup of components that have duplicate const object definitions across React and React Native. **Key workflow steps:** - Identify components with duplicate const objects across platforms - Create shared types in @metamask/design-system-shared - Update platform packages to import (not re-export) from shared - Remove duplicate exports from platform type indices - Two-file export pattern: .types.ts imports only, index.ts exports **Critical mistakes documented:** - Re-exporting const objects from .types.ts (causes coverage loss) - Using interface instead of type for shared props (ESLint error) - Separate type exports causing "Duplicate identifier" errors - Wrong import ordering (platform before shared) - Including platform props in shared types **NOT for new components:** - This rule is for existing component cleanup only - For new components, see component-creation.md or component-migration.md Validated through BadgeCount migration (PR #942) which demonstrated the exact coverage issue this rule prevents. References BadgeStatus as the golden path and PR #912 as complete migration example.
Provides priority workflow for bringing components from MetaMask extension or mobile codebases into the design system monorepo. This is the PRIMARY workflow AI agents should use for component work, as most components originate from consumer applications. **Complete migration workflow:** 1. Assess component in extension/mobile for suitability 2. Scaffold using create-component scripts 3. Extract implementation from source (preserving logic and tests) 4. Create shared types in @metamask/design-system-shared 5. Transform to use Box/Text primitives and design tokens 6. Implement in BOTH React and React Native for consistency 7. Create Storybook stories and documentation 8. Verify build/test/lint before PR **Critical patterns:** - Two-file export structure: .types.ts imports only, index.ts exports - Shared types in @metamask/design-system-shared (ADR-0004) - Const objects with as const (ADR-0003) - Cross-platform consistency between implementations - References component-creation.md for technical scaffolding steps **When to use:** - Bringing component from extension or mobile (PRIORITY) - Component exists in consumer app but not in design system - User references extension or mobile component **NOT for:** - Brand new components with no prior implementation (use component-creation.md) - Internal monorepo refactoring (use component-enum-union-migration.md) Validated through BadgeCount migration (PR #942) which demonstrated the importance of correct export patterns for test coverage. References BadgeStatus as the golden path proof-of-concept.
- Remove Box/Button examples (align with PR #943 - these components don't follow ADR-0003/0004 yet) - Add Step 11: Verify Coverage section with critical warning about duplicate exports - Add coverage check to verification checklist - Clarify type vs interface rule with composition/intersection rationale - Reference BadgeCount PR #942 coverage issue as real-world example Addresses review feedback from PR #944 code review
## **Description** Adds `component-architecture.md` cursor rule establishing foundational architectural patterns for design system components. This rule documents [ADR-0003](https://github.com/MetaMask/decisions/blob/main/decisions/design-system/0003-enum-to-string-union-migration.md) (const objects over TypeScript enums) and [ADR-0004](https://github.com/MetaMask/decisions/blob/main/decisions/design-system/0004-centralized-types-architecture.md) (centralized types architecture) patterns. **Key patterns documented:** - Two-file export structure: `.types.ts` for imports only, `index.ts` for exports only - Const objects with `as const` instead of TypeScript enums - Centralized types in `@metamask/design-system-shared` package - Layered architecture with one-way dependencies (shared → platform packages) - Cross-platform consistency between React and React Native **Why this matters:** We need to ensure our components are consistent across platforms and use the most up to date typescript conventions **References:** - Golden path: BadgeStatus component implementation - Validation: BadgeCount PR #942 was created using component-architecture.md, component-creation.md, component-migration.md ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-339 ## **Manual testing steps** 1. Review the cursor rule file: `.cursor/rules/component-architecture.md` 2. Check out the BadgeCount PR #942 3. Verify CLAUDE.md includes reference to this new rule ## **Screenshots/Recordings** N/A - Documentation only ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable (N/A - documentation) - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable (N/A - markdown) - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.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] > **Low Risk** > Low risk documentation-only change that doesn’t affect runtime behavior. Risk is limited to developers/agents following the new guidance incorrectly. > > **Overview** > Adds a new Cursor rule, `.cursor/rules/component-architecture.md`, documenting the design-system component architecture standards (ADR-0003 const-object string unions over enums, ADR-0004 centralized shared types, and layered shared vs platform props), including export/re-export patterns and a cross-platform verification checklist. > > Updates AI-agent docs to reference the new rule (`CLAUDE.md`, `docs/ai-agents.md`) and clarifies Storybook story naming conventions in `.cursor/rules/component-documentation.md` with explicit prop-to-story examples. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit bc55943. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude <noreply@anthropic.com>
## **Description** Adds `component-creation.md` cursor rule providing step-by-step technical guidance for creating design system components. This HOW-TO guide ensures AI agents follow ADR-0003 (const objects) and ADR-0004 (centralized types) patterns when scaffolding new components. **Key guidance provided:** - Using `create-component:react` and `create-component:react-native` scripts - Two-file export structure: `.types.ts` imports only, `index.ts` exports only - Creating shared types in `@metamask/design-system-shared` package - Transforming generated templates to use Box/Text primitives and design tokens generated tailwind classnames - Cross-platform consistency between React and React Native implementations **Critical anti-patterns documented:** - Using TypeScript enums instead of const objects with `as const` - Not creating shared types (violates ADR-0004 layered architecture) - Including platform-specific props in shared types - Leaving template code unchanged (raw div/View elements) **Why this matters:** We need to ensure our components are consistent across platforms and use the most up to date typescript conventions **Relationship to other rules:** - Depends on: `component-architecture.md` (foundational patterns) - Used by: `component-migration.md` (references this for scaffolding steps) **References:** - Golden path: BadgeStatus component (complete implementation) - Validation: BadgeCount PR #942 (demonstrated coverage issue and fix) - Scripts: `create-component:react` and `create-component:react-native` ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-341 ## **Manual testing steps** 1. Review the cursor rule file: `.cursor/rules/component-creation.md` 2. Verify changes in BadgeCount PR #942 align with most up to date coventions 3. Verify golden path examples reference BadgeStatus implementation 4. Check that CLAUDE.md includes reference to this new rule ## **Screenshots/Recordings** N/A - Documentation only ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable (N/A - documentation) - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable (N/A - markdown) - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.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] > **Low Risk** > Documentation-only changes that affect developer/agent guidance, not runtime behavior. Risk is limited to potential confusion if guidance is inaccurate or drifts from actual scripts/architecture. > > **Overview** > Adds a new Cursor rule `component-creation.md` that standardizes the step-by-step process for creating cross-platform design-system components, including shared-types-first (ADR-0003/0004), Box/Text + token usage, and a strict export pattern (`.types.ts` imports only; `index.ts` is the single const-object export point to avoid coverage gaps). > > Updates existing AI-agent docs to reference this new rule, expands Storybook story naming guidance with concrete examples, and documents the `create-component:react-native` scaffolding command alongside the React one. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3d3ae57. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude <noreply@anthropic.com>
## **Description** Adds `component-enum-union-migration.md` cursor rule providing workflow for refactoring existing monorepo components to ADR-0003 (const objects) and ADR-0004 (centralized types) patterns. This rule is specifically for internal cleanup of components with duplicate const object definitions across React and React Native packages. **When to use this workflow:** - Component already exists in both React and React Native packages - Const objects are duplicated across platform packages - Types are defined separately in each platform (no shared source) - Component API needs to be unified/modernized **Key workflow documented:** 1. Identify components with duplicate const objects across platforms 2. Create shared types in `@metamask/design-system-shared` package 3. Update React package to import (not re-export) shared types 4. Update React Native package to import (not re-export) shared types 5. Remove old duplicates from platform type indices 6. Update component index.ts to export directly from shared 7. Verify build/test/lint **Critical mistakes documented:** - Re-exporting const objects from `.types.ts` files (causes test coverage loss) - Using `interface` instead of `type` for shared props (ESLint error) - Separate type exports causing "Duplicate identifier" TypeScript errors - Wrong import ordering (platform imports before shared imports) - Including platform-specific props in shared types (violates ADR-0004) **Why this matters:** The BadgeCount migration (PR #942) revealed that duplicate const object exports in `.types.ts` files create uncovered code paths, failing Jest coverage thresholds at 81.81% instead of 100%. This rule documents the exact two-file pattern needed to prevent this issue. **Relationship to other rules:** - Depends on: `component-architecture.md` (foundational ADR patterns) - Different from: `component-creation.md` (new components) and `component-migration.md` (extension/mobile migration) - Use case: Internal monorepo cleanup only **References:** - Golden path: BadgeStatus component (SOURCE OF TRUTH for shared types) - Complete example: PR #912 (full migration with all file changes) - Validation: BadgeCount PR #942 (demonstrated coverage issue and fix) ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-344 ## **Manual testing steps** 1. Review the cursor rule file: `.cursor/rules/component-enum-union-migration.md` 2. Verify it references BadgeCount PR #942 and PR #912 3. Check workflow steps match the BadgeCount migration pattern 4. Confirm anti-patterns section shows duplicate export mistake 5. Verify it clarifies this is NOT for new components 6. Check golden path examples reference BadgeStatus implementation 7. Confirm verification checklist covers both platforms 8. Verify CLAUDE.md includes reference to this new rule ## **Screenshots/Recordings** N/A - Documentation only ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable (N/A - documentation) - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable (N/A - markdown) - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.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] > **Low Risk** > Documentation-only changes: adds an internal workflow guide and links it from `CLAUDE.md`, with no runtime or build impact. > > **Overview** > Adds a new Cursor rule, `.cursor/rules/component-enum-union-migration.md`, documenting a step-by-step workflow to migrate existing cross-platform components from duplicated enums/consts to **shared const-object + string-union types** per ADR-0003/ADR-0004, including common pitfalls (notably avoiding const re-exports from `.types.ts` to prevent coverage drops). > > Updates `CLAUDE.md` to reference this new rule in the AI-agent documentation list. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 690c364. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
| * Convert from enum to const object (ADR-0003) | ||
| */ | ||
| export const BadgeCountSize = { | ||
| /** |
There was a problem hiding this comment.
Const object with derived string union type following ADR-0003. This replaces the TypeScript enum to enable better tree-shaking and avoid runtime overhead.
| * BadgeCount component shared props (ADR-0004) | ||
| * Platform-independent properties shared across React and React Native | ||
| */ | ||
| export type BadgeCountPropsShared = { |
There was a problem hiding this comment.
Shared props interface (ADR-0004) defines platform-independent properties. Both React and React Native will extend this with their platform-specific concerns like className/twClassName and textProps.
| */ | ||
| style?: React.CSSProperties; | ||
| }; | ||
| export type BadgeCountProps = ComponentProps<'div'> & |
There was a problem hiding this comment.
Platform extension layer following ADR-0004. Extends BadgeCountPropsShared with React-specific concerns: ComponentProps for HTML attributes, className for Tailwind styling, textProps for Text component customization.
| */ | ||
| style?: StyleProp<ViewStyle>; | ||
| } & Omit<ViewProps, 'children'>; | ||
| export type BadgeCountProps = BadgeCountPropsShared & |
There was a problem hiding this comment.
Platform extension layer for React Native following ADR-0004. Extends BadgeCountPropsShared with React Native-specific concerns: ViewProps for native attributes, twClassName for TWRNC styling, textProps for Text component customization.
| @@ -1,3 +1,3 @@ | |||
| export { BadgeCountSize } from '../../types'; | |||
| export { BadgeCountSize } from '@metamask/design-system-shared'; | |||
There was a problem hiding this comment.
Direct export from shared package following ADR-0004 pattern. Component index files should import directly from design-system-shared rather than re-exporting through src/types/index.ts to maintain clear dependency relationships.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Truncated Tailwind class
h-missing height value- Updated the large size container classes to use 'h-5' instead of the invalid truncated 'h-'.
- ✅ Fixed: Test assertion mismatches component's actual
twClassNameoutput- Changed the test to assert
.toContain('custom')to match the component’s'leading-0 custom'output.
- Changed the test to assert
Or push these changes by commenting:
@cursor push fbd98e7e88
Preview (fbd98e7e88)
diff --git a/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.test.tsx b/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.test.tsx
--- a/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.test.tsx
+++ b/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.test.tsx
@@ -140,7 +140,7 @@
expectedTextProps.fontWeight,
);
// Custom twClassName should be applied
- expect(textElement.props.twClassName).toBe('custom');
+ expect(textElement.props.twClassName).toContain('custom');
});
it('applies additional container style and forwards extra props', () => {
diff --git a/packages/design-system-react/src/components/BadgeCount/BadgeCount.constants.ts b/packages/design-system-react/src/components/BadgeCount/BadgeCount.constants.ts
--- a/packages/design-system-react/src/components/BadgeCount/BadgeCount.constants.ts
+++ b/packages/design-system-react/src/components/BadgeCount/BadgeCount.constants.ts
@@ -16,5 +16,5 @@
string
> = {
[BadgeCountSize.Md]: 'min-w-4 h-4 px-1', // min-width 16px, height 14px, padding-horizontal 4
- [BadgeCountSize.Lg]: 'min-w-6 h- px-1.5', // min-width 24px, height 20px, padding-horizontal 6
+ [BadgeCountSize.Lg]: 'min-w-6 h-5 px-1.5', // min-width 24px, height 20px, padding-horizontal 6
};| [BadgeCountSize.Md]: 'min-w-4 h-3.5 py-0 px-1', // min-width 16px, height 14px, padding-vertical 0, padding-horizontal 4 | ||
| [BadgeCountSize.Lg]: 'min-w-6 h-5 py-0.5 px-1.5', // min-width 24px, height 20px, padding-vertical 2, padding-horizontal 6 | ||
| [BadgeCountSize.Md]: 'min-w-4 h-4 px-1', // min-width 16px, height 14px, padding-horizontal 4 | ||
| [BadgeCountSize.Lg]: 'min-w-6 h- px-1.5', // min-width 24px, height 20px, padding-horizontal 6 |
There was a problem hiding this comment.
Truncated Tailwind class h- missing height value
High Severity
The Lg size container class string 'min-w-6 h- px-1.5' contains a truncated h- Tailwind utility with no value. The React Native counterpart correctly uses h-5 for 20px height. This invalid class means the large BadgeCount variant in the React package will have no explicit height, breaking its visual appearance.
|
Bugbot Autofix prepared a fix for the issue found in the latest run.
Or push these changes by commenting: Preview (c33a17570b)diff --git a/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.constants.ts b/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.constants.ts
--- a/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.constants.ts
+++ b/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.constants.ts
@@ -18,3 +18,12 @@
[BadgeCountSize.Md]: 'min-w-4 h-3.5 py-0 px-1', // min-width 16px, height 14px, padding-vertical 0, padding-horizontal 4
[BadgeCountSize.Lg]: 'min-w-6 h-5 py-0.5 px-1.5', // min-width 24px, height 20px, padding-vertical 2, padding-horizontal 6
};
+
+export const TWCLASSMAP_BADGECOUNT_SIZE_LINEHEIGHT: Record<
+ BadgeCountSize,
+ string
+> = {
+ // Match text line-height to container height
+ [BadgeCountSize.Md]: 'leading-[14px]',
+ [BadgeCountSize.Lg]: 'leading-[16px]',
+};
diff --git a/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.tsx b/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.tsx
--- a/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.tsx
+++ b/packages/design-system-react-native/src/components/BadgeCount/BadgeCount.tsx
@@ -8,6 +8,7 @@
import {
MAP_BADGECOUNT_SIZE_TEXTVARIANT,
TWCLASSMAP_BADGECOUNT_SIZE_CONTAINER,
+ TWCLASSMAP_BADGECOUNT_SIZE_LINEHEIGHT,
} from './BadgeCount.constants';
import type { BadgeCountProps } from './BadgeCount.types';
@@ -38,8 +39,12 @@
variant={MAP_BADGECOUNT_SIZE_TEXTVARIANT[size]}
color={TextColor.ErrorInverse}
fontWeight={FontWeight.Medium}
- twClassName="leading-0"
{...textProps}
+ twClassName={
+ textProps?.twClassName
+ ? textProps.twClassName
+ : TWCLASSMAP_BADGECOUNT_SIZE_LINEHEIGHT[size]
+ }
>
{count > max ? `${max}+` : `${count}`}
</Text> |
| @@ -0,0 +1,41 @@ | |||
| /** | |||
There was a problem hiding this comment.
New shared type location following ADR-0004 centralized types architecture. This is the single source of truth for BadgeCount types across both React and React Native platforms.
| * BadgeCount - size | ||
| * Convert from enum to const object (ADR-0003) | ||
| */ | ||
| export const BadgeCountSize = { |
There was a problem hiding this comment.
Const object pattern following ADR-0003. Using const object with derived union type instead of TypeScript enum enables better tree-shaking and type safety.
| * BadgeCount component shared props (ADR-0004) | ||
| * Platform-independent properties shared across React and React Native | ||
| */ | ||
| export type BadgeCountPropsShared = { |
There was a problem hiding this comment.
SharedPropsShared suffix indicates platform-independent properties. These props are shared across React and React Native implementations, with platform-specific extensions added in each package.
| */ | ||
| style?: StyleProp<ViewStyle>; | ||
| } & Omit<ViewProps, 'children'>; | ||
| export type BadgeCountProps = BadgeCountPropsShared & |
There was a problem hiding this comment.
Platform-specific extension pattern. React Native adds ViewProps, twClassName, and style to shared props. Notice textProps is platform-specific since Text component APIs differ between web and native.
| @@ -1,3 +1,3 @@ | |||
| export { BadgeCountSize } from '../../types'; | |||
| export { BadgeCountSize } from '@metamask/design-system-shared'; | |||
There was a problem hiding this comment.
Direct import from shared package. Component index.ts is the single export location for const objects, preventing duplicate exports that would reduce test coverage.
| @@ -77,20 +77,6 @@ export enum AvatarIconSeverity { | |||
| Warning = 'warning', | |||
There was a problem hiding this comment.
Enum removal from platform type index. Old enum definition removed since shared package is now the source of truth. Platform type indices no longer re-export shared types to prevent duplicate exports.
| variant={MAP_BADGECOUNT_SIZE_TEXTVARIANT[size]} | ||
| color={TextColor.ErrorInverse} | ||
| fontWeight={FontWeight.Medium} | ||
| {...textProps} |
There was a problem hiding this comment.
Simplified line-height implementation. Using leading-0 directly in the component eliminates the need for size-specific line-height mappings while maintaining visual alignment.
| export const TWCLASSMAP_BADGECOUNT_SIZE_CONTAINER: Record< | ||
| BadgeCountSize, | ||
| string | ||
| > = { |
There was a problem hiding this comment.
| size: { | ||
| control: 'select', | ||
| options: BadgeCountSize, | ||
| options: Object.keys(BadgeCountSize), |
There was a problem hiding this comment.
Storybook argTypes improvement. Using Object.keys with mapping enables proper select control in Storybook UI while maintaining type safety with the const object pattern.
| <BadgeCount key={size} size={size} count={100} /> | ||
| ))} | ||
| </View> | ||
| <Box flexDirection={BoxFlexDirection.Row} gap={2}> |
There was a problem hiding this comment.
Component-first refactoring. Replaced raw View with Box component following design system styling conventions. This ensures consistent spacing and layout patterns.
| MAP_BADGECOUNT_SIZE_LINEHEIGHT[BadgeCountSize.Md], | ||
| ); | ||
| expect(textElement.props.twClassName).toContain('custom'); | ||
| // Custom twClassName should be applied |
There was a problem hiding this comment.
Test assertion cleanup. Removed line-height test assertions since MAP_BADGECOUNT_SIZE_LINEHEIGHT constant was eliminated. The component now uses a consistent leading-0 approach.
| */ | ||
| style?: React.CSSProperties; | ||
| }; | ||
| export type BadgeCountProps = ComponentProps<'div'> & |
There was a problem hiding this comment.
Cross-platform consistency. React implementation matches React Native changes - same shared type imports, same container simplification, same const object pattern.
## **Description** Adds `component-migration.md` cursor rule providing the PRIORITY workflow for bringing components from MetaMask extension or mobile codebases into the design system monorepo. This is the primary workflow AI agents should use for component work, as most components originate from consumer applications rather than being created from scratch. **Complete migration workflow documented:** 1. **Assess component** - Determine if extension/mobile component is suitable for design system 2. **Scaffold** - Use `create-component:react` and `create-component:react-native` scripts 3. **Extract implementation** - Preserve logic, behavior, and tests from source 4. **Create shared types** - Move to `@metamask/design-system-shared` (ADR-0004) 5. **Transform code** - Replace raw elements with Box/Text primitives and design tokens 6. **Implement both platforms** - Ensure cross-platform consistency 7. **Create documentation** - Storybook stories and README files 8. **Verify quality** - Build, test, lint must pass **Critical patterns:** - Two-file export structure: `.types.ts` imports only, `index.ts` exports only - Shared types in `@metamask/design-system-shared` package (ADR-0004) - Const objects with `as const` instead of TypeScript enums (ADR-0003) - Cross-platform consistency between React and React Native - References `component-creation.md` for technical scaffolding details **When to use this workflow:** ✅ Bringing component from extension or mobile (PRIORITY) ✅ Component exists in consumer app but not in design system ✅ User references extension or mobile component **NOT for:** ❌ Brand new components with no prior implementation (use `component-creation.md`) ❌ Internal monorepo refactoring (use `component-enum-union-migration.md`) **Why this matters:** Most design system components originate from MetaMask extension or mobile applications. This workflow ensures proper migration that maintains functionality while adopting design system patterns. The BadgeCount migration (PR #942) validated these patterns, particularly the importance of correct export structure for test coverage. **Relationship to other rules:** - Depends on: `component-architecture.md` (foundational patterns) - Uses: `component-creation.md` (references for technical scaffolding steps) - Different from: `component-enum-union-migration.md` (internal refactoring only) **References:** - Golden path: BadgeStatus component (complete implementation) - Validation: BadgeCount PR #942 (demonstrated coverage issue and fix) - Scripts: `create-component:react` and `create-component:react-native` ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-343 ## **Manual testing steps** 1. Review the cursor rule file: `.cursor/rules/component-migration.md` 2. Verify it clearly marks this as PRIORITY workflow for extension/mobile components 3. Check workflow steps are comprehensive (assess → scaffold → extract → transform → implement) 4. Confirm it references `component-creation.md` for scaffolding details 5. Verify it clarifies when NOT to use this workflow 6. Check golden path examples reference BadgeStatus 7. Confirm verification checklist covers assessment, implementation, and quality 8. Verify CLAUDE.md includes reference to this new rule 9. Confirm it references BadgeCount PR #942 ## **Screenshots/Recordings** N/A - Documentation only ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable (N/A - documentation) - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable (N/A - markdown) - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.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] > **Low Risk** > Low risk; changes are documentation-only, updating AI-agent guidance without affecting runtime code or build artifacts. > > **Overview** > Adds a new `.cursor/rules/component-migration.md` rule that documents the *primary* workflow for migrating components from MetaMask Extension/Mobile into the monorepo, including a conservative vs unified strategy, phased checklist, and example patterns for shared types, deprecations, and platform layering. > > Updates `CLAUDE.md` to include this new rule in the repository’s AI-agent documentation index. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 03c1bb5. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
📖 Storybook Preview |
Update test expectation to include leading-0 prefix that the component now applies to all text elements.
62940b3 to
e882119
Compare
| "./../../packages/design-system-react-native/src/components/Button/variants/ButtonSecondary/ButtonSecondary.stories.tsx": require("../../../packages/design-system-react-native/src/components/Button/variants/ButtonSecondary/ButtonSecondary.stories.tsx"), | ||
| "./../../packages/design-system-react-native/src/components/Button/variants/ButtonTertiary/ButtonTertiary.stories.tsx": require("../../../packages/design-system-react-native/src/components/Button/variants/ButtonTertiary/ButtonTertiary.stories.tsx"), | ||
| "./../../packages/design-system-react-native/src/components/ButtonBase/ButtonBase.stories.tsx": require("../../../packages/design-system-react-native/src/components/ButtonBase/ButtonBase.stories.tsx"), | ||
| "./../../packages/design-system-react-native/src/components/ButtonHero/ButtonHero.stories.tsx": require("../../../packages/design-system-react-native/src/components/ButtonHero/ButtonHero.stories.tsx"), |
There was a problem hiding this comment.
Adds missing ButtonHero story
📖 Storybook Preview |
## Release 24.0.0 This release includes BadgeCount type migration updates and new React Native components. ### 📦 Package Versions - `@metamask/design-system-shared`: **0.3.0** - `@metamask/design-system-react`: **0.10.0** - `@metamask/design-system-react-native`: **0.10.0** ### 🔄 Shared + React Type Updates #### BadgeCount ADR Migration (#942) Updated `BadgeCount` types to follow ADR-0003 and ADR-0004 patterns across shared, React, and React Native packages. **What Changed:** - `BadgeCountSize` now uses const-object + string-union typing instead of enum-based typing - Shared `BadgeCount` props/types are centralized in `@metamask/design-system-shared` - Platform packages consume and re-export shared `BadgeCount` types **Impact:** - Consistent type architecture across packages - Better alignment with design-system ADRs - Potentially breaking for enum-specific consumer type usage ### 📱 React Native Updates (0.10.0) #### Added - Added `ActionListItem` component (#951) - Added `SensitiveText` component (#922) - Added `ButtonSemantic` component (#950) - Added `BottomSheetHeader` component (#927) - Added `ButtonHero` component to React Native package (#934) ###⚠️ Breaking Changes - `BadgeCount` type exports were migrated from enum-style to const-object/union style (#942) - Continue importing from package entrypoints, but update enum-specific type assumptions in consuming code ### ✅ Checklist - [x] Changelogs updated with human-readable descriptions - [x] Changelog validation passed (`yarn changelog:validate`) - [x] Version bumps follow semantic versioning - design-system-shared: minor (0.2.0 → 0.3.0) - design-system-react: minor (0.9.0 → 0.10.0) - design-system-react-native: minor (0.9.0 → 0.10.0) - [x] Breaking changes documented with migration guidance - [x] PR references included in changelog entries <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Changes are limited to version bumps and changelog updates; no runtime code is modified. The main risk is downstream impact from the documented breaking `BadgeCount` type export migration when consumers upgrade. > > **Overview** > Bumps the monorepo and package versions for the `24.0.0` release (`@metamask/design-system-react`/`react-native` to `0.10.0`, `@metamask/design-system-shared` to `0.3.0`). > > Updates changelogs to publish release notes, including a **breaking** `BadgeCount` type export migration to the const-object + string-union pattern and documenting newly added React Native components in `0.10.0`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 6c194fe. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->





Description
BREAKING CHANGE
This PR migrates the BadgeCount component to follow ADR-0003 (const objects instead of enums) and ADR-0004 (centralized shared types) patterns. This migration validates the effectiveness of our condensed cursor rules for component migrations.
What is the reason for the change?
To align BadgeCount with our architectural decision records and create a consistent pattern across all design system components.
What is the improvement/solution?
BadgeCountSizeenum to const object pattern withas const@metamask/design-system-sharedpackageclassNamefor React,twClassNamefor React Native)Related issues
Fixes: DSYS-469
Manual testing steps
yarn buildto build all packagesyarn storybookto start StorybookScreenshots/Recordings
Before
BadgeCount used platform-local enums and types exported from each package's
src/types/index.ts.React Native
React
After
BadgeCount Storybook stories demonstrating the component works correctly after migration:
React Native
React
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Medium risk due to a breaking type export migration (enum -> const-object/union) that can affect downstream imports, plus small styling/classname changes that could subtly alter BadgeCount layout.
Overview
BadgeCount is migrated to shared, ADR-aligned types.
BadgeCountSizeis removed from the React and React Nativesrc/types/index.tsfiles and replaced by a new sharedBadgeCountSizeconst-object +BadgeCountPropsSharedin@metamask/design-system-shared, which both platform packages now re-export and extend.BadgeCount styling/tests/stories are updated to match the new API. Storybook controls now use
Object.keys(BadgeCountSize)withmapping, RN stories switch layout wrappers and renameSizes->Size, and RN drops per-size line-height mapping in favor of a fixedleading-0text class; container tailwind classes are adjusted accordingly (and React’sLgcontainer class is updated but appears to have a typo:h-).Written by Cursor Bugbot for commit e882119. This will update automatically on new commits. Configure here.