Skip to content

Conversation

@georgewrmarshall
Copy link
Contributor

@georgewrmarshall georgewrmarshall commented Jan 15, 2026

Description

Refactors ButtonBase and ButtonAnimated to properly support flex layouts in React Native.

What is the reason for the change?

ButtonBase components couldn't use flex-1 in flex row layouts due to nested wrapper architecture in ButtonAnimated. The w-auto width constraint also prevented proper flex behavior.

What is the improvement/solution?

  1. Simplified ButtonAnimated: Uses Animated.createAnimatedComponent(Pressable) (68 lines vs 111)
  2. Refactored ButtonBase: Uses tw.style() for better readability and removed content wrapper View
  3. Fixed width behavior: Changed from w-auto to self-start for proper flex support
  4. Updated documentation: Clear guidance on horizontal vs vertical flex layouts

Related issues

Fixes: (internal issue)

Manual testing steps

  1. Run Storybook: yarn storybook:ios or yarn storybook:android
  2. Navigate to Components > ButtonBase
  3. Test FlexLayout story - buttons should share space equally with flex-1
  4. Test isFullWidth story - first button content-width, second full-width
  5. Test Sizes story - verify all size variants render correctly
  6. Test IsLoading story - verify loading state and spinner
  7. Test button press animations - should scale to 0.97 on press
  8. Test all Button variant stories (ButtonPrimary, ButtonSecondary, ButtonTertiary)

Screenshots/Recordings

Before

Buttons couldn't use flex-1 in horizontal layouts - would either overflow or not stretch properly.

After

Buttons properly support flex layouts with twClassName="flex-1" in horizontal rows and isFullWidth in vertical columns.

Screen.Recording.2026-01-15.at.10.00.55.AM.mov

Pre-merge author checklist

  • I've followed MetaMask Contributor Docs
  • 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 format if applicable
  • I've applied the right labels on the PR (see labeling guidelines). 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
Copy link

cursor bot commented Jan 15, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@github-actions
Copy link
Contributor

📖 Storybook Preview

@github-actions
Copy link
Contributor

📖 Storybook Preview

- Add separate sections for horizontal (flex row) and vertical (flex column) layouts
- Clarify that isFullWidth is for vertical layouts only
- Document flex-1 as the correct approach for horizontal layouts
- Add warning about semantic conflicts when using isFullWidth in rows
- Update isFullWidth prop documentation with explicit guidance
@georgewrmarshall georgewrmarshall force-pushed the cursor/buttonbase-flex-layout-ad42 branch from c3b133c to e3e19a4 Compare January 15, 2026 02:21
@github-actions
Copy link
Contributor

📖 Storybook Preview

@georgewrmarshall georgewrmarshall self-assigned this Jan 15, 2026
@georgewrmarshall georgewrmarshall changed the title ButtonBase flex layout fix: ButtonBase for flex layouts Jan 15, 2026
- Remove unused BoxBackgroundColor import
- Remove unused Text import
- Update FlexLayout story simplified to not need these imports
@github-actions
Copy link
Contributor

📖 Storybook Preview

georgewrmarshall and others added 2 commits January 15, 2026 10:32
- Add tests for function-based twClassName prop
- Add tests for textClassName in normal and loading states
- Add tests for iconClassName in loading state
- Add tests for custom accessories in loading state
- Fix ButtonIcon tests to work with new AnimatedPressable style structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Flatten style arrays in ButtonAnimated to avoid nested structure
- Change from [[styles], animatedStyle] to [styles, animatedStyle]
- Update ButtonIcon tests to use style[0] instead of style[0][0]
- Simplifies style structure and makes tests more readable

All tests pass with 100% coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Contributor

📖 Storybook Preview

- Remove opacity-100 from spinner container expectations (not applied to container)
- Remove content-container checks (wrapper was removed in refactoring)
- Tests now correctly verify spinner presence and button disabled state

All tests pass with 100% coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Contributor

📖 Storybook Preview

Comment on lines -2 to +4
import { View } from 'react-native';

import { ButtonBaseSize } from '../../types';
import { BoxFlexDirection, ButtonBaseSize } from '../../types';
import { Box } from '../Box';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing View to Box per conventions

Comment on lines +137 to +142
export const FlexLayout: Story = {
render: () => (
<Box flexDirection={BoxFlexDirection.Row} gap={4}>
<ButtonBase twClassName="flex-1">Lorem ipsum</ButtonBase>
<ButtonBase twClassName="flex-1">Lorem ipsum</ButtonBase>
</Box>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding flex layout story to check flex layout

- Add checks that text content has opacity-0 when loading
- Restores the "hides content" part of "shows spinner + hides content" tests
- Uses getByText and flattenStyles to verify opacity: 0 is applied

All tests pass with 100% coverage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Contributor

📖 Storybook Preview

Comment on lines +73 to +91
it('applies function-based twClassName correctly', () => {
const twClassNameFn = (pressed: boolean) =>
pressed ? 'bg-pressed' : 'bg-default';

const tree = ReactTestRenderer.create(
<ButtonBase twClassName={twClassNameFn}>Function ClassName</ButtonBase>,
);

const buttonAnimated = tree.root.findByProps({
accessibilityRole: 'button',
});
const styleFn = buttonAnimated.props.style as (p: {
pressed: boolean;
}) => unknown[];

// Test both pressed states to cover the branch
expect(styleFn({ pressed: false })).toBeDefined();
expect(styleFn({ pressed: true })).toBeDefined();
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding test coverage for the ternary operator on line 107 (typeof twClassName === 'function' ? twClassName(pressed) : twClassName) was never exercised in tests.

georgewrmarshall and others added 2 commits January 15, 2026 11:54
- Verify custom accessories have opacity-0 when isLoading=true
- Use ReactTestRenderer to properly access wrapper props
- Test now accurately verifies "in loading state" behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Verify textClassName and iconClassName functions work with pressed states
- Test both pressed=false and pressed=true cases
- Change from simple existence checks to actual behavior verification
- Tests now accurately verify "applies" behavior they describe

All tests pass with 100% coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Contributor

📖 Storybook Preview

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants