Skip to content

Commit ef93601

Browse files
fix: use first dropdown option as the default value (#2465)
Fixes a problem where the default state for a dropdown would be `null` even though the UI would show option 1 as selected. We fix this by using option 1 as the default state value if nothing is specified.
1 parent 0c166a9 commit ef93601

File tree

4 files changed

+74
-5
lines changed

4 files changed

+74
-5
lines changed

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ describe('onRpcRequest', () => {
7676
const resultScreen = await response.getInterface();
7777

7878
expect(resultScreen).toRender(
79-
<Result values={{ 'example-input': '', 'example-dropdown': '' }} />,
79+
<Result
80+
values={{ 'example-input': '', 'example-dropdown': 'option1' }}
81+
/>,
8082
);
8183
await resultScreen.ok();
8284

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"branches": 91.59,
3-
"functions": 96.76,
2+
"branches": 91.64,
3+
"functions": 96.77,
44
"lines": 97.89,
55
"statements": 97.57
66
}

packages/snaps-controllers/src/interface/utils.test.tsx

+36
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,22 @@ describe('constructState', () => {
232232
});
233233
});
234234

235+
it('sets default value for root level dropdown', () => {
236+
const element = (
237+
<Box>
238+
<Dropdown name="foo">
239+
<Option value="option1">Option 1</Option>
240+
<Option value="option2">Option 2</Option>
241+
</Dropdown>
242+
</Box>
243+
);
244+
245+
const result = constructState({}, element);
246+
expect(result).toStrictEqual({
247+
foo: 'option1',
248+
});
249+
});
250+
235251
it('supports root level dropdowns', () => {
236252
const element = (
237253
<Box>
@@ -248,6 +264,26 @@ describe('constructState', () => {
248264
});
249265
});
250266

267+
it('sets default value for dropdowns in forms', () => {
268+
const element = (
269+
<Box>
270+
<Form name="form">
271+
<Field label="foo">
272+
<Dropdown name="foo">
273+
<Option value="option1">Option 1</Option>
274+
<Option value="option2">Option 2</Option>
275+
</Dropdown>
276+
</Field>
277+
</Form>
278+
</Box>
279+
);
280+
281+
const result = constructState({}, element);
282+
expect(result).toStrictEqual({
283+
form: { foo: 'option1' },
284+
});
285+
});
286+
251287
it('supports dropdowns in forms', () => {
252288
const element = (
253289
<Box>

packages/snaps-controllers/src/interface/utils.ts

+33-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
FieldElement,
1212
InputElement,
1313
JSXElement,
14+
OptionElement,
1415
} from '@metamask/snaps-sdk/jsx';
1516
import { isJSXElementUnsafe } from '@metamask/snaps-sdk/jsx';
1617
import {
@@ -48,6 +49,26 @@ export function assertNameIsUnique(state: InterfaceState, name: string) {
4849
);
4950
}
5051

52+
/**
53+
* Construct default state for a component.
54+
*
55+
* This function is meant to be used inside constructInputState to account
56+
* for component specific defaults and will not override the component value or existing form state.
57+
*
58+
* @param element - The input element.
59+
* @returns The default state for the specific component, if any.
60+
*/
61+
function constructComponentSpecificDefaultState(
62+
element: InputElement | DropdownElement,
63+
) {
64+
if (element.type === 'Dropdown') {
65+
const children = getJsxChildren(element) as OptionElement[];
66+
return children[0].props.value;
67+
}
68+
69+
return null;
70+
}
71+
5172
/**
5273
* Construct the state for an input field.
5374
*
@@ -59,7 +80,12 @@ function constructInputState(
5980
oldState: InterfaceState,
6081
element: InputElement | DropdownElement,
6182
) {
62-
return element.props.value ?? oldState[element.props.name] ?? null;
83+
return (
84+
element.props.value ??
85+
oldState[element.props.name] ??
86+
constructComponentSpecificDefaultState(element) ??
87+
null
88+
);
6389
}
6490

6591
/**
@@ -77,7 +103,12 @@ function constructFormInputState(
77103
) {
78104
const oldFormState = oldState[form] as FormState;
79105
const oldInputState = oldFormState?.[component.props.name];
80-
return component.props.value ?? oldInputState ?? null;
106+
return (
107+
component.props.value ??
108+
oldInputState ??
109+
constructComponentSpecificDefaultState(component) ??
110+
null
111+
);
81112
}
82113

83114
/**

0 commit comments

Comments
 (0)