Skip to content

Commit cbd2618

Browse files
authored
feat: Fix truncation of prices (#31953)
## **Description** On the Predict bet buttons/cards, the odds/prices were being truncated (e.g. `DRAW 1…` instead of `DRAW 15¢`), so users could not reliably read the price they were betting at. It was most visible on the "DRAW" outcome (longest label) and on Android devices with larger system font scaling, but it is a general layout/sizing issue rather than a device- or platform-specific one. This is distinct from PRED-960 (game-clock width cutting off the score on the match page); this change is specifically about the odds/prices on the bet buttons/cards. **Reason for the change:** the label and price render as a single line inside three equal `flex-1` columns. The design-system `Button` wraps its children in a content wrapper (`BoxRow`) that sizes to the text's intrinsic width, so `adjustsFontSizeToFit` had no bounded width to shrink against and the text simply overflowed and got clipped. The baked-in `px-4` button padding made the usable width even smaller. **Solution:** force the button content wrapper and label to a bounded full width so the font can actually auto-shrink to fit, and reduce the internal horizontal padding to reclaim space. Applied to both code paths that render the `LABEL PRICE¢` string: - `app/components/UI/Predict/components/PredictMarketSportCard/PredictMarketSportCard.tsx` (World Cup / sport list cards and the Live Now carousel). - `app/components/UI/Predict/components/PredictActionButtons/PredictBetButton.tsx` (`inline` / `inlineNoSeparator` layouts used on game-details outcome cards and the sticky footer). Specifically, each bet-button label now uses `numberOfLines={1}`, `adjustsFontSizeToFit`, `minimumFontScale={0.7}`, `ellipsizeMode="clip"`, and `flex-1`; the `Button` gets `contentWrapperProps={{ twClassName: 'w-full' }}` and reduced padding (`px-1`, while the compact carousel keeps `p-0` to maximize space). The worst case (`DRAW 99¢`) is bounded, so it always fits or gracefully scales down. New bet-card designs in the pipeline can adopt the same pattern. ## **Changelog** CHANGELOG entry: Fixed odds/prices being truncated on Predict bet buttons so the full price is always readable. ## **Related issues** Refs: ## **Manual testing steps** ```gherkin Feature: Predict bet button odds are fully readable Scenario: User views sport market cards with a draw outcome Given the user is on the Predictions screen And a sport market with home, draw, and away outcomes is displayed When the user looks at the three bet buttons Then each button shows the full label and price (e.g. "DRAW 15¢") with no truncation Scenario: User views odds on a large Android device with increased system font size Given the user has increased the device font scale in Android system settings And the user is on the Predictions screen When the user looks at the bet buttons on a sport card and on the Live Now carousel Then the odds/prices auto-shrink to fit and remain fully readable Scenario: User views the game details moneyline cards and sticky footer Given the user opens a sport game details screen When the user looks at the moneyline outcome cards and the sticky footer buttons Then the inline label and price (e.g. "SEA 70¢") are fully visible without being cut off ``` ## **Screenshots/Recordings** ### **Before** <!-- Android sport card showing "DRAW 3…" / "DRAW 1…" truncation --> ### **After** <!-- Same card showing full "DRAW 31¢" / "DRAW 15¢" --> <img width="1536" height="2048" alt="WhatsApp Image 2026-06-18 at 00 54 12" src="https://github.com/user-attachments/assets/613a6f2a-8eec-4b57-bb47-2033c47cdd7f" /> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **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** > UI-only layout and typography on Predict bet buttons; no auth, data, or business-logic changes. > > **Overview** > Fixes **truncated odds** on Predict sport cards and inline bet buttons (e.g. `DRAW 1…` instead of full `DRAW 15¢`) by giving the label a **bounded width** and enabling **auto-shrink**. > > For **`PredictBetButton`** inline / `inlineNoSeparator` layouts and **`PredictMarketSportCard`** outcome buttons, the `Button` now uses **`contentWrapperProps={{ twClassName: 'w-full' }}`**, tighter horizontal padding (`px-1`, carousel compact stays `p-0`), and the combined `LABEL PRICE¢` `Text` uses **`flex-1`**, **`adjustsFontSizeToFit`**, **`minimumFontScale={0.7}`**, and **`ellipsizeMode="clip"`** so long labels like DRAW scale down instead of clipping. > > Tests assert **`numberOfLines={1}`** and **`adjustsFontSizeToFit`** on representative labels. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f6c15d8. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 902adaa commit cbd2618

4 files changed

Lines changed: 40 additions & 4 deletions

File tree

app/components/UI/Predict/components/PredictActionButtons/PredictBetButton.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,21 @@ describe('PredictBetButton', () => {
194194
expect(screen.getByText('YES')).toBeOnTheScreen();
195195
expect(screen.getByText('65¢')).toBeOnTheScreen();
196196
});
197+
198+
it('keeps the inline label on one line and shrinks to fit to prevent truncation', () => {
199+
const props = createDefaultProps({
200+
label: 'Draw',
201+
price: 100,
202+
variant: 'draw',
203+
layout: 'inlineNoSeparator',
204+
});
205+
206+
renderWithProvider(<PredictBetButton {...props} />);
207+
208+
const label = screen.getByText('DRAW 100¢');
209+
expect(label.props.numberOfLines).toBe(1);
210+
expect(label.props.adjustsFontSizeToFit).toBe(true);
211+
});
197212
});
198213

199214
describe('edge cases', () => {

app/components/UI/Predict/components/PredictActionButtons/PredictBetButton.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const PredictBetButton: React.FC<PredictBetButtonProps> = ({
4545
};
4646

4747
const textStyle = tw.style('font-medium text-center', getTextColor());
48+
const isInline = layout === 'inline' || layout === 'inlineNoSeparator';
4849
const inlineLabel =
4950
layout === 'inlineNoSeparator'
5051
? `${label.toUpperCase()} ${price}¢`
@@ -56,16 +57,21 @@ const PredictBetButton: React.FC<PredictBetButtonProps> = ({
5657
isDisabled={disabled}
5758
testID={testID}
5859
style={{ backgroundColor: getBackgroundColor() }}
60+
twClassName={isInline ? 'px-1' : undefined}
61+
contentWrapperProps={isInline ? { twClassName: 'w-full' } : undefined}
5962
isFullWidth
6063
size={size}
6164
>
62-
{layout === 'inline' || layout === 'inlineNoSeparator' ? (
65+
{isInline ? (
6366
<Text
6467
variant={
6568
layout === 'inlineNoSeparator' ? TextVariant.BodySm : undefined
6669
}
67-
style={textStyle}
70+
style={tw.style('flex-1', textStyle)}
6871
numberOfLines={1}
72+
adjustsFontSizeToFit
73+
minimumFontScale={0.7}
74+
ellipsizeMode="clip"
6975
>
7076
{inlineLabel}
7177
</Text>

app/components/UI/Predict/components/PredictMarketSportCard/PredictMarketSportCard.test.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ describe('PredictMarketSportCard', () => {
191191
expect(getByText('ENG 62¢')).toBeOnTheScreen();
192192
});
193193

194+
it('keeps outcome button labels on one line and shrinks to fit to prevent truncation', () => {
195+
const { getByText } = renderWithProvider(
196+
<PredictMarketSportCard market={mockMarket} />,
197+
{ state: initialState },
198+
);
199+
200+
const drawLabel = getByText('DRAW 15¢');
201+
expect(drawLabel.props.numberOfLines).toBe(1);
202+
expect(drawLabel.props.adjustsFontSizeToFit).toBe(true);
203+
});
204+
194205
it('renders live Moneyline best ask prices when available', () => {
195206
mockGetLivePrice.mockImplementation((tokenId: string) => ({
196207
tokenId,

app/components/UI/Predict/components/PredictMarketSportCard/PredictMarketSportCard.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,8 @@ const PredictMarketSportCard: React.FC<PredictMarketSportCardProps> = ({
390390
<Button
391391
onPress={() => handleBuy(item)}
392392
style={{ backgroundColor: getButtonBackgroundColor(item) }}
393-
twClassName={`${isCompact ? 'p-0' : ''}`}
393+
twClassName={isCompact ? 'p-0' : 'px-1'}
394+
contentWrapperProps={{ twClassName: 'w-full' }}
394395
isFullWidth
395396
size={isCompact ? ButtonBaseSize.Md : ButtonBaseSize.Lg}
396397
testID={
@@ -400,8 +401,11 @@ const PredictMarketSportCard: React.FC<PredictMarketSportCardProps> = ({
400401
<Text
401402
variant={TextVariant.BodySm}
402403
numberOfLines={1}
404+
adjustsFontSizeToFit
405+
minimumFontScale={0.7}
406+
ellipsizeMode="clip"
403407
style={tw.style(
404-
'font-medium text-center',
408+
'font-medium text-center flex-1',
405409
getButtonTextColorClass(item),
406410
)}
407411
>

0 commit comments

Comments
 (0)