-
-
Notifications
You must be signed in to change notification settings - Fork 723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add new sticky component to handle stacked stickies #5088
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
1 Ignored Deployment
|
const registerStickyItem = useCallback( | ||
(item: RefObject<HTMLDivElement>) => { | ||
setStickyItems((prevItems) => { | ||
if (item && !prevItems.includes(item)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a non trivial logic without tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please clarify the part that you find non-trivial? The line on this comment is a simple "add if not added yet".
Anyways, added some tests: eb8a0aa. Let me know if there are any specific tests you would like to see.
I agree that it's a non-trivial issue to fix overall, and it can also be non-trivial to write tests for, since it's expecting a real DOM, in a browser environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non trivial UI logic here is:
- checking includes
- adding to the end of the list
- sorting
- checking presence of both elements
- subtraction
If I change any of those I'd expect my tests to catch me by hand.
We can separate sorting algorithm from DOM and unit test the sorting independently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW if the code will tell it's difficult to do then I wouldn't proceed with the refactoring.
|
||
const { registerStickyItem, unregisterStickyItem, getTopOffset } = context; | ||
|
||
useEffect(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about combining useEffects?
useEffect(() => {
if (!ref.current) return;
if (initialTopOffset === null) {
const topValue = parseInt(getComputedStyle(ref.current).getPropertyValue('top'), 10) || 0;
setInitialTopOffset(topValue);
}
setTop(getTopOffset(ref) + (initialTopOffset || 0));
registerStickyItem(ref);
return () => unregisterStickyItem(ref);
}, [getTopOffset, initialTopOffset, registerStickyItem, unregisterStickyItem]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could potentially work. Something like:
useEffect(() => {
if (!ref.current) {
return;
}
// We should only set the initial top offset once - when the component is mounted
// This value will be set based on the initial top that was set for this component
// After that, the top will be calculated based on the height of the previous sticky items + this initial top offset
if (initialTopOffset === null) {
setInitialTopOffset(
parseInt(getComputedStyle(ref.current).getPropertyValue('top')),
);
}
// We should register the sticky item when it is mounted...
registerStickyItem(ref);
// (Re)calculate the top offset based on the sticky items
setTop(getTopOffset(ref) + (initialTopOffset || 0));
// ...and unregister it when it is unmounted
return () => {
unregisterStickyItem(ref);
};
}, [ref, registerStickyItem, unregisterStickyItem, getTopOffset]);
But I think I prefer having the separation of concerns, at least in this first iteration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicely done. That's a tricky library component to write.
Tiny zIndex fix for the draft banner for a regression introduced in #5088 ### Before - Draft banner is displayed on top of the profile popup: ![image](https://github.com/Unleash/unleash/assets/14320932/63865f01-9bbe-42f1-9cc4-85c3c985334c) ### After - Profile popup displays on top of the draft banner: ![image](https://github.com/Unleash/unleash/assets/14320932/565e1017-5163-445d-bc0c-ee957023241b)
https://linear.app/unleash/issue/2-1509/discovery-stacked-sticky-elements
Adds a new
Sticky
element that will attempt to stack sticky elements in the DOM in a smart way.This needs a wrapping
StickyProvider
that will keep track of sticky elements.This PR adapts a few components to use this new element:
DemoBanner
FeatureOverviewSidePanel
DraftBanner
MaintenanceBanner
MessageBanner
Pre-existing
top
properties are taken into consideration for the top offset, so we can have nice margins like in the feature overview side panel.Before - Sticky elements overlap 😞
After - Sticky elements stack 😄