Skip to content

Commit 4468314

Browse files
authored
feat(Form): allow custom components as children (#4777)
Closes #4497 #4597
1 parent d5f2375 commit 4468314

File tree

8 files changed

+432
-140
lines changed

8 files changed

+432
-140
lines changed

packages/main/src/components/Form/Form.cy.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,21 @@ describe('Form', () => {
3535
cy.viewport(393, 852); // iPhone 14 Pro
3636
cy.mount(component);
3737
cy.findByText('item 1:').should('have.css', 'grid-column', '1 / span 12');
38-
cy.findByTestId('formInput').parent().should('have.css', 'grid-column', 'auto / span 12');
38+
cy.findByTestId('formInput').parent().should('have.css', 'grid-column', '1 / span 12');
3939
});
4040

4141
it('size M - label should cover 2/12, field 10/12', () => {
4242
cy.viewport(834, 1194); // iPad Pro
4343
cy.mount(component);
4444
cy.findByText('item 1:').should('have.css', 'grid-column', '1 / span 2');
45-
cy.findByTestId('formInput').parent().should('have.css', 'grid-column', 'auto / span 10');
45+
cy.findByTestId('formInput').parent().should('have.css', 'grid-column', '3 / span 10');
4646
});
4747

4848
it('size L - label should cover 1/3, field 2/3', () => {
4949
cy.viewport(1280, 1024);
5050
cy.mount(component);
5151
cy.findByText('item 1:').should('have.css', 'grid-column', '1 / span 4');
52-
cy.findByTestId('formInput').parent().should('have.css', 'grid-column', 'auto / span 8');
52+
cy.findByTestId('formInput').parent().should('have.css', 'grid-column', '5 / span 8');
5353
});
5454

5555
it('size XL - render two columns with 1/3 and 2/3 each', () => {

packages/main/src/components/Form/Form.mdx

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,122 @@ import * as ComponentStories from './Form.stories';
1919

2020
<ControlsWithNote of={ComponentStories.Default} />
2121

22+
# More Examples
23+
24+
## Form with wrapped FormItems and FormGroups
25+
26+
This feature is supported **since v1.17.0**.
27+
28+
It is possible to nest `FormItem`s and `FormGroup`s in custom components, the only thing you have to take care of is that no other elements (e.g. `div` or `span`) are mounted outside of FilterItems, as this will most likely result in breaking the layout of the `Form`.
29+
30+
<Canvas of={ComponentStories.FormWithCustomComponents} sourceState="none" />
31+
32+
### Code
33+
34+
<details>
35+
36+
<summary>Show Code</summary>
37+
38+
```jsx
39+
const CustomComponent = ({ children }) => {
40+
return <>{children}</>;
41+
};
42+
43+
const CustomComponent2 = () => {
44+
return (
45+
<CustomComponent>
46+
<FormGroup titleText="Group 1 inside custom component2">
47+
<CustomComponent>
48+
<FormItem label="FormItem 1 within group">
49+
<Input />
50+
</FormItem>
51+
<FormItem label="FormItem 2 within group">
52+
<Input />
53+
</FormItem>
54+
</CustomComponent>
55+
</FormGroup>
56+
<FormGroup titleText="Group 2 inside custom component2">
57+
<FormItem label="FormItem 1 within group">
58+
<Input />
59+
</FormItem>
60+
<FormItem label="FormItem 2 within group">
61+
<Input />
62+
</FormItem>
63+
</FormGroup>
64+
</CustomComponent>
65+
);
66+
};
67+
68+
const FormComponent = (props) => {
69+
return (
70+
<Form {...props}>
71+
<FormItem label="Standalone FormItem">
72+
<Input />
73+
</FormItem>
74+
<FormGroup titleText="Standalone FormGroup">
75+
<FormItem label="Standalone FormItem within group">
76+
<Input />
77+
</FormItem>
78+
<FormItem label="Standalone FormItem within group">
79+
<Input />
80+
</FormItem>
81+
</FormGroup>
82+
<FormItem label="Standalone FormItem">
83+
<Input />
84+
</FormItem>
85+
<CustomComponent>
86+
<FormItem label="FormItem within custom component">
87+
<Input />
88+
</FormItem>
89+
</CustomComponent>
90+
<FormGroup titleText="Standalone FormGroup with custom component">
91+
<CustomComponent>
92+
<FormItem label="FormItem 1 within custom component">
93+
<Input />
94+
</FormItem>
95+
<FormItem label="FormItem 2 within custom component">
96+
<Input />
97+
</FormItem>
98+
</CustomComponent>
99+
</FormGroup>
100+
<CustomComponent>
101+
<FormGroup titleText="FormGroup within custom component">
102+
<FormItem label="FormItem 1 within group">
103+
<Input />
104+
</FormItem>
105+
<FormItem label="FormItem 2 within group">
106+
<Input />
107+
</FormItem>
108+
</FormGroup>
109+
</CustomComponent>
110+
<CustomComponent>
111+
<FormGroup titleText="Group 1 inside custom component">
112+
<CustomComponent>
113+
<FormItem label="FormItem 1 within group">
114+
<Input />
115+
</FormItem>
116+
<FormItem label="FormItem 2 within group">
117+
<Input />
118+
</FormItem>
119+
</CustomComponent>
120+
</FormGroup>
121+
<FormGroup titleText="Group 2 inside custom component">
122+
<FormItem label="FormItem 1 within group">
123+
<Input />
124+
</FormItem>
125+
<FormItem label="FormItem 2 within group">
126+
<Input />
127+
</FormItem>
128+
</FormGroup>
129+
</CustomComponent>
130+
<CustomComponent2 />
131+
</Form>
132+
);
133+
};
134+
```
135+
136+
</details>
137+
22138
<Markdown>{SubcomponentsSection}</Markdown>
23139

24140
## Form Group

packages/main/src/components/Form/Form.stories.tsx

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,9 @@ export const Default: Story = {
6060
<Option>Italy</Option>
6161
</Select>
6262
</FormItem>
63-
<FormItem
64-
style={{ alignSelf: 'start' }}
65-
label={<Label style={{ alignSelf: 'start', paddingTop: '0.25rem' }}>Additional Comment</Label>}
66-
>
63+
<FormItem label={<Label style={{ alignSelf: 'start', paddingTop: '0.25rem' }}>Additional Comment</Label>}>
6764
<TextArea
6865
rows={5}
69-
style={{ width: '210px', '--_ui5_textarea_margin': 0 }}
7066
placeholder="The styles of the Label of the TextArea FormItem is set to: alignSelf: 'start', paddingTop: '0.25rem'"
7167
/>
7268
</FormItem>
@@ -128,3 +124,101 @@ export const Default: Story = {
128124
);
129125
}
130126
};
127+
128+
const CustomComponent = ({ children }) => {
129+
return <>{children}</>;
130+
};
131+
132+
const CustomComponent2 = () => {
133+
return (
134+
<CustomComponent>
135+
<FormGroup titleText="Group 1 inside custom component2">
136+
<CustomComponent>
137+
<FormItem label="FormItem 1 within group">
138+
<Input />
139+
</FormItem>
140+
<FormItem label="FormItem 2 within group">
141+
<Input />
142+
</FormItem>
143+
</CustomComponent>
144+
</FormGroup>
145+
<FormGroup titleText="Group 2 inside custom component2">
146+
<FormItem label="FormItem 1 within group">
147+
<Input />
148+
</FormItem>
149+
<FormItem label="FormItem 2 within group">
150+
<Input />
151+
</FormItem>
152+
</FormGroup>
153+
</CustomComponent>
154+
);
155+
};
156+
157+
export const FormWithCustomComponents: Story = {
158+
render: (args) => {
159+
return (
160+
<Form {...args}>
161+
<FormItem label="Standalone FormItem">
162+
<Input />
163+
</FormItem>
164+
<FormGroup titleText="Standalone FormGroup">
165+
<FormItem label="Standalone FormItem within group">
166+
<Input />
167+
</FormItem>
168+
<FormItem label="Standalone FormItem within group">
169+
<Input />
170+
</FormItem>
171+
</FormGroup>
172+
<FormItem label="Standalone FormItem">
173+
<Input />
174+
</FormItem>
175+
<CustomComponent>
176+
<FormItem label="FormItem within custom component">
177+
<Input />
178+
</FormItem>
179+
</CustomComponent>
180+
<FormGroup titleText="Standalone FormGroup with custom component">
181+
<CustomComponent>
182+
<FormItem label="FormItem 1 within custom component">
183+
<Input />
184+
</FormItem>
185+
<FormItem label="FormItem 2 within custom component">
186+
<Input />
187+
</FormItem>
188+
</CustomComponent>
189+
</FormGroup>
190+
<CustomComponent>
191+
<FormGroup titleText="FormGroup within custom component">
192+
<FormItem label="FormItem 1 within group">
193+
<Input />
194+
</FormItem>
195+
<FormItem label="FormItem 2 within group">
196+
<Input />
197+
</FormItem>
198+
</FormGroup>
199+
</CustomComponent>
200+
<CustomComponent>
201+
<FormGroup titleText="Group 1 inside custom component">
202+
<CustomComponent>
203+
<FormItem label="FormItem 1 within group">
204+
<Input />
205+
</FormItem>
206+
<FormItem label="FormItem 2 within group">
207+
<Input />
208+
</FormItem>
209+
</CustomComponent>
210+
</FormGroup>
211+
<FormGroup titleText="Group 2 inside custom component">
212+
<FormItem label="FormItem 1 within group">
213+
<Input />
214+
</FormItem>
215+
<FormItem label="FormItem 2 within group">
216+
<Input />
217+
</FormItem>
218+
</FormGroup>
219+
</CustomComponent>
220+
<CustomComponent2 />
221+
</Form>
222+
);
223+
}
224+
};
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { createContext, useContext } from 'react';
2+
import type { FormContextType, GroupContextType } from './types.js';
23

3-
export const FormContext = createContext({ labelSpan: null });
4+
export const FormContext = createContext<FormContextType>({ labelSpan: null });
45

56
export function useFormContext() {
67
return useContext(FormContext);
78
}
9+
10+
export const GroupContext = createContext<GroupContextType>({});
11+
12+
export function useFormGroupContext() {
13+
return useContext(GroupContext);
14+
}

0 commit comments

Comments
 (0)