-
Notifications
You must be signed in to change notification settings - Fork 16
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
ui: modal infrastructure #94
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raynasorint Thanks for the PR.
-
https://vitest.dev/guide/#configuring-vitest says that if you're using vite to just use the existing vite.config.ts and add the related config there.
-
CI should execute tests.
-
I'll put the test file in the same directory of the source and with .test.ts extension (i.e
src/components/modals/configmationDialog.spec.ts
af0017b
to
57ccfbf
Compare
57ccfbf
to
d3dcd4e
Compare
d3dcd4e
to
faa4c21
Compare
faa4c21
to
1ba65ee
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raynasorint Modal usually requires also focus trapping (avoid focusing on elements outside the modal using keys like tab). You can add this in this PR or add a TODO and implement it in another PR. I suggest to use vueuse useFocusTrap composable function.
src/components/modals/baseModal.vue
Outdated
@@ -0,0 +1,20 @@ | |||
<template> | |||
<Teleport to="#modals"> | |||
<div v-if="show" class=""> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is show here and in confirmationDialog really needed? Isn't enough to put a v-if on the component tag?
1ba65ee
to
795f448
Compare
id="modal-confirm" | ||
type="button" | ||
class="btn btn-blue font-bold py-2 px-4 rounded" | ||
@click="$emit('result', true)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reuse emitResult.
id="modal-cancel" | ||
type="button" | ||
class="btn btn-gray font-bold py-2 px-4 rounded" | ||
@click="$emit('result', false)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reuse emitResult.
emit('result', value); | ||
} | ||
const dismissModal = () => { | ||
emit('result', false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reuse emitResult.
src/components/modals/baseModal.vue
Outdated
><UseFocusTrap :options="options"> | ||
<div | ||
class="fixed inset-0 bg-gray-900 bg-opacity-40" | ||
ref="target" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're using the UseFocusTrap component so the ref isn't used and needed.
So, or you only use the component (https://vueuse.org/integrations/useFocusTrap/#using-component) or you only use the composable (https://vueuse.org/integrations/useFocusTrap/#usage).
src/components/modals/baseModal.vue
Outdated
|
||
const dismissModal = () => { | ||
emit('dismiss', false); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could also add a test on baseModal to test that it works correctly and also focus trap works.
expect(wrapper.emitted()).toHaveProperty('result'); | ||
const result = wrapper.emitted('result')!; | ||
expect(result).toHaveLength(1); | ||
expect(result[0]).toStrictEqual([true]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could also add a new test to check the correct handling of baseElement dismiss.
44f6534
to
14f61f8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raynasorint Looks like confirmation dialog disappeared. The new basemodal is instead a confirmation dialog... I'm missing something.
/*test('baseModal esc keyboard', async () => { | ||
const wrapper = mount(baseModal, { | ||
props: { show: true }, | ||
global: { | ||
stubs: { | ||
teleport: true, | ||
}, | ||
}, | ||
}); | ||
// Simulate Escape key press | ||
await wrapper.trigger('keydown.esc'); | ||
|
||
const dismissEvent = wrapper.emitted('result')!; | ||
expect(dismissEvent).toBeCalled(); | ||
// expect(dismissEvent).toBeTruthy(); | ||
expect(dismissEvent[0]).toEqual([false]); | ||
// expect(dismissEvent[0][0]).toBe(false); | ||
});*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this test is commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the confirmationDialog was removed due some problems with our tests.
I had difficulty with esting the focus trap in more modals.
I prefered to use only baseModal for now, including useConfirmDialog().
I removed the useless line too and export in cost the repeated code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please restore both dialogs since:
- they were logically better
- Changing an implementation because tests don't work isn't the right thing to do.
- Additionally baseModal is not a "base modal anymore" but a confirmation dialog.
If you have issues with some tests push also them so I can take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I restored them.
@sgotti I also pushed. Thank you
14f61f8
to
fa2f9e6
Compare
e9b186d
to
c9951fb
Compare
@sgotti I merged your pr, |
I don't know what you mean by "merging my pr" but your branch is totally messed up. You should use |
c9951fb
to
6580633
Compare
Sorry my bad. I've finished to rebase now. |
src/components/modals/baseModal.vue
Outdated
tabbableOptions: { | ||
displayCheck: 'none', | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide some details on why this option has been defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this option to fix an error in my coverage test. The error message was the following:
Error: Your focus-trap must have at least one container with at least one tabbable node in it at all times.
activate node_modules/.pnpm/@vueuse+integrations@9.13.0_focus-trap@7.5.3_universal-cookie@4.0.4_vue@3.2.47/node_modules/@vueuse/integrations/useFocusTrap/component.mjs:11:41
9| let trap;
10| const target = ref();
11| const activate = () => trap && trap.activate();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should only activate this option in tests by mocking it in some ways. See https://github.com/focus-trap/tabbable#testing-in-jsdom
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to mock the test but didn't work anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are many ways to do this. A simple one could be:
diff --git a/src/components/modals/baseModal.vue b/src/components/modals/baseModal.vue
index 8ffb8d6..211023c 100644
--- a/src/components/modals/baseModal.vue
+++ b/src/components/modals/baseModal.vue
@@ -20,10 +20,16 @@ const options: UseFocusTrapOptions = {
onDeactivate: () => {
emit('dismiss', false);
},
- tabbableOptions: {
- displayCheck: 'none',
- },
};
+
+// tests outside real browsers like jsdom require tabbableOptions.displayCheck
+// set to none (https://github.com/focus-trap/tabbable#testing-in-jsdom)
+if (import.meta.env.TEST) {
+ options.tabbableOptions = {
+ displayCheck: 'none',
+ };
+}
+
const emit = defineEmits<{
(e: 'dismiss', value: boolean): void;
}>();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sgotti somewhere where ? To the same pr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create a new branch on your fork and push a commit with the changes that cause issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the call to it(...
never executed the test since you wrapped it by an async function that's never awaited. So removing it(...)
made the test run and it failed because it wasn't correct.
This is a working test
import { mount } from '@vue/test-utils';
import baseModal from './baseModal.vue';
test('baseModal', async () => {
const wrapper = mount(baseModal, {
props: { show: true },
global: {
stubs: {
teleport: true,
},
},
attachTo: document.body,
});
// wait for focus trap to be ready
await wrapper.vm.$nextTick();
await wrapper.find('[tabindex="0"]').trigger('keydown', { key: 'Escape' });
expect(wrapper.emitted().dismiss).toHaveLength(1);
expect(wrapper.emitted().dismiss[0]).toEqual([false]);
});
The primary issue was that the component by default isn't attached to the document so focus trap wasn't receiving the key events.
Also note the use of expect(wrapper...
like explained in the vitest docs to avoid the 'non-null assertion' that's reported as a warning by eslint. You should do the same also in the confirmationDialog tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sgotti thank you so much for the help.
I tried to add the nextTrick() function but I didn't attached the primary component so I think that was the problem.
I put the correct code now.
Thanks
14219d5
to
19e0b45
Compare
19e0b45
to
8774cc5
Compare
8774cc5
to
fb6aaa3
Compare
Required for the PR #89.