diff --git a/.storybook/preview.js b/.storybook/preview.js
index 7029879d6..645534a61 100644
--- a/.storybook/preview.js
+++ b/.storybook/preview.js
@@ -22,7 +22,7 @@ import Tab from '@/components/Tabs/Tab.vue';
import Tabs from '@/components/Tabs/Tabs.vue';
import FloatingVue from 'floating-vue';
-import PkpDialog from '@/components/Modal/Dialog.vue';
+import ModalManager from '@/components/Modal/ModalManager.vue';
import VueScrollTo from 'vue-scrollto';
@@ -116,9 +116,9 @@ const preview = {
/** Globally Available Dialog */
(story) => ({
setup() {},
- components: {story, PkpDialog},
+ components: {story, ModalManager},
template: `
`,
}),
diff --git a/src/components/Container/Page.vue b/src/components/Container/Page.vue
index d762f98a8..c15ecff97 100644
--- a/src/components/Container/Page.vue
+++ b/src/components/Container/Page.vue
@@ -1,6 +1,5 @@
diff --git a/src/components/Modal/Dialog.vue b/src/components/Modal/Dialog.vue
index acb28f5e0..8246acda6 100644
--- a/src/components/Modal/Dialog.vue
+++ b/src/components/Modal/Dialog.vue
@@ -1,6 +1,6 @@
-
+
diff --git a/src/components/Modal/SideModal.mdx b/src/components/Modal/SideModal.mdx
index 0b5457ce3..19a37ed05 100644
--- a/src/components/Modal/SideModal.mdx
+++ b/src/components/Modal/SideModal.mdx
@@ -19,9 +19,7 @@ To correctly handle accessibility (title, description) and focus management - [h
## Usage
-Important to keep in mind that if you need to nest these modals, its necessary to define them inside each other, which ensures they correctly handle mouse/keyboard events.
-
-We recommend to define modals as individual component files, rather than inline them, that ensures that the `setup` function and any other component life cycle event are triggered as modal is opened/closed. Therefore its easier to control when for example to fetch data from API. Note that in stories the `SideModalBody` is inlined inside `SideModal`, so its easier to see the source code of examples.
+We recommend to define modals as individual component files, rather than inline them, that ensures that the `setup` function and any other component life cycle event are triggered as modal is opened/closed. Therefore its easier to control when for example to fetch data from API. We might introduce option to define inline modals for basic use cases in future
### Defining Modal Component
@@ -55,29 +53,27 @@ We recommend to define modals as individual component files, rather than inline
```
-### Controlling Modal Component
+### Opening SideModal
-```html
-/** SubmissionModal.vue */
-
- Open Modal
-
-
-
-
-
-```
+### Closing SideModal
-## SideModal Props
+To close the modal from the inside of the modal, there is `closeModal` slot prop available. Full example is in SideModalWithTabs story.
-
+```html
+
+ Title
+
+
+
+
+```
## SideModalBody Slots
diff --git a/src/components/Modal/SideModal.stories.js b/src/components/Modal/SideModal.stories.js
index 3aa7ac09c..bf01fd646 100644
--- a/src/components/Modal/SideModal.stories.js
+++ b/src/components/Modal/SideModal.stories.js
@@ -1,6 +1,7 @@
import {within, userEvent} from '@storybook/test';
import {ref} from 'vue';
import SideModal from './SideModal.vue';
+import {useModal} from '@/composables/useModal.js';
import SideModalBody from './SideModalBody.vue';
import SideModalLayout2Columns from './SideModalLayout2Columns.vue';
import PkpForm from '@/components/Form/Form.vue';
@@ -16,44 +17,45 @@ export default {
component: SideModal,
};
+const SideModalBase = {
+ components: {SideModalBody},
+ template: `
+
+ 325
+ Title
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+
+ Additional info, check SubmissionSummaryModal.vue for good example
+
+
+
+ View submission in detail
+
+
+
+
+
+ `,
+};
+
export const Base = {
render: (args) => ({
- components: {SideModal, SideModalBody},
+ components: {SideModal, SideModalBase},
setup() {
- const isModalOpened = ref(false);
- function closeModal() {
- isModalOpened.value = false;
+ const {openSideModal} = useModal();
+ function openModal() {
+ openSideModal(SideModalBase, {});
}
- return {isModalOpened, closeModal};
+ return {openModal};
},
template: `
-
+
Open Modal
-
-
- 325
- Title
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-
-
- Additional info, check SubmissionSummaryModal.vue for good example
-
-
-
- View submission in detail
-
-
-
-
-
-
`,
}),
decorators: [
@@ -72,76 +74,81 @@ export const Base = {
},
};
+const SideModalTwoColumns = {
+ components: {SideModal, SideModalBody, SideModalLayout2Columns},
+ setup() {
+ const metadata = [
+ {name: 'Type', value: 'Article'},
+ {
+ name: 'Abstract',
+ value:
+ 'Nam id quam mollis, porta dui vel, accumsan sem. Sed commodo felis tristique justo pulvinar lacinia. Etiam pulvinar tortor et dui malesuada fringilla. Nullam leo risus, luctus ut sem ut, dignissim luctus quam. Nulla vel varius urna. Cras eget odio tellus. Proin eu nisi vehicula nulla laculis lobortis.',
+ },
+ ];
+ const generalInformation = [
+ {name: 'Editors request', value: 'dd-mm-yyyy'},
+ {
+ name: 'Response due date',
+ value: 'dd-mm-yyy',
+ },
+ ];
+ return {metadata, generalInformation};
+ },
+ template: `
+
+ 970
+ Round 1 Review submitted by you for
+
+ Is blended e-learning as measured by an achievement test and self-assessment better than traditional classroom learning for vocational high school students?
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
+ minim veniam, quis nostrud exercitation ullamco laboris nisi ut
+ aliquip ex ea commodo consequat.
+
+
+
+ Article Metadata
+
+
{{ item.name }}:
+
{{item.value}}
+
+
+
+
+ General Inforation
+
+
{{ item.name }}:
+
{{item.value}}
+
+
+
+
+
+
+ `,
+};
+
export const TwoColumnsLayout = {
render: (args) => ({
- components: {SideModal, SideModalBody, SideModalLayout2Columns},
+ components: {SideModalTwoColumns},
setup() {
- const isModalOpened = ref(false);
- function closeModal() {
- isModalOpened.value = false;
+ const {openSideModal} = useModal();
+
+ function openModal() {
+ openSideModal(SideModalTwoColumns);
}
- const metadata = [
- {name: 'Type', value: 'Article'},
- {
- name: 'Abstract',
- value:
- 'Nam id quam mollis, porta dui vel, accumsan sem. Sed commodo felis tristique justo pulvinar lacinia. Etiam pulvinar tortor et dui malesuada fringilla. Nullam leo risus, luctus ut sem ut, dignissim luctus quam. Nulla vel varius urna. Cras eget odio tellus. Proin eu nisi vehicula nulla laculis lobortis.',
- },
- ];
- const generalInformation = [
- {name: 'Editors request', value: 'dd-mm-yyyy'},
- {
- name: 'Response due date',
- value: 'dd-mm-yyy',
- },
- ];
-
- return {isModalOpened, closeModal, metadata, generalInformation};
+
+ return {openModal};
},
template: `
-
+
Open Modal
-
-
- 970
- Round 1 Review submitted by you for
-
- Is blended e-learning as measured by an achievement test and self-assessment better than traditional classroom learning for vocational high school students?
-
-
-
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
- minim veniam, quis nostrud exercitation ullamco laboris nisi ut
- aliquip ex ea commodo consequat.
-
-
-
- Article Metadata
-
-
{{ item.name }}:
-
{{item.value}}
-
-
-
-
- General Inforation
-
-
{{ item.name }}:
-
{{item.value}}
-
-
-
-
-
-
-
`,
}),
decorators: [
@@ -149,7 +156,6 @@ export const TwoColumnsLayout = {
template: '
',
}),
],
-
args: {},
play: async ({canvasElement}) => {
// Assigns canvas to the component root element
@@ -159,46 +165,51 @@ export const TwoColumnsLayout = {
await user.click(canvas.getByText('Open Modal'));
},
};
+
+const SideModalWithForm = {
+ components: {SideModalBody, PkpForm},
+ setup() {
+ const form = ref({
+ ...cloneDeep(FormMock),
+ action: 'https://httpbin.org',
+ method: 'GET',
+ });
+
+ return {form};
+ },
+ template: `
+
+
+ Title
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+
+
+
+ `,
+};
+
export const WithForm = {
render: (args) => ({
- components: {SideModal, SideModalBody, PkpForm},
+ components: {},
setup() {
- const isModalOpened = ref(false);
- function closeModal() {
- isModalOpened.value = false;
+ const {openSideModal} = useModal();
+ function openModal() {
+ openSideModal(SideModalWithForm);
}
- const form = ref({
- ...cloneDeep(FormMock),
- action: 'https://httpbin.org',
- method: 'GET',
- });
-
- return {isModalOpened, closeModal, form};
+ return {openModal};
},
template: `
-
+
Modal with Form
-
-
-
- Title
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-
-
-
-
`,
}),
play: async ({canvasElement}) => {
@@ -228,66 +239,67 @@ export const WithForm = {
args: {},
};
+const SideModalWithTabs = {
+ components: {SideModalBody, Tab, Tabs},
+ setup() {},
+ template: `
+
+
+ Title
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+
+
+
+
+ Avoid complex interactions in modals whenever possible. Deeply nested
+ tabs like this are often a sign that a single modal is trying to do too
+ much. If there is more than one task that can be completed in a modal,
+ it is usually better to use a different UI pattern.
+
+
+
+
+
+ Lorem ipsum dolor sit amet
+
+
+ Amet sit dolor ipsum Lorem
+
+
+ Dolor ipsum sit lorem amet
+
+
+
+ This is the second tab.
+ This is the third tab.
+
+
+
+
+
+
+ `,
+};
+
export const WithTabs = {
render: (args) => ({
- components: {SideModal, SideModalBody, Tab, Tabs},
+ components: {},
setup() {
- const isModalOpened = ref(false);
- function closeModal() {
- isModalOpened.value = false;
+ const {openSideModal} = useModal();
+ function openModal() {
+ openSideModal(SideModalWithTabs);
}
- return {isModalOpened, closeModal};
+ return {openModal};
},
template: `
-
+
Modal with Tabs
-
-
-
- Title
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-
-
-
-
- Avoid complex interactions in modals whenever possible. Deeply nested
- tabs like this are often a sign that a single modal is trying to do too
- much. If there is more than one task that can be completed in a modal,
- it is usually better to use a different UI pattern.
-
-
-
-
-
- Lorem ipsum dolor sit amet
-
-
- Amet sit dolor ipsum Lorem
-
-
- Dolor ipsum sit lorem amet
-
-
-
- This is the second tab.
- This is the third tab.
-
-
-
-
-
-
-
-
`,
}),
decorators: [
@@ -305,50 +317,60 @@ export const WithTabs = {
},
};
+const SideModalNested2 = {
+ components: {SideModalBody},
+ template: `
+
+ Second Level Side Modal
+
+
+
+ `,
+};
+
+const SideModalNested1 = {
+ components: {SideModalBody, SideModalNested2},
+
+ setup() {
+ const {openSideModal} = useModal();
+ function openModal() {
+ openSideModal(SideModalNested2);
+ }
+ return {openModal};
+ },
+ template: `
+
+ First Level Side Modal
+
+
+
+ `,
+};
+
export const NestedModal = {
render: (args) => ({
- components: {SideModal, SideModalBody},
+ components: {SideModalNested1},
setup() {
- const isModalOpened = ref(false);
- function closeModal() {
- isModalOpened.value = false;
- }
+ const {openSideModal} = useModal();
- const isModalOpened2 = ref(false);
- function closeModal2() {
- isModalOpened2.value = false;
+ function openModal() {
+ openSideModal(SideModalNested1);
}
-
- return {isModalOpened, closeModal, isModalOpened2, closeModal2};
+ return {openModal};
},
template: `
-
+
Nested modal
-
-
- First Level Side Modal
-
-
-
- Second Level Side Modal
-
-
-
-
-
+
`,
}),
decorators: [
@@ -361,7 +383,6 @@ export const NestedModal = {
// Get parent element, as modals are in canvasElement sibling element
const canvas = within(canvasElement.parentElement);
const user = userEvent.setup();
- console.log(canvasElement);
await user.click(canvas.getByText('Nested modal'));
await user.click(await canvas.findByText('Open Second Modal'));
diff --git a/src/components/Modal/SideModal.vue b/src/components/Modal/SideModal.vue
index b25fccf8d..ad471f676 100644
--- a/src/components/Modal/SideModal.vue
+++ b/src/components/Modal/SideModal.vue
@@ -46,11 +46,16 @@ import {
TransitionChild,
} from '@headlessui/vue';
-defineProps({
+const props = defineProps({
open: {
type: Boolean,
required: true,
},
+ modalLevel: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
});
const emit = defineEmits(['close']);
@@ -60,7 +65,7 @@ function registerCloseCallback(callback) {
closeCallbacks.value.push(callback);
}
-function handleClose() {
+function handleClose(event) {
let canClose = true;
closeCallbacks.value.forEach((callback) => (canClose = callback()));
if (canClose) {
@@ -72,4 +77,5 @@ function handleClose() {
provide('closeModal', handleClose);
provide('registerCloseCallback', registerCloseCallback);
+provide('modalLevel', ref(props.modalLevel));
diff --git a/src/components/Modal/SideModalBody.vue b/src/components/Modal/SideModalBody.vue
index 40176a653..f98490cbc 100644
--- a/src/components/Modal/SideModalBody.vue
+++ b/src/components/Modal/SideModalBody.vue
@@ -53,34 +53,24 @@
diff --git a/src/components/Modal/SideModalBodyAjax.vue b/src/components/Modal/SideModalBodyLegacyAjax.vue
similarity index 73%
rename from src/components/Modal/SideModalBodyAjax.vue
rename to src/components/Modal/SideModalBodyLegacyAjax.vue
index 604a6c09b..08560fbf0 100644
--- a/src/components/Modal/SideModalBodyAjax.vue
+++ b/src/components/Modal/SideModalBodyLegacyAjax.vue
@@ -1,9 +1,9 @@
-
- Assign editors
+
+ {{ options.title }}
diff --git a/src/composables/useDialog.js b/src/composables/useDialog.js
deleted file mode 100644
index f6f017ea0..000000000
--- a/src/composables/useDialog.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import {useModalStore} from '@/stores/modalStore';
-
-export function useDialog() {
- const modalStore = useModalStore();
-
- function openDialog(props) {
- modalStore.openDialog(props);
- }
-
- return {openDialog};
-}
diff --git a/src/composables/useDialog.mdx b/src/composables/useDialog.mdx
deleted file mode 100644
index 93f575dea..000000000
--- a/src/composables/useDialog.mdx
+++ /dev/null
@@ -1,54 +0,0 @@
-import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks';
-
-import * as UseDialogStories from './useDialog.stories.js';
-
-
-
-# useDialog
-
-# Usage
-
-Dialogs provide a quick way to show a simple confirmation prompt. Import `useDialog` composable and use the openDialog() method to create a dialog.
-
-```html
-
- Open Dialog
-
-
-
-```
-
-
diff --git a/src/composables/useModal.js b/src/composables/useModal.js
new file mode 100644
index 000000000..2fb67e2b5
--- /dev/null
+++ b/src/composables/useModal.js
@@ -0,0 +1,15 @@
+import {useModalStore} from '@/stores/modalStore';
+
+export function useModal() {
+ const modalStore = useModalStore();
+
+ function openDialog(props) {
+ modalStore.openDialog(props);
+ }
+
+ function openSideModal(component, props) {
+ modalStore.openSideModal(component, props);
+ }
+
+ return {openDialog, openSideModal};
+}
diff --git a/src/composables/useModal.mdx b/src/composables/useModal.mdx
new file mode 100644
index 000000000..a5e494b25
--- /dev/null
+++ b/src/composables/useModal.mdx
@@ -0,0 +1,72 @@
+import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks';
+
+import * as useModalStories from './useModal.stories.js';
+
+
+
+# useModal
+
+## openDialog
+
+Dialogs provide a quick way to show a simple confirmation prompt. Import `useModal` composable and use the openDialog() method to create a dialog.
+
+```html
+
+ Open Dialog
+
+
+
+```
+
+## openSideModal
+
+SideModal should be created as separate component, follow examples from [SideModal](../?path=/docs/components-sidemodal--docs) component, where `openSideModal()` is also used to control the SideModals.
+
+Once you have the SideModal component created just pass the component as first argument and its props as second argument.
+
+Alternatively its possible to pass component name if its globally registered.
+
+```javascript
+
+
+
+```
diff --git a/src/composables/useDialog.stories.js b/src/composables/useModal.stories.js
similarity index 92%
rename from src/composables/useDialog.stories.js
rename to src/composables/useModal.stories.js
index ca08c2813..aaba9ca85 100644
--- a/src/composables/useDialog.stories.js
+++ b/src/composables/useModal.stories.js
@@ -1,14 +1,14 @@
import {within, userEvent} from '@storybook/test';
-import {useDialog} from './useDialog';
+import {useModal} from './useModal';
import PkpButton from '@/components/Button/Button.vue';
export default {
- title: 'composables/useDialog',
+ title: 'composables/useModal',
render: (args) => ({
components: {PkpButton},
setup() {
- const {openDialog} = useDialog();
+ const {openDialog} = useModal();
return {openDialog, args};
},
@@ -18,7 +18,7 @@ export default {
}),
};
-export const BasicExample = {
+export const DialogBasic = {
args: {
buttonName: 'Basic Example',
name: 'basic',
@@ -54,7 +54,7 @@ export const BasicExample = {
],
};
-export const FullExample = {
+export const DialogComplex = {
args: {
buttonName: 'Full Example',
diff --git a/src/pages/submissions/AssignEditorsModal.vue b/src/pages/submissions/AssignEditorsModal.vue
deleted file mode 100644
index 3f8f7b01f..000000000
--- a/src/pages/submissions/AssignEditorsModal.vue
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
- Assign editors
-
-
-
-
diff --git a/src/pages/submissions/SubmissionSummaryModal.vue b/src/pages/submissions/SubmissionSummaryModal.vue
index bafd73bbf..8be2e6060 100644
--- a/src/pages/submissions/SubmissionSummaryModal.vue
+++ b/src/pages/submissions/SubmissionSummaryModal.vue
@@ -92,7 +92,12 @@
+ dashboardStore.openAssignParticipantModal(
+ props.selectedSubmission,
+ )
+ "
>
Assign Editors
@@ -100,13 +105,6 @@
-
-
-
@@ -115,14 +113,17 @@ import {storeToRefs} from 'pinia';
import PkpButton from '@/components/Button/Button.vue';
import SideModalBody from '@/components/Modal/SideModalBody.vue';
import StageBubble from '@/components/StageBubble/StageBubble.vue';
-import SideModal from '@/components/Modal/SideModal.vue';
-import AssignEditorsModal from '@/pages/submissions/AssignEditorsModal.vue';
+
+import {useSubmissionsPageStore} from './submissionsPageStore';
import {useSubmissionSummaryStore} from '@/pages/submissions/submissionSummaryStore';
const pkp = window.pkp;
-const summaryStore = useSubmissionSummaryStore();
+const props = defineProps({selectedSubmission: {type: Object, required: true}});
+
+const summaryStore = useSubmissionSummaryStore(props);
+const dashboardStore = useSubmissionsPageStore(props);
const {submission} = storeToRefs(summaryStore);
diff --git a/src/pages/submissions/SubmissionsPage.vue b/src/pages/submissions/SubmissionsPage.vue
index b0d349bd2..3efa0d6a5 100644
--- a/src/pages/submissions/SubmissionsPage.vue
+++ b/src/pages/submissions/SubmissionsPage.vue
@@ -50,40 +50,14 @@
-
-
-
-
-
-
-
-
-
-