Skip to content

Commit d8de673

Browse files
authored
Merge pull request #758 from gadget-inc/feature/shadcn-autobelongs-form
Feature/Shadcn-AutoBelongsForm
2 parents 7fae92d + b4f8f25 commit d8de673

File tree

8 files changed

+512
-192
lines changed

8 files changed

+512
-192
lines changed

packages/react/cypress/component/auto/form/PolarisAutoBelongsToForm.cy.tsx renamed to packages/react/cypress/component/auto/form/AutoBelongsToForm.cy.tsx

Lines changed: 139 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,173 @@
11
import React from "react";
2-
import { PolarisAutoForm } from "../../../../src/auto/polaris/PolarisAutoForm.js";
3-
import { PolarisAutoInput } from "../../../../src/auto/polaris/inputs/PolarisAutoInput.js";
4-
import { PolarisAutoBelongsToForm } from "../../../../src/auto/polaris/inputs/relationships/PolarisAutoBelongsToForm.js";
5-
import { PolarisAutoSubmit } from "../../../../src/auto/polaris/submit/PolarisAutoSubmit.js";
6-
import { PolarisSubmitResultBanner } from "../../../../src/auto/polaris/submit/PolarisSubmitResultBanner.js";
72
import { api } from "../../../support/api.js";
8-
import { PolarisWrapper } from "../../../support/auto.js";
3+
import { describeForEachAutoAdapter } from "../../../support/auto.js";
4+
import { SUITE_NAMES } from "../../../support/constants.js";
95

106
const originalSectionLinkedToWidget = { id: "1", name: "Section 1", label: "Label 1" };
117

12-
describe("PolarisAutoBelongsToForm", () => {
13-
const interceptModelUpdateActionMetadata = () => {
14-
cy.intercept({ method: "POST", url: `${api.connection.endpoint}?operation=ModelActionMetadata` }, RealWidgetMetadata).as(
15-
"ModelCreateActionMetadata"
16-
);
17-
};
8+
describeForEachAutoAdapter(
9+
"AutoBelongsToForm",
10+
({ name, adapter: { AutoForm, AutoInput, AutoSubmit, SubmitResultBanner, AutoBelongsToForm }, wrapper }) => {
11+
const interceptModelUpdateActionMetadata = () => {
12+
cy.intercept({ method: "POST", url: `${api.connection.endpoint}?operation=ModelActionMetadata` }, RealWidgetMetadata).as(
13+
"ModelCreateActionMetadata"
14+
);
15+
};
1816

19-
const expectUpdateActionSubmissionVariables = (expectedQueryValue?: any) => {
20-
cy.intercept({ method: "POST", url: `${api.connection.endpoint}?operation=updateWidget` }, (req) => {
21-
// eslint-disable-next-line
22-
expect(req.body.variables).to.deep.equal(expectedQueryValue);
23-
req.reply({ data: { updateWidget: { success: true, errors: null, x: {} } } });
24-
}).as("updateWidget");
25-
};
17+
const getDropdownMenuTriggerSelector = () => {
18+
return name == SUITE_NAMES.SHADCN ? "[data-testid='widget.section-dropdown-menu-trigger']" : ".Polaris-Button__Icon";
19+
};
2620

27-
const interceptWidgetQuery = () => {
28-
cy.intercept({ method: "POST", url: `${api.connection.endpoint}?operation=widget` }, (req) => {
29-
req.reply({
30-
data: {
31-
widget: {
32-
__typename: "Widget",
33-
id: "42",
34-
name: "test record",
35-
section: originalSectionLinkedToWidget,
36-
sectionId: parseInt(originalSectionLinkedToWidget.id),
37-
},
38-
},
39-
});
40-
}).as("widget");
41-
};
21+
const expectUpdateActionSubmissionVariables = (expectedQueryValue?: any) => {
22+
cy.intercept({ method: "POST", url: `${api.connection.endpoint}?operation=updateWidget` }, (req) => {
23+
// eslint-disable-next-line
24+
expect(req.body.variables).to.deep.equal(expectedQueryValue);
25+
req.reply({ data: { updateWidget: { success: true, errors: null, x: {} } } });
26+
}).as("updateWidget");
27+
};
4228

43-
const interceptSectionsOptionsQuery = () => {
44-
cy.intercept(
45-
{
46-
method: "POST",
47-
url: `${api.connection.endpoint}?operation=sections`,
48-
},
49-
(req) => {
50-
const sections = [
51-
{
52-
cursor: "eyJpZCI6IjEwODgifQ==",
53-
node: {
54-
__typename: "Section",
55-
id: "1",
56-
createdAt: "2023-09-07T19:18:50.742Z",
57-
name: "Section 1",
58-
label: "Label 1",
59-
updatedAt: "2024-07-09T14:42:20.788Z",
29+
const interceptWidgetQuery = () => {
30+
cy.intercept({ method: "POST", url: `${api.connection.endpoint}?operation=widget` }, (req) => {
31+
req.reply({
32+
data: {
33+
widget: {
34+
__typename: "Widget",
35+
id: "42",
36+
name: "test record",
37+
section: originalSectionLinkedToWidget,
38+
sectionId: parseInt(originalSectionLinkedToWidget.id),
6039
},
61-
__typename: "SectionEdge",
6240
},
63-
];
41+
});
42+
}).as("widget");
43+
};
6444

65-
req.reply({
66-
data: {
67-
sections: {
68-
pageInfo: {
69-
hasNextPage: false,
70-
hasPreviousPage: false,
71-
startCursor: "eyJpZCI6IjEifQ==",
72-
endCursor: "eyJpZCI6IjIifQ==",
73-
__typename: "PageInfo",
45+
const interceptSectionsOptionsQuery = () => {
46+
cy.intercept(
47+
{
48+
method: "POST",
49+
url: `${api.connection.endpoint}?operation=sections`,
50+
},
51+
(req) => {
52+
const sections = [
53+
{
54+
cursor: "eyJpZCI6IjEwODgifQ==",
55+
node: {
56+
__typename: "Section",
57+
id: "1",
58+
createdAt: "2023-09-07T19:18:50.742Z",
59+
name: "Section 1",
60+
label: "Label 1",
61+
updatedAt: "2024-07-09T14:42:20.788Z",
7462
},
75-
edges: sections,
76-
__typename: "SectionConnection",
63+
__typename: "SectionEdge",
7764
},
78-
gadgetMeta: {
79-
hydrations: {
80-
createdAt: "DateTime",
81-
updatedAt: "DateTime",
65+
];
66+
67+
req.reply({
68+
data: {
69+
sections: {
70+
pageInfo: {
71+
hasNextPage: false,
72+
hasPreviousPage: false,
73+
startCursor: "eyJpZCI6IjEifQ==",
74+
endCursor: "eyJpZCI6IjIifQ==",
75+
__typename: "PageInfo",
76+
},
77+
edges: sections,
78+
__typename: "SectionConnection",
79+
},
80+
gadgetMeta: {
81+
hydrations: {
82+
createdAt: "DateTime",
83+
updatedAt: "DateTime",
84+
},
85+
__typename: "GadgetApplicationMeta",
8286
},
83-
__typename: "GadgetApplicationMeta",
8487
},
85-
},
86-
});
87-
}
88-
).as("sections");
89-
};
88+
});
89+
}
90+
).as("sections");
91+
};
9092

91-
beforeEach(() => {
92-
cy.viewport("macbook-13");
93+
beforeEach(() => {
94+
cy.viewport("macbook-13");
9395

94-
interceptModelUpdateActionMetadata();
95-
interceptSectionsOptionsQuery();
96-
interceptWidgetQuery();
97-
});
96+
interceptModelUpdateActionMetadata();
97+
interceptSectionsOptionsQuery();
98+
interceptWidgetQuery();
99+
});
98100

99-
it("renders form fields for related record", () => {
100-
cy.mountWithWrapper(
101-
<PolarisAutoForm action={api.widget.update} findBy="42">
102-
<PolarisSubmitResultBanner />
103-
<PolarisAutoBelongsToForm field="section" primaryLabel="name" secondaryLabel="label">
104-
<PolarisAutoInput field="name" />
105-
<PolarisAutoInput field="label" />
106-
</PolarisAutoBelongsToForm>
107-
<PolarisAutoSubmit id="submit" />
108-
</PolarisAutoForm>,
109-
PolarisWrapper
110-
);
101+
it("renders form fields for related record", () => {
102+
cy.mountWithWrapper(
103+
<AutoForm action={api.widget.update} findBy="42">
104+
<SubmitResultBanner />
105+
<AutoBelongsToForm field="section" primaryLabel="name" secondaryLabel="label">
106+
<AutoInput field="name" />
107+
<AutoInput field="label" />
108+
</AutoBelongsToForm>
109+
<AutoSubmit id="submit" />
110+
</AutoForm>,
111+
wrapper
112+
);
111113

112-
cy.wait("@ModelCreateActionMetadata");
113-
cy.wait("@widget");
114+
cy.wait("@ModelCreateActionMetadata");
115+
cy.wait("@widget");
114116

115-
cy.contains("Section 1"); // primary label
116-
cy.contains("Label 1"); // secondary label
117+
cy.contains("Section 1"); // primary label
118+
cy.contains("Label 1"); // secondary label
117119

118-
cy.get(".Polaris-Button__Icon").first().click();
119-
cy.contains("Edit section").click();
120+
cy.get(getDropdownMenuTriggerSelector()).first().click();
121+
cy.contains("Edit section").click();
120122

121-
cy.get('input[id="widget.section.name"]').should("exist").click().type(" - updated");
122-
cy.get('input[id="widget.section.label"]').should("exist").click().type(" - updated");
123+
cy.clickAndType('input[id="widget.section.name"]', "Section 1 - updated", true);
124+
cy.clickAndType('input[id="widget.section.label"]', "Label 1 - updated", true);
123125

124-
cy.contains("Save").click();
126+
cy.contains("Save").click();
125127

126-
expectUpdateActionSubmissionVariables({
127-
id: "42",
128-
widget: {
129-
section: { update: { id: "1", name: "Section 1 - updated", label: "Label 1 - updated" } },
130-
},
128+
expectUpdateActionSubmissionVariables({
129+
id: "42",
130+
widget: {
131+
section: { update: { id: "1", name: "Section 1 - updated", label: "Label 1 - updated" } },
132+
},
133+
});
134+
cy.get('[id="submit"]').click();
135+
cy.wait("@updateWidget");
131136
});
132-
cy.get('[id="submit"]').click();
133-
cy.wait("@updateWidget");
134-
});
135137

136-
it("can unlink a belongsTo relationship", () => {
137-
cy.mountWithWrapper(
138-
<PolarisAutoForm action={api.widget.update} findBy="42">
139-
<PolarisSubmitResultBanner />
140-
<PolarisAutoBelongsToForm field="section" primaryLabel="name" secondaryLabel="label">
141-
<PolarisAutoInput field="name" />
142-
<PolarisAutoInput field="label" />
143-
</PolarisAutoBelongsToForm>
144-
<PolarisAutoSubmit id="submit" />
145-
</PolarisAutoForm>,
146-
PolarisWrapper
147-
);
138+
it("can unlink a belongsTo relationship", () => {
139+
cy.mountWithWrapper(
140+
<AutoForm action={api.widget.update} findBy="42">
141+
<SubmitResultBanner />
142+
<AutoBelongsToForm field="section" primaryLabel="name" secondaryLabel="label">
143+
<AutoInput field="name" />
144+
<AutoInput field="label" />
145+
</AutoBelongsToForm>
146+
<AutoSubmit id="submit" />
147+
</AutoForm>,
148+
wrapper
149+
);
148150

149-
cy.wait("@ModelCreateActionMetadata");
150-
cy.wait("@widget");
151+
cy.wait("@ModelCreateActionMetadata");
152+
cy.wait("@widget");
151153

152-
cy.contains("Section 1"); // primary label
153-
cy.contains("Label 1"); // secondary label
154+
cy.contains("Section 1"); // primary label
155+
cy.contains("Label 1"); // secondary label
154156

155-
cy.get(".Polaris-Button__Icon").first().click();
156-
cy.contains("Remove section").click();
157+
cy.get(getDropdownMenuTriggerSelector()).first().click();
158+
cy.contains("Remove section").click();
157159

158-
expectUpdateActionSubmissionVariables({
159-
id: "42",
160-
widget: {
161-
section: { _link: null },
162-
},
160+
expectUpdateActionSubmissionVariables({
161+
id: "42",
162+
widget: {
163+
section: { _link: null },
164+
},
165+
});
166+
cy.get('[id="submit"]').click();
167+
cy.wait("@updateWidget");
163168
});
164-
cy.get('[id="submit"]').click();
165-
cy.wait("@updateWidget");
166-
});
167-
});
169+
}
170+
);
168171

169172
const RealWidgetMetadata = {
170173
data: {

packages/react/cypress/support/auto.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const ONLY_RUN_SUITES = {
3333
"AutoForm - ID field",
3434
"AutoForm - Upsert Action",
3535
"AutoHasOneForm",
36+
"AutoBelongsToForm",
3637
"AutoFormDateTimePicker",
3738
"AutoFormJSONInput",
3839
"AutoPasswordInput",

packages/react/spec/auto/shadcn-defaults/inputs/form/ShadcnAutoRelationshipForm.stories.jsx

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,34 @@ import { elements } from "../../index.tsx";
88

99
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
1010

11-
const { AutoForm, AutoHasOneForm, AutoInput, AutoSubmit, SubmitResultBanner } = makeAutocomponents(elements);
11+
const { AutoForm, AutoHasOneForm, AutoInput, AutoSubmit, SubmitResultBanner, AutoBelongsToForm } = makeAutocomponents(elements);
12+
const { Label } = elements;
1213

1314
const Component = (props) => {
1415
return (
1516
<AutoForm {...props} action={api.widget.create}>
16-
<SubmitResultBanner />
17-
<AutoHasOneForm
18-
field="doodad"
19-
primaryLabel="name"
20-
secondaryLabel={(record) => `${record.weight ?? "N/A"} (${record.active ?? "N/A"})`}
21-
tertiaryLabel="size"
22-
>
23-
<div className="flex flex-col gap-4">
24-
<AutoInput field="name" />
25-
<AutoInput field="weight" />
26-
<AutoInput field="active" />
27-
<AutoInput field="size" />
17+
<div className="flex flex-col gap-4">
18+
<div>
19+
<AutoBelongsToForm field="section" primaryLabel="name"
20+
renderSelectedRecord={(record) => <Label>this is a custom belongsTo render for {record.name}</Label>}
21+
>
22+
<AutoInput field="name" />
23+
</AutoBelongsToForm>
2824
</div>
29-
</AutoHasOneForm>
30-
<AutoSubmit />
25+
<SubmitResultBanner />
26+
<AutoHasOneForm field="doodad" primaryLabel="name"
27+
secondaryLabel={(record) => `${record.weight ?? 'N/A'} (${record.active ?? 'N/A'})`} tertiaryLabel="size"
28+
>
29+
30+
<div className="flex flex-col gap-4">
31+
<AutoInput field="name" />
32+
<AutoInput field="weight" />
33+
<AutoInput field="active" />
34+
<AutoInput field="size" />
35+
</div>
36+
</AutoHasOneForm>
37+
<AutoSubmit />
38+
</div>
3139
</AutoForm>
3240
);
3341
};

packages/react/src/auto/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface AutoAdapter<T = any, F = any, B = any> {
1111
AutoSubmit: React.ComponentType<any>;
1212
SubmitResultBanner: React.ComponentType<any>;
1313
AutoHasOneForm: React.ComponentType<any>;
14+
AutoBelongsToForm: React.ComponentType<any>;
1415
AutoDateTimePicker: React.ComponentType<{
1516
suiteName?: string;
1617
field: string;

0 commit comments

Comments
 (0)