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

Allow scroll_into_view to work for components rendered in the same tick #902

Merged
merged 1 commit into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mesop/examples/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
from mesop.examples import readme_app as readme_app
from mesop.examples import responsive_layout as responsive_layout
from mesop.examples import scroll_into_view as scroll_into_view
from mesop.examples import (
scroll_into_view_deferred as scroll_into_view_deferred,
)
from mesop.examples import starter_kit as starter_kit
from mesop.examples import sxs as sxs
from mesop.examples import testing as testing
Expand Down
22 changes: 22 additions & 0 deletions mesop/examples/scroll_into_view_deferred.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import mesop as me


@me.stateclass
class State:
show_bottom_line: bool = False


def on_load(e: me.LoadEvent):
me.scroll_into_view(key="bottom_line")
state = me.state(State)
state.show_bottom_line = True


@me.page(path="/scroll_into_view_deferred", on_load=on_load)
def app():
me.text("Scroll into view deferred")
for _ in range(100):
me.text("filler line")
state = me.state(State)
if state.show_bottom_line:
me.text("bottom line", key="bottom_line")
8 changes: 8 additions & 0 deletions mesop/tests/e2e/scroll_into_view_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ test('scroll_into_view', async ({page}) => {

await expect(page.getByText('bottom_line')).toBeInViewport();
});

test('scroll_into_view - works with components rendered in same tick', async ({
page,
}) => {
await page.setViewportSize({width: 200, height: 200});
await page.goto('/scroll_into_view_deferred');
await expect(page.getByText('bottom line')).toBeInViewport();
});
44 changes: 24 additions & 20 deletions mesop/web/src/shell/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,27 +130,31 @@ export class Shell {
} else if (command.hasScrollIntoView()) {
// Scroll into view
const key = command.getScrollIntoView()!.getKey();
const targetElements = document.querySelectorAll(
`[data-key="${key}"]`,
);
if (!targetElements.length) {
console.error(
`Could not scroll to component with key ${key} because no component found`,
// Schedule scroll into view to run after the current event loop tick
// so that the component has time to render.
setTimeout(() => {
const targetElements = document.querySelectorAll(
`[data-key="${key}"]`,
);
return;
}
if (targetElements.length > 1) {
console.warn(
'Found multiple components',
targetElements,
'to potentially scroll to for key',
key,
'. This is probably a bug and you should use a unique key identifier.',
);
}
targetElements[0].parentElement!.scrollIntoView({
behavior: 'smooth',
});
if (!targetElements.length) {
console.error(
`Could not scroll to component with key ${key} because no component found`,
);
return;
}
if (targetElements.length > 1) {
console.warn(
'Found multiple components',
targetElements,
'to potentially scroll to for key',
key,
'. This is probably a bug and you should use a unique key identifier.',
);
}
targetElements[0].parentElement!.scrollIntoView({
behavior: 'smooth',
});
}, 0);
} else if (command.hasFocusComponent()) {
// Focus on component
const key = command.getFocusComponent()!.getKey();
Expand Down
Loading