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
113 changes: 113 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,119 @@ pnpm build-testnet
pnpm build-mainnet
```

## Configure Theme (Optional)

This template comes with three built-in themes:
- TealLab (mvx:dark-theme)
- VibeMode (mvx:vibe-theme)
- BrightLight (mvx:light-theme)

But you can customize the appearance of your project by defining your own theme using CSS variables.
Follow these steps to set it up.

### Step 1. Update `tailwind.css` file

This is the main place where you customize your theme. You add your project specific colors and update
CSS variables that style your elements, according to your theme.

Define your color palette in the `:root` section:

```css
:root {
--mvx-custom-primary-color: #your-color;
--mvx-custom-secondary-color: #your-color;
}
```

Next, configure your theme-specific variables:

```css
:root[data-mvx-theme='mvx:your-theme'],
[data-mvx-theme='mvx:your-theme'] {
--mvx-bg-color-primary: var(--mvx-custom-primary-color);
--mvx-bg-color-secondary: var(--mvx-custom-secondary-color);
}
```

### Step 2. Add your theme in `useHandleThemeManagement.ts` hook

This hook registers and manages all available themes in your project.
It maintains a list of all theme options, tracks the currently active theme and
provides a `handleThemeSwitch` function that updates the `data-mvx-theme` attribute.

```typescript
const allThemeOptions: ThemeOptionType[] =
[
{ identifier: 'mvx:dark-theme', label: 'TealLab' },
{ identifier: 'mvx:vibe-theme', label: 'VibeMode' },
{ identifier: 'mvx:light-theme', label: 'BrightLight' },

{ identifier: 'mvx:your-theme', label: 'Your Theme Label' }
];
```

### Step 3. Add colors for your theme tooltip in `ThemeTooltip.tsx`

This allows you to see the theme options available in the project. They are listed in a tooltip
dropdown in header with visual color previews for each theme.

```typescript
const themeDotColors: Record<string, string[]> =
{
'mvx:dark-theme': ['#23F7DD', '#262626', '#B6B3AF', '#FFFFFF'],
'mvx:vibe-theme': ['#471150', '#5A2A62', '#D200FA', '#FFFFFF'],
'mvx:light-theme': ['#000000', '#A5A5A5', '#E2DEDC', '#F3EFED'],

'mvx:your-theme': ['#color1', '#color2', '#color3', '#color4']
};
```

### Step 4. Add theme properties for hero section in `HomeHero.tsx`

Add your icon in `assets` folder and import it as:

```typescript
import { ReactComponent as YourThemeIcon } from 'assets/icons/your-theme-icon.svg';
```

Add a background image for your theme in `public` folder and reference it in `tailwind.css`:

```css
@theme {
--background-image-your-theme: url('/your-theme-bg.png');
}
```

And then update `themeExtraProperties` object with your values. These properties are used for
customizing your hero section from home page. It adds background image and icon + title for the
theme switch section in hero.

```typescript
const themeExtraProperties: Record<
string,
Omit<HomeThemeOptionType, keyof ThemeOptionType>
> = {
// existing themes
'mvx:your-theme': {
icon: YourThemeIcon,
title: 'Your Title',
backgroundClass: 'bg-your-theme'
}
};
```

### Step 5. Set your theme as default in `initConfig.ts`

```typescript
dAppConfig: {
theme: 'mvx:your-theme',
}
```

Now the project will start with your configured theme.
All variables will have the colors you have set. If you don't set custom colors, the default ones will apply.
You can see the current theme in `data-mvx-theme` attribute in browser inspector.

## Learn More

You can learn more in the [Vite documentation](https://vitejs.dev/).
Expand Down
143 changes: 73 additions & 70 deletions src/pages/Home/components/HomeConnect/HomeConnect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,80 +22,83 @@ const styles = {
howToConnectContentCards: 'how-to-connect-content-cards grid grid-cols-1 items-stretch justify-center lg:grid-cols-3 gap-2 lg:gap-6'
} satisfies Record<string, string>;

const walletAddress = getNetworkConfig().network.walletAddress;
const detectedBrowser = getDetectedBrowser();
const isFirefox = detectedBrowser === BrowserEnum.Firefox;
export const HomeConnect = () => {
const walletAddress = getNetworkConfig().network.walletAddress;

const connectCards = [
{
icon: MetamaskIcon,
title: 'Metamask Snap',
description:
'Explore the entire MultiversX ecosystem with Metamask! Securely manage, swap and transfer your assets.',
linkTitle: 'Get Metamask',
linkDownloadAddress: isFirefox
? FIREFOX_METAMASK_ADDON_LINK
: CHROME_METAMASK_EXTENSION_LINK
},
// {
// icon: PasskeyIcon,
// title: 'Passkey',
// description:
// 'Passkeys offer a more secure and user-friendly way to authenticate and sign transactions.',
// linkTitle: 'Get Passkey',
// linkDownloadAddress: walletAddress
// },
{
icon: XPortalIcon,
title: 'xPortal Wallet',
description:
'The easiest way to invest, spend globally with a crypto card and earn yield across DeFi and stablecoins.',
linkTitle: 'Get xPortal',
linkDownloadAddress: GET_XPORTAL
},
{
icon: LedgerIcon,
title: 'Ledger',
description:
'You can safely store your EGLD by installing the MultiversX EGLD app on your Ledger Nano S or Ledger Nano X device',
linkTitle: 'Get Started',
linkDownloadAddress: GET_LEDGER
},
{
icon: WebWalletIcon,
title: 'MultiversX Web Wallet',
description:
'Store, swap, and transfer tokens or NFTs. Connect to Web3 apps on MultiversX blockchain.',
linkTitle: 'Get MultiversX Wallet',
linkDownloadAddress: walletAddress
}
];
const detectedBrowser = getDetectedBrowser();
const isFirefox = detectedBrowser === BrowserEnum.Firefox;

export const HomeConnect = () => (
<div className={styles.howToConnectContainer}>
<div className={styles.howToConnectHeader}>
<h1 className={styles.howToConnectTitle}>How can you connect</h1>
const connectCards = [
{
icon: MetamaskIcon,
title: 'Metamask Snap',
description:
'Explore the entire MultiversX ecosystem with Metamask! Securely manage, swap and transfer your assets.',
linkTitle: 'Get Metamask',
linkDownloadAddress: isFirefox
? FIREFOX_METAMASK_ADDON_LINK
: CHROME_METAMASK_EXTENSION_LINK
},
// {
// icon: PasskeyIcon,
// title: 'Passkey',
// description:
// 'Passkeys offer a more secure and user-friendly way to authenticate and sign transactions.',
// linkTitle: 'Get Passkey',
// linkDownloadAddress: walletAddress
// },
{
icon: XPortalIcon,
title: 'xPortal Wallet',
description:
'The easiest way to invest, spend globally with a crypto card and earn yield across DeFi and stablecoins.',
linkTitle: 'Get xPortal',
linkDownloadAddress: GET_XPORTAL
},
{
icon: LedgerIcon,
title: 'Ledger',
description:
'You can safely store your EGLD by installing the MultiversX EGLD app on your Ledger Nano S or Ledger Nano X device',
linkTitle: 'Get Started',
linkDownloadAddress: GET_LEDGER
},
{
icon: WebWalletIcon,
title: 'MultiversX Web Wallet',
description:
'Store, swap, and transfer tokens or NFTs. Connect to Web3 apps on MultiversX blockchain.',
linkTitle: 'Get MultiversX Wallet',
linkDownloadAddress: walletAddress
}
];

<p className={styles.howToConnectDescription}>
Choose your path, you must.
</p>
</div>
return (
<div className={styles.howToConnectContainer}>
<div className={styles.howToConnectHeader}>
<h1 className={styles.howToConnectTitle}>How can you connect</h1>

<p className={styles.howToConnectDescription}>
Choose your path, you must.
</p>
</div>

<div className={styles.howToConnectContent}>
<ExtensionConnect />
<div className={styles.howToConnectContent}>
<ExtensionConnect />

<div className={styles.howToConnectContentCards}>
{connectCards.map((card, index) => (
<ConnectCard
key={index}
icon={card.icon}
title={card.title}
description={card.description}
linkTitle={card.linkTitle}
linkDownloadAddress={card.linkDownloadAddress}
/>
))}
<div className={styles.howToConnectContentCards}>
{connectCards.map((card, index) => (
<ConnectCard
key={index}
icon={card.icon}
title={card.title}
description={card.description}
linkTitle={card.linkTitle}
linkDownloadAddress={card.linkDownloadAddress}
/>
))}
</div>
</div>
</div>
</div>
);
);
};
2 changes: 1 addition & 1 deletion src/provider/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const containerStyles = {
width: '100%',
maxWidth: '100%',
display: 'flex',
flexDirection: 'column',
flexDirection: 'column' as const,
gap: '10px'
}
};
Expand Down
3 changes: 0 additions & 3 deletions tests/TemplateActions/nativeAuth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ const keystoreConfig = {
};

test.describe('Native auth', () => {
// Native auth tests verify secure authentication token functionality
// Each test focuses on a specific aspect of the native auth feature

test.beforeEach(async ({ page }) => {
await TestActions.navigateToConnectWallet(page);
await TestActions.connectWebWallet({ page, loginMethod: keystoreConfig });
Expand Down
24 changes: 7 additions & 17 deletions tests/TemplateActions/pingAndPongAbi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ const keystoreConfig = {
password: TestDataEnums.keystoreFilePassword
};

test.describe('Ping & Pong', () => {
test.describe('Ping & Pong (ABI)', () => {
// Note: Ping/Pong buttons have a 3-minute cooldown period after being clicked
// Each test performs one transaction and verifies the result

test.beforeEach(async ({ page }) => {
await TestActions.navigateToConnectWallet(page);
Expand Down Expand Up @@ -113,33 +112,24 @@ test.describe('Ping & Pong', () => {
// Click on Sign button to confirm the transaction in the web wallet
await walletPage.getByTestId(SelectorsEnum.signButton).click();

// Wait for wallet page to be closed
// await walletPage.waitForEvent('close');

// Switch to template dashboard page
const templatePage = await TestActions.waitForPageByUrlSubstring({
page,
urlSubstring: OriginPageEnum.templateDashboard
});

// Verify we're back on the template page
await expect(templatePage).toHaveURL(/localhost:3000/);

// Wait for transaction toast to be displayed
await TestActions.waitForToastToBeDisplayed(templatePage);

// Wait for the transaction toast to be closed
// Wait for the transaction toast to be closed (indicates transaction completed)
await TestActions.waitForToastToBeClosed(templatePage);

// Check balance change based on the action performed
await TestActions.checkBalanceUpdate({
page,
// Check balance change based on the clicked button
await TestActions.checkPingPongBalanceUpdate({
page: templatePage,
containerSelector: SelectorsEnum.topInfoContainer,
initialBalance: accountBalance,
expectedChange:
clickedButton === 'ping'
? TEST_CONSTANTS.PING_BALANCE_CHANGE
: TEST_CONSTANTS.PONG_BALANCE_CHANGE
isPing: clickedButton === 'ping'
});

// Check that the button status changed after the action
Expand All @@ -153,7 +143,7 @@ test.describe('Ping & Pong', () => {
const allTransactions = await TestActions.parseTransactionsTable({
page,
tableType: 'ping-pong',
maxRows: 1 // only check the first/most recent transaction
maxRows: 5
});

// Define expected value based on the clicked button (ping = 1, pong = 0)
Expand Down
Loading
Loading