diff --git a/.gitignore b/.gitignore index 53ed9c301..31e89e058 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ next-env.d.ts # Sentry Config File .env.sentry-build-plugin + +certificates diff --git a/app/components/SearchBar.tsx b/app/components/SearchBar.tsx index ea191e4e8..5c9d4822f 100644 --- a/app/components/SearchBar.tsx +++ b/app/components/SearchBar.tsx @@ -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' }), }} diff --git a/app/components/stories/SearchBar.stories.tsx b/app/components/stories/SearchBar.stories.tsx new file mode 100644 index 000000000..e86ca7807 --- /dev/null +++ b/app/components/stories/SearchBar.stories.tsx @@ -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 = { + component: SearchBar, + decorators: [ + Story => ( + + + + ), + ], + parameters: { + layout: 'padded', + nextjs: { + appDirectory: true, + }, + }, + title: 'Components/SearchBar', +}; + +export default meta; +type Story = StoryObj; + +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'); + }, +}; diff --git a/bench/BUILD.md b/bench/BUILD.md index 7c36c5ae4..1346798c8 100644 --- a/bench/BUILD.md +++ b/bench/BUILD.md @@ -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 | \ No newline at end of file diff --git a/docs/development.md b/docs/development.md index 8847b033f..f5088b2d6 100644 --- a/docs/development.md +++ b/docs/development.md @@ -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. \ No newline at end of file +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. \ No newline at end of file