Skip to content

Commit 9f06c43

Browse files
chore: Added size prop to TitleLeft component (#24913)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Added a `size` prop to the `TitleLeft` component to support different title sizes. This allows consumers to choose between medium (`Md`) and small (`Sm`) variants: - `TitleLeftSize.Md` (default): Uses `TextVariant.DisplayMd` for the title - `TitleLeftSize.Sm`: Uses `TextVariant.HeadingLg` for the title This provides more flexibility when using `TitleLeft` in different contexts where a smaller title is needed. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: TitleLeft size prop Scenario: TitleLeft renders with default medium size Given the TitleLeft component is rendered without a size prop When user views the component Then the title should display using DisplayMd text variant Scenario: TitleLeft renders with small size Given the TitleLeft component is rendered with size={TitleLeftSize.Sm} When user views the component Then the title should display using HeadingLg text variant ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** https://github.com/user-attachments/assets/8b4c0271-76ef-433f-8869-992d6e19e305 <!-- [screenshots/recordings] --> ## **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. ## **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] > Introduces configurable sizing to `TitleLeft` for flexible typography. > > - Adds `size` prop and `TitleLeftSize` enum (default `Md`) mapping title to `DisplayMd` (Md) or `HeadingLg` (Sm) in `TitleLeft.tsx` > - Updates Storybook: `size` control and examples in `TitleLeft.stories.tsx` > - Adds tests validating text variants per size, including mock for `Text` in `TitleLeft.test.tsx` > - Exports `TitleLeftSize` via `index.ts`; updates types and JSDoc in `TitleLeft.types.ts` > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 26994a7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent f5b1322 commit 9f06c43

5 files changed

Lines changed: 78 additions & 2 deletions

File tree

app/component-library/components-temp/TitleLeft/TitleLeft.stories.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ import {
1313
} from '@metamask/design-system-react-native';
1414

1515
import TitleLeft from './TitleLeft';
16+
import { TitleLeftSize } from './TitleLeft.types';
1617

1718
const TitleLeftMeta = {
1819
title: 'Components Temp / TitleLeft',
1920
component: TitleLeft,
2021
argTypes: {
22+
size: {
23+
control: 'select',
24+
options: Object.values(TitleLeftSize),
25+
},
2126
title: {
2227
control: 'text',
2328
},
@@ -43,13 +48,23 @@ export const Default = {
4348
args: {
4449
topLabel: 'Send',
4550
title: '$4.42',
51+
size: TitleLeftSize.Md,
4652
},
4753
};
4854

4955
export const TitleOnly = {
5056
render: () => <TitleLeft title="$1,234.56" />,
5157
};
5258

59+
export const Size = {
60+
render: () => (
61+
<>
62+
<TitleLeft size={TitleLeftSize.Sm} topLabel="Balance" title="$1,234.56" />
63+
<TitleLeft size={TitleLeftSize.Md} topLabel="Balance" title="$1,234.56" />
64+
</>
65+
),
66+
};
67+
5368
export const WithTopLabel = {
5469
render: () => <TitleLeft topLabel="Total Balance" title="$5,432.10" />,
5570
};

app/component-library/components-temp/TitleLeft/TitleLeft.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,23 @@
22
import React from 'react';
33
import { render } from '@testing-library/react-native';
44
import { Text, View } from 'react-native';
5+
import { TextVariant } from '@metamask/design-system-react-native';
56

67
// Internal dependencies.
78
import TitleLeft from './TitleLeft';
9+
import { TitleLeftSize } from './TitleLeft.types';
10+
11+
const mockText = jest.fn();
12+
jest.mock('@metamask/design-system-react-native', () => {
13+
const actual = jest.requireActual('@metamask/design-system-react-native');
14+
return {
15+
...actual,
16+
Text: (props: Record<string, unknown>) => {
17+
mockText(props);
18+
return <actual.Text {...props} />;
19+
},
20+
};
21+
});
822

923
const TEST_IDS = {
1024
CONTAINER: 'title-left-container',
@@ -16,6 +30,7 @@ const TEST_IDS = {
1630
describe('TitleLeft', () => {
1731
beforeEach(() => {
1832
jest.clearAllMocks();
33+
mockText.mockClear();
1934
});
2035

2136
describe('rendering', () => {
@@ -42,6 +57,35 @@ describe('TitleLeft', () => {
4257
});
4358
});
4459

60+
describe('size', () => {
61+
it('uses DisplayMd variant by default', () => {
62+
render(<TitleLeft title="$4.42" />);
63+
64+
const titleCall = mockText.mock.calls.find(
65+
(call) => call[0].children === '$4.42',
66+
);
67+
expect(titleCall?.[0].variant).toBe(TextVariant.DisplayMd);
68+
});
69+
70+
it('uses DisplayMd variant for medium size', () => {
71+
render(<TitleLeft size={TitleLeftSize.Md} title="$4.42" />);
72+
73+
const titleCall = mockText.mock.calls.find(
74+
(call) => call[0].children === '$4.42',
75+
);
76+
expect(titleCall?.[0].variant).toBe(TextVariant.DisplayMd);
77+
});
78+
79+
it('uses HeadingLg variant for small size', () => {
80+
render(<TitleLeft size={TitleLeftSize.Sm} title="$4.42" />);
81+
82+
const titleCall = mockText.mock.calls.find(
83+
(call) => call[0].children === '$4.42',
84+
);
85+
expect(titleCall?.[0].variant).toBe(TextVariant.HeadingLg);
86+
});
87+
});
88+
4589
describe('topLabel and topAccessory', () => {
4690
it('renders topLabel', () => {
4791
const { getByText } = render(<TitleLeft title="$4.42" topLabel="Send" />);

app/component-library/components-temp/TitleLeft/TitleLeft.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '@metamask/design-system-react-native';
1414

1515
// Internal dependencies.
16-
import { TitleLeftProps } from './TitleLeft.types';
16+
import { TitleLeftProps, TitleLeftSize } from './TitleLeft.types';
1717

1818
/**
1919
* TitleLeft is a component that displays a title with optional accessories
@@ -29,6 +29,7 @@ import { TitleLeftProps } from './TitleLeft.types';
2929
* ```
3030
*/
3131
const TitleLeft: React.FC<TitleLeftProps> = ({
32+
size = TitleLeftSize.Md,
3233
title,
3334
titleAccessory,
3435
topAccessory,
@@ -42,6 +43,8 @@ const TitleLeft: React.FC<TitleLeftProps> = ({
4243
testID,
4344
twClassName,
4445
}) => {
46+
const titleVariant =
47+
size === TitleLeftSize.Sm ? TextVariant.HeadingLg : TextVariant.DisplayMd;
4548
// Render top content (topLabel takes priority over topAccessory)
4649
const renderTopContent = () => {
4750
if (topLabel) {
@@ -101,7 +104,7 @@ const TitleLeft: React.FC<TitleLeftProps> = ({
101104
alignItems={BoxAlignItems.Center}
102105
>
103106
{title && (
104-
<Text variant={TextVariant.DisplayMd} {...titleProps}>
107+
<Text variant={titleVariant} {...titleProps}>
105108
{title}
106109
</Text>
107110
)}

app/component-library/components-temp/TitleLeft/TitleLeft.types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@ import { ReactNode } from 'react';
44
// External dependencies.
55
import { TextProps } from '@metamask/design-system-react-native';
66

7+
/**
8+
* TitleLeft size variants.
9+
*/
10+
export enum TitleLeftSize {
11+
Md = 'Md',
12+
Sm = 'Sm',
13+
}
14+
715
/**
816
* TitleLeft component props.
917
*/
1018
export interface TitleLeftProps {
19+
/**
20+
* Size of the title. Md uses DisplayMd, Sm uses HeadingLg.
21+
* @default TitleLeftSize.Md
22+
*/
23+
size?: TitleLeftSize;
1124
/**
1225
* Main title text, rendered with TextVariant.DisplayMD.
1326
*/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { default } from './TitleLeft';
2+
export { TitleLeftSize } from './TitleLeft.types';
23
export type { TitleLeftProps } from './TitleLeft.types';

0 commit comments

Comments
 (0)