Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions memory-bank/usage/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,11 @@ The Content component is composed of several key parts:

1. **Title**: Displays the content title using the Title component
2. **Text**: Displays the main text content using the YFMWrapper component
3. **Content List**: Displays a list of content items using the ContentList component
4. **Additional Info**: Displays additional information using the YFMWrapper component
5. **Links**: Displays links using the Links component
6. **Buttons**: Displays buttons using the Buttons component
3. **Labels**: Displays labels using the ContentLabels component
4. **Content List**: Displays a list of content items using the ContentList component
5. **Additional Info**: Displays additional information using the YFMWrapper component
6. **Links**: Displays links using the Links component
7. **Buttons**: Displays buttons using the Buttons component

### Internal Structure

Expand All @@ -469,6 +470,11 @@ The Content component is composed of several key parts:
/>
</div>
)}
{labels?.length ? (
<div className={b('labels')}>
<ContentLabels labels={labels} theme={theme} />
</div>
) : null}
{list?.length ? (
<div className={b('list')}>
<ContentList list={list} size={size} qa={qaAttributes.list} theme={theme} />
Expand Down
9 changes: 7 additions & 2 deletions src/blocks/Form/Form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ $largeBorderRadius: 32px;
}
}

$labelsBlock: '.#{$ns}content-labels';
@media (min-width: map-get($gridBreakpoints, 'lg')) {
&_form-type_yandex {
#{$root}__row {
Expand Down Expand Up @@ -133,7 +134,7 @@ $largeBorderRadius: 32px;
}
}

@media (max-width: map-get($gridBreakpoints, 'lg')) and (min-width: map-get($gridBreakpoints, 'md')) {
@media (max-width: calc(map-get($gridBreakpoints, 'lg') - 1px)) and (min-width: map-get($gridBreakpoints, 'md')) {
&__row {
flex-direction: column;
}
Expand Down Expand Up @@ -161,6 +162,10 @@ $largeBorderRadius: 32px;
#{$root}__content-wrapper {
text-align: center;
padding-bottom: $indentM;

#{$labelsBlock} {
justify-content: center;
}
}
}
}
Expand All @@ -172,7 +177,7 @@ $largeBorderRadius: 32px;
}
}

@media (max-width: map-get($gridBreakpoints, 'md')) {
@media (max-width: calc(map-get($gridBreakpoints, 'md') - 1px)) {
&__full-form {
padding: $indentM;
}
Expand Down
51 changes: 34 additions & 17 deletions src/blocks/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import InnerForm from '../../components/InnerForm/InnerForm';
import {MobileContext} from '../../context/mobileContext';
import {useTheme} from '../../context/theme';
import {Col, Grid, GridAlignItems, GridColumnSize, Row} from '../../grid';
import {useDeviceValue} from '../../hooks/useDeviceValue';
import type {FormBlockProps} from '../../models';
import {
FormBlockDataTypes,
Expand All @@ -15,31 +16,42 @@ import {
import {Content} from '../../sub-blocks';
import {block, getThemedValue} from '../../utils';

import {hasBackgroundCSS} from './utils';

import './Form.scss';

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

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

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

const themedBackground = getThemedValue(background, theme) || undefined;
const themedBackgroundStyle = useDeviceValue(themedBackground?.style) || undefined;

const withBackground = Boolean(
themedBackground &&
(themedBackground.src ||
themedBackground.desktop ||
themedBackground.style?.backgroundColor),
hasBackgroundCSS(themedBackgroundStyle ?? {})),
);

const onContentLoad = React.useCallback(() => {
setContentLoaded(true);
}, []);

if (!formData) {
if (!formData && !customFormNode) {
return null;
}

Expand All @@ -61,6 +73,7 @@ const Form = (props: FormBlockProps) => {
{themedBackground && (
<BackgroundImage
{...themedBackground}
style={themedBackgroundStyle}
className={b('media')}
imageClassName={b('image')}
/>
Expand Down Expand Up @@ -96,21 +109,25 @@ const Form = (props: FormBlockProps) => {
hidden: !contentLoaded,
})}
>
{title && (
<Title
title={{
text: title,
textSize: 's',
}}
className={b('title', {mobile: isMobile})}
colSizes={{all: 12}}
/>
{customFormNode || (
<React.Fragment>
{title && (
<Title
title={{
text: title,
textSize: 's',
}}
className={b('title', {mobile: isMobile})}
colSizes={{all: 12}}
/>
)}
<InnerForm
className={b('form')}
formData={formData}
onContentLoad={onContentLoad}
/>
</React.Fragment>
)}
<InnerForm
className={b('form')}
formData={formData}
onContentLoad={onContentLoad}
/>
</div>
</div>
</Col>
Expand Down
17 changes: 16 additions & 1 deletion src/blocks/Form/__stories__/Form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ import * as FormBlockStories from './Form.stories.tsx';

`direction?: 'form-content' | 'content-form' | 'center'` - Direction.

`background?: BackgroundImage` — See [background](?path=/docs/components-pics-video-datalens-backgroundimage--docs) properties.
`background?: FormBlockBackgroundProps` — Same as `BackgroundImage` (see [background](?path=/docs/components-pics-video-datalens-backgroundimage--docs) properties), but the `style` prop supports `Device` breakpoints, for example:

```ts
{
style: {
desktop: {
background: 'red',
},
mobile: {
background: 'blue',
},
}
}
```

`customFormNode?: React.ReactNode` - A custom React node that will be rendered instead of the form.

</StoryTemplate>
5 changes: 5 additions & 0 deletions src/blocks/Form/__stories__/Form.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {blockTransform} from '../../../../.storybook/utils';
import {FormBlockDirection, FormBlockModel, FormBlockProps} from '../../../models';
import FormBlock from '../Form';

import ExampleStub from './components/ExmapleStub';

import data from './data.json';

export default {
Expand Down Expand Up @@ -42,6 +44,7 @@ export const WithBackgroundColor = VariantsTemplate.bind([]);
export const WithBackgroundImage = VariantsTemplate.bind([]);
export const DarkTheme = VariantsTemplate.bind([]);
export const FormData = VariantsTemplate.bind([]);
export const WithCustomFormNode = DefaultTemplate.bind([]);

Default.args = data.default as FormBlockModel;

Expand Down Expand Up @@ -88,3 +91,5 @@ FormData.parameters = {
include: Object.keys(FormData.args),
},
};

WithCustomFormNode.args = {...data.default, customFormNode: <ExampleStub />} as FormBlockModel;
23 changes: 23 additions & 0 deletions src/blocks/Form/__stories__/components/ExmapleStub.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {Button} from '@gravity-ui/uikit';

const ExampleStub = () => {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: 24,
}}
>
<h1>This is an example form component</h1>
<p>It can be anything</p>
<Button size="xl" view="action">
Got it!
</Button>
</div>
);
};

export default ExampleStub;
7 changes: 7 additions & 0 deletions src/blocks/Form/__tests__/Form.visual.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
FormData,
WithBackgroundColor,
WithBackgroundImage,
WithCustomFormNode,
} from './helpers';

const DEFAULT_FORM_DELAY = 20 * 1000;
Expand Down Expand Up @@ -48,4 +49,10 @@ test.describe('Form', () => {
await delay(DEFAULT_FORM_DELAY);
await expectScreenshot({skipTheme: 'dark'});
});

test.skip('render stories <WithCustomFormNode>', async ({mount, expectScreenshot, delay}) => {
await mount(<WithCustomFormNode />);
await delay(DEFAULT_FORM_DELAY);
await expectScreenshot({skipTheme: 'dark'});
});
});
1 change: 1 addition & 0 deletions src/blocks/Form/__tests__/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export const {
WithBackgroundImage,
DarkTheme,
FormData,
WithCustomFormNode,
} = composeStories(FormStories);
5 changes: 1 addition & 4 deletions src/blocks/Form/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ export const FormBlock = {
direction: {
enum: ['content-form', 'form-content', 'center'],
},
image: ImageProps,
backgroundColor: {
type: 'string',
},
background: ImageProps,
},
},
};
19 changes: 19 additions & 0 deletions src/blocks/Form/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';

export const BACKGROUND_STYLE_PROPS = [
'background',
'backgroundAttachment',
'backgroundBlendMode',
'backgroundClip',
'backgroundColor',
'backgroundImage',
'backgroundOrigin',
'backgroundPositionX',
'backgroundPositionY',
'backgroundRepeat',
'backgroundSize',
'backgroundPosition',
] as const;

export const hasBackgroundCSS = (style: React.CSSProperties) =>
BACKGROUND_STYLE_PROPS.some((backgroundStyleProp) => backgroundStyleProp in style);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {ClassNameProps, GravityIconProps, ImageProps, QAProps, SVGIcon} from '..
import {ThemeSupporting, getThemedValue} from '../../utils';
import Icon from '../Icon/Icon';

interface ListItemProps extends QAProps, ClassNameProps {
interface ContentIconProps extends QAProps, ClassNameProps {
icon?: ThemeSupporting<ImageProps | SVGIcon>;
gravityIcon?: ThemeSupporting<GravityIconProps>;
}
Expand All @@ -12,7 +12,7 @@ function isIconSvg(icon: ImageProps | SVGIcon): icon is SVGIcon {
return typeof icon === 'function';
}

const ContentListItemIcon = ({icon, className, qa, gravityIcon}: ListItemProps) => {
const ContentIcon = ({icon, className, qa, gravityIcon}: ContentIconProps) => {
const theme = useTheme();
const iconThemed = getThemedValue(icon, theme);
const gravityIconThemed = getThemedValue(gravityIcon, theme);
Expand All @@ -29,4 +29,4 @@ const ContentListItemIcon = ({icon, className, qa, gravityIcon}: ListItemProps)
return <Icon icon={iconThemed} gravityIcon={gravityIconThemed} className={className} qa={qa} />;
};

export default ContentListItemIcon;
export default ContentIcon;
72 changes: 72 additions & 0 deletions src/components/ContentLabels/ContentLabels.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';

$block: '.#{$ns}content-labels';

#{$block} {
display: flex;
gap: 10px;
flex-wrap: wrap;

&__label {
display: flex;
align-items: center;
flex-wrap: nowrap;
flex-shrink: 0;
padding: 5px 11px;
background-color: var(--g-color-base-generic);
border-radius: 6px;

&,
&-text {
color: var(--g-color-text-primary);
}

& > .pc-icon,
& > picture {
display: flex;
}

&-icon {
width: 16px;
height: 16px;
margin-inline-end: 6px;
}

$label: &;
&_theme {
&_light {
background-color: var(--g-color-private-black-50);

&,
#{$label}-text {
color: var(--g-color-text-dark-primary);
}
}

&_dark {
background-color: var(--g-color-private-white-100);

&,
#{$label}-text {
color: var(--g-color-text-light-primary);
}
}
}
}

&_size {
&_l,
&_m {
#{$block}__label {
@include text-body-3();
}
}

&_s {
#{$block}__label {
@include text-body-2();
}
}
}
}
Loading