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

WIP fix for referring local modal (see #53) #57

Merged
merged 3 commits into from
Nov 20, 2024
Merged
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
19 changes: 18 additions & 1 deletion demo-app/resources/js/Pages/Local.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<Container>
@@ -15,11 +26,17 @@ export default function Local() {
</ModalLink>
</div>
</Container>
<Modal name="local">
<Modal name="local" ref={modalRef}>
This is a local modal
<ModalLink href="/roles/create">
Create Role
</ModalLink>
<button onClick={closeModal}>
Close Modal through Ref
</button>
<button onClick={alertModalId}>
Alert Modal ID
</button>
</Modal>
</>
);
21 changes: 20 additions & 1 deletion demo-app/resources/js/Pages/Local.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
<script setup>
import Container from './Container.vue'
import { Modal, ModalLink } from '@inertiaui/modal-vue'
import { ref } from 'vue';

const modalRef = ref(null);

function closeModal() {
modalRef.value.close();
}

function alertModalId() {
alert(modalRef.value.id);
}
</script>

<template>
@@ -14,11 +25,19 @@ import { Modal, ModalLink } from '@inertiaui/modal-vue'
</div>
</Container>

<Modal name="local">
<Modal name="local" ref="modalRef">
This is a local modal

<ModalLink href="/roles/create">
Create Role
</ModalLink>

<button @click="closeModal">
Close Modal through Ref
</button>

<button @click="alertModalId">
Alert Modal ID
</button>
</Modal>
</template>
29 changes: 29 additions & 0 deletions demo-app/tests/Browser/LocalModalTest.php
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
2 changes: 1 addition & 1 deletion demo-app/tests/Feature/DispatchBaseUrlRequestTest.php
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ class DispatchBaseUrlRequestTest extends TestCase
{
protected DispatchBaseUrlRequest $dispatcher;

public function setUp(): void
protected function setUp(): void
{
parent::setUp();

2 changes: 1 addition & 1 deletion demo-app/tests/Feature/RedirectorTest.php
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 2 additions & 2 deletions demo-app/tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -12,15 +12,15 @@ abstract class TestCase extends BaseTestCase
use ModalTestCase;
use RefreshDatabase;

public function setUp(): void
protected function setUp(): void
{
parent::setUp();

$this->withoutVite();
Carbon::setTestNow('2024-06-01 12:00:00');
}

public function tearDown(): void
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
53 changes: 38 additions & 15 deletions react/src/HeadlessModal.jsx
Original file line number Diff line number Diff line change
@@ -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],
)
41 changes: 28 additions & 13 deletions vue/src/HeadlessModal.vue
Original file line number Diff line number Diff line change
@@ -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(() => {
31 changes: 23 additions & 8 deletions vue/src/Modal.vue
Original file line number Diff line number Diff line change
@@ -3,26 +3,41 @@ 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)

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
},
})
</script>