-
+
- Close panel
+ {{ t('common.close') }}
@@ -47,21 +52,43 @@
+
+
+```
+
+
diff --git a/src/composables/useDialog.stories.js b/src/composables/useDialog.stories.js
new file mode 100644
index 000000000..68856439c
--- /dev/null
+++ b/src/composables/useDialog.stories.js
@@ -0,0 +1,99 @@
+import {within, userEvent} from '@storybook/testing-library';
+
+import {useDialog} from './useDialog';
+import PkpButton from '@/components/Button/Button.vue';
+
+export default {
+ title: 'composables/useDialog',
+ render: (args) => ({
+ components: {PkpButton},
+ setup() {
+ const {openDialog} = useDialog();
+
+ return {openDialog, args};
+ },
+ template: `
+
{{args.buttonName}}
+ `,
+ }),
+};
+
+export const BasicExample = {
+ args: {
+ buttonName: 'Basic Example',
+ name: 'basic',
+ title: 'Submit Article',
+ message: 'Are you sure you want to submit this article?',
+ actions: [
+ {
+ label: 'Confirm',
+ isPrimary: true,
+ callback: (close) => {
+ // Simulate a server request
+ setTimeout(() => close(), 2000);
+ },
+ },
+ {
+ label: 'Cancel',
+ isWarnable: true,
+ callback: (close) => close(),
+ },
+ ],
+ },
+ play: async ({canvasElement}) => {
+ // Assigns canvas to the component root element
+ const canvas = within(canvasElement);
+ const user = userEvent.setup();
+
+ await user.click(canvas.getByText('Basic Example'));
+ },
+ decorators: [
+ () => ({
+ template: '
',
+ }),
+ ],
+};
+
+export const FullExample = {
+ args: {
+ buttonName: 'Full Example',
+
+ name: 'full',
+ title: 'Several Actions',
+ message:
+ 'This dialog includes an action that is a link (Visit Page). It also logs something to the console when the modal is closed. Dialogs should have 2 or 3 actions at the most.',
+ actions: [
+ {
+ label: 'Confirm',
+ isPrimary: true,
+ callback: (close) => {
+ // Simulate a server request
+ setTimeout(() => close(), 2000);
+ },
+ },
+ {
+ label: 'Visit Page',
+ element: 'a',
+ href: 'https://example.org',
+ },
+ {
+ label: 'Cancel',
+ isWarnable: true,
+ callback: (close) => close(),
+ },
+ ],
+ close: () => console.log('closed full example dialog'), // eslint-disable-line
+ },
+ play: async ({canvasElement}) => {
+ // Assigns canvas to the component root element
+ const canvas = within(canvasElement);
+ const user = userEvent.setup();
+
+ await user.click(canvas.getByText('Full Example'));
+ },
+ decorators: [
+ () => ({
+ template: '
',
+ }),
+ ],
+};
diff --git a/src/composables/useFetch.js b/src/composables/useFetch.js
index 4ec8906b5..c2816daba 100644
--- a/src/composables/useFetch.js
+++ b/src/composables/useFetch.js
@@ -1,6 +1,6 @@
import {ref, unref} from 'vue';
import {ofetch, createFetch} from 'ofetch';
-import {useDialogStore} from '@/stores/dialogStore';
+import {useModalStore} from '@/stores/modalStore';
let ofetchInstance = ofetch;
@@ -53,7 +53,7 @@ export function useFetch(url, options = {}) {
const query = ref(_query || {});
const body = ref(_body || undefined);
- const dialogStore = useDialogStore();
+ const modalStore = useModalStore();
const isLoading = ref(false);
const data = ref(null);
const validationError = ref(null);
@@ -111,7 +111,7 @@ export function useFetch(url, options = {}) {
return;
}
- dialogStore.openDialogNetworkError(e);
+ modalStore.openDialogNetworkError(e);
} finally {
lastRequestController = null;
isLoading.value = false;
diff --git a/src/composables/useFetch.test.js b/src/composables/useFetch.test.js
index 5f9333697..27bd3a3e6 100644
--- a/src/composables/useFetch.test.js
+++ b/src/composables/useFetch.test.js
@@ -14,7 +14,7 @@ import {HttpResponse, http, delay} from 'msw';
import {setActivePinia, createPinia} from 'pinia';
import {useFetch} from './useFetch';
-import {useDialogStore} from '@/stores/dialogStore';
+import {useModalStore} from '@/stores/modalStore';
export const restHandlers = [
http.get('http://mock/get/status200', async ({request}) => {
@@ -198,14 +198,14 @@ describe('features', () => {
});
test('network dialog error is displayed if there is http code other than 2XX', async () => {
const url = ref('http://mock/get/status500');
- const dialogStore = useDialogStore();
- expect(dialogStore.dialogOpened).toBe(false);
+ const modalStore = useModalStore();
+ expect(modalStore.dialogOpened).toBe(false);
const {fetch} = useFetch(url);
await fetch();
- expect(dialogStore.dialogOpened).toBe(true);
- dialogStore.closeDialog();
- expect(dialogStore.dialogOpened).toBe(false);
+ expect(modalStore.dialogOpened).toBe(true);
+ modalStore.closeDialog();
+ expect(modalStore.dialogOpened).toBe(false);
});
});
diff --git a/src/mixins/dialog.js b/src/mixins/dialog.js
index 1b66f8cae..81eb2afb4 100644
--- a/src/mixins/dialog.js
+++ b/src/mixins/dialog.js
@@ -7,7 +7,7 @@
* @see https://vuejs.org/v2/guide/mixins.html
*/
-import {useDialogStore} from '@/stores/dialogStore';
+import {useModalStore} from '@/stores/modalStore';
export default {
methods: {
@@ -17,8 +17,8 @@ export default {
* @param {Object} props Props to pass to the
component
*/
openDialog(props, modalProps, modalEvents) {
- const dialogStore = useDialogStore();
- dialogStore.openDialog(props);
+ const modalStore = useModalStore();
+ modalStore.openDialog(props);
},
},
};
diff --git a/src/pages/submissions/AssignEditorsModal.vue b/src/pages/submissions/AssignEditorsModal.vue
index aff5a94b8..0ef7cbb3e 100644
--- a/src/pages/submissions/AssignEditorsModal.vue
+++ b/src/pages/submissions/AssignEditorsModal.vue
@@ -1,9 +1,9 @@
-
- Assign editors
+
+ Assign editors
diff --git a/src/pages/submissions/SubmissionsPage.vue b/src/pages/submissions/SubmissionsPage.vue
index 9cc59bfca..9115cbe4a 100644
--- a/src/pages/submissions/SubmissionsPage.vue
+++ b/src/pages/submissions/SubmissionsPage.vue
@@ -36,7 +36,6 @@
@@ -46,7 +45,6 @@
/>
diff --git a/src/stores/dialogStore.js b/src/stores/dialogStore.js
deleted file mode 100644
index c79dbc965..000000000
--- a/src/stores/dialogStore.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import {defineStore} from 'pinia';
-import {ref} from 'vue';
-import {t} from '@/utils/i18n';
-export const useDialogStore = defineStore('dialog', () => {
- const currentLevel = ref(0);
- const dialogProps = ref({});
- const dialogOpened = ref(false);
-
- function openDialog(_dialogProps) {
- dialogProps.value = _dialogProps;
- dialogOpened.value = true;
- }
-
- function openDialogNetworkError(fetchError) {
- const msg = fetchError?.data?.errorMessage || t('common.unknownError');
-
- openDialog({
- name: 'ajaxError',
- title: t('common.error'),
- message: msg,
- actions: [
- {
- label: t('common.ok'),
- callback: (close) => close(),
- },
- ],
- });
- }
-
- function closeDialog() {
- dialogProps.value = {};
- dialogOpened.value = false;
- }
-
- return {
- currentLevel,
- dialogProps,
- dialogOpened,
- openDialogNetworkError,
- openDialog,
- closeDialog,
- };
-});
diff --git a/src/stores/modalStore.js b/src/stores/modalStore.js
new file mode 100644
index 000000000..edeb8fd37
--- /dev/null
+++ b/src/stores/modalStore.js
@@ -0,0 +1,81 @@
+import {defineStore} from 'pinia';
+import {ref} from 'vue';
+import {t} from '@/utils/i18n';
+export const useModalStore = defineStore('modal', () => {
+ /**
+ * Dialog Level
+ *
+ * Because Modal from headlessui requires to be correctly nested
+ * Dialog is also available inside SideModalBody and level helps to track
+ * which of the existing dialogs should open
+ */
+ const dialogLevel = ref(0);
+ function increaseDialogLevel() {
+ dialogLevel.value++;
+ }
+
+ function decreaseDialogLevel() {
+ dialogLevel.value--;
+ }
+
+ /** dialogProps coming from openDialog */
+ const dialogProps = ref({});
+ const dialogOpened = ref(false);
+
+ function openDialog(_dialogProps) {
+ dialogProps.value = _dialogProps;
+ dialogOpened.value = true;
+ }
+ function closeDialog() {
+ dialogProps.value = {};
+ dialogOpened.value = false;
+ }
+
+ /** Default network error */
+ function openDialogNetworkError(fetchError) {
+ const msg = fetchError?.data?.errorMessage || t('common.unknownError');
+
+ openDialog({
+ name: 'ajaxError',
+ title: t('common.error'),
+ message: msg,
+ actions: [
+ {
+ label: t('common.ok'),
+ callback: (close) => close(),
+ },
+ ],
+ });
+ }
+
+ /**
+ * Side Modal Level
+ *
+ * To recognise how many side modals is opened to adjust styling
+ */
+ const modalLevel = ref(0);
+ function increaseModalLevel() {
+ modalLevel.value++;
+ }
+
+ function decreaseModalLevel() {
+ modalLevel.value--;
+ }
+
+ return {
+ /** dialog level */
+ decreaseDialogLevel,
+ increaseDialogLevel,
+ /** opening dialog */
+ dialogLevel,
+ dialogProps,
+ dialogOpened,
+ openDialogNetworkError,
+ openDialog,
+ closeDialog,
+ /** side modal level */
+ modalLevel,
+ increaseModalLevel,
+ decreaseModalLevel,
+ };
+});
diff --git a/tailwind.config.js b/tailwind.config.js
index 45bfd3948..7854d66a6 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -25,7 +25,7 @@ const colors = {
};
export default {
- content: ['./index.html', './**/*.vue', './**/*.tpl'],
+ content: ['./index.html', './**/*.vue', './**/*stories.js', './**/*.tpl'],
theme: {
backgroundColor: {
...colors,