Skip to content

Commit

Permalink
feat: add blocks/Form (#659)
Browse files Browse the repository at this point in the history
* feat: add blocks/Form
---------

Co-authored-by: gravity-ui-bot <[email protected]>
  • Loading branch information
qradle-yndx and gravity-ui-bot authored Nov 8, 2023
1 parent 135af0d commit 783afae
Show file tree
Hide file tree
Showing 21 changed files with 723 additions and 26 deletions.
2 changes: 2 additions & 0 deletions .storybook/stories/documentation/Blocks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ _[Common field types](?id=documentation-types&viewMode=docs)_
## [Table](?path=/story/blocks-table--docs&viewMode=docs)

## [Tabs](?path=/story/blocks-tabs--docs&viewMode=docs)

## [Form](?path=/story/blocks-form--docs&viewMode=docs)
228 changes: 228 additions & 0 deletions src/blocks/Form/Form.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
@import '../../../styles/mixins.scss';
@import '../../../styles/variables.scss';

$block: '.#{$ns}form-block';

$textPadding: 10px;
$maxLargeWidth: 609px;
$yandexFormDesktopMinWidth: 475px;
$largeBorderRadius: 32px;

#{$block} {
$root: &;

border-radius: $largeBorderRadius;
position: relative;

&__title {
margin: 0 0 $indentSM $textPadding;

&_mobile {
margin-left: 4px;
}
}

&__full-form {
background-color: var(--g-color-base-background);
padding: $indentL $indentXL $indentL calc(#{$indentXL} - #{$textPadding});
border-radius: $borderRadius;
}

&__media {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
border-radius: $largeBorderRadius;
}

&__image {
height: 100%;
width: 100%;
object-fit: cover;
}

&__row {
&_direction {
&_form-content {
flex-direction: row-reverse;
}

&_center {
padding-top: $indentXL;
padding-bottom: $indentL;
flex-direction: column;

#{$root}__content-wrapper {
margin-bottom: $indentM;
}
}
}
}

&:not(#{$root}_with-background) {
#{$root}__full-form {
box-shadow: 0 4px 24px var(--pc-color-sfx-shadow), 0 2px 8px var(--pc-color-sfx-shadow);
}
#{$root}__row {
&_direction {
&_form-content {
#{$root}__content-wrapper {
padding: $indentL 0 $indentXL $indentXL;
}
}
&_content-form {
#{$root}__content-wrapper {
padding: $indentL $indentXL $indentXL 0;
}
}
}
}
}

&_with-background {
#{$root}__row {
&_direction {
&_form-content {
#{$root}__form-wrapper {
padding: $indentXS 0 $indentSM $indentXS;
}
}
&_content-form {
#{$root}__form-wrapper {
padding: $indentXS $indentXS $indentSM 0;
}
}
&_form-content,
&_content-form {
#{$root}__content-wrapper {
padding: $indentXL;
}
}
}
}
}

@media (min-width: map-get($gridBreakpoints, 'lg')) {
&_form-type_yandex {
#{$root}__row {
&_direction {
&_form-content,
&_content-form {
#{$root}__content-col {
flex: 1 0 0;
}
}

&_form-content,
&_content-form,
&_center {
#{$root}__form {
min-width: $yandexFormDesktopMinWidth;
}
#{$root}__form-col {
max-width: initial;
width: fit-content;
}
}
}
}
}
}

@media (max-width: map-get($gridBreakpoints, 'lg')) and (min-width: map-get($gridBreakpoints, 'md')) {
&__row {
flex-direction: column;
}

&_with-background,
&:not(#{$root}_with-background) {
#{$root}__row {
#{$root}__form-wrapper,
#{$root}__content-wrapper {
max-width: $maxLargeWidth;
}

#{$root}__center,
#{$root}__form-wrapper,
#{$root}__content-wrapper {
margin: 0 auto;
padding-right: 0;
padding-left: 0;
}

#{$root}__form-wrapper {
padding-top: 0;
}

#{$root}__content-wrapper {
text-align: center;
padding-bottom: $indentM;
}
}
}

&:not(#{$root}_with-background) &__row {
#{$root}__content-wrapper {
padding: 0 0 $indentM 0;
}
}
}

@media (max-width: map-get($gridBreakpoints, 'md')) {
&__full-form {
padding: $indentM;
}

&_with-background,
&:not(#{$root}_with-background) {
#{$root}__row {
padding: 0;

#{$root}__form-wrapper,
#{$root}__content-wrapper {
padding: 0;
}

#{$root}__content-wrapper {
padding-bottom: $indentM;
margin-bottom: 0;
}
}
}

&_with-background {
padding: 0 $indentXXXS;

#{$root}__row {
padding-top: $indentL;

&_padding-bottom {
&_m {
padding-bottom: $indentM;
}
&_l {
padding-bottom: $indentL;
}
}

&_direction {
&_form-content,
&_content-form,
&_center {
#{$root}__content-wrapper {
padding-right: $indentXS;
padding-left: $indentXS;
}
}

&_form-content,
&_content-form {
padding-top: $indentM;
}
}
}
}
}
}
117 changes: 117 additions & 0 deletions src/blocks/Form/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, {useCallback, useContext, useState} from 'react';

import {BackgroundImage, Title} from '../../components';
import {MobileContext} from '../../context/mobileContext';
import {Col, Grid, GridAlignItems, GridColumnSize, Row} from '../../grid';
import type {FormBlockProps} from '../../models';
import {
FormBlockDataTypes,
FormBlockDirection,
isHubspotDataForm,
isYandexDataForm,
} from '../../models';
import {Content} from '../../sub-blocks';
import {block} from '../../utils';

import InnerForm from './InnerForm/InnerForm';

import './Form.scss';

const b = block('form-block');

const colSizes = {[GridColumnSize.Lg]: 6, [GridColumnSize.All]: 12};

const FormBlock: React.FC<FormBlockProps> = (props) => {
const {formData, title, textContent, direction = FormBlockDirection.Center, background} = props;
const [contentLoaded, setContentLoaded] = useState(false);
const isMobile = useContext(MobileContext);

const hasImage = background && (background.src || background.desktop);
const paddingBottom = background && background.style?.backgroundColor && !hasImage ? 'l' : 'm'; // bigger padding for case with background color and no image
const onContentLoad = useCallback(() => {
setContentLoaded(true);
}, []);

if (!formData) {
return null;
}

let formType;

if (isYandexDataForm(formData)) {
formType = FormBlockDataTypes.YANDEX;
} else if (isHubspotDataForm(formData)) {
formType = FormBlockDataTypes.HUBSPOT;
}

return (
<div
className={b({
'with-background': Boolean(background),
'form-type': formType,
})}
>
{background && (
<BackgroundImage
{...background}
className={b('media')}
imageClassName={b('image')}
/>
)}
<Grid>
<Row
alignItems={
direction === FormBlockDirection.Center
? GridAlignItems.Center
: GridAlignItems.Start
}
className={b('row', {
direction,
'padding-bottom': paddingBottom,
})}
>
<Col sizes={colSizes} className={b('content-col')}>
{textContent && (
<div className={b('content-wrapper')}>
<Content
theme="default"
{...textContent}
centered={direction === FormBlockDirection.Center}
colSizes={{all: 12}}
className={b('content')}
/>
</div>
)}
</Col>
<Col sizes={colSizes} className={b('form-col')}>
<div className={b('form-wrapper')}>
<div
className={b('full-form', {
hidden: !contentLoaded,
})}
>
{title && (
<Title
title={{
text: title,
textSize: 's',
}}
className={b('title', {mobile: isMobile})}
colSizes={{all: 12}}
/>
)}
<InnerForm
className={b('form')}
formData={formData}
onContentLoad={onContentLoad}
/>
</div>
</div>
</Col>
</Row>
</Grid>
</div>
);
};

export default FormBlock;
44 changes: 44 additions & 0 deletions src/blocks/Form/InnerForm/InnerForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, {useEffect} from 'react';

import {YandexForm} from '../../../components';
import {FormBlockData, isHubspotDataForm, isYandexDataForm} from '../../../models';
import {HubspotForm} from '../../../sub-blocks';

interface InnerFormProps {
formData: FormBlockData;
onContentLoad: () => void;
className?: string;
}

const InnerForm: React.FC<InnerFormProps> = (props) => {
const {formData, onContentLoad, className} = props;

useEffect(() => {
if (isHubspotDataForm(formData)) {
onContentLoad();
}
}, [onContentLoad, formData]);

if (isYandexDataForm(formData)) {
const {onLoad, ...rest} = formData.yandex;
return (
<div className={className}>
<YandexForm
{...rest}
onLoad={() => {
onContentLoad();
onLoad?.();
}}
/>
</div>
);
}

if (isHubspotDataForm(formData)) {
return <HubspotForm createDOMElement={true} {...formData.hubspot} />;
}

return null;
};

export default InnerForm;
Loading

0 comments on commit 783afae

Please sign in to comment.