1
0
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:
Scott Bishel 2022-08-10 03:18:59 -06:00 committed by GitHub
parent f2b5c724bf
commit c28620594a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 366 additions and 4 deletions

View File

@ -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>
`;

View File

@ -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;
}
}

View File

@ -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()
})
})

View File

@ -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>
)