Skip to content

Commit 0f15023

Browse files
committed
add tests
1 parent d85444b commit 0f15023

2 files changed

Lines changed: 127 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ The `MentionsInput` component supports the following props:
107107
| onKeyDown | function (event) | empty function | A callback that is invoked when the user presses a key in the mentions input |
108108
| singleLine | boolean | `false` | Renders a single line text input instead of a textarea, if set to `true` |
109109
| autoResize | boolean | `false` | When `true`, resizes the textarea to match its scroll height after each input change (ignored when `singleLine` is `true`) |
110+
| anchorMode | `'caret' \| 'left'` | `'caret'` | Controls whether the overlay follows the caret (`'caret'`) or pins to the control’s leading edge (`'left'`) |
110111
| onMentionBlur | function (event, clickedSuggestion) | `undefined` | Receives an extra `clickedSuggestion` flag when focus left via the suggestions list |
111112
| suggestionsPortalHost | DOM Element | undefined | Render suggestions into the DOM in the supplied host element. |
112113
| inputRef | React ref | undefined | Accepts a React ref to forward to the underlying input element |

src/MentionsInput.spec.tsx

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,7 @@ describe('MentionsInput', () => {
18511851
const suggestions = document.createElement('div')
18521852
const container = document.createElement('div')
18531853
highlighter.style.fontSize = '18px'
1854-
suggestions.style.marginLeft = '5px'
1854+
suggestions.style.marginLeft = '0px'
18551855
suggestions.style.marginTop = '7px'
18561856
Object.defineProperty(highlighter, 'getBoundingClientRect', {
18571857
value: () => ({
@@ -1871,6 +1871,10 @@ describe('MentionsInput', () => {
18711871
instance.highlighterElement = highlighter
18721872
instance.suggestionsElement = suggestions
18731873
instance.containerElement = container
1874+
highlighter.scrollLeft = 5
1875+
highlighter.scrollTop = 3
1876+
Object.defineProperty(highlighter, 'offsetWidth', { value: 200, configurable: true })
1877+
Object.defineProperty(container, 'offsetWidth', { value: 320, configurable: true })
18741878

18751879
const setStateMock = jest.spyOn(instance, 'setState').mockImplementation((update, cb) => {
18761880
const nextState =
@@ -1887,9 +1891,92 @@ describe('MentionsInput', () => {
18871891
})
18881892

18891893
expect(instance.state.suggestionsPosition.position).toBe('fixed')
1890-
expect(typeof instance.state.suggestionsPosition.left).toBe('number')
1894+
expect(instance.state.suggestionsPosition.left).toBe(9)
18911895
expect(typeof instance.state.suggestionsPosition.top).toBe('number')
18921896

1897+
act(() => {
1898+
instance.state.suggestionsPosition = {}
1899+
})
1900+
Object.assign(instance.props, { anchorMode: 'left' })
1901+
1902+
act(() => {
1903+
instance.updateSuggestionsPosition()
1904+
})
1905+
1906+
expect(instance.state.suggestionsPosition.position).toBe('fixed')
1907+
expect(instance.state.suggestionsPosition.left).toBe(4)
1908+
1909+
highlighter.remove()
1910+
suggestions.remove()
1911+
container.remove()
1912+
setStateMock.mockRestore()
1913+
unmount()
1914+
})
1915+
1916+
it('anchors suggestions to the control edge when using anchorMode="left" outside portals.', async () => {
1917+
const ref = React.createRef<MentionsInput>()
1918+
const { unmount } = render(
1919+
<MentionsInput ref={ref} value="" anchorMode="left">
1920+
<Mention trigger="@" data={data} />
1921+
</MentionsInput>
1922+
)
1923+
1924+
await waitFor(() => {
1925+
expect(ref.current).not.toBeNull()
1926+
})
1927+
1928+
const instance = ref.current as unknown as any
1929+
Object.defineProperty(instance, 'resolvePortalHost', {
1930+
value: () => null,
1931+
configurable: true,
1932+
writable: true,
1933+
})
1934+
1935+
const highlighter = document.createElement('div')
1936+
const suggestions = document.createElement('div')
1937+
const container = document.createElement('div')
1938+
highlighter.style.fontSize = '16px'
1939+
Object.defineProperty(highlighter, 'getBoundingClientRect', {
1940+
value: () => ({
1941+
left: 2,
1942+
top: 8,
1943+
right: 0,
1944+
bottom: 0,
1945+
width: 0,
1946+
height: 0,
1947+
}),
1948+
})
1949+
Object.defineProperty(highlighter, 'offsetWidth', { value: 180, configurable: true })
1950+
Object.defineProperty(container, 'offsetWidth', { value: 220, configurable: true })
1951+
Object.defineProperty(suggestions, 'offsetHeight', { value: 40, configurable: true })
1952+
document.body.append(highlighter)
1953+
document.body.append(suggestions)
1954+
document.body.append(container)
1955+
1956+
highlighter.scrollLeft = 14
1957+
highlighter.scrollTop = 5
1958+
1959+
instance.highlighterElement = highlighter
1960+
instance.suggestionsElement = suggestions
1961+
instance.containerElement = container
1962+
instance.state.caretPosition = { left: 32, top: 18 }
1963+
instance.state.suggestionsPosition = {}
1964+
1965+
const setStateMock = jest.spyOn(instance, 'setState').mockImplementation((update, cb) => {
1966+
const nextState =
1967+
typeof update === 'function' ? update(instance.state, instance.props) : update
1968+
Object.assign(instance.state, nextState)
1969+
cb?.()
1970+
})
1971+
1972+
act(() => {
1973+
instance.updateSuggestionsPosition()
1974+
})
1975+
1976+
expect(instance.state.suggestionsPosition.position).toBeUndefined()
1977+
expect(instance.state.suggestionsPosition.left).toBe(0)
1978+
expect(instance.state.suggestionsPosition.right).toBeUndefined()
1979+
18931980
highlighter.remove()
18941981
suggestions.remove()
18951982
container.remove()
@@ -1941,6 +2028,21 @@ describe('MentionsInput', () => {
19412028
}
19422029
;(globalThis as any).ResizeObserver = MockResizeObserver
19432030

2031+
const originalAdd = window.addEventListener
2032+
const originalRemove = window.removeEventListener
2033+
const handlers: Partial<Record<string, EventListener>> = {}
2034+
const addListener = jest
2035+
.spyOn(window, 'addEventListener')
2036+
.mockImplementation((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => {
2037+
handlers[type] = listener as EventListener
2038+
return originalAdd.call(window, type, listener, options)
2039+
})
2040+
const removeListener = jest
2041+
.spyOn(window, 'removeEventListener')
2042+
.mockImplementation((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions) => {
2043+
return originalRemove.call(window, type, listener, options)
2044+
})
2045+
19442046
const bridgeElement = instance.renderMeasurementBridge() as React.ReactElement
19452047
const { unmount: unmountBridge } = render(bridgeElement)
19462048

@@ -1957,10 +2059,32 @@ describe('MentionsInput', () => {
19572059
expect(syncScroll.mock.calls.length).toBe(syncCalls + 1)
19582060
expect(updatePosition.mock.calls.length).toBe(positionCalls + 1)
19592061

2062+
act(() => {
2063+
window.dispatchEvent(new Event('resize'))
2064+
})
2065+
2066+
expect(syncScroll.mock.calls.length).toBeGreaterThan(syncCalls + 1)
2067+
expect(updatePosition.mock.calls.length).toBeGreaterThan(positionCalls + 1)
2068+
2069+
act(() => {
2070+
window.dispatchEvent(new Event('orientationchange'))
2071+
})
2072+
2073+
expect(syncScroll.mock.calls.length).toBeGreaterThan(syncCalls + 2)
2074+
expect(updatePosition.mock.calls.length).toBeGreaterThan(positionCalls + 2)
2075+
19602076
unmountBridge()
19612077
for (const observer of observers) {
19622078
expect(observer.disconnect).toHaveBeenCalled()
19632079
}
2080+
expect(handlers.resize).toBeDefined()
2081+
expect(handlers.orientationchange).toBeDefined()
2082+
expect(addListener).toHaveBeenCalledWith('resize', handlers.resize)
2083+
expect(addListener).toHaveBeenCalledWith('orientationchange', handlers.orientationchange)
2084+
expect(removeListener).toHaveBeenCalledWith('resize', handlers.resize)
2085+
expect(removeListener).toHaveBeenCalledWith('orientationchange', handlers.orientationchange)
2086+
addListener.mockRestore()
2087+
removeListener.mockRestore()
19642088
;(globalThis as any).ResizeObserver = originalResizeObserver
19652089
unmount()
19662090
})

0 commit comments

Comments
 (0)