MetaMask / MetaMask/metamask-design-system
Bug: DSRN ButtonBase textClassName type and loading issue
- 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
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