Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate Card component #32

Merged
merged 13 commits into from
Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from 12 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
109 changes: 109 additions & 0 deletions src/components/Card/Card.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
@import '../variables';

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

#{$block} {
background-color: transparent;
box-sizing: border-box;
border-radius: 5px;

&_type_action {
background-color: var(--yc-color-base-float);
box-shadow: 0px 1px 5px var(--yc-color-sfx-shadow);

&#{$block}_clickable:hover {
box-shadow: 0px 3px 10px var(--yc-color-sfx-shadow);
cursor: pointer;
}
}

&_type_selection {
position: relative;
border: 1px solid var(--yc-color-line-generic);

&::before {
position: absolute;
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
border-radius: 5px;
pointer-events: none;
}

&#{$block}_clickable:hover {
border-color: transparent;
cursor: pointer;

&::before {
content: '';
border: 2px solid var(--yc-color-line-selection-hover);
}
}

&#{$block}_selected:not(#{$block}_disabled) {
border-color: transparent;

&::before {
content: '';
border: 2px solid var(--yc-color-line-selection-active);
}
}

&#{$block}_view_clear {
border-color: transparent;
}
}

&_type_container {
&#{$block}_theme_normal {
&#{$block}_view_outlined {
border: 1px solid var(--yc-color-line-generic);
}
&#{$block}_view_filled {
background-color: var(--yc-color-base-generic);
}
}

&#{$block}_theme_info {
&#{$block}_view_outlined {
border: 1px solid var(--yc-color-line-info);
}
&#{$block}_view_filled {
background-color: var(--yc-color-base-info);
}
}

&#{$block}_theme_positive {
&#{$block}_view_outlined {
border: 1px solid var(--yc-color-line-positive);
}
&#{$block}_view_filled {
background-color: var(--yc-color-base-positive);
}
}

&#{$block}_theme_warning {
&#{$block}_view_outlined {
border: 1px solid var(--yc-color-line-warning);
}
&#{$block}_view_filled {
background-color: var(--yc-color-base-warning);
}
}

&#{$block}_theme_danger {
&#{$block}_view_outlined {
border: 1px solid var(--yc-color-line-danger);
}
&#{$block}_view_filled {
background-color: var(--yc-color-base-danger);
}
}

&#{$block}_view_raised {
background-color: var(--yc-color-base-float);
box-shadow: 0px 1px 5px var(--yc-color-sfx-shadow);
}
}
}
73 changes: 73 additions & 0 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import {block} from '../utils/cn';

import './Card.scss';

const b = block('card');

type SelectionCardView = 'outlined' | 'clear';
type ContainerCardView = 'outlined' | 'filled' | 'raised';

export type CardType = 'selection' | 'action' | 'container';
export type CardTheme = 'normal' | 'info' | 'positive' | 'warning' | 'danger';
export type CardView = SelectionCardView | ContainerCardView;

export interface CardProps {
children: React.ReactNode;
className?: string;
/** Card click handler. Available for type: 'selection', 'action' */
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
/** Disabled card. Available for type: 'selection', 'action' */
disabled?: boolean;
/** Selected card. Available for type: 'selection' */
selected?: boolean;
/** Card's type affects on available properties */
type?: CardType;
/** Available for type: 'container' and 'selection' */
view?: CardView;
/** Card's base color. Available for type: 'container' */
theme?: CardTheme;
}

export function Card({
type = 'container',
theme,
view,
children,
className,
onClick,
disabled,
selected,
}: CardProps) {
const isTypeAction = type === 'action';
const isTypeSelection = type === 'selection';
const isTypeContainer = type === 'container';

/* Clickable card — only with type 'action' or 'selection' and not selected or disabled */
const hasAction = isTypeAction || isTypeSelection;
const isClickable = hasAction && Boolean(onClick) && !(disabled || selected);

/* Theme only with type 'conatiner' */
const defaultTheme = isTypeContainer ? 'normal' : undefined;
/* View only with type 'conatiner' and 'selection' */
const defaultView = isTypeContainer || isTypeSelection ? 'outlined' : undefined;

return (
<div
className={b(
{
theme: theme || defaultTheme,
view: view || defaultView,
type,
selected,
disabled,
clickable: isClickable,
},
className,
)}
onClick={isClickable ? onClick : undefined}
>
{children}
</div>
);
}
45 changes: 45 additions & 0 deletions src/components/Card/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Card

Card is content container. It supports multiple types: `selection`, `action`, `container`.

## Properties

| Property | Type | Required | Default | Description |
| :-------- | :-------------------------------------------------- | :------: | :------------ | :------------------------------------------------------------------ |
| children | `ReactNode` | ✓ | | Card's content |
| type | `CardType` | | `'container'` | Card's type affects on available properties |
| view | `SelectionCardView` \| `ContainerCardView` | | `'outlined'` | Available for `type`: `'container'` and `'selection'` |
| theme | `CardTheme` | | `'normal'` | Card's base color. Available for `type`: `'container'` |
| className | `String` | | | CSS class |
| onClick | `(event: React.MouseEvent<HTMLDivElement>) => void` | | | Card click handler. Available for `type`: `'selection'`, `'action'` |
| selected | `Boolean` | | | Selected card. Available for type: `'selection'` |
| disabled | `Boolean` | | | Disabled card. Available for type: `'selection'`, `'action'` |

### Typings

```typescript
type CardType = 'selection' | 'action' | 'container';

type SelectionCardView = 'outlined' | 'clear';
type ContainerCardView = 'outlined' | 'filled' | 'raised';
amje marked this conversation as resolved.
Show resolved Hide resolved

type CardTheme = 'normal' | 'info' | 'positive' | 'warning' | 'danger';
```

### Examples

```ts
const containerFilledCard = (
<Card className="my-card" view="filled" theme="positive">
<div>Card's content</div>
</Card>
);
```

```ts
const selectedCard = (
<Card className="my-card" type="selection" view="clear" onClick={() => {}} selected disabled>
<div>Card's content</div>
</Card>
);
```
11 changes: 11 additions & 0 deletions src/components/Card/__stories__/Card.stories.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.card-stories {
width: 120px;
height: 120px;
}

.card-content-stories {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
21 changes: 21 additions & 0 deletions src/components/Card/__stories__/Card.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import {Meta, Story} from '@storybook/react';
import {Card, CardProps} from '../Card';
import {CardShowcase} from './CardShowcase';

import './Card.stories.scss';

export default {
title: 'Components/Card',
component: Card,
} as Meta;

const DefaultTemplate: Story<CardProps> = (args: any) => (
<Card {...args} className="card-stories">
<div className="card-content-stories">card&lsquo;s content</div>
</Card>
);
export const Default = DefaultTemplate.bind({});

const ShowcaseTemplate: Story<CardProps> = (args: any) => <CardShowcase {...args} />;
export const Showcase = ShowcaseTemplate.bind({});
18 changes: 18 additions & 0 deletions src/components/Card/__stories__/CardShowcase.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.cards-showcase-stories {
& .card-showcase-stories:not(:first-child) {
margin-top: 8px;
}
}

.card-showcase-stories {
amje marked this conversation as resolved.
Show resolved Hide resolved
width: 100px;
height: 100px;
}

.card-content-showcase-stories {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
text-align: center;
}
Loading