mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-21 13:38:56 +02:00
Add category to the board search (#3626)
* Add category to the board search * cleanup * Handled long board names and long category names Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com>
This commit is contained in:
parent
f2b5c724bf
commit
c28620594a
@ -20,7 +20,6 @@ exports[`component/BoardSwitcherDialog base case 1`] = `
|
||||
>
|
||||
<button
|
||||
aria-label="Close dialog"
|
||||
class="IconButton size--medium"
|
||||
title="Close dialog"
|
||||
type="button"
|
||||
>
|
||||
@ -100,3 +99,233 @@ exports[`component/BoardSwitcherDialog base case 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`component/BoardSwitcherDialog find one, default category 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="Dialog dialog-back BoardSwitcherDialog"
|
||||
>
|
||||
<div
|
||||
class="backdrop"
|
||||
/>
|
||||
<div
|
||||
class="wrapper"
|
||||
>
|
||||
<div
|
||||
class="dialog"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="toolbar"
|
||||
>
|
||||
<button
|
||||
aria-label="Close dialog"
|
||||
title="Close dialog"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-close CloseIcon"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
class="toolbar--right"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="BoardSwitcherDialogBody"
|
||||
>
|
||||
<div
|
||||
class="head"
|
||||
>
|
||||
<h3
|
||||
class="text-heading4"
|
||||
>
|
||||
Find Boards
|
||||
</h3>
|
||||
<h5>
|
||||
Type to find a board. Use
|
||||
<b>
|
||||
UP/DOWN
|
||||
</b>
|
||||
to browse.
|
||||
<b>
|
||||
ENTER
|
||||
</b>
|
||||
to select,
|
||||
<b>
|
||||
ESC
|
||||
</b>
|
||||
to dismiss
|
||||
</h5>
|
||||
<div
|
||||
class="queryWrapper"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-magnify MagnifyIcon"
|
||||
/>
|
||||
<input
|
||||
class="searchQuery"
|
||||
maxlength="100"
|
||||
placeholder="Search for boards"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="searchResults"
|
||||
>
|
||||
<div
|
||||
class="searchResult"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="blockSearchResult"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-lock-outline LockOutlineIcon"
|
||||
/>
|
||||
<div
|
||||
class="resultTitle"
|
||||
>
|
||||
<span>
|
||||
hello
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="categoryTitle"
|
||||
>
|
||||
<span
|
||||
class="ml-2 text-light"
|
||||
>
|
||||
Boards
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="teamTitle"
|
||||
>
|
||||
Dunder Mifflin
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`component/BoardSwitcherDialog find one, with custom category 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="Dialog dialog-back BoardSwitcherDialog"
|
||||
>
|
||||
<div
|
||||
class="backdrop"
|
||||
/>
|
||||
<div
|
||||
class="wrapper"
|
||||
>
|
||||
<div
|
||||
class="dialog"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="toolbar"
|
||||
>
|
||||
<button
|
||||
aria-label="Close dialog"
|
||||
title="Close dialog"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-close CloseIcon"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
class="toolbar--right"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="BoardSwitcherDialogBody"
|
||||
>
|
||||
<div
|
||||
class="head"
|
||||
>
|
||||
<h3
|
||||
class="text-heading4"
|
||||
>
|
||||
Find Boards
|
||||
</h3>
|
||||
<h5>
|
||||
Type to find a board. Use
|
||||
<b>
|
||||
UP/DOWN
|
||||
</b>
|
||||
to browse.
|
||||
<b>
|
||||
ENTER
|
||||
</b>
|
||||
to select,
|
||||
<b>
|
||||
ESC
|
||||
</b>
|
||||
to dismiss
|
||||
</h5>
|
||||
<div
|
||||
class="queryWrapper"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-magnify MagnifyIcon"
|
||||
/>
|
||||
<input
|
||||
class="searchQuery"
|
||||
maxlength="100"
|
||||
placeholder="Search for boards"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="searchResults"
|
||||
>
|
||||
<div
|
||||
class="searchResult"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="blockSearchResult"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-lock-outline LockOutlineIcon"
|
||||
/>
|
||||
<div
|
||||
class="resultTitle"
|
||||
>
|
||||
<span>
|
||||
hello
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="categoryTitle"
|
||||
>
|
||||
<span
|
||||
class="ml-2 text-light"
|
||||
>
|
||||
TestCategory
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="teamTitle"
|
||||
>
|
||||
Dunder Mifflin
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -15,7 +15,6 @@
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -24,11 +23,36 @@
|
||||
|
||||
.resultTitle {
|
||||
max-width: 60%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.teamTitle {
|
||||
right: auto;
|
||||
margin-left: auto;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
.categoryTitle,
|
||||
.teamTitle {
|
||||
max-width: 20%;
|
||||
width: max-content;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.categoryTitle {
|
||||
margin-left: auto;
|
||||
flex: 2;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.resultTitle {
|
||||
flex: 6;
|
||||
}
|
||||
}
|
||||
|
@ -4,22 +4,40 @@
|
||||
import React from 'react'
|
||||
|
||||
import {MockStoreEnhanced} from "redux-mock-store"
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
import {render} from "@testing-library/react"
|
||||
import {render, screen, act} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import {createMemoryHistory, History} from "history"
|
||||
|
||||
import {Router} from "react-router-dom"
|
||||
|
||||
import octoClient from '../../octoClient'
|
||||
|
||||
import {Team} from "../../store/teams"
|
||||
import {TestBlockFactory} from "../../test/testBlockFactory"
|
||||
|
||||
import {mockStateStore, wrapDNDIntl} from "../../testUtils"
|
||||
|
||||
import {Board, createBoard} from '../../../../webapp/src/blocks/board'
|
||||
|
||||
import {Utils} from '../../../../webapp/src/utils'
|
||||
|
||||
import BoardSwitcherDialog from "./boardSwitcherDialog"
|
||||
|
||||
jest.mock('../../octoClient')
|
||||
const mockedOctoClient = mocked(octoClient, true)
|
||||
jest.mock('../../../../webapp/src/utils')
|
||||
const mockedUtils = mocked(Utils, true)
|
||||
|
||||
const wait = (ms: number) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
setTimeout(resolve, ms)
|
||||
})
|
||||
}
|
||||
|
||||
describe('component/BoardSwitcherDialog', () => {
|
||||
const team1: Team = {
|
||||
@ -47,6 +65,9 @@ describe('component/BoardSwitcherDialog', () => {
|
||||
teams: {
|
||||
allTeams: [team1, team2],
|
||||
current: team1,
|
||||
},
|
||||
sidebar: {
|
||||
categoryAttributes: [],
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,4 +94,71 @@ describe('component/BoardSwitcherDialog', () => {
|
||||
const {container} = render(component)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('find one, default category', async () => {
|
||||
const onCloseHandler = jest.fn()
|
||||
const newBoard = createBoard({id: 'found-board', title: 'hello', teamId: 'team-id-1'} as Board)
|
||||
|
||||
mockedOctoClient.searchAll.mockResolvedValue([newBoard])
|
||||
mockedUtils.uuid.mockReturnValue('Really a UUID')
|
||||
|
||||
const component = wrapDNDIntl(
|
||||
<Router history={history}>
|
||||
<ReduxProvider store={store}>
|
||||
<BoardSwitcherDialog onClose={onCloseHandler}/>
|
||||
</ReduxProvider>
|
||||
</Router>
|
||||
)
|
||||
|
||||
const {container} = render(component)
|
||||
await act(async () => {
|
||||
const inputElement = screen.getByPlaceholderText('Search for boards')
|
||||
await userEvent.type(inputElement, 'test')
|
||||
await wait(300)
|
||||
})
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('find one, with custom category', async () => {
|
||||
const onCloseHandler = jest.fn()
|
||||
|
||||
const myState = {
|
||||
users: {
|
||||
me: me,
|
||||
},
|
||||
teams: {
|
||||
allTeams: [team1, team2],
|
||||
current: team1,
|
||||
},
|
||||
sidebar: {
|
||||
categoryAttributes: [{
|
||||
name: 'TestCategory',
|
||||
boardIDs: ['found-board']
|
||||
},],
|
||||
}
|
||||
}
|
||||
|
||||
const myStore = mockStateStore([], myState)
|
||||
|
||||
const newBoard = createBoard({id: 'found-board', title: 'hello', teamId: 'team-id-1'} as Board)
|
||||
|
||||
mockedOctoClient.searchAll.mockResolvedValue([newBoard])
|
||||
mockedUtils.uuid.mockReturnValue('Really a UUID')
|
||||
|
||||
const component = wrapDNDIntl(
|
||||
<Router history={history}>
|
||||
<ReduxProvider store={myStore}>
|
||||
<BoardSwitcherDialog onClose={onCloseHandler}/>
|
||||
</ReduxProvider>
|
||||
</Router>
|
||||
)
|
||||
|
||||
const {container} = render(component)
|
||||
await act(async () => {
|
||||
const inputElement = screen.getByPlaceholderText('Search for boards')
|
||||
await userEvent.type(inputElement, 'test')
|
||||
await wait(300)
|
||||
})
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
@ -18,6 +18,12 @@ import {Utils} from '../../utils'
|
||||
import {BoardTypeOpen, BoardTypePrivate} from '../../blocks/board'
|
||||
import { Constants } from '../../constants'
|
||||
|
||||
import {
|
||||
CategoryBoards,
|
||||
DefaultCategory,
|
||||
getSidebarCategories,
|
||||
} from '../../store/sidebar'
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
}
|
||||
@ -29,6 +35,7 @@ const BoardSwitcherDialog = (props: Props): JSX.Element => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const [IDs, setIDs] = useState<any>({})
|
||||
const intl = useIntl()
|
||||
const partialCategories = useAppSelector<Array<CategoryBoards>>(getSidebarCategories)
|
||||
const team = useAppSelector(getCurrentTeam)
|
||||
const me = useAppSelector(getMe)
|
||||
const title = intl.formatMessage({id: 'FindBoardsDialog.Title', defaultMessage: 'Find Boards'})
|
||||
@ -69,6 +76,15 @@ const BoardSwitcherDialog = (props: Props): JSX.Element => {
|
||||
return items.map((item, i) => {
|
||||
const resultTitle = item.title || untitledBoardTitle
|
||||
const teamTitle = teamsById[item.teamId].title
|
||||
|
||||
let categoryTitle = DefaultCategory.name
|
||||
for(const category of partialCategories){
|
||||
if(category.boardIDs.find(id => id === item.id)){
|
||||
categoryTitle = category.name
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
setIDs((prevIDs: any) => ({
|
||||
...prevIDs,
|
||||
@ -83,7 +99,12 @@ const BoardSwitcherDialog = (props: Props): JSX.Element => {
|
||||
>
|
||||
{item.type === BoardTypeOpen && <Globe/>}
|
||||
{item.type === BoardTypePrivate && <LockOutline/>}
|
||||
<span className='resultTitle'>{resultTitle}</span>
|
||||
<div className='resultTitle'>
|
||||
<span>{resultTitle}</span>
|
||||
</div>
|
||||
<div className='categoryTitle'>
|
||||
<span className='ml-2 text-light'>{categoryTitle}</span>
|
||||
</div>
|
||||
<span className='teamTitle'>{teamTitle}</span>
|
||||
</div>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user