Skip to content

Commit

Permalink
feat: playing card atom
Browse files Browse the repository at this point in the history
  • Loading branch information
gvieiraschwade committed Aug 9, 2020
1 parent e20a4f8 commit 1142ed7
Show file tree
Hide file tree
Showing 7 changed files with 2,107 additions and 4 deletions.
31 changes: 31 additions & 0 deletions docs/playing-cards-svg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Playing cards svg

The SVG embedded in this project comes from [https://github.com/htdebeer/SVG-cards](https://github.com/htdebeer/SVG-cards), after a little optmization via [SVGOMG](https://jakearchibald.github.io/svgomg/)

In order to use them:

```jsx
import svgCards from '[RELATIVE_PATH_TO_ASSETS]/svg-cards-optimized.svg';

<svg viewBox="0 0 171 251" className={classes.svgStyle}>
<use xlinkHref={`${svgCards}#joker_black`} />
</svg>
```

The various cards have the following names:

* Jokers: joker_black and joker_red
* Back card: back or alternative-back
* Picture cards: {club,diamond,heart,spade}_{king,queen,jack}
* Pip cards:{club,diamond,heart,spade}_{1,2,3,4,5,6,7,8,9,10}

Examples:

* The ace of club is `club_1`.
* The queen of diamonds is `diamond_queen`.

The cards have the following natural positions:

* width: `169.075`
* height: `244.64`
* center: `(+98.0375, +122.320)`
1,849 changes: 1,849 additions & 0 deletions src/assets/svg-cards-optimized.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions src/components/atoms/PlayingCard/PlayingCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import { render } from '@testing-library/react';
import { PlayingCard } from './PlayingCard';

const testCardSvg = (el: HTMLElement, pattern: string | RegExp) =>
expect(el).toHaveAttribute('xlink:href', expect.stringMatching(pattern));

describe('PlayingCard', () => {
it('should render correctly', () => {
const { getByTestId } = render(<PlayingCard />);
expect(getByTestId('BaseCard')).toBeInTheDocument();
});

describe('on default rendering', () => {
let frontFace: HTMLElement;
let backFace: HTMLElement;
beforeEach(() => {
const { getByTestId } = render(<PlayingCard />);
frontFace = getByTestId('PlayingCard_front_use');
backFace = getByTestId('PlayingCard_back_use');
});

it('should default front to joker_black', () => {
testCardSvg(frontFace, /#joker_black$/i);
});

it('should default front to no fill', () => {
expect(frontFace).toHaveAttribute('fill', '');
});

it('should default back to back', () => {
testCardSvg(backFace, /#back$/i);
});

it('should default back to black color', () => {
expect(backFace).toHaveAttribute('fill', 'black');
});
});

describe('on rendering', () => {
let frontFace: HTMLElement;
let backFace: HTMLElement;
beforeEach(() => {
const { getByTestId } = render(<PlayingCard card="spade_4" backColor="red" />);
frontFace = getByTestId('PlayingCard_front_use');
backFace = getByTestId('PlayingCard_back_use');
});

it('should render the correct card', () => {
testCardSvg(frontFace, /#spade_4$/i);
});

it('should always show back as back', () => {
testCardSvg(backFace, /#back$/i);
});

it('should render back as backColor property', () => {
expect(backFace).toHaveAttribute('fill', 'red');
});
});
});
40 changes: 40 additions & 0 deletions src/components/atoms/PlayingCard/PlayingCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { createUseStyles } from 'react-jss';
import svgCards from '../../../assets/svg-cards-optimized.svg';
import { BaseCard } from '../BaseCard/BaseCard';
import { PlayingCardType } from '../../../models/PlayingCardType';

/**
* Properties for PlayingCard
*/
export interface PlayingCardProps {
/** Card to be rendered */
card?: PlayingCardType;
/** Color to be applied to back face */
backColor?: string;
}

const useStyles = createUseStyles({
svgStyle: {
width: '100%',
},
});

export const PlayingCard: React.FC<PlayingCardProps> = ({ card = 'joker_black', backColor = 'black' }) => {
const classes = useStyles();

const generateSvg = (isFront: boolean) => {
const testId = `PlayingCard_${isFront ? 'front' : 'back'}`;
return (
<svg data-testid={testId} viewBox="0 0 171 251" className={classes.svgStyle}>
<use
data-testid={`${testId}_use`}
xlinkHref={`${svgCards}#${isFront ? card : 'back'}`}
fill={isFront ? '' : backColor}
/>
</svg>
);
};

return <BaseCard height={251} width={171} frontFace={generateSvg(true)} backFace={generateSvg(false)} />;
};
2 changes: 1 addition & 1 deletion src/components/pages/HomePage/HomePage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import HomePage from './HomePage';
describe('HomePage', () => {
it('should render home page', () => {
const { getByText } = render(<HomePage />);
const divElement = getByText(/I am on homepage/i);
const divElement = getByText(/Card UI/i);
expect(divElement).toBeInTheDocument();
});
});
72 changes: 69 additions & 3 deletions src/components/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,88 @@
import React from 'react';
import { createUseStyles } from 'react-jss';
import { BaseCard } from '../../atoms/BaseCard/BaseCard';
import { PlayingCard } from '../../atoms/PlayingCard/PlayingCard';
import { PlayingCardType } from '../../../models/PlayingCardType';

const useStyles = createUseStyles({
homePage: {
background: '#cccccc',
height: '100%',
color: 'white',
overflow: 'auto',
display: 'grid',
gridTemplateColumns: 'repeat(5, 1fr)',
},
});

const HomePage: React.FC = () => {
const classes = useStyles();

const allCards: PlayingCardType[] = [
'club_1',
'club_2',
'club_3',
'club_4',
'club_5',
'club_6',
'club_7',
'club_8',
'club_9',
'club_10',
'club_jack',
'club_queen',
'club_king',
'diamond_1',
'diamond_2',
'diamond_3',
'diamond_4',
'diamond_5',
'diamond_6',
'diamond_7',
'diamond_8',
'diamond_9',
'diamond_10',
'diamond_jack',
'diamond_queen',
'diamond_king',
'heart_1',
'heart_2',
'heart_3',
'heart_4',
'heart_5',
'heart_6',
'heart_7',
'heart_8',
'heart_9',
'heart_10',
'heart_jack',
'heart_queen',
'heart_king',
'spade_1',
'spade_2',
'spade_3',
'spade_4',
'spade_5',
'spade_6',
'spade_7',
'spade_8',
'spade_9',
'spade_10',
'spade_jack',
'spade_queen',
'spade_king',
'joker_black',
'joker_red',
];
const backColors = ['black', 'red', 'blue', '#048b9a'];

return (
<div className={classes.homePage}>
I am on homepage
<BaseCard frontFace={<div>MY FRONT</div>} backFace={<div>MY BACK</div>} />
<h1>Card UI</h1>
{allCards.map((card, index) => (
<div key={card}>
<PlayingCard card={card} backColor={backColors[index % backColors.length]} />
</div>
))}
</div>
);
};
Expand Down
56 changes: 56 additions & 0 deletions src/models/PlayingCardType.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/** Possible playing cards */
export type PlayingCardType =
| 'club_1'
| 'club_2'
| 'club_3'
| 'club_4'
| 'club_5'
| 'club_6'
| 'club_7'
| 'club_8'
| 'club_9'
| 'club_10'
| 'club_jack'
| 'club_queen'
| 'club_king'
| 'diamond_1'
| 'diamond_2'
| 'diamond_3'
| 'diamond_4'
| 'diamond_5'
| 'diamond_6'
| 'diamond_7'
| 'diamond_8'
| 'diamond_9'
| 'diamond_10'
| 'diamond_jack'
| 'diamond_queen'
| 'diamond_king'
| 'heart_1'
| 'heart_2'
| 'heart_3'
| 'heart_4'
| 'heart_5'
| 'heart_6'
| 'heart_7'
| 'heart_8'
| 'heart_9'
| 'heart_10'
| 'heart_jack'
| 'heart_queen'
| 'heart_king'
| 'spade_1'
| 'spade_2'
| 'spade_3'
| 'spade_4'
| 'spade_5'
| 'spade_6'
| 'spade_7'
| 'spade_8'
| 'spade_9'
| 'spade_10'
| 'spade_jack'
| 'spade_queen'
| 'spade_king'
| 'joker_black'
| 'joker_red';

0 comments on commit 1142ed7

Please sign in to comment.