Skip to content

Commit

Permalink
feat(SelectableCard): changes after review
Browse files Browse the repository at this point in the history
  • Loading branch information
marcinsawicki committed Dec 9, 2024
1 parent 283e170 commit 191c1de
Show file tree
Hide file tree
Showing 14 changed files with 113 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import { ThumbnailSelectableCard } from '@livechat/design-system-react-component
description="Card description"
icon={<Icon source={Palette} />}
isSelected
onClick={() = {}}
onClick={() => {}}
/>
```

Expand All @@ -68,7 +68,7 @@ The `GallerySelectableCard` is a component built on top of `SelectableCard`, con

##### Important

Since the interface does not assume mandatory props related to the UI for this type of card, you must remember to provide an `icon` or `customElemnt` or `label`, otherwise the card will be empty.
Since the interface does not assume mandatory props related to the UI for this type of card, you must remember to provide an `icon` or `customElement`.

<Canvas of={GallerySelectableCard.RadioTypeExamples} sourceState="none" />

Expand All @@ -83,7 +83,7 @@ import { GallerySelectableCard } from '@livechat/design-system-react-components'
selectionType="radio"
icon={<Icon source={Palette} />}
isSelected
onClick={() = {}}
onClick={() => {}}
/>
```

Expand All @@ -104,7 +104,7 @@ import { InteractiveSelectableCard, GridWrapper, GridCol } from '@livechat/desig
<InteractiveSelectableCard
selectionType="radio"
isSelected
onClick={() = {}}
onClick={() => {}}
>
<GridWrapper>
<GridCol>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ $base-class: 'selectable-card';
box-shadow var(--transition-duration-fast-2) ease-in-out;
border: 1px solid var(--border-basic-secondary);
border-radius: var(--radius-3);
box-shadow: 0;
box-shadow: none;
background: var(--surface-primary-default);
width: max-content;

Expand All @@ -28,6 +28,7 @@ $base-class: 'selectable-card';

.#{$base-class}__select-indicator {
margin-right: var(--spacing-2);
height: 21px;
}
}

Expand All @@ -36,8 +37,8 @@ $base-class: 'selectable-card';

.#{$base-class}__select-indicator {
position: absolute;
top: 9px;
left: 12px;
top: var(--spacing-3);
left: var(--spacing-3);
}
}

Expand All @@ -47,8 +48,8 @@ $base-class: 'selectable-card';

.#{$base-class}__select-indicator {
position: absolute;
top: 13px;
left: 16px;
top: var(--spacing-4);
left: var(--spacing-4);
}
}

Expand All @@ -61,7 +62,7 @@ $base-class: 'selectable-card';
&__select-indicator {
display: flex;
align-items: center;
height: 21px;
height: 16px;

&__checkbox {
height: 16px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
.thumbnail-custom-element {
.base-custom-element {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px dashed var(--border-default);
border-radius: var(--radius-1);
background-color: var(--surface-basic-subtle);
padding: var(--spacing-2);
color: var(--content-subtle);
}

.thumbnail-custom-element {
padding: var(--spacing-2);
font-size: 12px;
}

.gallery-custom-element {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px dashed var(--border-default);
border-radius: var(--radius-1);
background-color: var(--surface-basic-subtle);
padding: var(--spacing-2);
width: 100px;
height: 100px;
text-align: center;
color: var(--content-subtle);
font-size: 12px;
}

Expand All @@ -36,17 +31,9 @@
}

.interactive-custom-element {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px dashed var(--border-default);
border-radius: var(--radius-1);
background-color: var(--surface-basic-subtle);
padding: var(--spacing-8);
width: 100%;
height: 100%;
text-align: center;
color: var(--content-subtle);
font-size: 14px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const SelectableCard: FC<ISelectableCardProps> = ({
isSelected = false,
onClick,
style,
...props
}) => {
const mergedClassName = cx(
styles[baseClass],
Expand Down Expand Up @@ -51,12 +52,14 @@ export const SelectableCard: FC<ISelectableCardProps> = ({
return (
<div
aria-selected={isSelected}
aria-label={typeof children === 'string' ? children : undefined}
role="button"
tabIndex={0}
className={mergedClassName}
onClick={handleInteractiveClick}
onKeyDown={handleOnKeyDown}
style={style}
{...props}
>
<div className={styles[`${baseClass}__select-indicator`]}>
{selectionType === 'radio' ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,53 @@ import {
import { Icon } from '../../../Icon';

import { GallerySelectableCard } from './GallerySelectableCard';
import { IGallerySelectableCardProps } from './types';

const ICONS = [Palette, Building, AccountCircle, Group];

interface IRadioCardsProps {
withIcon?: boolean;
withCustomElement?: boolean;
}

const getComponent = (
index: number,
isSelected: boolean,
setSelectedIndex: (index: number) => void,
withIcon: boolean | undefined,
withCustomElement: boolean | undefined,
withIcon: boolean,
withCustomElement: boolean,
type: 'radio' | 'checkbox'
) => (
<GallerySelectableCard
key={index}
label={`Card ${index + 1}`}
selectionType={type}
isSelected={isSelected}
onClick={() => setSelectedIndex(index)}
{...(withIcon && { icon: <Icon size="xlarge" source={ICONS[index]} /> })}
{...(withCustomElement && {
) => {
let props: IGallerySelectableCardProps = {
label: `Card ${index + 1}`,
selectionType: type,
isSelected,
onClick: () => setSelectedIndex(index),
icon: undefined,
customElement: undefined,
};

if (withIcon) {
const { customElement: _, ...newProps } = props;
props = {
...newProps,
icon: <Icon size="xlarge" source={ICONS[index]} />,
};
} else if (withCustomElement) {
const { icon: _, ...newProps } = props;
props = {
...newProps,
customElement: (
<div className="gallery-custom-element">
<div className="base-custom-element gallery-custom-element">
<Icon size="small" source={ICONS[index]} />
<div>{`Custom element ${index + 1}`}</div>
</div>
),
})}
/>
);
};
}

return <GallerySelectableCard key={index} {...props} />;
};

interface IRadioCardsProps {
withIcon: boolean;
withCustomElement: boolean;
}

export const RadioCards: React.FC<IRadioCardsProps> = ({
withIcon,
Expand All @@ -63,8 +77,8 @@ export const RadioCards: React.FC<IRadioCardsProps> = ({
};

interface ICheckboxCardsProps {
withIcon?: boolean;
withCustomElement?: boolean;
withIcon: boolean;
withCustomElement: boolean;
}

export const CheckboxCards: React.FC<ICheckboxCardsProps> = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ $base-class: 'gallery-selectable-card';
min-width: 60px;
min-height: 72px;

&--with-label {
margin-bottom: 29px;
}

&__label {
position: absolute;
top: 100%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('<GallerySelectableCard> component', () => {
...DEFAULT_PROPS,
className: 'my-class',
contentClassName: 'my-content-class',
icon: <div role="img" />,
});

expect(container.firstChild).toHaveClass('my-class');
Expand All @@ -41,6 +42,7 @@ describe('<GallerySelectableCard> component', () => {
const { container, getByText } = renderComponent({
...DEFAULT_PROPS,
label: 'Label',
icon: <div role="img" />,
});

expect(getByText('Label')).toBeInTheDocument();
Expand All @@ -55,15 +57,4 @@ describe('<GallerySelectableCard> component', () => {

expect(getByText('Custom element')).toBeInTheDocument();
});

it('should not render icon if custom element is provided', () => {
const { getByText, queryByRole } = renderComponent({
...DEFAULT_PROPS,
icon: <div role="img" />,
customElement: <div>Custom element</div>,
});

expect(queryByRole('img')).not.toBeInTheDocument();
expect(getByText('Custom element')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ export default {
export const RadioTypeExamples = () => (
<>
<StoryDescriptor title="With Icon">
<RadioCards withIcon />
<RadioCards withIcon withCustomElement={false} />
</StoryDescriptor>
<StoryDescriptor title="With Custom element">
<RadioCards withIcon withCustomElement />
<RadioCards withIcon={false} withCustomElement />
</StoryDescriptor>
</>
);

export const CheckboxTypeExamples = () => (
<>
<StoryDescriptor title="With Icon">
<CheckboxCards withIcon />
<CheckboxCards withIcon withCustomElement={false} />
</StoryDescriptor>
<StoryDescriptor title="With Custom element">
<CheckboxCards withIcon withCustomElement />
<CheckboxCards withIcon={false} withCustomElement />
</StoryDescriptor>
</>
);
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,16 @@ export const GallerySelectableCard: FC<IGallerySelectableCardProps> = ({
icon,
customElement,
contentClassName,
className,
...props
}) => {
return (
<SelectableCard
{...props}
className={cx(className, {
[styles[`${baseClass}--with-label`]]: label,
})}
kind="gallery"
style={
label
? {
marginBottom: '29px',
}
: {}
}
>
<div
role="presentation"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,34 @@ import * as React from 'react';

import { ISelectableCardCoreProps } from '../../types';

export interface IGallerySelectableCardProps
extends Omit<ISelectableCardCoreProps, 'children'> {
type BaseProps = {
/**
* The label of the card
*/
label?: string;
};

type WithIcon = BaseProps & {
/**
* The icon of the card
*/
icon?: React.ReactNode;
icon: React.ReactNode;
/**
* The custom element of the card
*/
customElement?: React.ReactNode;
}
customElement?: never;
};

type WithCustomElement = BaseProps & {
/**
* The icon of the card
*/
icon?: never;
/**
* The custom element of the card
*/
customElement: React.ReactNode;
};

export type IGallerySelectableCardProps = (WithIcon | WithCustomElement) &
Omit<ISelectableCardCoreProps, 'children'>;
Loading

0 comments on commit 191c1de

Please sign in to comment.