-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…lTo` hooks (#3372) * Support activityID in useObserveScrollPosition and useScrollTo * Add tests * Add entry * Fix ESLint * Fix test * Send activity ID if only available * Fix test Co-authored-by: Chris Whitten <[email protected]>
- Loading branch information
Showing
17 changed files
with
351 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+53.8 KB
...activity-id-js-use-scroll-to-hook-should-scroll-based-on-activity-id-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+48.5 KB
...activity-id-js-use-scroll-to-hook-should-scroll-based-on-activity-id-2-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+72.5 KB
...activity-id-js-use-scroll-to-hook-should-scroll-based-on-activity-id-3-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
<!DOCTYPE html> | ||
<html lang="en-US"> | ||
<head> | ||
<script crossorigin="anonymous" src="/__dist__/testharness.js"></script> | ||
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script> | ||
<style type="text/css"> | ||
body { | ||
background-color: #f7f7f7; | ||
} | ||
|
||
#app { | ||
height: 100%; | ||
} | ||
|
||
.scroll-panel { | ||
left: 0; | ||
position: fixed; | ||
top: 0; | ||
} | ||
|
||
.webchat { | ||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05); | ||
margin: 0 auto; | ||
max-width: 480px; | ||
min-width: 360px; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script type="text/babel" data-presets="env,stage-3,react"> | ||
const { | ||
React: { useCallback, useLayoutEffect, useRef, useState }, | ||
WebChat: { | ||
Components: { BasicWebChat, Composer }, | ||
createDirectLine, | ||
hooks: { useObserveScrollPosition, useScrollTo, useSendMessage } | ||
}, | ||
WebChatTest: { | ||
conditions, | ||
createDeferred, | ||
createDirectLineWithTranscript, | ||
createStore, | ||
elements, | ||
expect, | ||
host, | ||
pageObjects, | ||
timeouts, | ||
token | ||
} | ||
} = window; | ||
|
||
function generateTranscript() { | ||
const messages = [ | ||
'Do incididunt qui sit nulla dolor.', | ||
'Ipsum dolor laborum veniam sunt irure nulla aliquip minim ad veniam culpa sit ut.', | ||
'Labore ut ex anim in nisi enim deserunt anim minim esse veniam.', | ||
'Lorem amet occaecat voluptate fugiat elit cillum.', | ||
'Sit et velit qui laboris et elit eu pariatur velit occaecat.', | ||
'Nostrud minim deserunt excepteur elit aliquip excepteur.', | ||
'Laboris pariatur minim ad incididunt.', | ||
'Reprehenderit sint id elit laboris nisi ipsum pariatur et id deserunt dolore.', | ||
'Quis dolor ut dolor qui.', | ||
'Fugiat commodo ipsum irure deserunt duis ea est amet Lorem esse eiusmod.', | ||
'Magna occaecat enim magna laboris sunt consequat esse elit ipsum esse quis culpa amet.', | ||
'Laborum officia est elit officia voluptate dolore elit veniam aute velit.', | ||
'Et esse sint incididunt irure et amet deserunt consectetur dolor.', | ||
'Ipsum adipisicing nisi nulla eiusmod commodo ad enim veniam velit.', | ||
'Consectetur labore adipisicing do dolor dolor eiusmod sint irure in labore ullamco incididunt voluptate.', | ||
'Duis voluptate elit tempor quis consequat incididunt excepteur anim in.', | ||
'Aliquip cupidatat exercitation magna aute nostrud fugiat deserunt.', | ||
'Nulla eu do duis consequat sint irure proident cupidatat duis.', | ||
'Ipsum laborum commodo sint tempor fugiat esse est sit officia qui cupidatat nisi minim.', | ||
'Veniam consequat ut anim consequat ea incididunt ipsum proident duis veniam irure consequat exercitation.\n\nVoluptate dolor nostrud ipsum amet in velit cupidatat veniam voluptate ipsum.\n\nPariatur ipsum eiusmod deserunt commodo elit aute in velit proident.\n\nCulpa amet deserunt excepteur ex quis Lorem commodo ipsum sint consectetur id.\n\nAliquip officia qui ea cillum duis labore consectetur sunt excepteur labore.\n\nElit adipisicing et consectetur occaecat sint nulla Lorem id anim Lorem.\n\nPariatur officia velit officia do nisi cupidatat enim nulla et sit.\n\nDuis anim Lorem reprehenderit mollit occaecat sunt.\n\nLorem cupidatat id culpa anim velit qui irure.\n\nVoluptate aute incididunt cillum culpa laborum est sunt et ea proident minim non.\n\nId Lorem eiusmod amet sint nulla velit ullamco tempor incididunt pariatur.\n\nElit elit fugiat dolore amet dolor voluptate.\n\nEa pariatur nulla dolor excepteur enim sit aliquip incididunt laboris pariatur fugiat commodo officia minim.\n\nConsequat elit amet minim consectetur tempor.\n\nPariatur excepteur consectetur adipisicing quis laborum.\n\nIn aute consectetur ullamco eiusmod reprehenderit consequat non aliquip consequat eiusmod et laboris.\n\nMagna amet nulla do nulla ea ad do occaecat adipisicing.\n\nConsequat quis laborum nisi ut exercitation reprehenderit cupidatat proident incididunt est eiusmod ea.\n\nAdipisicing aliqua elit nostrud sint magna aliqua nisi deserunt ex occaecat velit ipsum duis.\n\nEnim veniam sunt cillum voluptate laborum do.\n\nVeniam ea aute reprehenderit et ad reprehenderit non do deserunt minim eu elit.\n\nAnim irure fugiat nostrud occaecat amet sint pariatur irure cupidatat commodo fugiat Lorem minim deserunt.\n\nAd consectetur excepteur enim nisi adipisicing.\n\nMollit duis est ipsum nulla aliquip.\n\nSunt reprehenderit quis in ea eu tempor fugiat ad dolore ea adipisicing consectetur elit.\n\nConsequat minim culpa ea sit ullamco ex id exercitation.\n\nCulpa cillum non ipsum eu Lorem nostrud nostrud consequat anim in culpa nostrud.\n\nNostrud nisi quis et Lorem aliquip anim deserunt culpa.\n\nSunt cupidatat commodo quis elit consequat ullamco irure id tempor.\n\nCommodo dolor officia magna amet aliqua proident qui ipsum voluptate.\n\nDo officia cupidatat id in proident.' | ||
]; | ||
|
||
return messages.map((text, index) => { | ||
const fromUser = index % 2; | ||
|
||
return { | ||
...(fromUser | ||
? { | ||
channelData: { | ||
clientActivityID: index + '', | ||
clientTimestamp: 0, | ||
state: 'sent' | ||
} | ||
} | ||
: {}), | ||
from: { role: fromUser ? 'user' : 'bot' }, | ||
id: index + '', | ||
text: `#${index}: ${text}`, | ||
timestamp: 0, | ||
type: 'message' | ||
}; | ||
}); | ||
} | ||
|
||
(async function() { | ||
const directLine = createDirectLineWithTranscript(generateTranscript()); | ||
const store = createStore(); | ||
|
||
let lastScrollPosition; | ||
|
||
const RunFunction = ({ fn }) => { | ||
fn(); | ||
|
||
return false; | ||
}; | ||
|
||
const ScrollPanel = () => { | ||
const ref = useRef(); | ||
|
||
useObserveScrollPosition(position => { | ||
lastScrollPosition = position; | ||
}); | ||
|
||
return <div className="scroll-panel" ref={ref}></div>; | ||
}; | ||
|
||
const WebChat = ({ children, className }) => { | ||
return ( | ||
<Composer directLine={directLine} store={store}> | ||
<BasicWebChat className={className} /> | ||
{children} | ||
<ScrollPanel /> | ||
</Composer> | ||
); | ||
}; | ||
|
||
const renderWithFunction = fn => { | ||
const deferreds = [createDeferred(), createDeferred()]; | ||
|
||
ReactDOM.render( | ||
<WebChat className="webchat"> | ||
<RunFunction fn={() => deferreds[0].resolve(fn && fn())} key={Date.now() + ''} /> | ||
</WebChat>, | ||
document.getElementById('app'), | ||
deferreds[1].resolve | ||
); | ||
|
||
return Promise.all(deferreds.map(({ promise }) => promise)); | ||
}; | ||
|
||
window.renderWithFunction = renderWithFunction; | ||
|
||
await renderWithFunction(); | ||
|
||
await pageObjects.wait(conditions.uiConnected(), timeouts.directLine); | ||
|
||
// Wait until the view is not sticky. | ||
await new Promise(resolve => setTimeout(resolve, 200)); | ||
await pageObjects.wait(conditions.scrollToBottomCompleted(), timeouts.scrollToBottom); | ||
|
||
// "12" should appears right above the send box. | ||
await renderWithFunction(() => useScrollTo()({ activityID: '12' })); | ||
await host.snapshot(); | ||
|
||
// When on top, it should always return the first activity in the transcript. | ||
await renderWithFunction(() => useScrollTo()({ scrollTop: 0 })); | ||
await new Promise(resolve => setTimeout(resolve, 200)); // Wait until "scroll" event | ||
expect(lastScrollPosition).toHaveProperty('activityID', '0'); | ||
expect(lastScrollPosition).toHaveProperty('scrollTop', 0); | ||
await host.snapshot(); | ||
|
||
// Since "19" is a super-long activity, it should appears on the top of the page. | ||
await renderWithFunction(() => useScrollTo()({ activityID: '19' })); | ||
await host.snapshot(); | ||
|
||
await host.done(); | ||
})().catch(async err => { | ||
console.error(err); | ||
|
||
await host.error(err); | ||
}); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** | ||
* @jest-environment ./__tests__/html/__jest__/WebChatEnvironment.js | ||
*/ | ||
|
||
describe('useScrollTo hook', () => { | ||
test('should scroll based on activity ID', () => runHTMLTest('hooks.useScrollTo.activityID.html')); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
packages/component/src/hooks/internal/useGetTranscriptActivityElementByID.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { useCallback } from 'react'; | ||
|
||
import useTranscriptActivityElementsRef from './useTranscriptActivityElementsRef'; | ||
|
||
export default function useGetTranscriptActivityElementByID() { | ||
const [activityElementsRef] = useTranscriptActivityElementsRef(); | ||
|
||
return useCallback( | ||
activityID => { | ||
const { element } = activityElementsRef.current.find(entry => entry.activityID === activityID) || {}; | ||
|
||
return element; | ||
}, | ||
[activityElementsRef] | ||
); | ||
} |
11 changes: 11 additions & 0 deletions
11
packages/component/src/hooks/internal/useGetTranscriptScrollableElement.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { useCallback } from 'react'; | ||
|
||
import useTranscriptRootElementRef from './useTranscriptRootElementRef'; | ||
|
||
export default function useGetTranscriptScrollableElement() { | ||
const [rootElementRef] = useTranscriptRootElementRef(); | ||
|
||
return useCallback(() => rootElementRef.current.querySelector('.webchat__basic-transcript__scrollable'), [ | ||
rootElementRef | ||
]); | ||
} |
5 changes: 5 additions & 0 deletions
5
packages/component/src/hooks/internal/useTranscriptActivityElementsRef.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import useWebChatUIContext from './useWebChatUIContext'; | ||
|
||
export default function useTranscriptActivityElementsRef() { | ||
return [useWebChatUIContext().transcriptActivityElementsRef]; | ||
} |
5 changes: 5 additions & 0 deletions
5
packages/component/src/hooks/internal/useTranscriptRootElementRef.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import useWebChatUIContext from './useWebChatUIContext'; | ||
|
||
export default function useTranscriptRootElementRef() { | ||
return [useWebChatUIContext().transcriptRootElementRef]; | ||
} |
Oops, something went wrong.