1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-01-23 18:34:02 +02:00

All team users autocomplete new (#3016)

* WIP

* Made search debounced

* Lint fiX

* Fixed tests

* Calledn un-debounced version on first load
This commit is contained in:
Harshil Sharma 2022-05-10 11:06:04 +05:30 committed by GitHub
parent 72eb7fa5a4
commit 859bc53cbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 8 deletions

View File

@ -15,6 +15,8 @@ import {mockDOM, wrapDNDIntl, mockStateStore} from '../../testUtils'
import {Utils} from '../../utils'
import {TestBlockFactory} from "../../test/testBlockFactory"
import TextElement from './textElement'
jest.mock('../../utils')
@ -41,6 +43,9 @@ describe('components/content/TextElement', () => {
mockDOM()
})
const board1 = TestBlockFactory.createBoard()
board1.id = 'board-id-1'
const state = {
users: {
boardUsers: {
@ -51,6 +56,12 @@ describe('components/content/TextElement', () => {
5: {username: 'g'},
},
},
boards: {
current: 'board-id-1',
boards: {
[board1.id]: board1,
}
}
}
const store = mockStateStore([], state)

View File

@ -65,6 +65,9 @@ describe('components/contentBlock', () => {
addBlock: jest.fn(),
})
const board1 = TestBlockFactory.createBoard()
board1.id = 'board-id-1'
const state = {
users: {
boardUsers: {
@ -75,6 +78,12 @@ describe('components/contentBlock', () => {
5: {username: 'g'},
},
},
boards: {
current: 'board-id-1',
boards: {
[board1.id]: board1,
}
}
}
const store = mockStateStore([], state)

View File

@ -7,6 +7,8 @@ import {Provider as ReduxProvider} from 'react-redux'
import {mockDOM, wrapDNDIntl, mockStateStore} from '../testUtils'
import {TestBlockFactory} from "../test/testBlockFactory"
import {MarkdownEditor} from './markdownEditor'
jest.mock('../utils')
@ -16,6 +18,10 @@ jest.mock('draft-js/lib/generateRandomKey', () => () => '123')
describe('components/markdownEditor', () => {
beforeAll(mockDOM)
beforeEach(jest.clearAllMocks)
const board1 = TestBlockFactory.createBoard()
board1.id = 'board-id-1'
const state = {
users: {
boardUsers: {
@ -26,6 +32,12 @@ describe('components/markdownEditor', () => {
5: {username: 'g'},
},
},
boards: {
current: 'board-id-1',
boards: {
[board1.id]: board1,
}
}
}
const store = mockStateStore([], state)
test('should match snapshot', async () => {

View File

@ -3,10 +3,7 @@
import Editor from '@draft-js-plugins/editor'
import createEmojiPlugin from '@draft-js-plugins/emoji'
import '@draft-js-plugins/emoji/lib/plugin.css'
import createMentionPlugin, {
defaultSuggestionsFilter,
MentionData,
} from '@draft-js-plugins/mention'
import createMentionPlugin from '@draft-js-plugins/mention'
import '@draft-js-plugins/mention/lib/plugin.css'
import {ContentState, DraftHandleValue, EditorState, getDefaultKeyBinding} from 'draft-js'
import React, {
@ -15,6 +12,8 @@ import React, {
useState,
} from 'react'
import {debounce} from "lodash"
import {useAppSelector} from '../../store/hooks'
import {IUser} from '../../user'
import {getBoardUsersList} from '../../store/users'
@ -22,10 +21,20 @@ import createLiveMarkdownPlugin from '../live-markdown-plugin/liveMarkdownPlugin
import './markdownEditorInput.scss'
import {BoardTypeOpen} from "../../blocks/board"
import {getCurrentBoard} from "../../store/boards"
import octoClient from "../../octoClient"
import Entry from './entryComponent/entryComponent'
const imageURLForUser = (window as any).Components?.imageURLForUser
type MentionUser = {
name: string
avatar: string
is_bot: boolean
}
type Props = {
onChange?: (text: string) => void
onFocus?: () => void
@ -38,9 +47,38 @@ type Props = {
const MarkdownEditorInput = (props: Props): ReactElement => {
const {onChange, onFocus, onBlur, initialText, id, isEditing} = props
const boardUsers = useAppSelector<IUser[]>(getBoardUsersList)
const mentions: MentionData[] = useMemo(() => boardUsers.map((user) => ({name: user.username, avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`, is_bot: user.is_bot})), [boardUsers])
const board = useAppSelector(getCurrentBoard)
const ref = useRef<Editor>(null)
const [suggestions, setSuggestions] = useState<Array<MentionUser>>([])
const loadSuggestions = async (term: string) => {
let users: Array<IUser>
if (board && board.type === BoardTypeOpen) {
users = await octoClient.searchTeamUsers(term)
} else {
users = boardUsers
}
const mentions = users.map(
(user) => ({
name: user.username,
avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`,
is_bot: user.is_bot}
))
setSuggestions(mentions)
}
const debouncedLoadSuggestion = useMemo(() => debounce(loadSuggestions, 200), [])
useEffect(() => {
// Get the ball rolling. Searching for empty string
// returns first 10 users in alphabetical order.
loadSuggestions('')
}, [])
const generateEditorState = (text?: string) => {
const state = EditorState.createWithContent(ContentState.createFromText(text || ''))
return EditorState.moveSelectionToEnd(state)
@ -67,7 +105,6 @@ const MarkdownEditorInput = (props: Props): ReactElement => {
const [isMentionPopoverOpen, setIsMentionPopoverOpen] = useState(false)
const [isEmojiPopoverOpen, setIsEmojiPopoverOpen] = useState(false)
const [suggestions, setSuggestions] = useState(mentions)
const {MentionSuggestions, plugins, EmojiSuggestions} = useMemo(() => {
const mentionPlugin = createMentionPlugin({mentionPrefix: '@'})
@ -144,8 +181,8 @@ const MarkdownEditorInput = (props: Props): ReactElement => {
}, [])
const onSearchChange = useCallback(({value}: { value: string }) => {
setSuggestions(defaultSuggestionsFilter(value, mentions))
}, [mentions])
debouncedLoadSuggestion(value)
}, [suggestions])
let className = 'MarkdownEditorInput'
if (!isEditing) {