filament icon indicating copy to clipboard operation
filament copied to clipboard

Closing Nested Modals Disables Page Scrolling

Open nkeena opened this issue 1 year ago • 16 comments

Package

filament/filament

Package Version

v3.2.6

Laravel Version

v10.41.0

Livewire Version

v3.0.0.0

PHP Version

PHP 8.2.11

Problem description

When opening a nested modal, scroll lock is lost on the backdrop, allowing you to scroll the backdrop. After closing the nested modal (using the cancel button, x icon, or clicking off the modal) scroll lock is resumed on the backdrop. After closing the parent (or first) modal, page scrolling becomes disabled until refreshing the page or navigating away.

There are no console errors and this problem seems to occur on all browsers when using npm run dev or npm run build with vite.

Expected behavior

When opening a nested modal (or slide-over), scroll lock on the backdrop should not be lost. After closing all modals, you should be able to resume scrolling the page without having to refresh or navigate away.

Steps to reproduce

  1. Install the reproduction repository locally
  2. Run composer install
  3. Run the migrations and seeders php artisan migrate --seed
  4. Create a filament user php artisan make:filament-user
  5. Run npm run build
  6. Login using your credentials /admin/login
  7. Navigate to the orders table and set the number of visible order to "all" so that the page scrolls
  8. Click "New order"
  9. Click "+" suffix action on either the product or customer input
  10. Observe the backdrop scrolls
  11. Close all modals
  12. Observe the page won't scroll
  13. Observer no console errors
  14. Refresh the page and observe the page is scrollable again

Reproduction repository

https://github.com/nkeena/filament-modal-bug

Relevant log output

No response

Donate 💰 to fund this issue

  • You can donate funding to this issue. We receive the money once the issue is completed & confirmed by you.
  • 100% of the funding will be distributed between the Filament core team to run all aspects of the project.
  • Thank you in advance for helping us make maintenance sustainable!
Fund with Polar

nkeena avatar Jan 19 '24 14:01 nkeena

i also have this issue.

mohammadkamil avatar Jan 19 '24 14:01 mohammadkamil

This is an Alpine.js bug, so I've replaced their scroll handling implementation with a separate library

danharrin avatar Jan 21 '24 13:01 danharrin

I've had to temporarily revert the fix (https://github.com/filamentphp/filament/commit/02d4fad9db8cdcc261c6ab5f2a09b3599a6504c3) since it caused issues with modals on iOS (they were no longer scrollable).

zepfietje avatar Jan 25 '24 13:01 zepfietje

Will look into using this fork: https://github.com/rick-liruixin/body-scroll-lock-upgrade.

zepfietje avatar Jan 29 '24 10:01 zepfietje

I'm having this problem too. is there any news on a fix for it please?

david-lobo avatar Feb 08 '24 20:02 david-lobo

@david-lobo I haven't found the time to work on this issue yet. If you want to help fixing it, try using the scroll lock package fork I posted above and test if any of the iOS issues still occur.

Let me know if you want to work on this and what your findings are. 🙂

zepfietje avatar Feb 14 '24 07:02 zepfietje

i think this workaround is best if you don't want to preserve the scroll state, just dispatch a page reload event after closing the last modal

FilamentView::registerRenderHook( 'panels::body.end', static fn () => '<script>window.addEventListener("reload", () => window.location.reload())</script>', );

$this->dispatch('reload');

Dispatch a page reload after create action for example

Tables\Actions\CreateAction::make()->after(function ($data,$record) { $this->dispatch('reload'); })

nabildz avatar Mar 28 '24 00:03 nabildz

That's the same as just doing a redirect really from the action

danharrin avatar Mar 28 '24 08:03 danharrin

Any solution here yet?

In the meantime, is there a way to trigger a page refresh when the parent modal closes?

nkeena avatar Apr 24 '24 20:04 nkeena

Any solution here yet?

In the meantime, is there a way to trigger a page refresh when the parent modal closes?

Do you find any temporary solution ?

Having the same problem right now :)

KhairulAzmi21 avatar May 03 '24 16:05 KhairulAzmi21

If you want to help fixing it, try using the scroll lock package fork I posted above and test if any of the iOS issues still occur.

danharrin avatar May 03 '24 17:05 danharrin

Alright, I used the suggested fork body-scroll-lock-upgrade instead of body-scroll-lock and uncommented all changes in your initial PR for the modal js and blade view. Sadly, this fork still has the iOS Issue, where you can't scroll the modal. It fixes the alpine bug though. :/

Tested on an iPhone 15 Pro.

flolanger avatar Jun 11 '24 15:06 flolanger

Thank you for playing a part there in testing, it is appreciated. Not sure where to go from here then

danharrin avatar Jun 11 '24 15:06 danharrin

Thank you for playing a part there in testing, it is appreciated. Not sure where to go from here then

Would it be possible to contribute to alpine, if that is a known issue there?

flolanger avatar Jun 11 '24 15:06 flolanger

Potentially, I don't know if I have the knowledge to do so but we can look into it

danharrin avatar Jun 11 '24 15:06 danharrin

Today I asked a frontend dev, and he said that (with the alpine version) this could be a scoping issue. Because both modals use "isOpen", this could lead to scoping issues when dealing with multiple instances. Each modal should have its own variable.

Maybe this would be a soultion.

flolanger avatar Jun 12 '24 14:06 flolanger

Just spent 3 hours figuring this out.

It's far from ideal but "working" on Windows and iPhone.

I implemented this commit : https://github.com/filamentphp/filament/pull/10969/commits/671891382c2b8fb39c1f67e59cd375c37c24cd3c

Overriden this view : /resources/views/vendor/filament/components/modal/index.blade.php To add a basic check to disable the JS Scroll Body Locks if IOS.

x-data="{
        isOpen: false,

        livewire: null,

        isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream,

        close: function () {
            this.isOpen = false

            this.$refs.modalContainer.dispatchEvent(
                new CustomEvent('modal-closed', { id: '{{ $id }}' }),
            )

            this.$nextTick(() => {
                if (document.getElementsByClassName('fi-modal-open').length || this.isIOS) {
                    return
                }

                window.clearAllBodyScrollLocks()
            })
        },

        open: function () {
            this.isOpen = true

            if(!this.isIOS) {
                window.clearAllBodyScrollLocks()
                window.disableBodyScroll(this.$root)
            }

            this.$refs.modalContainer.dispatchEvent(
                new CustomEvent('modal-opened', { id: '{{ $id }}' }),
            )
        },
    }

VincentLahaye avatar Jul 09 '24 15:07 VincentLahaye

That sounds like a good workaround, I would welcome a PR

danharrin avatar Jul 10 '24 10:07 danharrin

Addressed by @wychoong in #13740

danharrin avatar Jul 31 '24 10:07 danharrin