feat(DSYS-484): migrate ButtonBase and Button to ADR-0003 and ADR-0004#1074
feat(DSYS-484): migrate ButtonBase and Button to ADR-0003 and ADR-0004#1074cursor[bot] wants to merge 8 commits intomainfrom
Conversation
📖 Storybook Preview |
- Create ButtonBaseSize const object and ButtonBasePropsShared in shared package - Create ButtonVariant const object and ButtonPropsShared in shared package - Export new types from @metamask/design-system-shared index - Replace ButtonBaseSize and ButtonVariant enums in both platform type indices with re-exports from shared package (backwards compatible) - Update ButtonBase.types.ts, ButtonBase.tsx, ButtonBase.constants.ts in both platforms - Update Button.types.ts, Button.tsx in both platforms - Update ButtonBase/index.ts and Button/index.ts in both platforms to export from shared - Update ButtonHero/index.ts and all ButtonPrimary/Secondary/Tertiary variant index files to use ButtonBaseSize aliases from shared package Co-authored-by: George Marshall <georgewrmarshall@users.noreply.github.com>
13de5dd to
ff43317
Compare
📖 Storybook Preview |
…ed and update internal imports - Export ButtonSize, ButtonPrimarySize, ButtonSecondarySize, ButtonTertiarySize, and ButtonHeroSize aliases from @metamask/design-system-shared - Update all component index.ts files to re-export named aliases from shared (no more `as` redirection) - Update all stories and tests to import Button types directly from @metamask/design-system-shared instead of ../../types - Remove Button-related re-exports from src/types/index.ts in both platform packages
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 97c2365. Configure here.
📖 Storybook Preview |
| export { ButtonBaseSize as ButtonPrimarySize } from './types/ButtonBase'; | ||
| export { ButtonBaseSize as ButtonSecondarySize } from './types/ButtonBase'; | ||
| export { ButtonBaseSize as ButtonTertiarySize } from './types/ButtonBase'; | ||
| export { ButtonBaseSize as ButtonHeroSize } from './types/ButtonBase'; |
There was a problem hiding this comment.
Size aliases like ButtonPrimarySize, ButtonSecondarySize, ButtonTertiarySize, and ButtonHeroSize are all the same underlying ButtonBaseSize const object — the variants share identical sizing tokens. Exporting these named aliases from the shared package means consumers can write import { ButtonPrimarySize } from '@metamask/design-system-shared' without needing an as redirect at the call site, matching the ergonomics of the old enum-based API.
| Lg: 'lg', | ||
| } as const; | ||
| export type ButtonBaseSize = | ||
| (typeof ButtonBaseSize)[keyof typeof ButtonBaseSize]; |
There was a problem hiding this comment.
ADR-0003 pattern: the const object + derived union type (export type ButtonBaseSize = (typeof ButtonBaseSize)[keyof typeof ButtonBaseSize]) replaces the TypeScript enum that previously lived in both platform src/types/index.ts files. The const object is tree-shakable and has no runtime overhead beyond a plain object — the enum compiled to an IIFE in JS output.
| * @default false | ||
| */ | ||
| isFullWidth?: boolean; | ||
| }; |
There was a problem hiding this comment.
ButtonBasePropsShared collects the six props that are truly cross-platform (children, size, isLoading, loadingText, isDisabled, isFullWidth) so they are defined exactly once in the shared package. Platform-specific concerns — className/twClassName, asChild, icon props, aria attributes, and the PressableProps/ComponentProps<'button'> base — stay in their respective platform .types.ts files.
| } & ( | ||
| | (Omit<ButtonPrimaryProps, 'ref'> & { | ||
| variant?: ButtonVariant.Primary; | ||
| variant?: Extract<ButtonVariant, 'primary'>; |
There was a problem hiding this comment.
Extract<ButtonVariant, 'primary'> is needed here because ButtonVariant is now a string union ('primary' | 'secondary' | 'tertiary') rather than an enum. With enums you could narrow a discriminated union member using ButtonVariant.Primary; with a string union the equivalent narrowing requires Extract. The external-facing API is unchanged — callers still pass ButtonVariant.Primary or the string 'primary'.
| */ | ||
| style?: React.CSSProperties; | ||
| export type ButtonBaseProps = ButtonBasePropsShared & | ||
| ComponentProps<'button'> & { |
There was a problem hiding this comment.
Intersecting ButtonBasePropsShared here (and in the RN equivalent) rather than inlining the props means any future change to the shared interface — adding a prop, tightening a type — is automatically reflected in both platforms without touching these files. The props removed from the local definition (children, size, isLoading, loadingText, isDisabled, isFullWidth) are now inherited through the intersection.
| @@ -211,47 +211,6 @@ export enum BoxBorderColor { | |||
| Transparent = 'border-transparent', | |||
There was a problem hiding this comment.
These removals are the payoff of ADR-0004: ButtonBaseSize, all five named aliases (ButtonSize, ButtonPrimarySize, etc.), and ButtonVariant no longer need to be defined here as TypeScript enums. They were duplicated across both platform src/types/index.ts files; now there is a single const-object definition in design-system-shared. ButtonIconSize and ButtonVariant for ButtonIcon remain as enums here because those components haven't been migrated yet.
| ButtonSize, | ||
| FontWeight, | ||
| TextVariant, | ||
| } from '@metamask/design-system-shared'; |
There was a problem hiding this comment.
BannerBase wasn't part of the original ButtonBase/Button migration scope, but removing ButtonSize from src/types/index.ts surfaced it as a broken consumer. Moving ButtonSize into the @metamask/design-system-shared import alongside FontWeight and TextVariant is the correct fix — the same pattern applies identically in both the React and React Native versions of this file.
📖 Storybook Preview |
📖 Storybook Preview |
| } & ( | ||
| | (Omit<ButtonPrimaryProps, 'ref'> & { | ||
| variant?: ButtonVariant.Primary; | ||
| variant?: typeof ButtonVariant.Primary; |
There was a problem hiding this comment.
typeof ButtonVariant.Primary is the idiomatic way to narrow a discriminated union member when working with ADR-0003 const objects. It resolves to the string literal type 'primary' at compile time, but unlike a bare string literal it keeps a live reference to the const object — so if the value ever changes, this type stays in sync automatically. Extract<ButtonVariant, 'primary'> was an earlier workaround that works but obscures intent; typeof ButtonVariant.X makes the narrowing intent explicit. Switching from import type to import is required so the const object value is available for typeof to reference.
|
Duplicate #1034 |

Description
Migrates
ButtonBaseandButtoncomponents to follow ADR-0003 (String Unions) and ADR-0004 (Centralized Types Architecture) as part of the DSYS-468 epic.What changed:
ButtonBaseSizeconst object (replacesenum ButtonBaseSize) andButtonBasePropsSharedin@metamask/design-system-sharedButtonVariantconst object (replacesenum ButtonVariant) andButtonPropsSharedin@metamask/design-system-sharedtypes/index.tsfiles with re-exports from shared package (backwards compatible — all existing imports still work)ButtonBase.types.ts,ButtonBase.tsx,ButtonBase.constants.tsin both React and React Native platforms to import from@metamask/design-system-sharedButton.tsx,Button.types.tsin both platforms to import from@metamask/design-system-sharedindex.tsfiles (ButtonBase, Button, ButtonHero, ButtonPrimary, ButtonSecondary, ButtonTertiary) to re-export const objects from@metamask/design-system-sharedMigration details:
ButtonBaseSizevalues are identical across platforms (sm/md/lg) → moved to sharedButtonVariantvalues are identical across platforms (primary/secondary/tertiary) → moved to sharedButtonBasePropsSharedcontains:children,size,isLoading,loadingText,isDisabled,isFullWidthButtonPropsSharedextendsButtonBasePropsSharedwithvariantButtonSize,ButtonPrimarySize,ButtonSecondarySize,ButtonTertiarySize,ButtonHeroSize) are preserved as re-exports from shared for backwards compatibilityclassName,asChild,style,textProps, a11y props; RN:twClassName,textClassName,iconClassName,spinnerProps,style) remain in platform packagesRelated issues
Fixes: DSYS-484
Manual testing steps
yarn buildyarn testButtonBaseandButtoncomponents work with existing size/variant propsButtonBaseSize,ButtonVariant,ButtonSize,ButtonPrimarySize, etc. continue to work (re-exported from shared via types/index.ts)Screenshots/Recordings
Before
Enums defined locally in each platform's
src/types/index.ts:After
Const objects in
@metamask/design-system-shared:Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Touches core
ButtonBase/ButtonAPIs across both React and React Native by replacing local enums with shared const-union types, which could introduce subtle typing/import breakages for downstream consumers despite runtime behavior being largely unchanged.Overview
Migrates
ButtonBasesizing andButtonvariant types from platform-local enums to centralized, ADR-aligned shared types in@metamask/design-system-shared(newButtonBaseSize/ButtonVariantconst objects plusButtonBasePropsShared/ButtonPropsShared).Updates React and React Native components, stories/tests, and component barrel exports to import/re-export these shared types (including preserved aliases like
ButtonSize/ButtonPrimarySize/etc.), and removes the duplicated button enum definitions from each platform’stypes/index.ts.Reviewed by Cursor Bugbot for commit 53ed92a. Bugbot is set up for automated code reviews on this repo. Configure here.