Skip to content
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

fix(form-builder): move handling of ensuring parent item visibility inputs #2329

Merged
merged 1 commit into from
Feb 23, 2021

Conversation

bjoerge
Copy link
Member

@bjoerge bjoerge commented Feb 23, 2021

When a document node value is beeing edited in a dialog we want to make sure the input/preview of the parent/enclosing is scrolled into view in the background. Originally we solved this by responding to changes in the first element of the focus path (e.g. the top most document field name). Whenever the first element in the focus path changed, we'd then scroll to the dom element that had this as the value for data-focus-path. As a consequence, opening an item for edit would scroll the editor to the path of the parent input into view, and this would sometimes trigger a scroll jump actually scrolling the field out of view because scrollIntoView would ensure the top edge of the parent input was in view. Instead, what we really wanted was to have the edited item in view.

Fixes: #2280
Fixes: #2312

Note for release

  • Fixed various issues causing unexpected scroll jumps in the editor

As a bonus, this PR also adds a subtle focus ring on the background element that is currently being edited.

image

@vercel
Copy link

vercel bot commented Feb 23, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployments, click below or on the icon next to each commit.

test-studio – ./

🔍 Inspect: https://vercel.com/sanity-io/test-studio/C84gpSgJ9cNqgN291b61WpvmFcJ7
✅ Preview: https://test-studio-git-fix-ch4500fix-scroll-jumps.sanity.build

perf-studio – ./

🔍 Inspect: https://vercel.com/sanity-io/perf-studio/8QrA6TyTUCFrjkkkoCwtuinZKjgk
✅ Preview: https://perf-studio-git-fix-ch4500fix-scroll-jumps.sanity.build

import {useEffect} from 'react'
import {usePrevious} from './usePrevious'

export function useDidUpdate<T>(value: T, didUpdate: (current: T, previous: T) => void): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a hard thing to name right. Maybe a more explicit naming could be useOnChange?

Also, think it would be good if the signature of the callback would have the same order as shouldComponentUpdate, e.g.

type OnChangeCallback<T> = (prev: T | undefined, next: T) => void

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally considered onChange but landed on useDidUpdate because onChange is usually about events coming from the UI whereas this one is "the value updated since previous render", so it more closely aligns with componentDidUpdate semantics.

Agree on the order of callback params - your suggestion also aligns better with setState, reduce, etc. which expects the previous first.

import {useEffect, useRef} from 'react'

export function usePrevious<T>(value: T, initial: T): T
export function usePrevious<T>(value: T, initial?: T): T | undefined {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t understand what this hook is doing. Can you add a description?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And since it’s only being used by useDidUpdate, maybe this is not needed but should instead be part of useDidUpdate?

Copy link
Member Author

@bjoerge bjoerge Feb 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a description?

Yes, good point. Thanks!

And since it’s only being used by useDidUpdate, maybe this is not needed but should instead be part of useDidUpdate?

It has well defined semantics and is very useful by itself, so it makes sense to have it separate. It might also one day be exported by React and then it can be deleted.

From the FAQ

It’s possible that in the future React will provide a usePrevious Hook out of the box since it’s a relatively common use case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think it’s a little unnecessary to make a new hook for this, since it’s used by another hook and nothing else yet. But it’s not critical of course.

Copy link
Member Author

@bjoerge bjoerge Feb 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally agree that we shouldn't prematurely make things generic and divide up functions/hooks etc that isn't reused, but since this is such a common hook (I've needed it several times myself) and I don't see the harm in having it separate and readily available for the next person that needs it, I suggest we keep it for now. It's also better separation of concerns and IMO makes the intent behind the resulting code more clear to the reader.

…ty to the inputs that needs it.

When a document node value is beeing edited in a dialog we want to make sure the input/preview of the parent/enclosing is scrolled into view in the background. Originally we solved this by responding to changes in the first element of the focus path (e.g. the top most document field name), and whenever this changed, scroll to the dom element with this as the value for `data-focus-path`. As a consequence, opening an item for edit would scroll the editor to the path of the _parent input_ into view, and this would sometimes trigger a scroll jump actually scrolling the field out of view because scrollIntoView would ensure the top edge of the parent input was in view. Instead, what we really wanted was to have the edited _item_ in view.

As a bonus, this PR also adds a subtle focus ring on the background element that is currently being edited.
@bjoerge bjoerge merged commit 2b4e16e into next Feb 23, 2021
@bjoerge bjoerge deleted the fix/ch4500/fix-scroll-jumps branch February 23, 2021 15:38
bjoerge added a commit that referenced this pull request Feb 23, 2021
…ty to the inputs that needs it. (#2329)

When a document node value is being edited in a dialog we want to make sure the input/preview of the parent/enclosing is scrolled into view in the background. Originally we solved this by responding to changes in the first element of the focus path (e.g. the top most document field name), and whenever this changed, we would scroll to the dom element with this as the value for `data-focus-path`. As a consequence, opening an item for edit would scroll the editor to the path of the _parent input_ into view, and this would sometimes trigger a scroll jump actually scrolling the field out of view because scrollIntoView would ensure the top edge of the parent input was in view. Instead, what we really want to ensure here is that the edited _item_ is in the view.

This fixes the issue by moving the logic for keeping the item in view to the individual input components that needs this behavior. For now, it is only the ArrayOfObjectsInput and the PortableTextInput that requires this. Technically, ImageInput and FileInput also has this behavior, but these don't support deep linking, so it's less of a pressing issue here.

As a bonus, this PR also adds a subtle focus ring on the background element that is currently being edited.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sanity v.2.3.8 scroll bug Clicking an item further down on a desk page causes disruptive scroll jump
2 participants