Skip to content

Commit 8bfaf95

Browse files
feat: Add disabled state to interactive UI example (#3293)
Makes a couple of improvements to the interactive UI example Snap: - Use custom dialog type with footers instead of regular buttons - Add a `disabled` parameter which disables all of the components for testing and visual inspection - Remove broken "Get interface state" button ![image](https://github.com/user-attachments/assets/91fb1988-55e0-47fc-ab3a-1d1ee3d8270a)
1 parent 92f9962 commit 8bfaf95

File tree

6 files changed

+127
-74
lines changed

6 files changed

+127
-74
lines changed

packages/examples/packages/interactive-ui/snap.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://github.com/MetaMask/snaps.git"
88
},
99
"source": {
10-
"shasum": "ldOmsuDgyS+Dz5o+bZtxtgb4Ss5XVJQ0fYpJtbHFtz8=",
10+
"shasum": "ZfrR4llZ3X3UXNIvRClCXQoQ7qSZKdgO3PgVq6L1Gxk=",
1111
"location": {
1212
"npm": {
1313
"filePath": "dist/bundle.js",

packages/examples/packages/interactive-ui/src/components/InteractiveForm.tsx

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
Dropdown,
1515
Option,
1616
Checkbox,
17+
Container,
18+
Footer,
1719
} from '@metamask/snaps-sdk/jsx';
1820

1921
/**
@@ -46,54 +48,72 @@ export type InteractiveFormState = {
4648
'example-selector': string;
4749
};
4850

49-
export const InteractiveForm: SnapComponent = () => {
51+
export const InteractiveForm: SnapComponent<{ disabled?: boolean }> = ({
52+
disabled,
53+
}) => {
5054
return (
51-
<Box>
52-
<Heading>Interactive UI Example Snap</Heading>
53-
<Form name="example-form">
54-
<Field label="Example Input">
55-
<Input name="example-input" placeholder="Enter something..." />
56-
</Field>
57-
<Field label="Example Dropdown">
58-
<Dropdown name="example-dropdown">
59-
<Option value="option1">Option 1</Option>
60-
<Option value="option2">Option 2</Option>
61-
<Option value="option3">Option 3</Option>
62-
</Dropdown>
63-
</Field>
64-
<Field label="Example RadioGroup">
65-
<RadioGroup name="example-radiogroup">
66-
<Radio value="option1">Option 1</Radio>
67-
<Radio value="option2">Option 2</Radio>
68-
<Radio value="option3">Option 3</Radio>
69-
</RadioGroup>
70-
</Field>
71-
<Field label="Example Checkbox">
72-
<Checkbox name="example-checkbox" label="Checkbox" />
73-
</Field>
74-
<Field label="Example Selector">
75-
<Selector
76-
name="example-selector"
77-
title="Choose an option"
78-
value="option1"
79-
>
80-
<SelectorOption value="option1">
81-
<Card title="Option 1" value="option1" />
82-
</SelectorOption>
83-
<SelectorOption value="option2">
84-
<Card title="Option 2" value="option2" />
85-
</SelectorOption>
86-
<SelectorOption value="option3">
87-
<Card title="Option 3" value="option3" />
88-
</SelectorOption>
89-
</Selector>
90-
</Field>
91-
<Box center>
92-
<Button type="submit" name="submit">
93-
Submit
94-
</Button>
95-
</Box>
96-
</Form>
97-
</Box>
55+
<Container>
56+
<Box>
57+
<Heading>Interactive UI Example Snap</Heading>
58+
<Form name="example-form">
59+
<Field label="Example Input">
60+
<Input
61+
name="example-input"
62+
placeholder="Enter something..."
63+
disabled={disabled}
64+
/>
65+
</Field>
66+
<Field label="Example Dropdown">
67+
<Dropdown name="example-dropdown" disabled={disabled}>
68+
<Option value="option1">Option 1</Option>
69+
<Option value="option2">Option 2</Option>
70+
<Option value="option3">Option 3</Option>
71+
</Dropdown>
72+
</Field>
73+
<Field label="Example RadioGroup">
74+
<RadioGroup name="example-radiogroup" disabled={disabled}>
75+
<Radio value="option1">Option 1</Radio>
76+
<Radio value="option2">Option 2</Radio>
77+
<Radio value="option3">Option 3</Radio>
78+
</RadioGroup>
79+
</Field>
80+
<Field label="Example Checkbox">
81+
<Checkbox
82+
name="example-checkbox"
83+
label="Checkbox"
84+
disabled={disabled}
85+
/>
86+
</Field>
87+
<Field label="Example Selector">
88+
<Selector
89+
name="example-selector"
90+
title="Choose an option"
91+
value="option1"
92+
disabled={disabled}
93+
>
94+
<SelectorOption value="option1">
95+
<Card title="Option 1" value="option1" />
96+
</SelectorOption>
97+
<SelectorOption value="option2">
98+
<Card title="Option 2" value="option2" />
99+
</SelectorOption>
100+
<SelectorOption value="option3">
101+
<Card title="Option 3" value="option3" />
102+
</SelectorOption>
103+
</Selector>
104+
</Field>
105+
</Form>
106+
</Box>
107+
<Footer>
108+
<Button
109+
type="submit"
110+
name="submit"
111+
form="example-form"
112+
disabled={disabled}
113+
>
114+
Submit
115+
</Button>
116+
</Footer>
117+
</Container>
98118
);
99119
};
Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import type { SnapComponent } from '@metamask/snaps-sdk/jsx';
2-
import { Heading, Button, Box, Text, Copyable } from '@metamask/snaps-sdk/jsx';
2+
import {
3+
Heading,
4+
Button,
5+
Box,
6+
Text,
7+
Copyable,
8+
Container,
9+
Footer,
10+
} from '@metamask/snaps-sdk/jsx';
311

412
import type { InteractiveFormState } from './InteractiveForm';
513

@@ -9,17 +17,20 @@ type ResultProps = {
917

1018
export const Result: SnapComponent<ResultProps> = ({ values }) => {
1119
return (
12-
<Box>
13-
<Heading>Interactive UI Example Snap</Heading>
14-
<Text>You submitted the following values:</Text>
20+
<Container>
1521
<Box>
16-
{Object.values(values).map((value) => (
17-
<Copyable value={value?.toString() ?? ''} />
18-
))}
22+
<Heading>Interactive UI Example Snap</Heading>
23+
<Text>You submitted the following values:</Text>
24+
<Box>
25+
{Object.values(values).map((value) => (
26+
<Copyable value={value?.toString() ?? ''} />
27+
))}
28+
</Box>
1929
</Box>
20-
<Box center>
30+
<Footer>
2131
<Button name="back">Back</Button>
22-
</Box>
23-
</Box>
32+
<Button name="ok">OK</Button>
33+
</Footer>
34+
</Container>
2435
);
2536
};

packages/examples/packages/interactive-ui/src/index.test.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from '@jest/globals';
2-
import { assertIsConfirmationDialog, installSnap } from '@metamask/snaps-jest';
2+
import { assertIsCustomDialog, installSnap } from '@metamask/snaps-jest';
33

44
import {
55
Insight,
@@ -52,7 +52,7 @@ describe('onRpcRequest', () => {
5252
await formScreen.clickElement('submit');
5353

5454
const resultScreen = await response.getInterface();
55-
assertIsConfirmationDialog(resultScreen);
55+
assertIsCustomDialog(resultScreen);
5656

5757
expect(resultScreen).toRender(
5858
<Result
@@ -65,9 +65,9 @@ describe('onRpcRequest', () => {
6565
}}
6666
/>,
6767
);
68-
await resultScreen.ok();
68+
await resultScreen.clickElement('ok');
6969

70-
expect(await response).toRespondWith(true);
70+
expect(await response).toRespondWith(null);
7171
});
7272

7373
it('lets users input nothing', async () => {
@@ -84,7 +84,7 @@ describe('onRpcRequest', () => {
8484
await formScreen.clickElement('submit');
8585

8686
const resultScreen = await response.getInterface();
87-
assertIsConfirmationDialog(resultScreen);
87+
assertIsCustomDialog(resultScreen);
8888

8989
expect(resultScreen).toRender(
9090
<Result
@@ -97,9 +97,9 @@ describe('onRpcRequest', () => {
9797
}}
9898
/>,
9999
);
100-
await resultScreen.ok();
100+
await resultScreen.clickElement('ok');
101101

102-
expect(await response).toRespondWith(true);
102+
expect(await response).toRespondWith(null);
103103
});
104104
});
105105
});

packages/examples/packages/interactive-ui/src/index.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,19 @@ import { decodeData } from './utils';
3232
export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
3333
switch (request.method) {
3434
case 'dialog': {
35+
const params = request.params as { disabled: boolean };
36+
const disabled = params?.disabled;
37+
const interfaceId = await snap.request({
38+
method: 'snap_createInterface',
39+
params: {
40+
ui: <InteractiveForm disabled={disabled} />,
41+
context: { disabled },
42+
},
43+
});
3544
return await snap.request({
3645
method: 'snap_dialog',
3746
params: {
38-
type: 'confirmation',
39-
content: <InteractiveForm />,
47+
id: interfaceId,
4048
},
4149
});
4250
}
@@ -139,7 +147,17 @@ export const onUserInput: OnUserInputHandler = async ({
139147
method: 'snap_updateInterface',
140148
params: {
141149
id,
142-
ui: <InteractiveForm />,
150+
ui: <InteractiveForm disabled={context?.disabled as boolean} />,
151+
},
152+
});
153+
break;
154+
155+
case 'ok':
156+
await snap.request({
157+
method: 'snap_resolveInterface',
158+
params: {
159+
id,
160+
value: null,
143161
},
144162
});
145163
break;

packages/test-snaps/src/features/snaps/interactive-ui/InteractiveUI.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ import { getSnapId } from '../../../utils';
1414
export const InteractiveUI: FunctionComponent = () => {
1515
const [invokeSnap, { isLoading, data, error }] = useInvokeMutation();
1616

17-
const handleClick = (method: string) => () => {
17+
const handleClick = (method: string, disabled: boolean) => () => {
1818
invokeSnap({
1919
snapId: getSnapId(INTERACTIVE_UI_SNAP_ID, INTERACTIVE_UI_SNAP_PORT),
2020
method,
21+
params: {
22+
disabled,
23+
},
2124
}).catch(logError);
2225
};
2326
return (
@@ -33,16 +36,17 @@ export const InteractiveUI: FunctionComponent = () => {
3336
variant="primary"
3437
id="createDialogButton"
3538
disabled={isLoading}
36-
onClick={handleClick('dialog')}
39+
onClick={handleClick('dialog', false)}
3740
>
3841
Create Dialog
3942
</Button>
4043
<Button
4144
variant="primary"
42-
id="getInterfaceStateButton"
43-
onClick={handleClick('getState')}
45+
id="createDisabledDialogButton"
46+
disabled={isLoading}
47+
onClick={handleClick('dialog', true)}
4448
>
45-
Get interface state
49+
Create Disabled Dialog
4650
</Button>
4751
</ButtonGroup>
4852
<Result>

0 commit comments

Comments
 (0)