MetaMask / MetaMask/metamask-design-system

Bug: DSRN ButtonBase textClassName type and loading issue

Open
#797 0 comments 0 reactions 0 assignees View on GitHub
bug team-design-system
Dominant language
TypeScript
Stars
37
Forks
14
Avg merge
1d 9h
Merged PRs (30d)
60

Description

### **Description**

Two issues discovered in ButtonBase component during ButtonHero development:

1. **Performance Issue**: `textClassName` prop forces function creation even for static values, causing unnecessary re-renders
2. **Loading Text Bug**: When `isLoading=true` and `loadingText` is provided, the button displays both the loading text AND the children text instead of replacing the children

### **Steps to Reproduce**

**Issue 1 - textClassName Performance:**

1. Create a ButtonBase component with static text styling
2. Try to use `textClassName="text-primary-inverse"`
3. Get TypeScript error: `Type 'string' is not assignable to type '(pressed: boolean) => string'`
4. Must use `textClassName={() => 'text-primary-inverse'}` creating new function on every render

**Issue 2 - Loading Text Display:**

1. Create a ButtonBase with `children="Click Me"`
2. Set `isLoading={true}` and `loadingText="Loading..."`
3. Render the component
4. Observe both texts are displayed: "Loading...Click Me"

### **Expected Behavior**

**Issue 1**: `textClassName` should accept both string and function:

```tsx
// Should work for static values
textClassName="text-primary-inverse"

// Should work for dynamic values
textClassName={(pressed) => pressed ? 'text-pressed' : 'text-default'}
```

**Issue 2**: Loading text should replace children, not append to them:

- **Expected**: Button displays only "Loading..."
- **Actual**: Button displays "Loading...Click Me"

### **Screenshots**

**Test Output showing the bug:**

```
● ButtonHero › handles loading state correctly

expect(element).toHaveTextContent()

Expected element to have text content:
loading...
Received:
loading...Test Button
```

**Accessibility output during loading:**

```html

```

### **Environment**

- OS: macOS 24.5.0
- React Native: MetaMask Mobile app
- Package: `@metamask/design-system-react-native`
- Component: `ButtonBase`

### **Additional Context**

**Issue 1 Impact:**

- Forces unnecessary function creation on every render
- Prevents memoization optimizations
- Makes API less ergonomic for simple use cases
- Common anti-pattern in React performance

**Issue 2 Impact:**

- Poor UX - confusing display during loading states
- Accessibility issues - screen readers get conflicting information
- May cause layout issues with longer text combinations

**Suggested Fixes:**

1. **textClassName API**: Change type to `string | ((pressed: boolean) => string)`
2. **Loading behavior**: When `isLoading=true`, only show loading content, hide children

**Workaround Currently Used:**

```tsx
// Issue 1 workaround
const getTextClassName = useCallback(() => 'text-primary-inverse', []);
textClassName = { getTextClassName };

// Issue 2 workaround
// Test for loading text presence rather than exact text content
expect(getByText('loading...')).toBeOnTheScreen();
```

**Related Files:**

- [`app/component-library/components-temp/Buttons/ButtonHero/ButtonHero.tsx`](https://github.com/MetaMask/metamask-mobile/blob/main/app/component-library/components-temp/Buttons/ButtonHero/ButtonHero.tsx)
- [`app/component-library/components-temp/Buttons/ButtonHero/ButtonHero.test.tsx`](https://github.com/MetaMask/metamask-mobile/blob/main/app/component-library/components-temp/Buttons/ButtonHero/ButtonHero.test.tsx)

Both issues were discovered during implementation of a new ButtonHero component in MetaMask Mobile that extends ButtonBase functionality from `@metamask/design-system-react-native`.

Contributor guide

Open the contributing guide

Research direction

Start with app/component-library/components-temp/Buttons/ButtonHero/ButtonHero.tsx and its ButtonHero.test.tsx, then trace how they use ButtonBase and inspect the related loading and textClassName behavior. Done means static and callback textClassName values type-check, loading shows only loadingText, and the ButtonHero loading test passes with the corrected text.

Written by the indexing model from the issue text.

Assessment

Tech stack
react-native, typescript
Domain
mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.