Skip to content

Commit 4dcf70a

Browse files
authored
feat: ImageCard extension (#876) (#909)
* feat: ImageCard extension * feat: ImageCard extension
1 parent 2633fb8 commit 4dcf70a

File tree

6 files changed

+156
-20
lines changed

6 files changed

+156
-20
lines changed

src/models/constructor-items/sub-blocks.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,18 @@ export interface LayoutItemProps extends ClassNameProps, CardLayoutProps, Analyt
190190
icon?: PositionedIcon;
191191
}
192192

193-
export interface ImageCardProps extends CardBaseProps, Pick<ContentBlockProps, 'title' | 'text'> {
193+
export interface ImageCardProps
194+
extends CardBaseProps,
195+
CardLayoutProps,
196+
Omit<ContentBlockProps, 'colSizes' | 'centered'> {
194197
image: ImageProps;
195198
enableImageBorderRadius?: boolean;
196199
margins?: ImageCardMargins;
197200
direction?: ImageCardDirection;
198201
backgroundColor?: string;
202+
url?: string;
203+
urlTitle?: string;
204+
target?: string;
199205
}
200206

201207
// sub-block models

src/sub-blocks/ImageCard/ImageCard.tsx

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22

3+
import {Link, useUniqId} from '@gravity-ui/uikit';
4+
35
import {Image} from '../../components';
46
import {getMediaImage} from '../../components/Media/Image/utils';
57
import {ImageCardDirection, ImageCardProps} from '../../models';
@@ -20,16 +22,23 @@ const ImageCard = (props: ImageCardProps) => {
2022
direction = ImageCardDirection.Direct,
2123
margins,
2224
backgroundColor,
25+
url,
26+
target,
27+
urlTitle,
28+
additionalInfo,
29+
links,
30+
buttons,
31+
list,
32+
theme: cardTheme = 'default',
33+
size = 's',
2334
} = props;
2435

25-
const hasContent = Boolean(text || title);
36+
const hasContent = Boolean(text || title || buttons || links || list);
2637
const imageProps = getMediaImage(image);
38+
const titleId = useUniqId();
2739

28-
return (
29-
<div
30-
className={b({border, 'with-content': hasContent, direction})}
31-
style={{backgroundColor}}
32-
>
40+
const cardContent = (
41+
<React.Fragment>
3342
<div className={b('image', {margins})}>
3443
<Image
3544
className={b('image_inner', {radius: enableImageBorderRadius})}
@@ -38,9 +47,43 @@ const ImageCard = (props: ImageCardProps) => {
3847
</div>
3948
{hasContent && (
4049
<div className={b('content')}>
41-
<Content title={title} text={text} colSizes={{all: 12, md: 12}} size="s" />
50+
<Content
51+
titleId={titleId}
52+
title={title}
53+
text={text}
54+
links={links}
55+
buttons={buttons}
56+
list={list}
57+
theme={cardTheme}
58+
additionalInfo={additionalInfo}
59+
size={size}
60+
colSizes={{all: 12, md: 12}}
61+
/>
4262
</div>
4363
)}
64+
</React.Fragment>
65+
);
66+
67+
return url ? (
68+
<Link
69+
href={url}
70+
target={target}
71+
rel={target === '_blank' ? 'noopener noreferrer' : undefined}
72+
className={b({border, 'with-content': hasContent, direction})}
73+
title={urlTitle}
74+
extraProps={{
75+
draggable: false,
76+
onDragStart: (e: React.DragEvent<HTMLAnchorElement>) => e.preventDefault(),
77+
}}
78+
>
79+
{cardContent}
80+
</Link>
81+
) : (
82+
<div
83+
className={b({border, 'with-content': hasContent, direction})}
84+
style={{backgroundColor}}
85+
>
86+
{cardContent}
4487
</div>
4588
);
4689
};

src/sub-blocks/ImageCard/__stories__/ImageCard.mdx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,14 @@ import * as ImageCardStories from './ImageCard.stories.tsx';
2525

2626
`enableImageBorderRadius?: boolean` — Set border-radius for the image. Affects only when `margins='none'`.
2727

28+
`url?: string` — URL that opens when clicking the card
29+
30+
`additionalInfo?: string` — Gray text (with YFM support)
31+
32+
`links?: Link[]` — An array with link objects (see [Content blocks](?path=/docs/documentation-types--docs))
33+
34+
`buttons?: Button[]` — An array with button objects (see [Content blocks](?path=/docs/documentation-types--docs))
35+
36+
`list?: ContentItemProps[]` - An Array of items with icon - [ContentList](?path=/story/components-contentlist--docs&viewMode=docs)
37+
2838
</StoryTemplate>

src/sub-blocks/ImageCard/__stories__/ImageCard.stories.tsx

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import React from 'react';
22

33
import {Meta, StoryFn} from '@storybook/react';
44

5-
import {ImageCardProps} from '../../../models';
5+
import {yfmTransform} from '../../../../.storybook/utils';
6+
import {ButtonProps, ContentItemProps, ImageCardProps, LinkProps} from '../../../models';
67
import ImageCard from '../ImageCard';
78

89
import data from './data.json';
@@ -42,6 +43,13 @@ const MultipleTemplate: StoryFn<ImageCardProps> = (args) => (
4243
</div>
4344
);
4445

46+
const transformedContentList = data.content.list.map((item) => {
47+
return {
48+
...item,
49+
text: item?.text && yfmTransform(item.text),
50+
};
51+
}) as ContentItemProps[];
52+
4553
const ContentTemplate: StoryFn<ImageCardProps> = (args) => (
4654
<div
4755
style={{display: 'flex', flexWrap: 'wrap', flexDirection: 'row', alignItems: 'flex-start'}}
@@ -55,14 +63,27 @@ const ContentTemplate: StoryFn<ImageCardProps> = (args) => (
5563
<div style={{width: 400, margin: 20}}>
5664
<ImageCard {...args} text="" title="" />
5765
</div>
58-
</div>
59-
);
60-
61-
const MultipleContentTemplate: StoryFn<ImageCardProps> = (args) => (
62-
<div>
63-
<ContentTemplate {...args} {...(data.margins.none as Partial<ImageCardProps>)} />
64-
<ContentTemplate {...args} {...(data.margins.small as Partial<ImageCardProps>)} />
65-
<ContentTemplate {...args} {...(data.margins.medium as Partial<ImageCardProps>)} />
66+
<div style={{width: 400, margin: 20}}>
67+
<ImageCard
68+
{...args}
69+
title="With buttons"
70+
buttons={data.content.buttons as ButtonProps[]}
71+
/>
72+
</div>
73+
<div style={{width: 400, margin: 20}}>
74+
<ImageCard {...args} title="With links" links={data.content.links as LinkProps[]} />
75+
</div>
76+
<div style={{width: 400, margin: 20}}>
77+
<ImageCard
78+
{...args}
79+
title="With url and list"
80+
url={data.content.url}
81+
list={transformedContentList}
82+
/>
83+
</div>
84+
<div style={{width: 400, margin: 20}}>
85+
<ImageCard {...args} text="" title="" size="l" list={transformedContentList} />
86+
</div>
6687
</div>
6788
);
6889

@@ -92,7 +113,7 @@ const BorderRadiusTemplate: StoryFn<ImageCardProps> = (args) => (
92113
export const Default = DefaultTemplate.bind({});
93114
export const Margins = MultipleTemplate.bind({});
94115
export const DirectionReverse = MultipleTemplate.bind({});
95-
export const Content = MultipleContentTemplate.bind({});
116+
export const Content = ContentTemplate.bind({});
96117
export const BackgroundColor = MultipleTemplate.bind({});
97118
export const Border = BorderTemplate.bind({});
98119
export const BorderRadius = BorderRadiusTemplate.bind({});

src/sub-blocks/ImageCard/__stories__/data.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,55 @@
4444
"content": {
4545
"backgroundColor": "#ccf0d2"
4646
}
47+
},
48+
"content": {
49+
"url": "https://example.com",
50+
"links": [
51+
{
52+
"url": "#",
53+
"text": "Link",
54+
"theme": "normal",
55+
"arrow": true
56+
}
57+
],
58+
"buttons": [
59+
{
60+
"text": "Button\r",
61+
"theme": "action",
62+
"url": "https://example.com"
63+
},
64+
{
65+
"text": "Button",
66+
"theme": "outlined",
67+
"url": "#"
68+
}
69+
],
70+
"list": [
71+
{
72+
"icon": {
73+
"light": "/story-assets/icon_1_light.svg",
74+
"dark": "/story-assets/icon_1_dark.svg"
75+
},
76+
"title": "Lorem ipsum",
77+
"text": "**Ut enim ad minim veniam** [quis nostrud](https://example.com) exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor"
78+
},
79+
{
80+
"icon": {
81+
"light": "/story-assets/icon_3_light.svg",
82+
"dark": "/story-assets/icon_3_dark.svg"
83+
},
84+
"title": "Lorem ipsum ipsum",
85+
"text": "**Ut enim ad minim veniam** [quis nostrud](https://example.com) exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor"
86+
},
87+
{
88+
"icon": {
89+
"light": "/story-assets/icon_2_light.svg",
90+
"dark": "/story-assets/icon_2_dark.svg"
91+
},
92+
"title": "Lorem ipsum",
93+
"text": "**Ut enim ad minim veniam** [quis nostrud](https://example.com) exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor"
94+
}
95+
],
96+
"additionalInfo": "Duis aute irure dolor in [reprehenderit](https://example.com) n voluptate velit esse cillum dolore eu fugiat nulla pariatur."
4797
}
4898
}

src/sub-blocks/ImageCard/schema.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import pick from 'lodash/pick';
1+
import omit from 'lodash/omit';
22

33
import {BaseProps, CardBase} from '../../schema/validators/common';
44
import {ImageProps} from '../../schema/validators/components';
55
import {ContentBase} from '../Content/schema';
66

7-
const ImageCardBlockContentProps = pick(ContentBase, ['title', 'text']);
7+
const ImageCardBlockContentProps = omit(ContentBase, ['centered', 'colSizes']);
88

99
export const ImageCard = {
1010
'image-card': {
@@ -26,6 +26,12 @@ export const ImageCard = {
2626
backgroundColor: {
2727
type: 'string',
2828
},
29+
url: {
30+
type: 'string',
31+
},
32+
urlTitle: {
33+
type: 'string',
34+
},
2935
},
3036
},
3137
};

0 commit comments

Comments
 (0)