diff --git a/playwright/e2e/smart-picker.spec.ts b/playwright/e2e/smart-picker.spec.ts index 06708d0dbed..e5ce2d7c6c3 100644 --- a/playwright/e2e/smart-picker.spec.ts +++ b/playwright/e2e/smart-picker.spec.ts @@ -34,3 +34,27 @@ test('Insert Link', async ({ editor }) => { await editor.referencePicker.press('Enter') await expect(editor.content.getByRole('link')).toContainText('github.com') }) + +test('Files provider is renamed to "Link a file"', async ({ editor }) => { + await editor.type('/') + await expect(editor.getSuggestion('Link a file')).toBeVisible() +}) + +test('Important items appear before remaining items', async ({ editor }) => { + await editor.type('/') + await expect(editor.getSuggestion('To-Do list')).toBeVisible() + const allTexts = await editor.suggestions + .locator('.suggestion-list__item') + .allTextContents() + + const todoIdx = allTexts.findIndex((t) => t.includes('To-Do list')) + const tableIdx = allTexts.findIndex((t) => t.includes('Table')) + const filesIdx = allTexts.findIndex((t) => t.includes('Link a file')) + const heading1Idx = allTexts.findIndex((t) => t.includes('Heading 1')) + + expect(todoIdx).toBeLessThan(filesIdx) + expect(todoIdx).toBeLessThan(heading1Idx) + expect(tableIdx).toBeLessThan(filesIdx) + expect(tableIdx).toBeLessThan(heading1Idx) + expect(filesIdx).toBeLessThan(heading1Idx) +}) diff --git a/src/components/Suggestion/LinkPicker/suggestions.js b/src/components/Suggestion/LinkPicker/suggestions.js index ecf89fcf19d..0cbb1a207d5 100644 --- a/src/components/Suggestion/LinkPicker/suggestions.js +++ b/src/components/Suggestion/LinkPicker/suggestions.js @@ -16,11 +16,14 @@ import { getIsActive } from '../../Menu/utils.js' import createSuggestions from '../suggestions.js' import { getMenuEntries } from './../../Menu/entries.ts' +const suggestGroupImportant = t('text', 'Suggestions') const suggestGroupFormat = t('text', 'Formatting') const suggestGroupPicker = t('text', 'Smart picker') -const important = ['task-list', 'table'] -const excluded = ['undo', 'redo', 'outline', 'emoji-picker'] +const important = ['task-list', 'table', 'callout-info'] +const excludedFormatting = ['undo', 'redo', 'outline', 'emoji-picker'] + +const isImportant = (item) => important.includes(item.key) || important.includes(item.providerId) /** * @@ -34,27 +37,17 @@ function isValidUrl(url) { } } -/** - * - * @param {Array} list of menu entries - */ -function sortImportantFirst(list) { - return [ - ...list.filter((e) => important.indexOf(e.key) > -1), - ...list.filter((e) => important.indexOf(e.key) === -1), - ] -} - /** * * @param {string} query to filter by + * @param {object} editor the editor instance */ -function formattingSuggestions(query) { +function formattingItems(query, editor) { const menuEntries = getMenuEntries(false, false) - return sortImportantFirst([ + return [ ...menuEntries.find((e) => e.key === 'headings').children, ...menuEntries.find((e) => e.key === 'lists').children, - ...menuEntries.filter((e) => e.action && !excluded.includes(e.key)), + ...menuEntries.filter((e) => e.action && !excludedFormatting.includes(e.key)), ...menuEntries.find((e) => e.key === 'blocks').children, { ...menuEntries.find((e) => e.key === 'emoji-picker'), @@ -62,7 +55,33 @@ function formattingSuggestions(query) { }, ] .filter((e) => e?.label?.toLowerCase?.()?.includes(query.toLowerCase())) - .map((e) => ({ ...e, suggestGroup: suggestGroupFormat }))) + .filter(({ action, isActive }) => { + const canRunState = action(editor?.can()) + const isActiveState + = isActive && getIsActive({ isActive }, editor) + return canRunState && !isActiveState + }) +} + +/** + * @param {string} query to filter by + */ +function pickerItems(query) { + return searchProvider(query) + .map((p) => { + let label = p.title + if (p.id === 'files') { + // Rename "Files" to "Link a file", less ambiguous + label = t('text', 'Link a file') + } + return { + label, + icon: p.icon_url, + providerId: p.id, + order: p.order, + } + }) + .filter((e) => e?.label?.toLowerCase?.()?.includes(query.toLowerCase())) } export default () => createSuggestions({ @@ -101,23 +120,25 @@ export default () => createSuggestions({ }) }, items: ({ editor, query }) => { + const pickers = pickerItems(query) + const formatting = formattingItems(query, editor) return [ - ...searchProvider(query) - .map((p) => { - return { - suggestGroup: suggestGroupPicker, - label: p.title, - icon: p.icon_url, - providerId: p.id, - } - }) - .filter((e) => e?.label?.toLowerCase?.()?.includes(query.toLowerCase())), - ...formattingSuggestions(query).filter(({ action, isActive }) => { - const canRunState = action(editor?.can()) - const isActiveState - = isActive && getIsActive({ isActive }, editor) - return canRunState && !isActiveState - }), + // pickers with order -1, then important pickers, then important formatting + ...[ + ...pickers.filter((e) => e.order === -1), + ...pickers.filter((e) => e.order !== -1 && isImportant(e)), + ...formatting.filter(isImportant), + ].map((e) => ({ ...e, suggestGroup: suggestGroupImportant })), + + // Smart picker: remaining (non-important) pickers + ...pickers + .filter((e) => e.order !== -1 && !isImportant(e)) + .map((e) => ({ ...e, suggestGroup: suggestGroupPicker })), + + // Formatting: remaining (non-important) formatting entries + ...formatting + .filter((e) => !isImportant(e)) + .map((e) => ({ ...e, suggestGroup: suggestGroupFormat })), ] }, })