From 30c88f85bcc3c7987609fe1061756151e2a04ce9 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Wed, 1 May 2024 21:11:46 +0200 Subject: [PATCH] Forward keys to JSX component (#2379) I assumed keys were part of the props, but it's actually a separate parameter for the `jsx` function. I've updated the runtime to properly pass keys to components. --- .../examples/packages/jsx/snap.manifest.json | 2 +- .../src/jsx/jsx-dev-runtime.test.tsx | 10 +++++ packages/snaps-sdk/src/jsx/jsx-dev-runtime.ts | 6 ++- .../snaps-sdk/src/jsx/jsx-runtime.test.tsx | 37 +++++++++++++++++++ packages/snaps-sdk/src/jsx/jsx-runtime.ts | 12 ++++-- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/packages/examples/packages/jsx/snap.manifest.json b/packages/examples/packages/jsx/snap.manifest.json index d28247f997..a7942d4cc2 100644 --- a/packages/examples/packages/jsx/snap.manifest.json +++ b/packages/examples/packages/jsx/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "9ZxIHOljzFNgcbZp4jElP88uVHJ/SY6YUn4HZimoYNQ=", + "shasum": "ATu40WUaWGs+bqWvB35o8x/JpXkDCebljE81/1L7vf4=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-sdk/src/jsx/jsx-dev-runtime.test.tsx b/packages/snaps-sdk/src/jsx/jsx-dev-runtime.test.tsx index b027e869d9..77da5f5373 100644 --- a/packages/snaps-sdk/src/jsx/jsx-dev-runtime.test.tsx +++ b/packages/snaps-sdk/src/jsx/jsx-dev-runtime.test.tsx @@ -11,6 +11,16 @@ describe('jsx', () => { }); }); + it('renders a JSX component with a key', () => { + const element = Hello; + + expect(element).toStrictEqual({ + type: 'Text', + key: 'foo', + props: { children: 'Hello' }, + }); + }); + it('renders a nested JSX component', () => { const element = ( diff --git a/packages/snaps-sdk/src/jsx/jsx-dev-runtime.ts b/packages/snaps-sdk/src/jsx/jsx-dev-runtime.ts index 2c1d328530..53d7fcacc7 100644 --- a/packages/snaps-sdk/src/jsx/jsx-dev-runtime.ts +++ b/packages/snaps-sdk/src/jsx/jsx-dev-runtime.ts @@ -13,14 +13,16 @@ import { assertJSXElement } from './validation'; * * @param component - The component to render. * @param props - The props to pass to the component. + * @param key - The key of the component. * @returns The rendered component. * @see https://www.typescriptlang.org/tsconfig/#jsx */ export function jsxDEV( component: SnapComponent, - props: Props & { key?: Key | null }, + props: Props, + key: Key | null, ): unknown | null { - const element = jsx(component, props); + const element = jsx(component, props, key); assertJSXElement(element); return element; diff --git a/packages/snaps-sdk/src/jsx/jsx-runtime.test.tsx b/packages/snaps-sdk/src/jsx/jsx-runtime.test.tsx index 013374cf6d..0f7d94fe94 100644 --- a/packages/snaps-sdk/src/jsx/jsx-runtime.test.tsx +++ b/packages/snaps-sdk/src/jsx/jsx-runtime.test.tsx @@ -11,6 +11,16 @@ describe('jsx', () => { }); }); + it('renders a JSX component with a key', () => { + const element = Hello; + + expect(element).toStrictEqual({ + type: 'Text', + key: 'foo', + props: { children: 'Hello' }, + }); + }); + it('does not validate the element', () => { // @ts-expect-error - Invalid props. expect(() => ).not.toThrow(); @@ -66,4 +76,31 @@ describe('jsxs', () => { }, }); }); + + it('renders a nested JSX component with a key', () => { + const element = ( + + + Hello, world + + + ); + + expect(element).toStrictEqual({ + type: 'Box', + key: null, + props: { + children: { + type: 'Text', + key: 'foo', + props: { + children: [ + 'Hello, ', + { type: 'Bold', key: 'bar', props: { children: 'world' } }, + ], + }, + }, + }, + }); + }); }); diff --git a/packages/snaps-sdk/src/jsx/jsx-runtime.ts b/packages/snaps-sdk/src/jsx/jsx-runtime.ts index 4cf88d1caf..6c8d39ba26 100644 --- a/packages/snaps-sdk/src/jsx/jsx-runtime.ts +++ b/packages/snaps-sdk/src/jsx/jsx-runtime.ts @@ -11,12 +11,14 @@ import type { JsonObject, Key, SnapComponent } from './component'; * * @param component - The component to render. * @param props - The props to pass to the component. + * @param key - The key of the component. * @returns The rendered component. * @see https://www.typescriptlang.org/tsconfig/#jsx */ export function jsx( component: SnapComponent, - props: Props & { key?: Key | null }, + props: Props, + key: Key | null, ): unknown | null { if (typeof component === 'string') { // If component is a string, it is a built-in HTML element. This is not @@ -36,7 +38,7 @@ export function jsx( ); } - return component(props); + return component({ ...props, key }); } /** @@ -52,12 +54,14 @@ export function jsx( * * @param component - The component to render. * @param props - The props to pass to the component. + * @param key - The key of the component. * @returns The rendered component. * @see https://www.typescriptlang.org/tsconfig/#jsx */ export function jsxs( component: SnapComponent, - props: Props & { key?: Key | null }, + props: Props, + key: Key | null, ): unknown | null { - return jsx(component, props); + return jsx(component, props, key); }