Skip to content

Commit

Permalink
feat(SubBlocks): add possibility to position controls within cards at…
Browse files Browse the repository at this point in the history
… footer (#823)

* feat(SubBlocks): add possibility to position controls within cards at footer

* fix: comments

* feat: add storybook for changes

* fix: apply the required behavioral changes

* fix: resolve comments

* fix: lint
  • Loading branch information
DC-RomanKarpov authored Mar 1, 2024
1 parent 8fe9e19 commit 7e3dc0b
Show file tree
Hide file tree
Showing 32 changed files with 725 additions and 174 deletions.
19 changes: 19 additions & 0 deletions src/components/Buttons/Buttons.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@import '../../../styles/variables.scss';

$block: '.#{$ns}buttons';

#{$block} {
display: flex;
flex-wrap: wrap;
column-gap: $indentXXS;

&_size {
&_s {
row-gap: $indentXXXS;
}

&_l {
row-gap: $indentXXS;
}
}
}
49 changes: 49 additions & 0 deletions src/components/Buttons/Buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';

import {ButtonProps, ContentSize} from '../../models';
import {block} from '../../utils';
import Button from '../Button/Button';

import './Buttons.scss';

const b = block('buttons');

type ButtonsProps = {
className?: string;
buttons?: ButtonProps[];
size?: ContentSize;
titleId?: string;
qa?: string;
buttonQa?: string;
};

function getButtonSize(size: ContentSize) {
switch (size) {
case 's':
return 'm';
case 'l':
default:
return 'xl';
}
}

const Buttons: React.FC<ButtonsProps> = ({className, titleId, buttons, size = 's', qa, buttonQa}) =>
buttons ? (
<div className={b({size}, className)} data-qa={qa}>
{buttons.map((item) => (
<Button
className={b('button')}
{...item}
key={item.url}
size={getButtonSize(size)}
qa={buttonQa}
extraProps={{
'aria-describedby': item.urlTitle ? undefined : titleId,
...item.extraProps,
}}
/>
))}
</div>
) : null;

export default Buttons;
22 changes: 14 additions & 8 deletions src/components/CardBase/CardBase.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import React, {Children, Fragment, HTMLAttributeAnchorTarget, ReactElement} from 'react';
import React, {
Children,
Fragment,
HTMLAttributeAnchorTarget,
PropsWithChildren,
ReactElement,
isValidElement,
} from 'react';

import {Link} from '@gravity-ui/uikit';

Expand All @@ -19,11 +26,10 @@ import RouterLink from '../RouterLink/RouterLink';

import './CardBase.scss';

export interface CardBaseProps extends AnalyticsEventsBase, CardBaseParams {
export interface CardBaseProps extends AnalyticsEventsBase, CardBaseParams, PropsWithChildren {
className?: string;
bodyClassName?: string;
contentClassName?: string;
children: ReactElement | ReactElement[];
url?: string;
urlTitle?: string;
target?: HTMLAttributeAnchorTarget;
Expand Down Expand Up @@ -86,11 +92,11 @@ export const Layout = (props: CardBaseProps) => {
}
}

if (Children.count(children) === 1) {
handleChild(children as ReactElement);
} else {
Children.forEach(children, handleChild);
}
Children.toArray(children).forEach((child) => {
if (isValidElement(child)) {
handleChild(child);
}
});

const cardContent = (
<Fragment>
Expand Down
27 changes: 0 additions & 27 deletions src/components/Link/Links.tsx

This file was deleted.

23 changes: 23 additions & 0 deletions src/components/Links/Links.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@import '../../../styles/variables.scss';

$block: '.#{$ns}links';

#{$block} {
display: flex;
flex-direction: column;
align-items: baseline;

&__link {
margin-top: 0px;
}

&_size {
&_s {
gap: $indentXXXS;
}

&_l {
gap: $indentXXS;
}
}
}
49 changes: 49 additions & 0 deletions src/components/Links/Links.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';

import {ContentSize, LinkProps} from '../../models';
import {block} from '../../utils';
import Link from '../Link/Link';

import './Links.scss';

const b = block('links');

function getLinkSize(size: ContentSize) {
switch (size) {
case 's':
return 'm';
case 'l':
default:
return 'l';
}
}

type LinksProps = {
className?: string;
titleId?: string;
links?: LinkProps[];
size?: ContentSize;
qa?: string;
linkQa?: string;
};

const Links: React.FC<LinksProps> = ({className, titleId, links, size = 's', qa, linkQa}) =>
links ? (
<div className={b({size}, className)} data-qa={qa}>
{links?.map((link) => (
<Link
className={b('link')}
{...link}
textSize={getLinkSize(size)}
key={link.url}
qa={linkQa}
extraProps={{
'aria-describedby': link.urlTitle ? undefined : titleId,
...link.extraProps,
}}
/>
))}
</div>
) : null;

export default Links;
3 changes: 2 additions & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {default as BackLink} from './BackLink/BackLink';
export {default as BalancedMasonry} from './BalancedMasonry/BalancedMasonry';
export {default as BlockBase} from './BlockBase/BlockBase';
export {default as Button} from './Button/Button';
export {default as Buttons} from './Buttons/Buttons';
export {default as CardBase} from './CardBase/CardBase';
export {default as ErrorWrapper} from './ErrorWrapper/ErrorWrapper';
export {default as FileLink} from './FileLink/FileLink';
Expand All @@ -16,7 +17,7 @@ export {default as HeaderBreadcrumbs} from './HeaderBreadcrumbs/HeaderBreadcrumb
export {default as Image} from './Image/Image';
export {default as ImageBase} from './ImageBase/ImageBase';
export {default as Link} from './Link/Link';
export {default as Links} from './Link/Links';
export {default as Links} from './Links/Links';
export {default as Media} from './Media/Media';
export {default as OutsideClick} from './OutsideClick/OutsideClick';
export {default as ReactPlayer} from './ReactPlayer/ReactPlayer';
Expand Down
5 changes: 5 additions & 0 deletions src/models/constructor-items/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,11 +392,16 @@ export type MediaView = 'fit' | 'full';
// card
export type MediaBorder = 'shadow' | 'line' | 'none';
export type CardBorder = MediaBorder;
export type ControlPosition = 'content' | 'footer';

export interface CardBaseProps {
border?: CardBorder;
}

export type CardLayoutProps = {
controlPosition?: ControlPosition;
};

//price
export interface PriceDescriptionProps {
title: string;
Expand Down
5 changes: 4 additions & 1 deletion src/models/constructor-items/sub-blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ButtonPixel,
ButtonProps,
CardBaseProps,
CardLayoutProps,
ContentTheme,
DividerSize,
ImageCardMargins,
Expand Down Expand Up @@ -134,6 +135,7 @@ export interface QuoteProps extends Themable, CardBaseProps {
export interface BackgroundCardProps
extends CardBaseProps,
AnalyticsEventsBase,
CardLayoutProps,
Omit<ContentBlockProps, 'colSizes' | 'centered'> {
url?: string;
urlTitle?: string;
Expand All @@ -145,6 +147,7 @@ export interface BackgroundCardProps
export interface BasicCardProps
extends CardBaseProps,
AnalyticsEventsBase,
CardLayoutProps,
Omit<ContentBlockProps, 'colSizes' | 'centered' | 'size' | 'theme'> {
url: string;
urlTitle?: string;
Expand Down Expand Up @@ -178,7 +181,7 @@ export interface PriceCardProps extends CardBaseProps, Pick<ContentBlockProps, '
list?: string[];
}

export interface LayoutItemProps extends ClassNameProps, AnalyticsEventsBase {
export interface LayoutItemProps extends ClassNameProps, CardLayoutProps, AnalyticsEventsBase {
content: Omit<ContentBlockProps, 'colSizes' | 'centered' | 'size'>;
media?: MediaProps;
metaInfo?: string[];
Expand Down
30 changes: 27 additions & 3 deletions src/sub-blocks/BackgroundCard/BackgroundCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React from 'react';
import React, {useMemo} from 'react';

import {useUniqId} from '@gravity-ui/uikit';

import {BackgroundImage, CardBase} from '../../components/';
import {useTheme} from '../../context/theme';
import {BackgroundCardProps} from '../../models';
import {block, getThemedValue} from '../../utils';
import renderContentControls from '../../utils/renderContentControls/renderContentControls';
import Content from '../Content/Content';
import renderCardFooterControlsContainer from '../renderCardFooterControlsContainer/renderCardFooterControlsContainer';

import './BackgroundCard.scss';

Expand All @@ -25,11 +29,29 @@ const BackgroundCard = (props: BackgroundCardProps) => {
buttons,
analyticsEvents,
urlTitle,
controlPosition = 'content',
} = props;

const titleId = useUniqId();

const theme = useTheme();
const hasBackgroundColor = backgroundColor || cardTheme !== 'default';
const borderType = hasBackgroundColor ? 'none' : border;
const areControlsInFooter = !paddingBottom && controlPosition === 'footer';

const footerControls = useMemo(
() =>
renderContentControls(
{
links: areControlsInFooter ? links : undefined,
buttons: areControlsInFooter ? buttons : undefined,
size: 's',
titleId,
},
renderCardFooterControlsContainer,
),
[areControlsInFooter, links, buttons, titleId],
);

return (
<CardBase
Expand All @@ -46,16 +68,18 @@ const BackgroundCard = (props: BackgroundCardProps) => {
style={{backgroundColor}}
/>
<Content
titleId={titleId}
title={title}
text={text}
additionalInfo={additionalInfo}
size="s"
theme={cardTheme}
links={links}
buttons={buttons}
links={areControlsInFooter ? undefined : links}
buttons={areControlsInFooter ? undefined : buttons}
colSizes={{all: 12, md: 12}}
/>
</CardBase.Content>
{footerControls}
</CardBase>
);
};
Expand Down
Loading

0 comments on commit 7e3dc0b

Please sign in to comment.