diff --git a/demo-app/resources/js/Pages/Local.jsx b/demo-app/resources/js/Pages/Local.jsx index 709899d..df72e62 100644 --- a/demo-app/resources/js/Pages/Local.jsx +++ b/demo-app/resources/js/Pages/Local.jsx @@ -1,7 +1,18 @@ import { Modal, ModalLink } from '@inertiaui/modal-react'; import Container from './Container'; +import { useRef } from 'react'; export default function Local() { + const modalRef = useRef(null); + + function closeModal() { + modalRef.current.close(); + } + + function alertModalId() { + alert(modalRef.current.id); + } + return ( <> @@ -15,11 +26,17 @@ export default function Local() { - + This is a local modal Create Role + + ); diff --git a/demo-app/resources/js/Pages/Local.vue b/demo-app/resources/js/Pages/Local.vue index ecdd684..dc6f1ab 100644 --- a/demo-app/resources/js/Pages/Local.vue +++ b/demo-app/resources/js/Pages/Local.vue @@ -1,6 +1,17 @@ diff --git a/demo-app/tests/Browser/LocalModalTest.php b/demo-app/tests/Browser/LocalModalTest.php index c862448..c22265c 100644 --- a/demo-app/tests/Browser/LocalModalTest.php +++ b/demo-app/tests/Browser/LocalModalTest.php @@ -20,4 +20,33 @@ public function it_can_open_a_local_modal_and_a_nested_one() ->waitUntilMissingModal(1); }); } + + #[Test] + public function it_can_access_a_prop_through_a_template_ref() + { + $this->browse(function (Browser $browser) { + $browser->visit('/local') + ->clickLink('Open Local Modal') + ->waitForTextIn('.im-modal-content', 'This is a local modal') + ->press('Alert Modal ID'); + + $message = $browser->driver->switchTo()->alert()->getText(); + + $this->assertStringStartsWith('inertiaui_modal_', $message); + + $browser->dismissDialog(); + }); + } + + #[Test] + public function it_can_close_a_local_modal_through_a_template_ref() + { + $this->browse(function (Browser $browser) { + $browser->visit('/local') + ->clickLink('Open Local Modal') + ->waitForTextIn('.im-modal-content', 'This is a local modal') + ->press('Close Modal through Ref') + ->waitUntilMissingModal(1); + }); + } } diff --git a/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php b/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php index fcec9c1..fef3710 100644 --- a/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php +++ b/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php @@ -15,7 +15,7 @@ class DispatchBaseUrlRequestTest extends TestCase { protected DispatchBaseUrlRequest $dispatcher; - public function setUp(): void + protected function setUp(): void { parent::setUp(); diff --git a/demo-app/tests/Feature/RedirectorTest.php b/demo-app/tests/Feature/RedirectorTest.php index c7e370b..60589ac 100644 --- a/demo-app/tests/Feature/RedirectorTest.php +++ b/demo-app/tests/Feature/RedirectorTest.php @@ -17,7 +17,7 @@ class RedirectorTest extends TestCase protected Request $request; - public function setUp(): void + protected function setUp(): void { parent::setUp(); $this->urlGenerator = app(UrlGenerator::class); diff --git a/demo-app/tests/TestCase.php b/demo-app/tests/TestCase.php index b66e347..33489d6 100644 --- a/demo-app/tests/TestCase.php +++ b/demo-app/tests/TestCase.php @@ -12,7 +12,7 @@ abstract class TestCase extends BaseTestCase use ModalTestCase; use RefreshDatabase; - public function setUp(): void + protected function setUp(): void { parent::setUp(); @@ -20,7 +20,7 @@ public function setUp(): void Carbon::setTestNow('2024-06-01 12:00:00'); } - public function tearDown(): void + protected function tearDown(): void { Carbon::setTestNow(); parent::tearDown(); diff --git a/react/src/HeadlessModal.jsx b/react/src/HeadlessModal.jsx index 7951221..dab2cce 100644 --- a/react/src/HeadlessModal.jsx +++ b/react/src/HeadlessModal.jsx @@ -1,4 +1,4 @@ -import { useMemo, useState, forwardRef, useImperativeHandle, useEffect } from 'react' +import { useMemo, useState, forwardRef, useImperativeHandle, useEffect, useRef } from 'react' import { getConfig, getConfigByType } from './config' import { useModalIndex } from './ModalRenderer.jsx' import { useModalStack } from './ModalRoot.jsx' @@ -50,23 +50,46 @@ const HeadlessModal = forwardRef(({ name, children, ...props }, ref) => { return modalContext.registerEventListenersFromProps(props) }, [name]) + // Store the latest modalContext in a ref to maintain reference + const modalContextRef = useRef(modalContext) + + // Update the ref whenever modalContext changes + useEffect(() => { + modalContextRef.current = modalContext + }, [modalContext]) + useImperativeHandle( ref, () => ({ - afterLeave: () => modalContext.afterLeave(), - close: () => modalContext.close(), - config, - emit: (...args) => modalContext.emit(...args), - getChildModal: () => modalContext.getChildModal(), - getParentModal: () => modalContext.getParentModal(), - id: modalContext?.id, - index: modalContext?.index, - isOpen: modalContext?.isOpen, - modalContext, - onTopOfStack: modalContext?.onTopOfStack, - reload: () => modalContext.reload(), - setOpen: () => modalContext.setOpen(), - shouldRender: modalContext?.shouldRender, + afterLeave: () => modalContextRef.current?.afterLeave(), + close: () => modalContextRef.current?.close(), + emit: (...args) => modalContextRef.current?.emit(...args), + getChildModal: () => modalContextRef.current?.getChildModal(), + getParentModal: () => modalContextRef.current?.getParentModal(), + reload: (...args) => modalContextRef.current?.reload(...args), + setOpen: () => modalContextRef.current?.setOpen(), + + get id() { + return modalContextRef.current?.id + }, + get index() { + return modalContextRef.current?.index + }, + get isOpen() { + return modalContextRef.current?.isOpen + }, + get config() { + return modalContextRef.current?.config + }, + get modalContext() { + return modalContextRef.current + }, + get onTopOfStack() { + return modalContextRef.current?.onTopOfStack + }, + get shouldRender() { + return modalContextRef.current?.shouldRender + }, }), [modalContext], ) diff --git a/vue/src/HeadlessModal.vue b/vue/src/HeadlessModal.vue index c39f3e6..a74e85e 100644 --- a/vue/src/HeadlessModal.vue +++ b/vue/src/HeadlessModal.vue @@ -92,20 +92,35 @@ function emit(event, ...args) { } defineExpose({ - afterLeave: modalContext.value.afterLeave, - close: modalContext.value.close, - config: config.value, emit, - getChildModal: modalContext.value.getChildModal, - getParentModal: modalContext.value.getParentModal, - id: modalContext.value.id, - index: modalContext.value.index, - isOpen: modalContext.value.isOpen, - modalContext: modalContext.value, - onTopOfStack: modalContext.value.onTopOfStack, - reload: modalContext.value.reload, - setOpen: modalContext.value.setOpen, - shouldRender: modalContext.value.shouldRender, + afterLeave: () => modalContext.value?.afterLeave(), + close: () => modalContext.value?.close(), + reload: (...args) => modalContext.value?.reload(...args), + setOpen: (...args) => modalContext.value?.setOpen(...args), + getChildModal: () => modalContext.value?.getChildModal(), + getParentModal: () => modalContext.value?.getParentModal(), + + get config() { + return modalContext.value?.config + }, + get id() { + return modalContext.value?.id + }, + get index() { + return modalContext.value?.index + }, + get isOpen() { + return modalContext.value?.isOpen + }, + get modalContext() { + return modalContext.value?.modalContext + }, + get onTopOfStack() { + return modalContext.value?.onTopOfStack + }, + get shouldRender() { + return modalContext.value?.shouldRender + }, }) const nextIndex = computed(() => { diff --git a/vue/src/Modal.vue b/vue/src/Modal.vue index 19414ca..8d5b649 100644 --- a/vue/src/Modal.vue +++ b/vue/src/Modal.vue @@ -3,7 +3,7 @@ import { DialogOverlay, DialogPortal, DialogRoot } from 'radix-vue' import ModalContent from './ModalContent.vue' import HeadlessModal from './HeadlessModal.vue' import SlideoverContent from './SlideoverContent.vue' -import { computed, ref } from 'vue' +import { ref } from 'vue' const modal = ref(null) const rendered = ref(false) @@ -11,18 +11,33 @@ const rendered = ref(false) defineExpose({ afterLeave: () => modal.value?.afterLeave(), close: () => modal.value?.close(), - config: computed(() => modal.value?.config), emit: (...args) => modal.value?.emit(...args), getChildModal: () => modal.value?.getChildModal(), getParentModal: () => modal.value?.getParentModal(), - id: computed(() => modal.value?.id), - index: computed(() => modal.value?.index), - isOpen: computed(() => modal.value?.isOpen), - modalContext: computed(() => modal.value?.modalContext), - onTopOfStack: computed(() => modal.value?.onTopOfStack), reload: (...args) => modal.value?.reload(...args), setOpen: (...args) => modal.value?.setOpen(...args), - shouldRender: computed(() => modal.value?.shouldRender), + + get config() { + return modal.value?.config + }, + get id() { + return modal.value?.id + }, + get index() { + return modal.value?.index + }, + get isOpen() { + return modal.value?.isOpen + }, + get modalContext() { + return modal.value?.modalContext + }, + get onTopOfStack() { + return modal.value?.onTopOfStack + }, + get shouldRender() { + return modal.value?.shouldRender + }, })