Revert "Add (updated) mention autocomplete to post comments (#13061)" (#13116)

This reverts commit a23caead07.
This commit is contained in:
Nick Taylor 2021-03-25 10:33:51 -04:00 committed by GitHub
parent af5a391429
commit e28bde043c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 19 additions and 261 deletions

View file

@ -306,11 +306,8 @@ function handleFocus(event) {
area.blur();
} else {
var form = event.target.closest(".comment-form");
if (form) {
form.classList.add("comment-form--initiated");
}
form.classList.add("comment-form--initiated");
handleSizeChange(event);
window.Forem.initializeMentionAutocompleteTextArea(area);
}
}
@ -437,13 +434,14 @@ function handleSizeChange(event) {
function handleButtonsActivation(event) {
var textarea = event.target;
var commentForm = textarea.closest('.comment-form');
if (commentForm) {
var buttons = textarea.closest('.comment-form').getElementsByClassName('js-btn-enable');
Array.from(buttons).forEach(function(button) {
button.disabled = textarea.value.length === 0;
});
};
var buttons = textarea.closest('.comment-form').getElementsByClassName('js-btn-enable');
Array.from(buttons).forEach(function(button) {
if (textarea.value.length > 0) {
button.disabled = false;
} else {
button.disabled = true;
}
});
}
function validateField(event) {

View file

@ -35,8 +35,6 @@ const replaceTextArea = (originalNodeToReplace, newNode) => {
originalNodeToReplace,
'',
).cssText;
// Make sure no transition replays when the new textarea is mounted
newNode.style.transition = 'none';
// We need to manually remove the element, as Preact's diffing algorithm won't replace it in render
originalNodeToReplace.remove();
@ -195,7 +193,7 @@ export const MentionAutocompleteTextArea = ({
const textWithSelection = `${textContent.substring(
0,
selectionInsertIndex,
)}${username} ${textContent.substring(inputRef.current.selectionStart)}`;
)}${username}${textContent.substring(inputRef.current.selectionStart)}`;
// Clear the current search
setSearchTerm('');
@ -205,8 +203,8 @@ export const MentionAutocompleteTextArea = ({
// Update the text area value
setTextContent(textWithSelection);
// Update the cursor to directly after the selection (+2 accounts for the @ sign, and adding a space after the username)
const newCursorPosition = selectionInsertIndex + username.length + 2;
// Update the cursor to directly after the selection
const newCursorPosition = selectionInsertIndex + username.length + 1;
setCursorPosition(newCursorPosition);
};

View file

@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<MentionAutocompleteTextArea /> should render 1`] = `"<div aria-live=\\"polite\\" class=\\"screen-reader-only\\"></div><div id=\\"combobox-container\\" class=\\"crayons-autocomplete\\" data-reach-combobox=\\"\\" data-state=\\"idle\\"><textarea aria-autocomplete=\\"both\\" aria-controls=\\"listbox--combobox-container\\" aria-expanded=\\"false\\" aria-haspopup=\\"listbox\\" role=\\"combobox\\" data-mention-autocomplete-active=\\"true\\" data-reach-combobox-input=\\"\\" data-state=\\"idle\\" aria-label=\\"test text area\\" id=\\"test-text-area\\" style=\\"font: -webkit-small-control; text-rendering: auto; letter-spacing: normal; word-spacing: normal; line-height: normal; text-transform: none; text-indent: 0; text-shadow: none; display: inline-block; text-align: start; background-color: white; border: 1px solid; flex-direction: column; resize: auto; cursor: auto; padding: 2px; white-space: pre-wrap; word-wrap: break-word; visibility: visible; transition: none;\\"></textarea></div>"`;
exports[`<MentionAutocompleteTextArea /> should render 1`] = `"<div aria-live=\\"polite\\" class=\\"screen-reader-only\\"></div><div id=\\"combobox-container\\" class=\\"crayons-autocomplete\\" data-reach-combobox=\\"\\" data-state=\\"idle\\"><textarea aria-autocomplete=\\"both\\" aria-controls=\\"listbox--combobox-container\\" aria-expanded=\\"false\\" aria-haspopup=\\"listbox\\" role=\\"combobox\\" data-mention-autocomplete-active=\\"true\\" data-reach-combobox-input=\\"\\" data-state=\\"idle\\" aria-label=\\"test text area\\" id=\\"test-text-area\\" style=\\"font: -webkit-small-control; text-rendering: auto; letter-spacing: normal; word-spacing: normal; line-height: normal; text-transform: none; text-indent: 0; text-shadow: none; display: inline-block; text-align: start; background-color: white; border: 1px solid; flex-direction: column; resize: auto; cursor: auto; padding: 2px; white-space: pre-wrap; word-wrap: break-word; visibility: visible;\\"></textarea></div>"`;

View file

@ -5,39 +5,6 @@ import {
initializeTouchDevice,
} from '../topNavigation/utilities';
// Namespace for functions which need to be accessed in plain JS initializers
window.Forem = {};
window.Forem.initializeMentionAutocompleteTextArea = async (
originalTextArea,
) => {
const parentContainer = originalTextArea.parentElement;
const alreadyInitialized = parentContainer.id === 'combobox-container';
if (alreadyInitialized) {
return;
}
const [
{ MentionAutocompleteTextArea },
{ fetchSearch },
{ render, h },
] = await Promise.all([
import('@crayons/MentionAutocompleteTextArea'),
import('@utilities/search'),
import('preact'),
]);
render(
<MentionAutocompleteTextArea
replaceElement={originalTextArea}
fetchSuggestions={(username) => fetchSearch('usernames', { username })}
/>,
parentContainer,
originalTextArea,
);
};
window.showModal = async ({
title,
contentSelector,

View file

@ -101,7 +101,11 @@ const getIndexOfCurrentWordAutocompleteSymbol = (content, selectionIndex) => {
const currentCharacter = content.charAt(selectionIndex);
const previousCharacter = content.charAt(selectionIndex - 1);
if (selectionIndex !== 0 && ![' ', '', '\n'].includes(previousCharacter)) {
if (
selectionIndex !== 0 &&
previousCharacter !== ' ' &&
previousCharacter !== ''
) {
return getIndexOfCurrentWordAutocompleteSymbol(content, selectionIndex - 1);
}

View file

@ -76,7 +76,7 @@
<%= f.hidden_field :commentable_type, value: json_data["comment"]["commentable"]["class"]["name"] %>
<%= f.hidden_field :parent_id, value: json_data["comment"]["id"] %>
<%= f.text_area :body_markdown, id: "comment-textarea-for-#{json_data['comment']['id']}", class: "crayons-textfield mb-2 resize-y", placeholder: "Reply...", 'aria-label': "Reply to a comment...", onfocus: "handleFocus(event)" %>
<%= f.text_area :body_markdown, id: "comment-textarea-for-#{json_data['comment']['id']}", class: "crayons-textfield mb-2 resize-y", placeholder: "Reply...", 'aria-label': "Reply to a comment..." %>
<div class="actions">
<button type="submit" class="crayons-btn comment-action-button" onclick="validateField(event)">Submit</button>
</div>

View file

@ -1 +0,0 @@
{ "result": [] }

View file

@ -1,39 +0,0 @@
{
"result": [
{
"username": "search_user_1",
"name": "Search user 1",
"profile_image_90": "/images/apple-icon.png"
},
{
"username": "search_user_2",
"name": "Search user 2",
"profile_image_90": "/images/apple-icon.png"
},
{
"username": "search_user_3",
"name": "Search user 3",
"profile_image_90": "/images/apple-icon.png"
},
{
"username": "search_user_4",
"name": "Search user 4",
"profile_image_90": "/images/apple-icon.png"
},
{
"username": "search_user_5",
"name": "Search user 5",
"profile_image_90": "/images/apple-icon.png"
},
{
"username": "search_user_6",
"name": "Search user 6",
"profile_image_90": "/images/apple-icon.png"
},
{
"username": "search_user_7",
"name": "Search user 7",
"profile_image_90": "/images/apple-icon.png"
}
]
}

View file

@ -1,169 +0,0 @@
describe('Comment on articles', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/articleEditorV1User.json').as('user');
cy.get('@user').then((user) => {
cy.loginUser(user).then(() => {
cy.visit('/');
cy.findAllByText('Test article').last().click();
});
});
});
it('should comment on an article with user mention autocomplete suggesting max 6 users', () => {
cy.intercept(
{ method: 'GET', url: '/search/usernames' },
{ fixture: 'search/usernames.json' },
);
cy.findByLabelText('Add a comment to the discussion').click();
cy.findByRole('combobox');
cy.findByLabelText('Add a comment to the discussion').as(
'autocompleteCommentBox',
);
cy.get('@autocompleteCommentBox').type('Some text @s');
cy.findByText('Type to search for a user').should('exist');
cy.get('@autocompleteCommentBox').type('earch');
const expectedUsernames = [
'@search_user_1',
'@search_user_2',
'@search_user_2',
'@search_user_3',
'@search_user_4',
'@search_user_5',
'@search_user_6',
];
expectedUsernames.forEach((name) => cy.findByText(name).should('exist'));
cy.findByText('@search_user_7').should('not.exist');
cy.findByText('@search_user_3').click();
cy.findByDisplayValue('Some text @search_user_3').should('exist');
});
it('should select a mention autocomplete suggestion by keyboard', () => {
cy.intercept(
{ method: 'GET', url: '/search/usernames' },
{ fixture: 'search/usernames.json' },
);
cy.findByLabelText('Add a comment to the discussion').click();
cy.findByRole('combobox');
cy.findByLabelText('Add a comment to the discussion').type(
'Some text @search_user{downarrow}{enter}',
);
cy.findByDisplayValue('Some text @search_user_1').should('exist');
});
it('should accept entered comment text without user mention if no autocomplete suggestions', () => {
cy.intercept(
{ method: 'GET', url: '/search/usernames' },
{ fixture: 'search/emptyUsernamesSearch.json' },
);
cy.findByLabelText('Add a comment to the discussion').click();
cy.findByRole('combobox');
cy.findByLabelText('Add a comment to the discussion').as(
'autocompleteCommentBox',
);
cy.get('@autocompleteCommentBox').type('Some text @user');
cy.findByText('No results found').should('exist');
cy.get('@autocompleteCommentBox').type(' ');
cy.findByText('No results found').should('not.exist');
cy.findByDisplayValue('Some text @user').should('exist');
});
it('should stop showing mention autocomplete suggestions on text delete', () => {
cy.intercept(
{ method: 'GET', url: '/search/usernames' },
{ fixture: 'search/usernames.json' },
);
cy.findByLabelText('Add a comment to the discussion').click();
cy.findByRole('combobox');
cy.findByLabelText('Add a comment to the discussion').as(
'autocompleteCommentBox',
);
cy.get('@autocompleteCommentBox').type('Some text @se');
cy.findByText('@search_user_1').should('exist');
cy.get('@autocompleteCommentBox').type('{backspace}{backspace}{backspace}');
cy.findByText('@search_user_1').should('not.exist');
});
it('should resume search suggestions when user types after deleting', () => {
cy.intercept(
{ method: 'GET', url: '/search/usernames' },
{ fixture: 'search/usernames.json' },
);
cy.findByLabelText('Add a comment to the discussion').click();
cy.findByRole('combobox');
cy.findByLabelText('Add a comment to the discussion').as(
'autocompleteCommentBox',
);
cy.get('@autocompleteCommentBox').type('Some text @se');
cy.get('@autocompleteCommentBox').type('{backspace}{backspace}');
cy.findByText('@search_user_1').should('not.exist');
cy.get('@autocompleteCommentBox').type('se');
cy.findByText('@search_user_1').should('exist');
});
it('should close the autocomplete suggestions on Escape press', () => {
cy.intercept(
{ method: 'GET', url: '/search/usernames' },
{ fixture: 'search/usernames.json' },
);
cy.findByLabelText('Add a comment to the discussion').click();
cy.findByRole('combobox');
cy.findByLabelText('Add a comment to the discussion').as(
'autocompleteCommentBox',
);
cy.get('@autocompleteCommentBox').type('Some text @search');
cy.findByText('@search_user_1').should('be.visible');
cy.get('@autocompleteCommentBox').type('{Esc}');
cy.findByText('@search_user_1').should('not.be.visible');
});
it('should reply to a comment with user mention autocomplete', () => {
cy.intercept(
{ method: 'GET', url: '/search/usernames' },
{ fixture: 'search/usernames.json' },
);
cy.findByLabelText('Add a comment to the discussion').click();
cy.findByRole('combobox');
cy.findByLabelText('Add a comment to the discussion').type('first comment');
cy.findByRole('button', { name: /Submit/ }).click();
cy.findByRole('link', { name: /Reply/ }).click();
cy.findByRole('combobox', { name: /Reply to a comment/ }).as(
'replyCombobox',
);
cy.get('@replyCombobox').click();
cy.get('@replyCombobox').type('Some text @search_user');
cy.findByText('@search_user_1').click();
cy.findByDisplayValue('Some text @search_user_1');
});
});