@lourenci/react-kanban
Advanced tools
Comparing version 0.3.0 to 0.4.0
{ | ||
"name": "@lourenci/react-kanban", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "A <ReactKanban /> for React Hooks", | ||
@@ -19,3 +19,3 @@ "main": "dist/index.js", | ||
"babel-loader": "8.0.5", | ||
"babel-plugin-react-remove-properties": "^0.3.0", | ||
"babel-plugin-react-remove-properties": "0.3.0", | ||
"cypress": "3.2.0", | ||
@@ -26,3 +26,3 @@ "eslint": "5.16.0", | ||
"eslint-plugin-cypress": "2.2.1", | ||
"eslint-plugin-import": "2.16.0", | ||
"eslint-plugin-import": "2.17.1", | ||
"eslint-plugin-node": "8.0.1", | ||
@@ -41,3 +41,3 @@ "eslint-plugin-promise": "4.1.1", | ||
"styled-components": "4.2.0", | ||
"webpack": "4.29.6", | ||
"webpack": "4.30.0", | ||
"webpack-cli": "3.3.0", | ||
@@ -44,0 +44,0 @@ "webpack-dev-server": "3.3.1" |
@@ -8,5 +8,8 @@ [![Maintainability](https://api.codeclimate.com/v1/badges/c602758e03850fdb8b64/maintainability)](https://codeclimate.com/github/lourenci/react-kanban/maintainability) | ||
![Kanban Demo](https://i.imgur.com/i0dX0q1.gif) | ||
![Kanban Demo](https://imgur.com/xyOoy2N.gif) | ||
## ▶️ Demo | ||
### ▶️ Demo | ||
[Usage](https://5k7py44kl.codesandbox.io/) | ||
[![Edit react-kanban-demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/5k7py44kl?fontsize=14) | ||
@@ -64,2 +67,3 @@ | ||
onLaneDragEnd={() => {}} | ||
renderCard={() => {}} | ||
> | ||
@@ -78,2 +82,3 @@ {board} | ||
| `onLaneDragEnd` | Callback that will be called when the lane move ends | | ||
| `renderCard` | A card to be rendered instead of the default card | | ||
@@ -84,5 +89,5 @@ #### `children` | ||
lanes: { | ||
id: ${unique-laneId}, | ||
id: ${unique-required-laneId}, | ||
cards: { | ||
id: ${unique-cardId}, | ||
id: ${unique-required-cardId}, | ||
title: ${cardTitle}, | ||
@@ -94,2 +99,3 @@ description: ${cardDescription} | ||
``` | ||
These cards props are required to the card's default template, except the id that is required for your template too. See [`renderCard`](#rendercard) | ||
@@ -128,2 +134,33 @@ #### `OnCardDragEnd` | ||
#### `renderCard` | ||
Use this if you want to render your own card. You have to pass a function and return your card component. | ||
The function will receive these parameters: | ||
| Arg | Description | | ||
|--------------|------------------------------------------------------- | | ||
| `card` | The card props | | ||
| `isDragging` | Whether the card is being dragged | | ||
Ex.: | ||
```js | ||
const board = { | ||
lanes: { | ||
id: ${unique-required-laneId}, | ||
cards: { | ||
id: ${unique-required-cardId}, | ||
dueDate: ${cardDueDate}, | ||
content: ${cardContent} | ||
} | ||
} | ||
} | ||
<Board | ||
renderCard={({ dueDate, content }, isDragging) => ( | ||
<YourCard dueDate={dueDate} content={content} isDragging={isDragging} /> | ||
)} | ||
> | ||
{board} | ||
</Board> | ||
``` | ||
## 🚴♀️ Roadmap | ||
@@ -130,0 +167,0 @@ |
@@ -12,3 +12,3 @@ import React from 'react' | ||
const StyledCard = styled(CardSkeleton)` | ||
const DefaultCard = styled(CardSkeleton)` | ||
border-radius: 3px; | ||
@@ -32,3 +32,3 @@ background-color: #fff; | ||
function Card ({ children, index }) { | ||
function Card ({ children, index, renderCard }) { | ||
return ( | ||
@@ -38,12 +38,18 @@ <Draggable draggableId={String(children.id)} index={index}> | ||
return ( | ||
<StyledCard | ||
<div | ||
ref={provided.innerRef} | ||
{...provided.draggableProps} | ||
{...provided.dragHandleProps} | ||
dragging={snapshot.isDragging} | ||
data-testid='card' | ||
> | ||
<CardTitle>{children.title}</CardTitle> | ||
<CardDescription>{children.description}</CardDescription> | ||
</StyledCard> | ||
{renderCard | ||
? renderCard(children, snapshot.isDragging) | ||
: ( | ||
<DefaultCard dragging={snapshot.isDragging}> | ||
<CardTitle>{children.title}</CardTitle> | ||
<CardDescription>{children.description}</CardDescription> | ||
</DefaultCard> | ||
) | ||
} | ||
</div> | ||
) | ||
@@ -50,0 +56,0 @@ }} |
@@ -9,12 +9,13 @@ import React from 'react' | ||
function mount () { | ||
const card = { | ||
id: 1, | ||
title: 'Card title', | ||
description: 'Card content' | ||
} | ||
const defaultCard = { | ||
id: 1, | ||
title: 'Card title', | ||
description: 'Card content' | ||
} | ||
subject = render(<Card>{card}</Card>) | ||
function mount ({ children = defaultCard, ...otherProps } = {}) { | ||
subject = render(<Card {...otherProps}>{children}</Card>) | ||
return subject | ||
} | ||
afterEach(() => { subject = undefined }) | ||
@@ -39,2 +40,3 @@ | ||
}) | ||
afterEach(() => { callbacks.isDragging(false) }) | ||
@@ -45,2 +47,32 @@ it('shows the card with a box shadow', () => { | ||
}) | ||
describe('about a custom card', () => { | ||
let renderCard | ||
const customCard = { | ||
id: 1, | ||
title: 'Card title', | ||
content: 'Card content' | ||
} | ||
afterEach(() => { renderCard = undefined }) | ||
describe('when it receives a "renderCard" prop', () => { | ||
beforeEach(() => { | ||
renderCard = jest.fn(cardContent => ( | ||
<div id='customCard'>{cardContent.id} - {cardContent.title} - {cardContent.content}</div> | ||
)) | ||
mount({ children: customCard, renderCard }) | ||
}) | ||
it('renders the custom card', () => { | ||
expect(subject.container.querySelector('div#customCard')).toHaveTextContent(/^1 - Card title - Card content$/) | ||
}) | ||
it('passes the card content and the isDragging as a parameter to the renderCard prop', () => { | ||
expect(renderCard).toHaveBeenCalledTimes(1) | ||
expect(renderCard).toHaveBeenCalledWith(customCard, false) | ||
}) | ||
}) | ||
}) | ||
}) | ||
@@ -47,0 +79,0 @@ |
@@ -22,5 +22,5 @@ import React from 'react' | ||
function Lane ({ children, index }) { | ||
function Lane ({ children, index: laneIndex, renderCard }) { | ||
return ( | ||
<Draggable draggableId={`lane-draggable-${children.id}`} index={index}> | ||
<Draggable draggableId={`lane-draggable-${children.id}`} index={laneIndex}> | ||
{laneProvided => ( | ||
@@ -31,3 +31,3 @@ <StyledLane ref={laneProvided.innerRef} {...laneProvided.draggableProps} data-testid='lane'> | ||
{children.cards.length | ||
? children.cards.map((card, idx) => (<Card key={card.id} index={idx}>{card}</Card>)) | ||
? children.cards.map((card, index) => (<Card key={card.id} index={index} renderCard={renderCard}>{card}</Card>)) | ||
: <CardSkeleton /> | ||
@@ -34,0 +34,0 @@ } |
@@ -7,4 +7,43 @@ import React from 'react' | ||
let subject | ||
const lane = { | ||
id: 1, | ||
title: 'Backlog', | ||
cards: [ | ||
{ | ||
id: 1, | ||
title: 'Card title 1', | ||
description: 'Card content' | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Card title 2', | ||
description: 'Card content' | ||
} | ||
] | ||
} | ||
beforeEach(() => { | ||
function mount ({ children = lane, ...otherProps } = {}) { | ||
subject = render(<Lane {...otherProps}>{children}</Lane>) | ||
return subject | ||
} | ||
afterEach(() => { subject = undefined }) | ||
it('renders a lane', () => { | ||
expect(mount().container.querySelector('div')).toBeInTheDocument() | ||
}) | ||
it("renders the lane's title", () => { | ||
expect(mount().queryByText(/^Backlog$/)).toBeInTheDocument() | ||
}) | ||
it('renders the specified cards in the lane ordered by its specified position', () => { | ||
const cards = mount().queryAllByText(/^Card title/) | ||
expect(cards).toHaveLength(2) | ||
expect(cards[0]).toHaveTextContent(/^Card title 1$/) | ||
expect(cards[1]).toHaveTextContent(/^Card title 2$/) | ||
}) | ||
describe("about the lane's custom card", () => { | ||
let renderCard | ||
const lane = { | ||
@@ -16,9 +55,9 @@ id: 1, | ||
id: 1, | ||
title: 'Card title 1', | ||
description: 'Card content' | ||
title: 'Card title', | ||
content: 'Card content' | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Card title 2', | ||
description: 'Card content' | ||
title: 'Card title', | ||
content: 'Card content' | ||
} | ||
@@ -28,20 +67,19 @@ ] | ||
subject = render(<Lane>{lane}</Lane>) | ||
}) | ||
afterEach(() => { subject = undefined }) | ||
afterEach(() => { renderCard = undefined }) | ||
it('renders a lane', () => { | ||
expect(subject.container.querySelector('div')).toBeInTheDocument() | ||
}) | ||
describe('when it receives a "renderCard" prop', () => { | ||
beforeEach(() => { | ||
renderCard = jest.fn(cardContent => ( | ||
<div>{cardContent.id} - {cardContent.title} - {cardContent.content}</div> | ||
)) | ||
it("renders the lane's title", () => { | ||
expect(subject.queryByText(/^Backlog$/)).toBeInTheDocument() | ||
}) | ||
mount({ children: lane, renderCard }) | ||
}) | ||
it('renders the specified cards in the lane ordered by its specified position', () => { | ||
const cards = subject.queryAllByText(/^Card title/) | ||
expect(cards).toHaveLength(2) | ||
expect(cards[0]).toHaveTextContent(/^Card title 1$/) | ||
expect(cards[1]).toHaveTextContent(/^Card title 2$/) | ||
it('renders the custom cards on the lane', () => { | ||
expect(subject.queryAllByTestId('card')).toHaveLength(2) | ||
expect(subject.queryByTestId('card')).toHaveTextContent(/^1 - Card title - Card content$/) | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -19,3 +19,3 @@ import React, { useState } from 'react' | ||
function Board ({ children, onCardDragEnd, onLaneDragEnd }) { | ||
function Board ({ children, onCardDragEnd, onLaneDragEnd, renderCard }) { | ||
const [board, setBoard] = useState(children) | ||
@@ -46,3 +46,3 @@ | ||
<DroppableBoard droppableId='board-droppable' direction='horizontal' type='BOARD'> | ||
{board.lanes.map((lane, idx) => (<Lane key={lane.id} index={idx}>{lane}</Lane>))} | ||
{board.lanes.map((lane, index) => (<Lane key={lane.id} index={index} renderCard={renderCard}>{lane}</Lane>))} | ||
</DroppableBoard> | ||
@@ -49,0 +49,0 @@ </DragDropContext> |
@@ -8,36 +8,36 @@ import React from 'react' | ||
let subject, onCardDragEnd, onLaneDragEnd | ||
const board = { | ||
lanes: [ | ||
{ | ||
id: 1, | ||
title: 'Lane Backlog', | ||
cards: [ | ||
{ | ||
id: 1, | ||
title: 'Card title', | ||
description: 'Card content' | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Card title', | ||
description: 'Card content' | ||
} | ||
] | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Lane Doing', | ||
cards: [ | ||
{ | ||
id: 3, | ||
title: 'Card title', | ||
description: 'Card content' | ||
} | ||
] | ||
} | ||
] | ||
} | ||
function mount (props) { | ||
const board = { | ||
lanes: [ | ||
{ | ||
id: 1, | ||
title: 'Lane Backlog', | ||
cards: [ | ||
{ | ||
id: 1, | ||
title: 'Card title', | ||
description: 'Card content' | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Card title', | ||
description: 'Card content' | ||
} | ||
] | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Lane Doing', | ||
cards: [ | ||
{ | ||
id: 3, | ||
title: 'Card title', | ||
description: 'Card content' | ||
} | ||
] | ||
} | ||
] | ||
} | ||
subject = render(<Board {...props}>{board}</Board>) | ||
function mount ({ children = board, ...otherProps } = {}) { | ||
subject = render(<Board {...otherProps}>{children}</Board>) | ||
return subject | ||
@@ -184,2 +184,59 @@ } | ||
}) | ||
describe("about the board's custom card", () => { | ||
let renderCard | ||
const board = { | ||
lanes: [ | ||
{ | ||
id: 1, | ||
title: 'Lane Backlog', | ||
cards: [ | ||
{ | ||
id: 1, | ||
title: 'Card title', | ||
content: 'Card content' | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Card title', | ||
content: 'Card content' | ||
} | ||
] | ||
}, | ||
{ | ||
id: 2, | ||
title: 'Lane Doing', | ||
cards: [ | ||
{ | ||
id: 3, | ||
title: 'Card title', | ||
content: 'Card content' | ||
} | ||
] | ||
} | ||
] | ||
} | ||
afterEach(() => { renderCard = undefined }) | ||
describe('when it receives a "renderCard" prop', () => { | ||
beforeEach(() => { | ||
renderCard = jest.fn(cardContent => ( | ||
<div>{cardContent.id} - {cardContent.title} - {cardContent.content}</div> | ||
)) | ||
mount({ children: board, renderCard }) | ||
}) | ||
it("renders the custom cards on the board's lane", () => { | ||
expect(subject.queryAllByTestId('card')).toHaveLength(3) | ||
expect(subject.queryByTestId('card')).toHaveTextContent(/^1 - Card title - Card content$/) | ||
}) | ||
it('passes the card content and the isDragging as a parameter to the renderCard prop', () => { | ||
expect(renderCard).toHaveBeenCalledTimes(3) | ||
expect(renderCard).toHaveBeenNthCalledWith(1, { id: 1, title: 'Card title', content: 'Card content' }, false) | ||
}) | ||
}) | ||
}) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
166184
958
197