Skip to content

Commit 456327b

Browse files
wanghoppehoppe
authored andcommitted
fix: subForm and RadioButton bugs (#2792)
* surface subForm update to parentForm * fix: radio button doesn't update paramter value * modify key in renderChildEntries --------- Co-authored-by: hoppe <hoppewang@microsoft.com>
1 parent 4e47972 commit 456327b

5 files changed

Lines changed: 87 additions & 7 deletions

File tree

packages/bonito-core/src/form/__tests__/form.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,42 @@ describe("Form tests", () => {
247247
color: "red",
248248
},
249249
});
250+
251+
const subFormColor = subForm.param("color", StringParameter, {
252+
value: "blue",
253+
});
254+
expect(subForm.values).toEqual({
255+
color: "blue",
256+
});
257+
258+
const subFormOnChangeSpy = jest.fn();
259+
const formOnChangeSpy = jest.fn();
260+
261+
subForm.on("change", subFormOnChangeSpy);
262+
form.on("change", formOnChangeSpy);
263+
264+
subFormColor.value = "green";
265+
expect(subFormOnChangeSpy).toHaveBeenCalledWith(
266+
{ color: "green" },
267+
{ color: "blue" }
268+
);
269+
270+
expect(formOnChangeSpy).toHaveBeenCalledWith(
271+
{
272+
applicant: "Sir Lancelot",
273+
applicantHouseholdSize: 2,
274+
answers: {
275+
color: "green",
276+
},
277+
},
278+
{
279+
applicant: "Sir Lancelot",
280+
applicantHouseholdSize: 2,
281+
answers: {
282+
color: "blue",
283+
},
284+
}
285+
);
250286
});
251287

252288
test("Validation", async () => {

packages/bonito-core/src/form/subform.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export class SubForm<
141141
this.parentForm.updateValue(name, this.values as S);
142142

143143
(this.parentForm as unknown as FormImpl<P>)._registerEntry(this);
144+
this._subscribeFormChange();
144145
}
145146

146147
childEntries(): IterableIterator<Entry<S>> {
@@ -250,4 +251,10 @@ export class SubForm<
250251
evaluate(): boolean {
251252
return this.form.evaluate();
252253
}
254+
255+
private _subscribeFormChange(): void {
256+
this.form.on("change", (newValues) => {
257+
this.parentForm.updateValue(this.name, newValues);
258+
});
259+
}
253260
}

packages/bonito-ui/src/components/form/__tests__/radio-button.spec.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { StringParameter } from "@azure/bonito-core/lib/form";
22
import { render, screen } from "@testing-library/react";
3+
import userEvent from "@testing-library/user-event";
34
import * as React from "react";
45
import { initMockBrowserEnvironment } from "../../../environment";
56
import { createParam } from "../../../form";
67
import { runAxe } from "../../../test-util/a11y";
8+
import { FormContainer } from "../form-container";
79
import { RadioButton } from "../radio-button";
810

911
describe("Checkbox control", () => {
@@ -42,4 +44,36 @@ describe("Checkbox control", () => {
4244
.getAttribute("type");
4345
expect(item2).toBe("radio");
4446
});
47+
48+
test("Radio button in form", async () => {
49+
const user = userEvent.setup();
50+
const param = createParam(StringParameter, {
51+
label: "First checkbox",
52+
value: "A",
53+
render: (props) => {
54+
return (
55+
<RadioButton
56+
{...props}
57+
options={[
58+
{ key: "A", text: "Option A" },
59+
{ key: "B", text: "Option B" },
60+
{ key: "C", text: "Option C" },
61+
]}
62+
></RadioButton>
63+
);
64+
},
65+
});
66+
67+
const { container } = render(<FormContainer form={param.parentForm} />);
68+
const ddEl = screen.getByRole("radiogroup");
69+
expect(ddEl).toBeDefined();
70+
const options = ddEl.getElementsByClassName("ms-ChoiceField-input");
71+
expect(options.length).toBe(3);
72+
73+
expect(param.value).toBe("A");
74+
await user.click(options[2]);
75+
expect(param.value).toBe("C");
76+
77+
expect(await runAxe(container)).toHaveNoViolations();
78+
});
4579
});

packages/bonito-ui/src/components/form/list-form-layout.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,24 @@ const ListForm = <V extends FormValues>(props: ListFormProps<V>) => {
9999

100100
function renderChildEntries<V extends FormValues>(
101101
entries: IterableIterator<Entry<V>>,
102-
rows: JSX.Element[]
102+
rows: JSX.Element[],
103+
parentKey: string = ""
103104
) {
104105
for (const entry of entries) {
106+
const key = `${parentKey}.${entry.name}`;
105107
if (entry instanceof AbstractParameter) {
106-
rows.push(<ParameterRow key={entry.name} param={entry} />);
108+
rows.push(<ParameterRow key={key} param={entry} />);
107109
} else if (entry instanceof ReactItem) {
108-
rows.push(<ItemRow key={entry.name} item={entry} />);
110+
rows.push(<ItemRow key={key} item={entry} />);
109111
} else if (entry instanceof Section) {
110-
rows.push(<SectionTitle key={entry.name} section={entry} />);
112+
rows.push(<SectionTitle key={key} section={entry} />);
111113
if (entry.childEntriesCount > 0) {
112-
renderChildEntries(entry.childEntries(), rows);
114+
renderChildEntries(entry.childEntries(), rows, key);
113115
}
114116
} else if (entry instanceof SubForm) {
115-
rows.push(<SubFormTitle key={entry.name} subForm={entry} />);
117+
rows.push(<SubFormTitle key={key} subForm={entry} />);
116118
if (entry.childEntriesCount > 0) {
117-
renderChildEntries(entry.childEntries(), rows);
119+
renderChildEntries(entry.childEntries(), rows, key);
118120
}
119121
}
120122
}

packages/bonito-ui/src/components/form/radio-button.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export function RadioButton<
6262
}}
6363
onBlur={onBlur}
6464
onChange={(event, option) => {
65+
param.value = option?.key as V[K];
6566
if (hasFocused) {
6667
setDirty(true);
6768
}

0 commit comments

Comments
 (0)