Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,23 @@ module.exports = {
},
},
{
// Temporary rollout strategy:
// Keep color-no-hex disabled for all tests by default, then re-enable it
// for specific folders in small PR batches. Once migration is complete,
// remove this override and enforce across all tests in:
// - app/components/
// - app/component-library/
files: ['**/*.test.{js,ts,tsx}', '**/*.stories.{js,ts,tsx}'],
rules: {
'@metamask/design-tokens/color-no-hex': 'off',
},
},
{
files: ['app/components/UI/Card/**/*.{js,jsx,ts,tsx}'],
rules: {
'@metamask/design-tokens/color-no-hex': 'error',
},
},
{
files: [
'app/components/UI/Name/**/*.{js,ts,tsx}',
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/build-android-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ jobs:
configure-keystores: true
target: ${{ inputs.keystore_target }} # qa for taget=main and flask for target=flask

- name: Restore .metamask folder
id: restore-metamask
uses: actions/cache@v4
with:
path: .metamask
key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }}

- name: Install Foundry if cache missed
if: steps.restore-metamask.outputs.cache-hit != 'true'
run: yarn install:foundryup
- name: Setup project dependencies with retry
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/build-ios-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ jobs:
- name: Clean iOS plist files
run: find ios -name "*.plist" -exec xattr -c {} \;

- name: Restore .metamask folder
id: restore-metamask
uses: actions/cache@v4
with:
path: .metamask
key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }}

- name: Install Foundry if cache missed
if: steps.restore-metamask.outputs.cache-hit != 'true'
run: yarn install:foundryup
# Run project setup with retry for better resilience
- name: Setup project dependencies with retry
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
Expand Down
29 changes: 21 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ jobs:
max_attempts: 3
retry_wait_seconds: 30
command: yarn install --immutable
- name: Restore .metamask folder
id: restore-metamask
uses: actions/cache@v4
with:
path: .metamask
key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }}

- name: Install Foundry if cache missed
if: steps.restore-metamask.outputs.cache-hit != 'true'
run: yarn install:foundryup
- name: Clean state and following up dependencies installation with retry
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
Expand All @@ -52,7 +62,7 @@ jobs:
else
echo "No changes detected"
fi

dedupe:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -392,6 +402,7 @@ jobs:
smart-e2e-selection:
name: 'Smart E2E Selection'
runs-on: ubuntu-latest
if: ${{ !github.event.pull_request.head.repo.fork }}
continue-on-error: true
permissions:
contents: read
Expand Down Expand Up @@ -589,7 +600,7 @@ jobs:
sonar-cloud:
runs-on: ubuntu-latest
needs: merge-unit-and-component-view-tests
if: ${{ !cancelled() && github.event_name != 'merge_group' }}
if: ${{ !cancelled() && github.event_name != 'merge_group' && !github.event.pull_request.head.repo.fork }}
steps:
- uses: actions/checkout@v3
with:
Expand Down Expand Up @@ -633,7 +644,7 @@ jobs:
sonar-cloud-quality-gate-status:
runs-on: ubuntu-latest
needs: sonar-cloud
if: ${{ !cancelled() && github.event_name != 'merge_group' }}
if: ${{ !cancelled() && github.event_name != 'merge_group' && !github.event.pull_request.head.repo.fork }}
steps:
- name: Checkout code
uses: actions/checkout@v3
Expand Down Expand Up @@ -709,10 +720,12 @@ jobs:
env:
NEEDS_CONTEXT: ${{ toJSON(needs) }}
EVENT_NAME: ${{ github.event_name }}
IS_FORK: ${{ github.event.pull_request.head.repo.fork }}
run: |
# Check results of all required jobs dynamically
# On merge_group events, "skipped" is acceptable (some jobs intentionally skip)
# On other events (PR, push), all jobs must succeed
# On fork PRs, "skipped" is acceptable (secret-dependent jobs are intentionally skipped)
# On other events (push to main), all jobs must succeed

FAILED="false"

Expand All @@ -721,8 +734,8 @@ jobs:
echo "::error::Job '$job_name' failed with result: $result"
FAILED="true"
elif [[ "$result" == "skipped" ]]; then
if [[ "$EVENT_NAME" == "merge_group" ]]; then
echo "Job '$job_name' was skipped (OK for merge_group events)"
if [[ "$EVENT_NAME" == "merge_group" ]] || [[ "$IS_FORK" == "true" ]]; then
echo "Job '$job_name' was skipped (OK for merge_group events and fork PRs)"
else
echo "::error::Job '$job_name' was unexpectedly skipped on $EVENT_NAME event"
FAILED="true"
Expand All @@ -738,7 +751,7 @@ jobs:
fi

echo "ALL_JOBS_PASSED=true" >> "$GITHUB_OUTPUT"

check-all-jobs-pass:
name: Check all jobs pass
if: ${{ !cancelled() }}
Expand Down Expand Up @@ -794,7 +807,7 @@ jobs:
name: Log merge group failure
runs-on: ubuntu-latest
# Only run this job if the merge group event fails, skip on forks
if: ${{ github.event_name == 'merge_group' && failure() && !github.event.repository.fork }}
if: ${{ github.event_name == 'merge_group' && failure() }}
needs:
- check-all-jobs-pass
steps:
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/performance-test-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ jobs:
command: yarn --immutable
## This installs dependencies and creates the node_modules state file

- name: Restore .metamask folder
id: restore-metamask
uses: actions/cache@v4
with:
path: .metamask
key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }}

- name: Install Foundry if cache missed
if: steps.restore-metamask.outputs.cache-hit != 'true'
run: yarn install:foundryup
- name: Setup project
run: yarn setup:github-ci
working-directory: '.'
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/run-e2e-smoke-tests-android-flask.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,23 @@ jobs:
retry_wait_seconds: 30
command: yarn install --immutable

- name: Restore .metamask folder
id: restore-metamask
uses: actions/cache@v4
with:
path: .metamask
key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }}

- name: Install Foundry if cache missed
if: steps.restore-metamask.outputs.cache-hit != 'true'
run: yarn install:foundryup
- name: Setup project
run: yarn setup:github-ci --no-build-ios
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: yarn setup:github-ci --no-build-ios

- name: Configure Keystore
uses: MetaMask/github-tools/.github/actions/configure-keystore@v1
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/run-e2e-smoke-tests-ios-flask.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,23 @@ jobs:
retry_wait_seconds: 30
command: yarn install --immutable

- name: Restore .metamask folder
id: restore-metamask
uses: actions/cache@v4
with:
path: .metamask
key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }}

- name: Install Foundry if cache missed
if: steps.restore-metamask.outputs.cache-hit != 'true'
run: yarn install:foundryup
- name: Setup project
run: yarn setup:github-ci --no-build-android
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: yarn setup:github-ci --no-build-android

- name: Download Main iOS App artifacts
uses: actions/download-artifact@v4
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/setup-node-modules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ jobs:
run: |
node scripts/apply-build-config.js ${{ inputs.build_name }} --export-github-env >> "$GITHUB_ENV"

- name: Restore .metamask folder
id: restore-metamask
if: inputs.platform != ''
uses: actions/cache@v4
with:
path: .metamask
key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }}

- name: Install Foundry if cache missed
if: inputs.platform != '' && steps.restore-metamask.outputs.cache-hit != 'true'
run: yarn install:foundryup

- name: Setup project
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
if: inputs.platform != ''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,47 @@ describe('HeaderCompactStandard', () => {
expect(getByText('Main Title')).toBeOnTheScreen();
expect(getByText('Supporting Text')).toBeOnTheScreen();
});

it('renders title when passed as React node', () => {
const TITLE_NODE_TEST_ID = 'custom-title-node';
const { getByTestId, getByText } = render(
<HeaderCompactStandard
title={<Text testID={TITLE_NODE_TEST_ID}>Custom Title Node</Text>}
/>,
);

expect(getByTestId(TITLE_NODE_TEST_ID)).toBeOnTheScreen();
expect(getByText('Custom Title Node')).toBeOnTheScreen();
});

it('renders subtitle when passed as React node', () => {
const SUBTITLE_NODE_TEST_ID = 'custom-subtitle-node';
const { getByTestId, getByText } = render(
<HeaderCompactStandard
title="Page Title"
subtitle={
<Text testID={SUBTITLE_NODE_TEST_ID}>Custom Subtitle Node</Text>
}
/>,
);

expect(getByTestId(SUBTITLE_NODE_TEST_ID)).toBeOnTheScreen();
expect(getByText('Custom Subtitle Node')).toBeOnTheScreen();
});

it('renders both title and subtitle as React nodes', () => {
const TITLE_NODE_TEST_ID = 'title-node';
const SUBTITLE_NODE_TEST_ID = 'subtitle-node';
const { getByTestId } = render(
<HeaderCompactStandard
title={<Text testID={TITLE_NODE_TEST_ID}>Node Title</Text>}
subtitle={<Text testID={SUBTITLE_NODE_TEST_ID}>Node Subtitle</Text>}
/>,
);

expect(getByTestId(TITLE_NODE_TEST_ID)).toBeOnTheScreen();
expect(getByTestId(SUBTITLE_NODE_TEST_ID)).toBeOnTheScreen();
});
});

describe('back button', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,31 @@ const HeaderCompactStandard: React.FC<HeaderCompactStandardProps> = ({
if (title) {
return (
<Box alignItems={BoxAlignItems.Center}>
<Text
variant={TextVariant.BodyMd}
fontWeight={FontWeight.Bold}
{...titleProps}
>
{title}
</Text>
{subtitle && (
{typeof title === 'string' ? (
<Text
variant={TextVariant.BodySm}
color={TextColor.TextAlternative}
{...subtitleProps}
twClassName={`-mt-0.5 ${subtitleProps?.twClassName ?? ''}`.trim()}
variant={TextVariant.BodyMd}
fontWeight={FontWeight.Bold}
{...titleProps}
>
{subtitle}
{title}
</Text>
) : (
title
)}
{subtitle && (
<Box twClassName="-mt-0.5">
{typeof subtitle === 'string' ? (
<Text
variant={TextVariant.BodySm}
color={TextColor.TextAlternative}
{...subtitleProps}
>
{subtitle}
</Text>
) : (
subtitle
)}
</Box>
)}
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// External dependencies.
import React from 'react';
import {
ButtonIconProps,
TextProps,
Expand All @@ -12,24 +13,28 @@ import { HeaderBaseProps } from '../../components/HeaderBase';
*/
export interface HeaderCompactStandardProps extends HeaderBaseProps {
/**
* Title text to display in the header.
* Title to display in the header. Can be a string or a React node.
* Used as children if children prop is not provided.
* Rendered with TextVariant.BodyMd and FontWeight.Bold by default.
* When string: rendered with TextVariant.BodyMd and FontWeight.Bold by default; titleProps apply.
* When node: rendered as-is; titleProps are not applied.
*/
title?: string;
title?: string | React.ReactNode;
/**
* Additional props to pass to the title Text component.
* Props are spread to the Text component and can override default values.
* Only applied when title is a string.
*/
titleProps?: Partial<TextProps>;
/**
* Subtitle text to display below the title.
* Rendered with TextVariant.BodySm and TextColor.TextAlternative by default.
* Subtitle to display below the title. Can be a string or a React node.
* When string: rendered with TextVariant.BodySm and TextColor.TextAlternative by default; subtitleProps apply.
* When node: rendered inside the same -mt-0.5 container; subtitleProps are not applied.
*/
subtitle?: string;
subtitle?: string | React.ReactNode;
/**
* Additional props to pass to the subtitle Text component.
* Props are spread to the Text component and can override default values.
* Only applied when subtitle is a string.
*/
subtitleProps?: Partial<TextProps>;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const TitleStandard: React.FC<TitleStandardProps> = ({
alignItems={BoxAlignItems.Center}
>
{title && (
<Text variant={TextVariant.DisplayMd} {...titleProps}>
<Text variant={TextVariant.HeadingLg} {...titleProps}>
{title}
</Text>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TextProps } from '@metamask/design-system-react-native';
*/
export interface TitleStandardProps {
/**
* Main title text, rendered with TextVariant.DisplayMd.
* Main title text, rendered with TextVariant.HeadingLg.
*/
title?: string;
/**
Expand Down
Loading
Loading