Skip to content

Commit 783afae

Browse files
feat: add blocks/Form (#659)
* feat: add blocks/Form --------- Co-authored-by: gravity-ui-bot <[email protected]>
1 parent 135af0d commit 783afae

File tree

21 files changed

+723
-26
lines changed

21 files changed

+723
-26
lines changed

.storybook/stories/documentation/Blocks.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,5 @@ _[Common field types](?id=documentation-types&viewMode=docs)_
5454
## [Table](?path=/story/blocks-table--docs&viewMode=docs)
5555

5656
## [Tabs](?path=/story/blocks-tabs--docs&viewMode=docs)
57+
58+
## [Form](?path=/story/blocks-form--docs&viewMode=docs)

src/blocks/Form/Form.scss

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
@import '../../../styles/mixins.scss';
2+
@import '../../../styles/variables.scss';
3+
4+
$block: '.#{$ns}form-block';
5+
6+
$textPadding: 10px;
7+
$maxLargeWidth: 609px;
8+
$yandexFormDesktopMinWidth: 475px;
9+
$largeBorderRadius: 32px;
10+
11+
#{$block} {
12+
$root: &;
13+
14+
border-radius: $largeBorderRadius;
15+
position: relative;
16+
17+
&__title {
18+
margin: 0 0 $indentSM $textPadding;
19+
20+
&_mobile {
21+
margin-left: 4px;
22+
}
23+
}
24+
25+
&__full-form {
26+
background-color: var(--g-color-base-background);
27+
padding: $indentL $indentXL $indentL calc(#{$indentXL} - #{$textPadding});
28+
border-radius: $borderRadius;
29+
}
30+
31+
&__media {
32+
position: absolute;
33+
top: 0;
34+
left: 0;
35+
bottom: 0;
36+
right: 0;
37+
border-radius: $largeBorderRadius;
38+
}
39+
40+
&__image {
41+
height: 100%;
42+
width: 100%;
43+
object-fit: cover;
44+
}
45+
46+
&__row {
47+
&_direction {
48+
&_form-content {
49+
flex-direction: row-reverse;
50+
}
51+
52+
&_center {
53+
padding-top: $indentXL;
54+
padding-bottom: $indentL;
55+
flex-direction: column;
56+
57+
#{$root}__content-wrapper {
58+
margin-bottom: $indentM;
59+
}
60+
}
61+
}
62+
}
63+
64+
&:not(#{$root}_with-background) {
65+
#{$root}__full-form {
66+
box-shadow: 0 4px 24px var(--pc-color-sfx-shadow), 0 2px 8px var(--pc-color-sfx-shadow);
67+
}
68+
#{$root}__row {
69+
&_direction {
70+
&_form-content {
71+
#{$root}__content-wrapper {
72+
padding: $indentL 0 $indentXL $indentXL;
73+
}
74+
}
75+
&_content-form {
76+
#{$root}__content-wrapper {
77+
padding: $indentL $indentXL $indentXL 0;
78+
}
79+
}
80+
}
81+
}
82+
}
83+
84+
&_with-background {
85+
#{$root}__row {
86+
&_direction {
87+
&_form-content {
88+
#{$root}__form-wrapper {
89+
padding: $indentXS 0 $indentSM $indentXS;
90+
}
91+
}
92+
&_content-form {
93+
#{$root}__form-wrapper {
94+
padding: $indentXS $indentXS $indentSM 0;
95+
}
96+
}
97+
&_form-content,
98+
&_content-form {
99+
#{$root}__content-wrapper {
100+
padding: $indentXL;
101+
}
102+
}
103+
}
104+
}
105+
}
106+
107+
@media (min-width: map-get($gridBreakpoints, 'lg')) {
108+
&_form-type_yandex {
109+
#{$root}__row {
110+
&_direction {
111+
&_form-content,
112+
&_content-form {
113+
#{$root}__content-col {
114+
flex: 1 0 0;
115+
}
116+
}
117+
118+
&_form-content,
119+
&_content-form,
120+
&_center {
121+
#{$root}__form {
122+
min-width: $yandexFormDesktopMinWidth;
123+
}
124+
#{$root}__form-col {
125+
max-width: initial;
126+
width: fit-content;
127+
}
128+
}
129+
}
130+
}
131+
}
132+
}
133+
134+
@media (max-width: map-get($gridBreakpoints, 'lg')) and (min-width: map-get($gridBreakpoints, 'md')) {
135+
&__row {
136+
flex-direction: column;
137+
}
138+
139+
&_with-background,
140+
&:not(#{$root}_with-background) {
141+
#{$root}__row {
142+
#{$root}__form-wrapper,
143+
#{$root}__content-wrapper {
144+
max-width: $maxLargeWidth;
145+
}
146+
147+
#{$root}__center,
148+
#{$root}__form-wrapper,
149+
#{$root}__content-wrapper {
150+
margin: 0 auto;
151+
padding-right: 0;
152+
padding-left: 0;
153+
}
154+
155+
#{$root}__form-wrapper {
156+
padding-top: 0;
157+
}
158+
159+
#{$root}__content-wrapper {
160+
text-align: center;
161+
padding-bottom: $indentM;
162+
}
163+
}
164+
}
165+
166+
&:not(#{$root}_with-background) &__row {
167+
#{$root}__content-wrapper {
168+
padding: 0 0 $indentM 0;
169+
}
170+
}
171+
}
172+
173+
@media (max-width: map-get($gridBreakpoints, 'md')) {
174+
&__full-form {
175+
padding: $indentM;
176+
}
177+
178+
&_with-background,
179+
&:not(#{$root}_with-background) {
180+
#{$root}__row {
181+
padding: 0;
182+
183+
#{$root}__form-wrapper,
184+
#{$root}__content-wrapper {
185+
padding: 0;
186+
}
187+
188+
#{$root}__content-wrapper {
189+
padding-bottom: $indentM;
190+
margin-bottom: 0;
191+
}
192+
}
193+
}
194+
195+
&_with-background {
196+
padding: 0 $indentXXXS;
197+
198+
#{$root}__row {
199+
padding-top: $indentL;
200+
201+
&_padding-bottom {
202+
&_m {
203+
padding-bottom: $indentM;
204+
}
205+
&_l {
206+
padding-bottom: $indentL;
207+
}
208+
}
209+
210+
&_direction {
211+
&_form-content,
212+
&_content-form,
213+
&_center {
214+
#{$root}__content-wrapper {
215+
padding-right: $indentXS;
216+
padding-left: $indentXS;
217+
}
218+
}
219+
220+
&_form-content,
221+
&_content-form {
222+
padding-top: $indentM;
223+
}
224+
}
225+
}
226+
}
227+
}
228+
}

src/blocks/Form/Form.tsx

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import React, {useCallback, useContext, useState} from 'react';
2+
3+
import {BackgroundImage, Title} from '../../components';
4+
import {MobileContext} from '../../context/mobileContext';
5+
import {Col, Grid, GridAlignItems, GridColumnSize, Row} from '../../grid';
6+
import type {FormBlockProps} from '../../models';
7+
import {
8+
FormBlockDataTypes,
9+
FormBlockDirection,
10+
isHubspotDataForm,
11+
isYandexDataForm,
12+
} from '../../models';
13+
import {Content} from '../../sub-blocks';
14+
import {block} from '../../utils';
15+
16+
import InnerForm from './InnerForm/InnerForm';
17+
18+
import './Form.scss';
19+
20+
const b = block('form-block');
21+
22+
const colSizes = {[GridColumnSize.Lg]: 6, [GridColumnSize.All]: 12};
23+
24+
const FormBlock: React.FC<FormBlockProps> = (props) => {
25+
const {formData, title, textContent, direction = FormBlockDirection.Center, background} = props;
26+
const [contentLoaded, setContentLoaded] = useState(false);
27+
const isMobile = useContext(MobileContext);
28+
29+
const hasImage = background && (background.src || background.desktop);
30+
const paddingBottom = background && background.style?.backgroundColor && !hasImage ? 'l' : 'm'; // bigger padding for case with background color and no image
31+
const onContentLoad = useCallback(() => {
32+
setContentLoaded(true);
33+
}, []);
34+
35+
if (!formData) {
36+
return null;
37+
}
38+
39+
let formType;
40+
41+
if (isYandexDataForm(formData)) {
42+
formType = FormBlockDataTypes.YANDEX;
43+
} else if (isHubspotDataForm(formData)) {
44+
formType = FormBlockDataTypes.HUBSPOT;
45+
}
46+
47+
return (
48+
<div
49+
className={b({
50+
'with-background': Boolean(background),
51+
'form-type': formType,
52+
})}
53+
>
54+
{background && (
55+
<BackgroundImage
56+
{...background}
57+
className={b('media')}
58+
imageClassName={b('image')}
59+
/>
60+
)}
61+
<Grid>
62+
<Row
63+
alignItems={
64+
direction === FormBlockDirection.Center
65+
? GridAlignItems.Center
66+
: GridAlignItems.Start
67+
}
68+
className={b('row', {
69+
direction,
70+
'padding-bottom': paddingBottom,
71+
})}
72+
>
73+
<Col sizes={colSizes} className={b('content-col')}>
74+
{textContent && (
75+
<div className={b('content-wrapper')}>
76+
<Content
77+
theme="default"
78+
{...textContent}
79+
centered={direction === FormBlockDirection.Center}
80+
colSizes={{all: 12}}
81+
className={b('content')}
82+
/>
83+
</div>
84+
)}
85+
</Col>
86+
<Col sizes={colSizes} className={b('form-col')}>
87+
<div className={b('form-wrapper')}>
88+
<div
89+
className={b('full-form', {
90+
hidden: !contentLoaded,
91+
})}
92+
>
93+
{title && (
94+
<Title
95+
title={{
96+
text: title,
97+
textSize: 's',
98+
}}
99+
className={b('title', {mobile: isMobile})}
100+
colSizes={{all: 12}}
101+
/>
102+
)}
103+
<InnerForm
104+
className={b('form')}
105+
formData={formData}
106+
onContentLoad={onContentLoad}
107+
/>
108+
</div>
109+
</div>
110+
</Col>
111+
</Row>
112+
</Grid>
113+
</div>
114+
);
115+
};
116+
117+
export default FormBlock;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React, {useEffect} from 'react';
2+
3+
import {YandexForm} from '../../../components';
4+
import {FormBlockData, isHubspotDataForm, isYandexDataForm} from '../../../models';
5+
import {HubspotForm} from '../../../sub-blocks';
6+
7+
interface InnerFormProps {
8+
formData: FormBlockData;
9+
onContentLoad: () => void;
10+
className?: string;
11+
}
12+
13+
const InnerForm: React.FC<InnerFormProps> = (props) => {
14+
const {formData, onContentLoad, className} = props;
15+
16+
useEffect(() => {
17+
if (isHubspotDataForm(formData)) {
18+
onContentLoad();
19+
}
20+
}, [onContentLoad, formData]);
21+
22+
if (isYandexDataForm(formData)) {
23+
const {onLoad, ...rest} = formData.yandex;
24+
return (
25+
<div className={className}>
26+
<YandexForm
27+
{...rest}
28+
onLoad={() => {
29+
onContentLoad();
30+
onLoad?.();
31+
}}
32+
/>
33+
</div>
34+
);
35+
}
36+
37+
if (isHubspotDataForm(formData)) {
38+
return <HubspotForm createDOMElement={true} {...formData.hubspot} />;
39+
}
40+
41+
return null;
42+
};
43+
44+
export default InnerForm;

0 commit comments

Comments
 (0)