From f393dd2bbc245e94e7306d2f8320d005f1f9121e Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Wed, 17 Mar 2021 15:59:38 -0400 Subject: [PATCH] Revert "Update storybook autocomplete component (#12980)" (#13027) --- app/assets/stylesheets/base/helpers.scss | 9 - .../stylesheets/components/autocomplete.scss | 3 +- .../MentionAutocomplete.jsx | 141 ++++++++++ .../MentionAutocompleteCombobox.jsx | 177 ++++++++++++ .../MentionAutocomplete.stories.jsx | 75 +++++ .../__stories__/mention-autocomplete.md | 14 + .../__tests__/MentionAutocomplete.test.jsx | 38 +++ .../MentionAutocompleteCombobox.test.jsx | 110 ++++++++ .../MentionAutocompleteCombobox.test.jsx.snap | 3 + .../crayons/MentionAutocomplete/index.js | 1 + .../MentionAutocompleteTextArea.jsx | 265 ------------------ .../MentionAutocompleteTextArea.stories.jsx | 99 ------- .../__stories__/mention-autocomplete.md | 18 -- .../MentionAutocompleteTextArea.test.jsx | 52 ---- .../MentionAutocompleteTextArea.test.jsx.snap | 3 - .../MentionAutocompleteTextArea/index.js | 1 - .../utilities/__tests__/textAreaUtils.test.js | 68 ----- app/javascript/utilities/textAreaUtils.js | 73 +---- config/webpack/environment.js | 2 - 19 files changed, 565 insertions(+), 587 deletions(-) create mode 100644 app/javascript/crayons/MentionAutocomplete/MentionAutocomplete.jsx create mode 100644 app/javascript/crayons/MentionAutocomplete/MentionAutocompleteCombobox.jsx create mode 100644 app/javascript/crayons/MentionAutocomplete/__stories__/MentionAutocomplete.stories.jsx create mode 100644 app/javascript/crayons/MentionAutocomplete/__stories__/mention-autocomplete.md create mode 100644 app/javascript/crayons/MentionAutocomplete/__tests__/MentionAutocomplete.test.jsx create mode 100644 app/javascript/crayons/MentionAutocomplete/__tests__/MentionAutocompleteCombobox.test.jsx create mode 100644 app/javascript/crayons/MentionAutocomplete/__tests__/__snapshots__/MentionAutocompleteCombobox.test.jsx.snap create mode 100644 app/javascript/crayons/MentionAutocomplete/index.js delete mode 100644 app/javascript/crayons/MentionAutocompleteTextArea/MentionAutocompleteTextArea.jsx delete mode 100644 app/javascript/crayons/MentionAutocompleteTextArea/__stories__/MentionAutocompleteTextArea.stories.jsx delete mode 100644 app/javascript/crayons/MentionAutocompleteTextArea/__stories__/mention-autocomplete.md delete mode 100644 app/javascript/crayons/MentionAutocompleteTextArea/__tests__/MentionAutocompleteTextArea.test.jsx delete mode 100644 app/javascript/crayons/MentionAutocompleteTextArea/__tests__/__snapshots__/MentionAutocompleteTextArea.test.jsx.snap delete mode 100644 app/javascript/crayons/MentionAutocompleteTextArea/index.js delete mode 100644 app/javascript/utilities/__tests__/textAreaUtils.test.js diff --git a/app/assets/stylesheets/base/helpers.scss b/app/assets/stylesheets/base/helpers.scss index f3a664adb..abc7db77e 100644 --- a/app/assets/stylesheets/base/helpers.scss +++ b/app/assets/stylesheets/base/helpers.scss @@ -36,15 +36,6 @@ border-top: var(--su-7) solid var(--accent-brand); } -.screen-reader-only { - position: absolute; - left: -10000px; - top: auto; - width: 1px; - height: 1px; - overflow: hidden; -} - @media print { .print-hidden { display: none; diff --git a/app/assets/stylesheets/components/autocomplete.scss b/app/assets/stylesheets/components/autocomplete.scss index 933a4afe3..e3d3323c1 100644 --- a/app/assets/stylesheets/components/autocomplete.scss +++ b/app/assets/stylesheets/components/autocomplete.scss @@ -2,9 +2,8 @@ .crayons-autocomplete { &__popover { - max-width: 300px; + min-width: 300px; padding: var(--su-1); - z-index: var(--z-elevate); &[data-reach-popover] { @include generate-box( diff --git a/app/javascript/crayons/MentionAutocomplete/MentionAutocomplete.jsx b/app/javascript/crayons/MentionAutocomplete/MentionAutocomplete.jsx new file mode 100644 index 000000000..3c541c3e8 --- /dev/null +++ b/app/javascript/crayons/MentionAutocomplete/MentionAutocomplete.jsx @@ -0,0 +1,141 @@ +import { h, render } from 'preact'; +import { useState, useEffect, useCallback } from 'preact/hooks'; +import { getCursorXY } from '@utilities/textAreaUtils'; +import { useMediaQuery, BREAKPOINTS } from '@components/useMediaQuery'; + +/** + * A component which listens for an '@' keypress, and encompasses the MentionAutocomplete functionality. + * When an autocomplete suggestion is selected, it is inserted into the textarea. + * + * @param {object} props + * @param {object} props.textAreaRef A reference to the text area where an '@' mention can take place + * @param {function} props.fetchSuggestions The callback to search for suggestions + * + * @example + * + * "`; diff --git a/app/javascript/crayons/MentionAutocompleteTextArea/index.js b/app/javascript/crayons/MentionAutocompleteTextArea/index.js deleted file mode 100644 index 566452145..000000000 --- a/app/javascript/crayons/MentionAutocompleteTextArea/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './MentionAutocompleteTextArea'; diff --git a/app/javascript/utilities/__tests__/textAreaUtils.test.js b/app/javascript/utilities/__tests__/textAreaUtils.test.js deleted file mode 100644 index e8e17979f..000000000 --- a/app/javascript/utilities/__tests__/textAreaUtils.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import { getMentionWordData } from '../textAreaUtils'; - -describe('getMentionWordData', () => { - it('returns userMention false for cursor at start of input', () => { - const inputState = { - selectionStart: 0, - value: 'text with @mention', - }; - - const { isUserMention, indexOfMentionStart } = getMentionWordData( - inputState, - ); - expect(isUserMention).toBe(false); - expect(indexOfMentionStart).toEqual(-1); - }); - - it('returns userMention false for empty input value', () => { - const inputState = { - selectionStart: 10, - value: '', - }; - - const { isUserMention, indexOfMentionStart } = getMentionWordData( - inputState, - ); - expect(isUserMention).toBe(false); - expect(indexOfMentionStart).toEqual(-1); - }); - - it('returns userMention false if no @ symbol exists at start of word', () => { - const inputState = { - selectionStart: 13, - value: 'text with no mention', - }; - - const { isUserMention, indexOfMentionStart } = getMentionWordData( - inputState, - ); - expect(isUserMention).toBe(false); - expect(indexOfMentionStart).toEqual(-1); - }); - - it('returns userMention true and correct index for an @ mention at beginning of input', () => { - const inputState = { - selectionStart: 3, - value: '@mention', - }; - - const { isUserMention, indexOfMentionStart } = getMentionWordData( - inputState, - ); - expect(isUserMention).toBe(true); - expect(indexOfMentionStart).toEqual(0); - }); - - it('returns userMention true and correct index for @ mention in middle of input', () => { - const inputState = { - selectionStart: 13, - value: 'text with @mention', - }; - - const { isUserMention, indexOfMentionStart } = getMentionWordData( - inputState, - ); - expect(isUserMention).toBe(true); - expect(indexOfMentionStart).toEqual(10); - }); -}); diff --git a/app/javascript/utilities/textAreaUtils.js b/app/javascript/utilities/textAreaUtils.js index 4768c44ed..5efbb07d2 100644 --- a/app/javascript/utilities/textAreaUtils.js +++ b/app/javascript/utilities/textAreaUtils.js @@ -11,25 +11,15 @@ * const coordinates = getCursorXY(elementRef.current, elementRef.current.selectionStart) */ export const getCursorXY = (input, selectionPoint) => { - const bodyRect = document.body.getBoundingClientRect(); - const elementRect = input.getBoundingClientRect(); - - const inputY = elementRect.top - bodyRect.top; - const inputX = elementRect.left - bodyRect.left; + const { offsetLeft: inputX, offsetTop: inputY } = input; // create a dummy element with the computed style of the input - const div = document.createElement('div'); + const div = top.document.createElement('div'); const copyStyle = getComputedStyle(input); for (const prop of copyStyle) { div.style[prop] = copyStyle[prop]; } - // set the div to the correct position - div.style['position'] = 'absolute'; - div.style['top'] = `${inputY}px`; - div.style['left'] = `${inputX}px`; - div.style['opacity'] = 0; - // replace whitespace with '.' when filling the dummy element if it's a single line const swap = '.'; const inputValue = @@ -43,75 +33,22 @@ export const getCursorXY = (input, selectionPoint) => { if (input.tagName === 'INPUT') div.style.width = 'auto'; // marker element to obtain caret position - const span = document.createElement('span'); + const span = top.document.createElement('span'); // give the span the textContent of remaining content so that the recreated dummy element is as close as possible span.textContent = inputValue.substr(selectionPoint) || '.'; // append the span marker to the div and the dummy element to the body div.appendChild(span); - document.body.appendChild(div); + top.document.body.appendChild(div); // get the marker position, this is the caret position top and left relative to the input const { offsetLeft: spanX, offsetTop: spanY } = span; // remove dummy element - document.body.removeChild(div); - + top.document.body.removeChild(div); // return object with the x and y of the caret. account for input positioning so that you don't need to wrap the input return { x: inputX + spanX, y: inputY + spanY, }; }; - -/** - * A helper function that searches back to the beginning of the currently typed word (indicated by cursor position) and verifies whether it begins with an '@' symbol for user mention - * - * @param {element} textArea The text area or input to inspect the current word of - * @returns {{isUserMention: boolean, indexOfMentionStart: number}} Object with the word's mention data - * - * @example - * const { isUserMention, indexOfMentionStart } = getMentionWordData(textArea); - * if (isUserMention) { - * // Do something - * } - */ -export const getMentionWordData = (textArea) => { - const { selectionStart, value: valueBeforeKeystroke } = textArea; - - if (selectionStart === 0 || valueBeforeKeystroke === '') { - return { - isUserMention: false, - indexOfMentionStart: -1, - }; - } - - const indexOfAutocompleteStart = getIndexOfCurrentWordAutocompleteSymbol( - valueBeforeKeystroke, - selectionStart, - ); - - return { - isUserMention: indexOfAutocompleteStart !== -1, - indexOfMentionStart: indexOfAutocompleteStart, - }; -}; - -const getIndexOfCurrentWordAutocompleteSymbol = (content, selectionIndex) => { - const currentCharacter = content.charAt(selectionIndex); - const previousCharacter = content.charAt(selectionIndex - 1); - - if ( - selectionIndex !== 0 && - previousCharacter !== ' ' && - previousCharacter !== '' - ) { - return getIndexOfCurrentWordAutocompleteSymbol(content, selectionIndex - 1); - } - - if (currentCharacter === '@') { - return selectionIndex; - } - - return -1; -}; diff --git a/config/webpack/environment.js b/config/webpack/environment.js index ed747673c..842312300 100644 --- a/config/webpack/environment.js +++ b/config/webpack/environment.js @@ -26,8 +26,6 @@ environment.splitChunks((config) => { __dirname, '../../app/javascript/shared/components', ), - react: 'preact/compat', - 'react-dom': 'preact/compat', }, }, };