Skip to content

Commit 31d1116

Browse files
authored
Public boards can be added (user must not be a member of it) (#17)
1 parent a083dd8 commit 31d1116

25 files changed

Lines changed: 514 additions & 31 deletions

File tree

CHANGES.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
88

99
### Added
1010

11+
* config can now contain the `id` of a board, which allows adding public
12+
Trello boards as well. Related to breaking change note regarding config.js below.
1113
* <TrelloCardUi /> can display labels (with toggle: show or hide)
1214
* `<ListTabs \/>` added
1315
* list `pattern` can be modified with query parameters. Examples:
@@ -16,13 +18,58 @@ adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
1618

1719
### Changed
1820

21+
* **Breaking**: boards config in `config.js` changed:
22+
23+
```js
24+
// previous config
25+
26+
boards: [
27+
{
28+
// ...
29+
board: 'hello-world', // renamed to 'name'
30+
},
31+
],
32+
```
33+
34+
```js
35+
// new config
36+
37+
boards: [
38+
{
39+
// ...
40+
name: 'hello-world', // new property 'name'
41+
id: 'board-1', // optional, see README
42+
},
43+
],
44+
```
45+
1946
* **Breaking**: `config.js` can now contain multiple `lists` patterns, each list
2047
will be available in the `<ListTabs \/>` component and can later be used to
2148
select a specific list. All filters work as expected and as before (also with
2249
deeplinking for the list patterns)
2350
* _Note_: even though semver would suggest adding releasing a major release, I
2451
decided to stick to 0.x.y still until further notice here.
2552

53+
```js
54+
// previous config
55+
boards: [
56+
{
57+
// ...
58+
lists: /#upcoming/,
59+
},
60+
],
61+
```
62+
63+
```js
64+
// new config
65+
lists: [/#upcoming/],
66+
boards: [
67+
{
68+
// does not contain lists anymore
69+
},
70+
],
71+
```
72+
2673
## 2018/06/09 [0.1.1][5]
2774

2875
* Bugfix TrelloCard `isHidden` calculation

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ preferred team members.
4242

4343
* :memo: flexible multiboard configuration (eg. select multiple lists to display from multiple boards)
4444
* :office: optimised teamwork experience, (eg. send deeplinks to your colleagues with their stories already filtered)
45+
* :door: add private (accessible only to invited members of the Board) and public Trello boards
4546
* :lock: your data stays secure in your browser and is not shared with others
4647
* :hammer: built with awesome technologies, like: [react 16.x](https://github.com/facebook/react),
4748
[react-router-dom](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom),
@@ -129,7 +130,8 @@ module.exports = {
129130
boards: [
130131
{
131132
shortcut: 'hw',
132-
board: 'hello-world',
133+
name: 'hello-world',
134+
id: 'board-1',
133135
estimates_with_round_brackets: true,
134136
estimates_with_square_brackets: true,
135137
},
@@ -154,10 +156,27 @@ The main config properties look like this:
154156
The list of boards should contain board objects like this:
155157

156158
* `shortcut` (string): the title above each list of tasks
157-
* `board` (string): name of trello board
159+
* `name` (string): name of trello board
160+
* `id` (string, optional): id of trello board, only required when board is public but user should see it
158161
* `estimates_with_round_brackets` (bool)
159162
* `estimates_with_square_brackets` (bool)
160163

164+
With the `id` property you can add boards, which are public and the user is not
165+
part of when accessing your TrelloMultiBoard App.
166+
167+
##### How to get the id of your board?
168+
169+
Let's assume the url of your board looks like this: https://trello.com/b/123asdf/hello-world.
170+
You can get the id of the board now by changing the url to: https://trello.com/b/123asdf/hello-world.json.
171+
Wait for some time (depends on the number of lists and card of the board) and
172+
you should see a result similar to
173+
174+
```
175+
{ "id": "IGVA5wQ67w2mBbkctLxi", "name": "hello-world", "desc":"", ... }
176+
```
177+
178+
The id at the beginning of the JSON is the id of the board.
179+
161180
#### Estimations Configuration
162181

163182
Additionally you can calculate and get the estimations from the cards, if their

config/config.example.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ module.exports = {
88
boards: [
99
{
1010
shortcut: 'hw',
11-
board: 'hello-world',
11+
name: 'hello-world',
12+
id: 'board-1', // optional: only required when board is public but user should see it
1213
estimates_with_round_brackets: true,
1314
estimates_with_square_brackets: true,
1415
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"lint": "eslint ./src",
3131
"lint:scss": "stylelint ./src",
3232
"postinstall": "./node_modules/.bin/cpy \"./config/config.example.js\" \"./config\" --no-overwrite --rename=\"config.js\"",
33-
"precommit": "npm run analyse:size && lint-staged",
33+
"precommit": "npm run build && npm run analyse:size && lint-staged",
3434
"prepush": "npm test",
3535
"start": "webpack-dev-server --hot --config webpack/dev.config.js",
3636
"test": "jest",

src/__mocks__/mocks.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { pick } from 'lodash'
22

33
// This file contains mocked responses from the data/trello API.
44
export const mockExampleBoardConfig = {
5-
board: 'hello-world',
5+
name: 'hello-world',
6+
id: 'board-1', // can be optional
67
estimates_with_round_brackets: true,
78
estimates_with_square_brackets: true,
89
shortcut: 'hw',
@@ -47,7 +48,7 @@ export const mockExampleBoardResponse = { name: 'hello-world', id: 'board-1' }
4748

4849
// most likely it should be an array with two lists (one matching and one not)
4950
export const mockExampleListResponse = {
50-
id: 'list-123',
51+
id: 'list-1',
5152
idBoard: 'board-1',
5253
name: 'Test List Name #upcoming',
5354
}

src/actions/boards/__tests__/index.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ describe('actions/board:async actions', () => {
6767
error: null,
6868
payload: [
6969
{
70+
idx: 0,
7071
board,
7172
config,
7273
},

src/actions/boards/index.js

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { forEach } from 'lodash'
1+
import { find, forEach, sortBy } from 'lodash'
22
import Config from '../../../config/config'
3-
import { getMeBoards } from '../../data/trello'
3+
import { getBoard, getMeBoards } from '../../data/trello'
44

55
const actions = {
66
RESET_BOARDS: 'RESET_BOARDS',
@@ -23,20 +23,48 @@ const requestBoards = () => async dispatch => {
2323
try {
2424
const result = await getMeBoards()
2525
const boards = []
26-
forEach(result, board => {
27-
forEach(Config.boards, configuredBoard => {
28-
if (configuredBoard.board === board.name) {
29-
const boardObj = {
30-
board,
31-
config: configuredBoard,
32-
}
33-
boards.push(boardObj)
26+
27+
const additionalBoards = []
28+
forEach(Config.boards, (configuredBoard, idx) => {
29+
// get a board by it's name (works for boards a user subscribed to)
30+
const nameBoard = find(result, b => b.name === configuredBoard.name)
31+
const idBoard = find(result, b => b.id === configuredBoard.id)
32+
33+
if (nameBoard || idBoard) {
34+
const boardObj = {
35+
idx,
36+
board: nameBoard,
37+
config: configuredBoard,
3438
}
35-
})
39+
boards.push(boardObj)
40+
} else if (configuredBoard.id && !idBoard) {
41+
// TODO: write test for this case
42+
// if the .id is set, but the board was not found we query boards
43+
// (eg. for public boards, where people are not member of it, which means
44+
// you would not find it in their me/boards result)
45+
additionalBoards.push(
46+
new Promise(resolve => {
47+
getBoard(configuredBoard.id)
48+
.then(data => resolve({ configuredBoard, data, idx }))
49+
.catch(error => resolve({ configuredBoard, error, idx }))
50+
}),
51+
)
52+
}
53+
})
54+
55+
const idBoards = await Promise.all(additionalBoards)
56+
forEach(idBoards, ({ configuredBoard, data, idx }) => {
57+
const boardObj = {
58+
idx,
59+
board: data,
60+
config: configuredBoard,
61+
}
62+
boards.push(boardObj)
3663
})
3764

3865
// then update the state once we got all boards
39-
dispatch(receiveBoards(boards))
66+
// TODO: evaluate if board.idx should be removed
67+
dispatch(receiveBoards(sortBy(boards, ['idx'])))
4068
} catch (e) {
4169
dispatch(receiveBoards(e, true))
4270
}

src/components/board/__tests__/index.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('Component/BoardContainer', () => {
4141
isLoading: false,
4242
lists: [
4343
{
44-
id: 'list-123',
44+
id: 'list-1',
4545
idBoard: 'board-1',
4646
name: 'Test List Name #upcoming',
4747
},
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { mockExampleUser1, mockExampleBoardResponse } from '../../../__mocks__/mocks'
2+
3+
export const storeStateMock = {
4+
boards: {
5+
data: [{ board: mockExampleBoardResponse }],
6+
error: null,
7+
isLoading: true,
8+
},
9+
app: {
10+
memberToggle: {
11+
togglePreferred: false,
12+
togglePreferredMember: null,
13+
},
14+
},
15+
members: { members: [mockExampleUser1] },
16+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react'
2+
import { shallow } from 'enzyme'
3+
import configureMockStore from 'redux-mock-store'
4+
import thunk from 'redux-thunk'
5+
6+
import { mockExampleBoardResponse } from '../../../__mocks__/mocks'
7+
import { storeStateMock } from '../__mocks__/boards-list'
8+
9+
// inspired by https://github.com/reactjs/redux/issues/1534#issuecomment-205061049
10+
const mockStore = configureMockStore([thunk])
11+
12+
describe('Component/BoardsListContainer', () => {
13+
let store
14+
15+
beforeEach(() => {
16+
store = mockStore(storeStateMock)
17+
})
18+
19+
afterAll(() => {
20+
jest.resetModules()
21+
})
22+
23+
test('should get data from the store and prepare props without throwing an error', () => {
24+
const BoardsListContainer = require('../').default
25+
const wrapper = shallow(<BoardsListContainer store={store} />)
26+
27+
const boardsList = [{ board: mockExampleBoardResponse }]
28+
const expectedProps = {
29+
boards: boardsList,
30+
error: null,
31+
getEstimations: expect.any(Function),
32+
isLoading: true,
33+
}
34+
35+
expect(wrapper.props()).toEqual(expect.objectContaining(expectedProps))
36+
})
37+
})

0 commit comments

Comments
 (0)