Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ next-env.d.ts

# Sentry Config File
.env.sentry-build-plugin

certificates
6 changes: 5 additions & 1 deletion app/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ export function SearchBar() {
onChange={onChange}
styles={{
control: style => ({ ...style, pointerEvents: 'all' }),
input: style => ({ ...style, width: '100%' }),
input: style => ({
...style,
gridTemplateColumns: '0 minmax(min-content, 1fr)',
width: '100%',
}),
/* work around for https://github.com/JedWatson/react-select/issues/3857 */
placeholder: style => ({ ...style, pointerEvents: 'none' }),
}}
Expand Down
65 changes: 65 additions & 0 deletions app/components/stories/SearchBar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from 'storybook/test';

import { ClusterProvider } from '@/app/providers/cluster';

import { SearchBar } from '../SearchBar';

const meta: Meta<typeof SearchBar> = {
component: SearchBar,
decorators: [
Story => (
<ClusterProvider>
<Story />
</ClusterProvider>
),
],
parameters: {
layout: 'padded',
nextjs: {
appDirectory: true,
},
},
title: 'Components/SearchBar',
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const input = canvas.getByRole('combobox');
expect(input).toBeInTheDocument();

const placeholder = canvas.getByText(/Search for blocks, accounts, transactions/i);
expect(placeholder).toBeInTheDocument();
},
};
export const MobileContextMenuFix: Story = {
name: 'Mobile Context Menu Fix (Copy/Paste)',
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const input = canvas.getByRole('combobox');

// Verify input is visible and interactable
expect(input).toBeVisible();
expect(input).toBeEnabled();

// Verify input has sufficient width for touch targets
// By default react-select input is only 2px wide, making it impossible to tap on mobile
// Minimum touch target is 44px (Apple HIG) / 48px (Material Design)
// See: https://github.com/JedWatson/react-select/issues/4106
const inputRect = input.getBoundingClientRect();
expect(inputRect.width).toBeGreaterThan(44);

// Verify we can click and type in the input
// This fails if pointer-events is 'none' on the control
// See: https://github.com/JedWatson/react-select/issues/3857
await userEvent.click(input);
expect(input).toHaveFocus();

await userEvent.type(input, 'test');
expect(input).toHaveValue('test');
},
};
96 changes: 48 additions & 48 deletions bench/BUILD.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
| Type | Route | Size | First Load JS |
| ------- | --------------------------------------------- | ------- | ------------- |
| Static | `/` | 77.6 kB | 1 MB |
| Static | `/_not-found` | 330 B | 156 kB |
| Dynamic | `/address/[address]` | 6.12 kB | 284 kB |
| Dynamic | `/address/[address]/anchor-account` | 6.35 kB | 985 kB |
| Dynamic | `/address/[address]/anchor-program` | 330 B | 156 kB |
| Dynamic | `/address/[address]/attestation` | 5.78 kB | 967 kB |
| Dynamic | `/address/[address]/attributes` | 2.49 kB | 926 kB |
| Dynamic | `/address/[address]/blockhashes` | 1.88 kB | 921 kB |
| Dynamic | `/address/[address]/compression` | 4.05 kB | 951 kB |
| Dynamic | `/address/[address]/concurrent-merkle-tree` | 2.93 kB | 945 kB |
| Dynamic | `/address/[address]/domains` | 13.6 kB | 926 kB |
| Dynamic | `/address/[address]/entries` | 2.88 kB | 932 kB |
| Dynamic | `/address/[address]/feature-gate` | 330 B | 156 kB |
| Dynamic | `/address/[address]/idl` | 52.3 kB | 465 kB |
| Dynamic | `/address/[address]/instructions` | 2.12 kB | 1.02 MB |
| Dynamic | `/address/[address]/metadata` | 3.85 kB | 940 kB |
| Dynamic | `/address/[address]/nftoken-collection-nfts` | 5.76 kB | 965 kB |
| Dynamic | `/address/[address]/program-multisig` | 3.56 kB | 987 kB |
| Dynamic | `/address/[address]/rewards` | 3.47 kB | 925 kB |
| Dynamic | `/address/[address]/security` | 8.06 kB | 1 MB |
| Dynamic | `/address/[address]/slot-hashes` | 3.38 kB | 925 kB |
| Dynamic | `/address/[address]/stake-history` | 3.51 kB | 925 kB |
| Dynamic | `/address/[address]/token-extensions` | 9.79 kB | 982 kB |
| Dynamic | `/address/[address]/tokens` | 8.18 kB | 1.12 MB |
| Dynamic | `/address/[address]/transfers` | 4.01 kB | 1.05 MB |
| Dynamic | `/address/[address]/verified-build` | 6.08 kB | 990 kB |
| Dynamic | `/address/[address]/vote-history` | 3.39 kB | 925 kB |
| Dynamic | `/api/anchor` | 0 B | 0 B |
| Dynamic | `/api/domain-info/[domain]` | 0 B | 0 B |
| Dynamic | `/api/metadata/proxy` | 0 B | 0 B |
| Dynamic | `/api/ping/[network]` | 0 B | 0 B |
| Dynamic | `/api/programMetadataIdl` | 0 B | 0 B |
| Dynamic | `/api/verified-programs/list/[page]` | 0 B | 0 B |
| Dynamic | `/api/verified-programs/metadata/[programId]` | 0 B | 0 B |
| Dynamic | `/block/[slot]` | 9.99 kB | 936 kB |
| Dynamic | `/block/[slot]/accounts` | 4.36 kB | 916 kB |
| Dynamic | `/block/[slot]/programs` | 4.95 kB | 917 kB |
| Dynamic | `/block/[slot]/rewards` | 4.88 kB | 922 kB |
| Dynamic | `/epoch/[epoch]` | 6.69 kB | 257 kB |
| Static | `/feature-gates` | 3.25 kB | 922 kB |
| Static | `/opengraph-image.png` | 0 B | 0 B |
| Static | `/supply` | 6.39 kB | 923 kB |
| Dynamic | `/tx/[signature]` | 37.6 kB | 1.31 MB |
| Dynamic | `/tx/[signature]/inspect` | 399 B | 1.11 MB |
| Static | `/tx/inspector` | 399 B | 1.11 MB |
| Static | `/verified-programs` | 6.02 kB | 165 kB |
| Type | Route | Size | First Load JS |
|------|-------|------|---------------|
| Static | `/` | 15.5 kB | 1.01 MB |
| Static | `/_not-found` | 326 B | 157 kB |
| Dynamic | `/address/[address]` | 6.2 kB | 284 kB |
| Dynamic | `/address/[address]/anchor-account` | 6.44 kB | 987 kB |
| Dynamic | `/address/[address]/anchor-program` | 326 B | 157 kB |
| Dynamic | `/address/[address]/attestation` | 5.78 kB | 967 kB |
| Dynamic | `/address/[address]/attributes` | 2.48 kB | 926 kB |
| Dynamic | `/address/[address]/blockhashes` | 1.88 kB | 921 kB |
| Dynamic | `/address/[address]/compression` | 4.05 kB | 953 kB |
| Dynamic | `/address/[address]/concurrent-merkle-tree` | 2.93 kB | 947 kB |
| Dynamic | `/address/[address]/domains` | 13.7 kB | 927 kB |
| Dynamic | `/address/[address]/entries` | 2.98 kB | 934 kB |
| Dynamic | `/address/[address]/feature-gate` | 325 B | 157 kB |
| Dynamic | `/address/[address]/idl` | 53.5 kB | 467 kB |
| Dynamic | `/address/[address]/instructions` | 2.11 kB | 1.03 MB |
| Dynamic | `/address/[address]/metadata` | 3.85 kB | 940 kB |
| Dynamic | `/address/[address]/nftoken-collection-nfts` | 5.84 kB | 966 kB |
| Dynamic | `/address/[address]/program-multisig` | 3.65 kB | 989 kB |
| Dynamic | `/address/[address]/rewards` | 3.46 kB | 925 kB |
| Dynamic | `/address/[address]/security` | 8.11 kB | 1.01 MB |
| Dynamic | `/address/[address]/slot-hashes` | 3.38 kB | 925 kB |
| Dynamic | `/address/[address]/stake-history` | 3.52 kB | 925 kB |
| Dynamic | `/address/[address]/token-extensions` | 11 kB | 984 kB |
| Dynamic | `/address/[address]/tokens` | 8.18 kB | 1.12 MB |
| Dynamic | `/address/[address]/transfers` | 4.01 kB | 1.06 MB |
| Dynamic | `/address/[address]/verified-build` | 6.56 kB | 992 kB |
| Dynamic | `/address/[address]/vote-history` | 3.4 kB | 925 kB |
| Dynamic | `/api/anchor` | 0 B | 0 B |
| Dynamic | `/api/domain-info/[domain]` | 0 B | 0 B |
| Dynamic | `/api/metadata/proxy` | 0 B | 0 B |
| Dynamic | `/api/ping/[network]` | 0 B | 0 B |
| Dynamic | `/api/programMetadataIdl` | 0 B | 0 B |
| Dynamic | `/api/verified-programs/list/[page]` | 0 B | 0 B |
| Dynamic | `/api/verified-programs/metadata/[programId]` | 0 B | 0 B |
| Dynamic | `/block/[slot]` | 10 kB | 938 kB |
| Dynamic | `/block/[slot]/accounts` | 4.46 kB | 918 kB |
| Dynamic | `/block/[slot]/programs` | 5.04 kB | 919 kB |
| Dynamic | `/block/[slot]/rewards` | 4.97 kB | 923 kB |
| Dynamic | `/epoch/[epoch]` | 6.78 kB | 257 kB |
| Static | `/feature-gates` | 3.35 kB | 923 kB |
| Static | `/opengraph-image.png` | 0 B | 0 B |
| Static | `/supply` | 6.48 kB | 925 kB |
| Dynamic | `/tx/[signature]` | 37.4 kB | 1.39 MB |
| Dynamic | `/tx/[signature]/inspect` | 411 B | 1.19 MB |
| Static | `/tx/inspector` | 410 B | 1.19 MB |
| Static | `/verified-programs` | 6.1 kB | 165 kB |
12 changes: 11 additions & 1 deletion docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,14 @@ To generate a component, use this script:
pnpm gen accordion
```

It translates needed component into `pnpx shadcn@version add accordion` and installs it.
It translates needed component into `pnpx shadcn@version add accordion` and installs it.

### Testing on mobile devices

To test on a remote mobile device (e.g., Safari on iPhone), run the dev server with HTTPS:

```bash
pnpm dev --experimental-https
```

This is required because Safari requires HTTPS for Web Cryptography API (`crypto.subtle`) access, which Solana dependencies need.