Set board category if not set (#4200)

* Set board category if not set

* Uncommented a commented test, which was commented for debugging
This commit is contained in:
Harshil Sharma 2022-11-15 16:11:22 +05:30 committed by GitHub
parent 0cd4257ebc
commit 93698c9574
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 673 additions and 1 deletions

View file

@ -180,6 +180,511 @@ exports[`components/sidebarSidebar dont show hidden boards 1`] = `
</div>
`;
exports[`components/sidebarSidebar should assign default category if current board doesnt have a category 1`] = `
<div>
<div
class="Sidebar octo-sidebar"
>
<div
class="octo-sidebar-header"
>
<div
class="heading"
>
<div
class="SidebarUserMenu"
>
<div
class="ModalWrapper"
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<div
class="logo"
>
<div
class="logo-title"
>
<svg
class="FocalboardLogoIcon Icon"
version="1.1"
viewBox="0 0 52.589677 64"
x="0px"
y="0px"
>
<path
d="m 33.071077,12.069805 c -12.663,-3.4670001 -27.0530002,3.289 -31.6760002,16.943 -4.655,13.75 2.719,28.67 16.4690002,33.325 13.75,4.655 28.67,-2.719 33.326,-16.469 3.804,-11.235 -0.462,-22.701 -8.976,-29.249 l -0.46,4.871 h -0.001 c 4.631,4.896 6.709,11.941 4.325,18.985 -3.362,9.931 -14.447,15.151 -24.76,11.66 -10.313,-3.49 -15.9480002,-14.37 -12.5870002,-24.301 2.9750002,-8.788 11.9980002,-13.715 20.7430002,-12.625 v -10e-4 z m -6.175,16.488 c 3.456,-0.665 6.986,2.754 5.762,6.37 -0.854,2.522 -3.67,3.85 -6.291,2.962 -2.62,-0.887 -4.052,-3.651 -3.197,-6.174 0.573,-1.697 2.034,-2.852 3.726,-3.158 z m -1.285,-4.944 c -1.786,0.323 -3.45,1.104 -4.812,2.258 -1.299,1.101 -2.319,2.545 -2.898,4.258 -0.879,2.597 -0.579,5.323 0.617,7.632 1.206,2.329 3.325,4.234 6.07,5.164 2.744,0.929 5.584,0.701 7.959,-0.417 2.352,-1.107 4.246,-3.091 5.125,-5.688 0.555,-1.639 0.633,-3.254 0.344,-4.761 -0.21,-1.093 -0.615,-2.134 -1.174,-3.091 l 1.019,-5.107 c 0.189,0.187 0.374,0.378 0.552,0.574 1.75,1.919 3.008,4.283 3.508,6.877 0.415,2.154 0.304,4.457 -0.484,6.784 -1.239,3.661 -3.898,6.453 -7.193,8.005 -3.273,1.541 -7.175,1.858 -10.93,0.588 -3.754,-1.271 -6.661,-3.895 -8.326,-7.108 -1.674,-3.233 -2.09,-7.065 -0.851,-10.728 0.819,-2.419 2.26,-4.46 4.097,-6.016 1.88,-1.593 4.181,-2.673 6.656,-3.125 l -0.001,-0.004 c 1.759,-0.339 3.522,-0.313 5.213,0.016 l -3.583,3.761 c -0.294,0.028 -0.588,0.071 -0.883,0.127 h -0.025 z"
/>
<polygon
points="26.057,32.594 37.495,11.658 36.79,8.44 41.066,0.207 43.683,4.611 48.803,4.434 44.185,12.48 40.902,13.697 29.542,34.491 "
transform="translate(7.6780426e-5,-0.21919512)"
/>
</svg>
<span>
Focalboard
</span>
<div
class="versionFrame"
>
<div
class="version"
title="v7.6.0"
>
v7.6.0
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="octo-spacer"
/>
<div
class="sidebarSwitcher"
>
<button
class="IconButton"
type="button"
>
<svg
class="HideSidebarIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="80,20 50,50 80,80"
/>
<polyline
points="50,20 20,50, 50,80"
/>
</svg>
</button>
</div>
</div>
<div
class="WorkspaceTitle"
/>
<div
class="BoardsSwitcherWrapper"
>
<div
class="BoardsSwitcher"
>
<i
class="CompassIcon icon-magnify MagnifyIcon"
/>
<div>
<span>
Find Boards
</span>
</div>
</div>
</div>
<div
class="octo-sidebar-list"
>
<div
class="SidebarCategory"
>
<div
class="octo-sidebar-item category ' expanded "
>
<div
class="octo-sidebar-title category-title"
title="Category 1"
>
<i
class="CompassIcon icon-chevron-down ChevronDownIcon"
/>
Category 1
<div
class="sidebarCategoriesTour"
/>
</div>
<div
class=""
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<button
class="IconButton"
type="button"
>
<i
class="CompassIcon icon-dots-horizontal OptionsIcon"
/>
</button>
</div>
</div>
</div>
</div>
<div
class="SidebarCategory"
>
<div
class="octo-sidebar-item category ' expanded "
>
<div
class="octo-sidebar-title category-title"
title="Boards"
>
<i
class="CompassIcon icon-chevron-down ChevronDownIcon"
/>
Boards
<div
class="sidebarCategoriesTour"
/>
</div>
<div
class=""
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<button
class="IconButton"
type="button"
>
<i
class="CompassIcon icon-dots-horizontal OptionsIcon"
/>
</button>
</div>
</div>
</div>
<div
class="octo-sidebar-item subitem no-views"
>
No boards inside
</div>
</div>
</div>
<div
class="octo-spacer"
/>
<div
class="add-board"
>
+ Add board
</div>
<div
class="SidebarSettingsMenu"
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<div
class="menu-entry"
>
Settings
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/sidebarSidebar shouldnt do any category assignment is board is in a category 1`] = `
<div>
<div
class="Sidebar octo-sidebar"
>
<div
class="octo-sidebar-header"
>
<div
class="heading"
>
<div
class="SidebarUserMenu"
>
<div
class="ModalWrapper"
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<div
class="logo"
>
<div
class="logo-title"
>
<svg
class="FocalboardLogoIcon Icon"
version="1.1"
viewBox="0 0 52.589677 64"
x="0px"
y="0px"
>
<path
d="m 33.071077,12.069805 c -12.663,-3.4670001 -27.0530002,3.289 -31.6760002,16.943 -4.655,13.75 2.719,28.67 16.4690002,33.325 13.75,4.655 28.67,-2.719 33.326,-16.469 3.804,-11.235 -0.462,-22.701 -8.976,-29.249 l -0.46,4.871 h -0.001 c 4.631,4.896 6.709,11.941 4.325,18.985 -3.362,9.931 -14.447,15.151 -24.76,11.66 -10.313,-3.49 -15.9480002,-14.37 -12.5870002,-24.301 2.9750002,-8.788 11.9980002,-13.715 20.7430002,-12.625 v -10e-4 z m -6.175,16.488 c 3.456,-0.665 6.986,2.754 5.762,6.37 -0.854,2.522 -3.67,3.85 -6.291,2.962 -2.62,-0.887 -4.052,-3.651 -3.197,-6.174 0.573,-1.697 2.034,-2.852 3.726,-3.158 z m -1.285,-4.944 c -1.786,0.323 -3.45,1.104 -4.812,2.258 -1.299,1.101 -2.319,2.545 -2.898,4.258 -0.879,2.597 -0.579,5.323 0.617,7.632 1.206,2.329 3.325,4.234 6.07,5.164 2.744,0.929 5.584,0.701 7.959,-0.417 2.352,-1.107 4.246,-3.091 5.125,-5.688 0.555,-1.639 0.633,-3.254 0.344,-4.761 -0.21,-1.093 -0.615,-2.134 -1.174,-3.091 l 1.019,-5.107 c 0.189,0.187 0.374,0.378 0.552,0.574 1.75,1.919 3.008,4.283 3.508,6.877 0.415,2.154 0.304,4.457 -0.484,6.784 -1.239,3.661 -3.898,6.453 -7.193,8.005 -3.273,1.541 -7.175,1.858 -10.93,0.588 -3.754,-1.271 -6.661,-3.895 -8.326,-7.108 -1.674,-3.233 -2.09,-7.065 -0.851,-10.728 0.819,-2.419 2.26,-4.46 4.097,-6.016 1.88,-1.593 4.181,-2.673 6.656,-3.125 l -0.001,-0.004 c 1.759,-0.339 3.522,-0.313 5.213,0.016 l -3.583,3.761 c -0.294,0.028 -0.588,0.071 -0.883,0.127 h -0.025 z"
/>
<polygon
points="26.057,32.594 37.495,11.658 36.79,8.44 41.066,0.207 43.683,4.611 48.803,4.434 44.185,12.48 40.902,13.697 29.542,34.491 "
transform="translate(7.6780426e-5,-0.21919512)"
/>
</svg>
<span>
Focalboard
</span>
<div
class="versionFrame"
>
<div
class="version"
title="v7.6.0"
>
v7.6.0
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="octo-spacer"
/>
<div
class="sidebarSwitcher"
>
<button
class="IconButton"
type="button"
>
<svg
class="HideSidebarIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="80,20 50,50 80,80"
/>
<polyline
points="50,20 20,50, 50,80"
/>
</svg>
</button>
</div>
</div>
<div
class="WorkspaceTitle"
/>
<div
class="BoardsSwitcherWrapper"
>
<div
class="BoardsSwitcher"
>
<i
class="CompassIcon icon-magnify MagnifyIcon"
/>
<div>
<span>
Find Boards
</span>
</div>
</div>
</div>
<div
class="octo-sidebar-list"
>
<div
class="SidebarCategory"
>
<div
class="octo-sidebar-item category ' expanded "
>
<div
class="octo-sidebar-title category-title"
title="Category 1"
>
<i
class="CompassIcon icon-chevron-down ChevronDownIcon"
/>
Category 1
<div
class="sidebarCategoriesTour"
/>
</div>
<div
class=""
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<button
class="IconButton"
type="button"
>
<i
class="CompassIcon icon-dots-horizontal OptionsIcon"
/>
</button>
</div>
</div>
</div>
</div>
<div
class="SidebarCategory"
>
<div
class="octo-sidebar-item category ' expanded "
>
<div
class="octo-sidebar-title category-title"
title="Category 2"
>
<i
class="CompassIcon icon-chevron-down ChevronDownIcon"
/>
Category 2
<div
class="sidebarCategoriesTour"
/>
</div>
<div
class=""
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<button
class="IconButton"
type="button"
>
<i
class="CompassIcon icon-dots-horizontal OptionsIcon"
/>
</button>
</div>
</div>
</div>
<div
class="SidebarBoardItem subitem "
>
<div
class="octo-sidebar-icon"
>
i
</div>
<div
class="octo-sidebar-title"
title="board title"
>
board title
</div>
<div>
<div
aria-label="menuwrapper"
class="MenuWrapper x"
role="button"
>
<button
class="IconButton"
type="button"
>
<i
class="CompassIcon icon-dots-horizontal OptionsIcon"
/>
</button>
</div>
</div>
</div>
</div>
<div
class="SidebarCategory"
>
<div
class="octo-sidebar-item category ' expanded "
>
<div
class="octo-sidebar-title category-title"
title="Boards"
>
<i
class="CompassIcon icon-chevron-down ChevronDownIcon"
/>
Boards
<div
class="sidebarCategoriesTour"
/>
</div>
<div
class=""
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<button
class="IconButton"
type="button"
>
<i
class="CompassIcon icon-dots-horizontal OptionsIcon"
/>
</button>
</div>
</div>
</div>
<div
class="octo-sidebar-item subitem no-views"
>
No boards inside
</div>
</div>
</div>
<div
class="octo-spacer"
/>
<div
class="add-board"
>
+ Add board
</div>
<div
class="SidebarSettingsMenu"
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<div
class="menu-entry"
>
Settings
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/sidebarSidebar sidebar expect hidden 1`] = `
<div>
<div

View file

@ -12,17 +12,27 @@ import userEvent from '@testing-library/user-event'
import thunk from 'redux-thunk'
import {mocked} from 'jest-mock'
import {mockMatchMedia, wrapIntl} from '../../testUtils'
import {TestBlockFactory} from '../../test/testBlockFactory'
import octoClient from '../../../../webapp/src/octoClient'
import Sidebar from './sidebar'
jest.mock('../../../../webapp/src/octoClient')
const mockedOctoClient = mocked(octoClient, true)
beforeAll(() => {
mockMatchMedia({matches: true})
})
describe('components/sidebarSidebar', () => {
beforeEach(() => {
jest.clearAllMocks()
})
const mockStore = configureStore([thunk])
const board = TestBlockFactory.createBoard()
@ -32,6 +42,10 @@ describe('components/sidebarSidebar', () => {
categoryAttribute1.name = 'Category 1'
categoryAttribute1.boardIDs = [board.id]
const defaultCategory = TestBlockFactory.createCategoryBoards()
defaultCategory.id = 'default_category'
defaultCategory.name = 'Boards'
test('sidebar hidden', () => {
const store = mockStore({
teams: {
@ -274,6 +288,125 @@ describe('components/sidebarSidebar', () => {
expect(sidebarCollapsedCategory.length).toBe(1)
})
test('should assign default category if current board doesnt have a category', () => {
const board2 = TestBlockFactory.createBoard()
board2.id = 'board2'
const store = mockStore({
teams: {
current: {id: 'team-id'},
},
boards: {
current: board2.id,
boards: {
[board2.id]: board2,
},
myBoardMemberships: {
[board2.id]: board2,
},
},
cards: {
cards: {
card_id_1: {title: 'Card'},
},
current: 'card_id_1',
},
views: {
views: [],
},
users: {
me: {
id: 'user_id_1',
props: {},
},
},
sidebar: {
categoryAttributes: [
categoryAttribute1,
defaultCategory,
],
},
})
const history = createMemoryHistory()
const onBoardTemplateSelectorOpen = jest.fn()
mockedOctoClient.moveBoardToCategory.mockResolvedValueOnce({} as Response)
const component = wrapIntl(
<ReduxProvider store={store}>
<Router history={history}>
<Sidebar onBoardTemplateSelectorOpen={onBoardTemplateSelectorOpen}/>
</Router>
</ReduxProvider>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
expect(mockedOctoClient.moveBoardToCategory).toBeCalledWith('team-id', 'board2', 'default_category', '')
})
test('shouldnt do any category assignment is board is in a category', () => {
const board2 = TestBlockFactory.createBoard()
board2.id = 'board2'
const categoryAttribute2 = TestBlockFactory.createCategoryBoards()
categoryAttribute2.name = 'Category 2'
categoryAttribute2.boardIDs = [board2.id]
const store = mockStore({
teams: {
current: {id: 'team-id'},
},
boards: {
current: board2.id,
boards: {
[board2.id]: board2,
},
myBoardMemberships: {
[board2.id]: board2,
},
},
cards: {
cards: {
card_id_1: {title: 'Card'},
},
current: 'card_id_1',
},
views: {
views: [],
},
users: {
me: {
id: 'user_id_1',
props: {},
},
},
sidebar: {
categoryAttributes: [
categoryAttribute1,
categoryAttribute2,
defaultCategory,
],
},
})
const history = createMemoryHistory()
const onBoardTemplateSelectorOpen = jest.fn()
const component = wrapIntl(
<ReduxProvider store={store}>
<Router history={history}>
<Sidebar onBoardTemplateSelectorOpen={onBoardTemplateSelectorOpen}/>
</Router>
</ReduxProvider>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
expect(mockedOctoClient.moveBoardToCategory).toBeCalledTimes(0)
})
// TODO: Fix this later
// test('global templates', () => {
// const store = mockStore({

View file

@ -8,7 +8,7 @@ import IconButton from '../../widgets/buttons/iconButton'
import HamburgerIcon from '../../widgets/icons/hamburger'
import HideSidebarIcon from '../../widgets/icons/hideSidebar'
import ShowSidebarIcon from '../../widgets/icons/showSidebar'
import {getMySortedBoards} from '../../store/boards'
import {getCurrentBoard, getMySortedBoards} from '../../store/boards'
import {useAppDispatch, useAppSelector} from '../../store/hooks'
import {Utils} from '../../utils'
import {IUser} from '../../user'
@ -35,6 +35,8 @@ import {Constants} from '../../constants'
import {getMe} from '../../store/users'
import {getCurrentViewId} from '../../store/views'
import octoClient from '../../octoClient'
import SidebarCategory from './sidebarCategory'
import SidebarSettingsMenu from './sidebarSettingsMenu'
import SidebarUserMenu from './sidebarUserMenu'
@ -62,6 +64,7 @@ const Sidebar = (props: Props) => {
const sidebarCategories = useAppSelector<CategoryBoards[]>(getSidebarCategories)
const me = useAppSelector<IUser|null>(getMe)
const activeViewID = useAppSelector(getCurrentViewId)
const currentBoard = useAppSelector(getCurrentBoard)
useEffect(() => {
wsClient.addOnChange((_: WSClient, categories: Category[]) => {
@ -98,6 +101,37 @@ const Sidebar = (props: Props) => {
hideSidebar()
}, [windowDimensions])
// This handles the case when a user opens a linked board from Channels RHS
// and thats the first time that user is opening that board.
// Here we check if that board has a associated category for the user. If not,
// we assign it to the default "Boards" category.
// We do this on the client side rather than the server side live for all other cases
// is because there is no good, explicit API call to add this logic to when opening
// a board that you have implicit access to.
useEffect(() => {
if (!sidebarCategories || sidebarCategories.length === 0 || !currentBoard || !team || currentBoard.isTemplate) {
return
}
// find the category the current board belongs to
const category = sidebarCategories.find((c) => c.boardIDs.indexOf(currentBoard.id) >= 0)
if (category) {
// Boards does belong to a category.
// All good here. Nothing to do
return
}
// if the board doesn't belong to a category
// we need to move it to the default "Boards" category
const boardsCategory = sidebarCategories.find((c) => c.name === 'Boards')
if (!boardsCategory) {
Utils.logError('Boards category not found for user')
return
}
octoClient.moveBoardToCategory(team.id, currentBoard.id, boardsCategory.id, '')
}, [sidebarCategories, currentBoard, team])
if (!boards) {
return <div/>
}