diff --git a/.storybook/main.js b/.storybook/main.js index 4cbc04cc3..5ca3a2dfe 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -2,12 +2,12 @@ const config = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ - '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-interactions', - 'storybook-addon-rtl', - "@storybook/addon-mdx-gfm" - ], + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + '@storybook/addon-themes', + '@storybook/addon-mdx-gfm', + ], framework: { name: '@storybook/vue3-vite', options: {}, diff --git a/.storybook/modes.js b/.storybook/modes.js new file mode 100644 index 000000000..9b616bd10 --- /dev/null +++ b/.storybook/modes.js @@ -0,0 +1,14 @@ +export const allModes = { + desktop: { + viewport: 'large', + theme: 'ltr', + }, + 'desktop rtl': { + viewport: 'large', + theme: 'rtl', + }, + // for snapshotting scrollable areas with all content, like modals + desktopLargeHeight: { + viewport: 'largeHeight', + }, +}; diff --git a/.storybook/preview.js b/.storybook/preview.js index d430dff6e..a6bf329ea 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,7 +1,7 @@ /** @type { import('@storybook/vue3').Preview } */ +import {withThemeByDataAttribute} from '@storybook/addon-themes'; import {setup} from '@storybook/vue3'; - import GlobalMixins from '@/mixins/global.js'; import emitter from 'tiny-emitter/instance'; @@ -20,19 +20,24 @@ 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 {useDialogStore} from '@/stores/dialogStore'; + +import VueScrollTo from 'vue-scrollto'; + import '../src/styles/_import.less'; import '../src/styles/_global.less'; -import {initializeRTL} from 'storybook-addon-rtl'; +import {allModes} from './modes'; import {initialize, mswLoader} from 'msw-storybook-addon'; import {createPinia} from 'pinia'; const pinia = createPinia(); -initializeRTL(); // Initialize MSW initialize({ - onUnhandledRequest: ({method, url}) => { + /** To be migrated to msw2 if neede */ + /*onUnhandledRequest: ({method, url}) => { if (url.pathname.includes('://mock/')) { console.error(`Unhandled ${method} request to ${url}. @@ -41,7 +46,7 @@ initialize({ If you wish to mock an error response, please refer to this guide: https://mswjs.io/docs/recipes/mocking-error-responses `); } - }, + },*/ }); setup((app) => { @@ -61,6 +66,8 @@ setup((app) => { }, }); + app.use(VueScrollTo); + app.component('Badge', Badge); app.component('Dropdown', Dropdown); app.component('Icon', Icon); @@ -86,6 +93,43 @@ setup((app) => { const preview = { loaders: [mswLoader], + decorators: [ + withThemeByDataAttribute({ + themes: { + ltr: 'ltr', + rtl: 'rtl', + }, + defaultTheme: 'ltr', + }), + (story, {globals}) => { + /** withThemebyDataAttribute decorator applies attribute after render, which + * is too late fort tinyMCE which needs to detect it on first render correctly + * + */ + document.body.setAttribute('dir', globals.theme); + return story(); + }, + (story) => ({ + components: {story}, + template: '
', + }), + /** Globally Available Dialog */ + (story) => ({ + setup() { + const dialogStore = useDialogStore(); + return {dialogStore}; + }, + components: {story, PkpDialog}, + template: `
+ + +
`, + }), + ], parameters: { actions: {argTypesRegex: '^on[A-Z].*'}, controls: { @@ -104,12 +148,27 @@ const preview = { if (b.id.includes('introduction--docs')) { return 1; } - return a.id === b.id ? 0 : a.id.localeCompare(b.id, undefined, {numeric: true}); },*/ }, + viewport: { + viewports: { + large: {name: 'Large', styles: {width: '1280px', height: '1000px'}}, + /** For scrollable scenarios */ + largeHeight: { + name: 'Large', + styles: {width: '1024px', height: '1500px'}, + }, + }, + }, + chromatic: { + modes: { + desktop: allModes['desktop'], + 'desktop rtl': allModes['desktop rtl'], + }, + }, }, }; diff --git a/.storybook/public/mockServiceWorker.js b/.storybook/public/mockServiceWorker.js index 51d85eeeb..e369128ec 100644 --- a/.storybook/public/mockServiceWorker.js +++ b/.storybook/public/mockServiceWorker.js @@ -2,13 +2,14 @@ /* tslint:disable */ /** - * Mock Service Worker (1.3.2). + * Mock Service Worker (2.0.11). * @see https://github.com/mswjs/msw * - Please do NOT modify this file. * - Please do NOT serve this file on production. */ -const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const INTEGRITY_CHECKSUM = 'c5f7f8e188b673ea4e677df7ea3c5a39' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() self.addEventListener('install', function () { @@ -86,12 +87,6 @@ self.addEventListener('message', async function (event) { self.addEventListener('fetch', function (event) { const { request } = event - const accept = request.headers.get('accept') || '' - - // Bypass server-sent events. - if (accept.includes('text/event-stream')) { - return - } // Bypass navigation requests. if (request.mode === 'navigate') { @@ -112,29 +107,8 @@ self.addEventListener('fetch', function (event) { } // Generate unique request ID. - const requestId = Math.random().toString(16).slice(2) - - event.respondWith( - handleRequest(event, requestId).catch((error) => { - if (error.name === 'NetworkError') { - console.warn( - '[MSW] Successfully emulated a network error for the "%s %s" request.', - request.method, - request.url, - ) - return - } - - // At this point, any exception indicates an issue with the original request/response. - console.error( - `\ -[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, - request.method, - request.url, - `${error.name}: ${error.message}`, - ) - }), - ) + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) }) async function handleRequest(event, requestId) { @@ -146,21 +120,24 @@ async function handleRequest(event, requestId) { // this message will pend indefinitely. if (client && activeClientIds.has(client.id)) { ;(async function () { - const clonedResponse = response.clone() - sendToClient(client, { - type: 'RESPONSE', - payload: { - requestId, - type: clonedResponse.type, - ok: clonedResponse.ok, - status: clonedResponse.status, - statusText: clonedResponse.statusText, - body: - clonedResponse.body === null ? null : await clonedResponse.text(), - headers: Object.fromEntries(clonedResponse.headers.entries()), - redirected: clonedResponse.redirected, + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, }, - }) + [responseClone.body], + ) })() } @@ -196,20 +173,20 @@ async function resolveMainClient(event) { async function getResponse(event, client, requestId) { const { request } = event - const clonedRequest = request.clone() + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone() function passthrough() { - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const headers = Object.fromEntries(clonedRequest.headers.entries()) + const headers = Object.fromEntries(requestClone.headers.entries()) - // Remove MSW-specific request headers so the bypassed requests - // comply with the server's CORS preflight check. - // Operate with the headers as an object because request "Headers" - // are immutable. - delete headers['x-msw-bypass'] + // Remove internal MSW request header so the passthrough request + // complies with any potential CORS preflight checks on the server. + // Some servers forbid unknown request headers. + delete headers['x-msw-intention'] - return fetch(clonedRequest, { headers }) + return fetch(requestClone, { headers }) } // Bypass mocking when the client is not active. @@ -227,31 +204,36 @@ async function getResponse(event, client, requestId) { // Bypass requests with the explicit bypass header. // Such requests can be issued by "ctx.fetch()". - if (request.headers.get('x-msw-bypass') === 'true') { + const mswIntention = request.headers.get('x-msw-intention') + if (['bypass', 'passthrough'].includes(mswIntention)) { return passthrough() } // Notify the client that a request has been intercepted. - const clientMessage = await sendToClient(client, { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - mode: request.mode, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: await request.text(), - bodyUsed: request.bodyUsed, - keepalive: request.keepalive, + const requestBuffer = await request.arrayBuffer() + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, }, - }) + [requestBuffer], + ) switch (clientMessage.type) { case 'MOCK_RESPONSE': { @@ -261,21 +243,12 @@ async function getResponse(event, client, requestId) { case 'MOCK_NOT_FOUND': { return passthrough() } - - case 'NETWORK_ERROR': { - const { name, message } = clientMessage.data - const networkError = new Error(message) - networkError.name = name - - // Rejecting a "respondWith" promise emulates a network error. - throw networkError - } } return passthrough() } -function sendToClient(client, message) { +function sendToClient(client, message, transferrables = []) { return new Promise((resolve, reject) => { const channel = new MessageChannel() @@ -287,17 +260,28 @@ function sendToClient(client, message) { resolve(event.data) } - client.postMessage(message, [channel.port2]) + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)), + ) }) } -function sleep(timeMs) { - return new Promise((resolve) => { - setTimeout(resolve, timeMs) +async function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, }) -} -async function respondWithMock(response) { - await sleep(response.delay) - return new Response(response.body, response) + return mockedResponse } diff --git a/package-lock.json b/package-lock.json index b07b6f953..4df24fc21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,14 +36,15 @@ }, "devDependencies": { "@rushstack/eslint-patch": "^1.3.3", - "@storybook/addon-essentials": "^7.6.0", - "@storybook/addon-interactions": "^7.6.0", - "@storybook/addon-links": "^7.6.0", - "@storybook/addon-mdx-gfm": "^7.6.0", - "@storybook/blocks": "^7.6.0", + "@storybook/addon-essentials": "^7.6.5", + "@storybook/addon-interactions": "^7.6.5", + "@storybook/addon-links": "^7.6.5", + "@storybook/addon-mdx-gfm": "^7.6.5", + "@storybook/addon-themes": "^7.6.5", + "@storybook/blocks": "^7.6.5", "@storybook/testing-library": "^0.2.2", - "@storybook/vue3": "^7.6.0", - "@storybook/vue3-vite": "^7.6.0", + "@storybook/vue3": "^7.6.5", + "@storybook/vue3-vite": "^7.6.5", "@vitejs/plugin-vue": "^4.3.4", "@vue/eslint-config-prettier": "^8.0.0", "autoprefixer": "^10.4.14", @@ -54,15 +55,14 @@ "husky": "^8.0.3", "less": "^4.2.0", "lint-staged": "^14.0.1", - "msw": "^1.3.2", - "msw-storybook-addon": "^1.10.0", + "msw": "^2.0.11", + "msw-storybook-addon": "^2.0.0--canary.122.b3ed3b1.0", "postcss": "^8.4.27", "prettier": "^3.0.2", "prettier-plugin-tailwindcss": "^0.5.6", "react": "^18.2.0", "react-dom": "^18.2.0", - "storybook": "^7.6.0", - "storybook-addon-rtl": "^0.5.0", + "storybook": "^7.6.5", "tailwindcss": "^3.3.3", "vite": "^4.4.9", "vitest": "^1.0.4" @@ -115,9 +115,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", - "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { "@babel/highlight": "^7.23.4", @@ -199,30 +199,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", - "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", - "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", + "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.3", + "@babel/helpers": "^7.23.6", + "@babel/parser": "^7.23.6", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.3", - "@babel/types": "^7.23.3", + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -238,12 +238,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.4.tgz", - "integrity": "sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.4", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -277,14 +277,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -293,17 +293,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", + "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -333,9 +333,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", - "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", + "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -535,9 +535,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -558,14 +558,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.4.tgz", - "integrity": "sha512-HfcMizYz10cr3h29VqyfGL6ZWIjTwWfvYBMsBVGwpcbhNGe3wQ1ZXZRPzZoAHhd9OqHadHqjQ89iVKINXnbzuw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", + "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", "dev": true, "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.4", - "@babel/types": "^7.23.4" + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6" }, "engines": { "node": ">=6.9.0" @@ -657,9 +657,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", - "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1121,9 +1121,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz", - "integrity": "sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", + "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -1270,12 +1270,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz", - "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", + "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1733,13 +1734,13 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.4.tgz", - "integrity": "sha512-39hCCOl+YUAyMOu6B9SmUTiHUU0t/CxJNUmY3qRdJujbqi+lrQcL11ysYUsAvFWPBdhihrv1z0oRG84Yr3dODQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-create-class-features-plugin": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-typescript": "^7.23.3" }, @@ -1814,15 +1815,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.3.tgz", - "integrity": "sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz", + "integrity": "sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", + "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", @@ -1846,25 +1847,25 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.4", "@babel/plugin-transform-async-to-generator": "^7.23.3", "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.3", - "@babel/plugin-transform-classes": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.5", "@babel/plugin-transform-computed-properties": "^7.23.3", "@babel/plugin-transform-destructuring": "^7.23.3", "@babel/plugin-transform-dotall-regex": "^7.23.3", "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.3", - "@babel/plugin-transform-for-of": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", "@babel/plugin-transform-member-expression-literals": "^7.23.3", "@babel/plugin-transform-modules-amd": "^7.23.3", "@babel/plugin-transform-modules-commonjs": "^7.23.3", @@ -1872,15 +1873,15 @@ "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.3", - "@babel/plugin-transform-numeric-separator": "^7.23.3", - "@babel/plugin-transform-object-rest-spread": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.3", - "@babel/plugin-transform-optional-chaining": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", "@babel/plugin-transform-parameters": "^7.23.3", "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", "@babel/plugin-transform-property-literals": "^7.23.3", "@babel/plugin-transform-regenerator": "^7.23.3", "@babel/plugin-transform-reserved-words": "^7.23.3", @@ -2127,20 +2128,20 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.4.tgz", - "integrity": "sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", + "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.23.4", - "@babel/generator": "^7.23.4", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.4", - "@babel/types": "^7.23.4", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -2148,9 +2149,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", - "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -2161,6 +2162,33 @@ "node": ">=6.9.0" } }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/js-levenshtein": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/js-levenshtein/-/js-levenshtein-2.0.1.tgz", + "integrity": "sha512-DERMS3yfbAljKsQc0U2wcqGKUWpdFjwqWuoMugEJlqBnKO180/n+4SR/J8MRDt1AN48X1ovgoD9KrdVXcaa3Rg==", + "dev": true, + "dependencies": { + "js-levenshtein": "^1.1.6" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2180,33 +2208,6 @@ "node": ">=10.0.0" } }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", - "dev": true, - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "dev": true - }, - "node_modules/@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", - "dev": true - }, - "node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", - "dev": true - }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", @@ -3035,44 +3036,29 @@ } }, "node_modules/@mswjs/cookies": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", - "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.0.tgz", + "integrity": "sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==", "dev": true, - "dependencies": { - "@types/set-cookie-parser": "^2.4.0", - "set-cookie-parser": "^2.4.6" - }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@mswjs/interceptors": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.10.tgz", - "integrity": "sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==", + "version": "0.25.13", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.13.tgz", + "integrity": "sha512-xfjR81WwXPHwhDbqJRHlxYmboJuiSaIKpP4I5TJVFl/EmByOU13jOBT9hmEnxcjR3jvFYoqoNKt7MM9uqerj9A==", "dev": true, "dependencies": { - "@open-draft/until": "^1.0.3", - "@types/debug": "^4.1.7", - "@xmldom/xmldom": "^0.8.3", - "debug": "^4.3.3", - "headers-polyfill": "3.2.5", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", "outvariant": "^1.2.1", - "strict-event-emitter": "^0.2.4", - "web-encoding": "^1.1.5" + "strict-event-emitter": "^0.5.1" }, "engines": { - "node": ">=14" - } - }, - "node_modules/@mswjs/interceptors/node_modules/strict-event-emitter": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz", - "integrity": "sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==", - "dev": true, - "dependencies": { - "events": "^3.3.0" + "node": ">=18" } }, "node_modules/@ndelangen/get-tarball": { @@ -3121,10 +3107,26 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, "node_modules/@open-draft/until": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", - "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", "dev": true }, "node_modules/@pkgjs/parseargs": { @@ -4032,12 +4034,12 @@ "dev": true }, "node_modules/@storybook/addon-actions": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.0.tgz", - "integrity": "sha512-yc4d/6j0XaTQPkEMkT0JxWPjRwZUg0oC929/vpouYhaC60Ch/b3PnzUFkSQ2BqgeUUH0c9wfzs/9np6USRXpBQ==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.5.tgz", + "integrity": "sha512-lW/m9YcaNfBZk+TZLxyzHdd563mBWpsUIveOKYjcPdl/q0FblWWZrRsFHqwLK1ldZ4AZXs8J/47G8CBr6Ew2uQ==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.0", + "@storybook/core-events": "7.6.5", "@storybook/global": "^5.0.0", "@types/uuid": "^9.0.1", "dequal": "^2.0.2", @@ -4050,9 +4052,9 @@ } }, "node_modules/@storybook/addon-backgrounds": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.0.tgz", - "integrity": "sha512-8BMwVXiazDQlqYS8Snzowzn6MbcsigaUxhWzWoCkLUmyOvciywvOjQD9241wbEdLc/rdFZAPRGoQLZfn/1BV9g==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.5.tgz", + "integrity": "sha512-wZZOL19vg4TTRtOTl71XKqPe5hQx3XUh9Fle0wOi91FiFrBdqusrppnyS89wPS8RQG5lXEOFEUvYcMmdCcdZfw==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -4065,12 +4067,12 @@ } }, "node_modules/@storybook/addon-controls": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.0.tgz", - "integrity": "sha512-x6OszUmbi+xO4TlFAmw7nFq3IEzM10yPtAMC+RZ8jfSnlfevtZvT/KYb6WySo0H3b7b6GIxLwzbHceZrkHYPHg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.5.tgz", + "integrity": "sha512-EdSZ2pYf74mOXZGGJ22lrDvdvL0YKc95iWv9FFEhUFOloMy/0OZPB2ybYmd2KVCy3SeIE4Zfeiw8pDXdCUniOQ==", "dev": true, "dependencies": { - "@storybook/blocks": "7.6.0", + "@storybook/blocks": "7.6.5", "lodash": "^4.17.21", "ts-dedent": "^2.0.0" }, @@ -4080,26 +4082,26 @@ } }, "node_modules/@storybook/addon-docs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.0.tgz", - "integrity": "sha512-gvBMqERBilXEQaDFbsmUZSVSRjBJUSmHaGRYLMb72PnK1pfoRSxvi0TrIqbOScTQLUukKN42OwA83xLS+2eubg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.5.tgz", + "integrity": "sha512-D9tZyD41IujCHiPYdfS2bKtZRJPNwO4EydzyqODXppomluhFbY3uTEaf0H1UFnJLQxWNXZ7rr3aS0V3O6yu8pA==", "dev": true, "dependencies": { "@jest/transform": "^29.3.1", "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.6.0", - "@storybook/client-logger": "7.6.0", - "@storybook/components": "7.6.0", - "@storybook/csf-plugin": "7.6.0", - "@storybook/csf-tools": "7.6.0", + "@storybook/blocks": "7.6.5", + "@storybook/client-logger": "7.6.5", + "@storybook/components": "7.6.5", + "@storybook/csf-plugin": "7.6.5", + "@storybook/csf-tools": "7.6.5", "@storybook/global": "^5.0.0", "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.6.0", - "@storybook/postinstall": "7.6.0", - "@storybook/preview-api": "7.6.0", - "@storybook/react-dom-shim": "7.6.0", - "@storybook/theming": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/node-logger": "7.6.5", + "@storybook/postinstall": "7.6.5", + "@storybook/preview-api": "7.6.5", + "@storybook/react-dom-shim": "7.6.5", + "@storybook/theming": "7.6.5", + "@storybook/types": "7.6.5", "fs-extra": "^11.1.0", "remark-external-links": "^8.0.0", "remark-slug": "^6.0.0", @@ -4115,24 +4117,24 @@ } }, "node_modules/@storybook/addon-essentials": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.0.tgz", - "integrity": "sha512-cLmLradZqGxh3xavAhPTmJYUdV66NZsEyAZKdc2Fjo9sVWbWstODe/IC1xhD1dRgxEMCTlBpmUENqeCbbnkpqA==", - "dev": true, - "dependencies": { - "@storybook/addon-actions": "7.6.0", - "@storybook/addon-backgrounds": "7.6.0", - "@storybook/addon-controls": "7.6.0", - "@storybook/addon-docs": "7.6.0", - "@storybook/addon-highlight": "7.6.0", - "@storybook/addon-measure": "7.6.0", - "@storybook/addon-outline": "7.6.0", - "@storybook/addon-toolbars": "7.6.0", - "@storybook/addon-viewport": "7.6.0", - "@storybook/core-common": "7.6.0", - "@storybook/manager-api": "7.6.0", - "@storybook/node-logger": "7.6.0", - "@storybook/preview-api": "7.6.0", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.5.tgz", + "integrity": "sha512-VCLj1JAEpGoqF5iFJOo1CZFFck/tg4m/98DLdQuNuXvxT6jqaF0NI9UUQuJLIGteDCR7NKRbTFc1hV3/Ev+Ziw==", + "dev": true, + "dependencies": { + "@storybook/addon-actions": "7.6.5", + "@storybook/addon-backgrounds": "7.6.5", + "@storybook/addon-controls": "7.6.5", + "@storybook/addon-docs": "7.6.5", + "@storybook/addon-highlight": "7.6.5", + "@storybook/addon-measure": "7.6.5", + "@storybook/addon-outline": "7.6.5", + "@storybook/addon-toolbars": "7.6.5", + "@storybook/addon-viewport": "7.6.5", + "@storybook/core-common": "7.6.5", + "@storybook/manager-api": "7.6.5", + "@storybook/node-logger": "7.6.5", + "@storybook/preview-api": "7.6.5", "ts-dedent": "^2.0.0" }, "funding": { @@ -4145,9 +4147,9 @@ } }, "node_modules/@storybook/addon-highlight": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.0.tgz", - "integrity": "sha512-9Ho4L47k9e36kIgNa0RA9ZXzn7AWmq/sXBMRK7PcgrMiKvYdRQMU0AMHDvICg2vI8IkMVXTEaT/CQPefG3XT0Q==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.5.tgz", + "integrity": "sha512-CxzmIb30F9nLPQwT0lCPYhOAwGlGF4IkgkO8hYA7VfGCGUkJZEyyN/YkP/ZCUSdCIRChDBouR3KiFFd4mDFKzg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -4158,13 +4160,13 @@ } }, "node_modules/@storybook/addon-interactions": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.6.0.tgz", - "integrity": "sha512-+u1+FFn8asqJ96qe8AHWhpazYGu60vsl6QFkS1AWcvcNHaevjcSyBkPoVJEywnnixd5H3rpKo6EkXLsUvdr3JQ==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.6.5.tgz", + "integrity": "sha512-8Hzt9u1DQzFvtGER/hCGIvGpCoVwzVoqpM98f2KAIVx/NMFmRW7UyKihXzw1j2t4q2ZaF2jZDYWCBqlP+iwILA==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.0", + "@storybook/types": "7.6.5", "jest-mock": "^27.0.6", "polished": "^4.2.2", "ts-dedent": "^2.2.0" @@ -4175,9 +4177,9 @@ } }, "node_modules/@storybook/addon-links": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.0.tgz", - "integrity": "sha512-Qfq6U7EuxlXUFSbWVH8XRzQWva+CNtNgqjwO2hGsbhCisX5cb8vjl1mQF9DH00Lc021UcUjDPXiCCEEhj6Mpvg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.5.tgz", + "integrity": "sha512-Lx4Ng+iXt0YpIrKGr+nOZlpN9ypOoEDoP/7bZ6m7GXuVAkDm3JrRCBp7e2ZKSKcTxPdjPuO9HVKkIjtqjINlpw==", "dev": true, "dependencies": { "@storybook/csf": "^0.1.2", @@ -4198,12 +4200,12 @@ } }, "node_modules/@storybook/addon-mdx-gfm": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-mdx-gfm/-/addon-mdx-gfm-7.6.0.tgz", - "integrity": "sha512-4XrtbGou3uzslAaKT5vwkHQhjSOBzkifc15iPyec8ANwhuQ6ejGWUD3qKx7bPVfAb+oWxOcR7EabwDMe9PoivA==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-mdx-gfm/-/addon-mdx-gfm-7.6.5.tgz", + "integrity": "sha512-TTqVD9rG4jdSXi1MBSDJLeGQP8bKzQ6KVUEF+uq8uDYCl3vj++6PcqtE/KZ7tKhmDrdM7W/PGUJoQZzsMZ3PSw==", "dev": true, "dependencies": { - "@storybook/node-logger": "7.6.0", + "@storybook/node-logger": "7.6.5", "remark-gfm": "^3.0.1", "ts-dedent": "^2.0.0" }, @@ -4213,9 +4215,9 @@ } }, "node_modules/@storybook/addon-measure": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.0.tgz", - "integrity": "sha512-CtGZ45LUvylvM7z53TbUdJ8qx5QRgWXAA1Lk/+yBlIYgXQhvMzSupxmhjRyZZdj1rMj8S5NYuIg43nsQYijnsQ==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.5.tgz", + "integrity": "sha512-tlUudVQSrA+bwI4dhO8J7nYHtYdylcBZ86ybnqMmdTthsnyc7jnaFVQwbb6bbQJpPxvEvoNds5bVGUFocuvymQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -4227,9 +4229,9 @@ } }, "node_modules/@storybook/addon-outline": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.0.tgz", - "integrity": "sha512-W5KcuxM2w9VugZmU8nCwRa1FZdLj+sMcLZG4R1JcplY4SkWXrVtqMRJE0TNvyUEPUJpqcUwZWvL31fOZHQaOwg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.5.tgz", + "integrity": "sha512-P7X4+Z9L/l/RZW9UvvM+iuK2SUHD22KPc+dbYOifRXDovUqhfmcKVh1CUqTDMyZrg2ZAbropehMz1eI9BlQfxg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -4240,39 +4242,36 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-toolbars": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.0.tgz", - "integrity": "sha512-IZ6LGqu4QYsXSKof8KO0bL9+UR4KXho4z/+o0OYBwQRnO1KBQh73yRoeRxZPsJWE0Ms5zZnAMEA6iSZ+Zyvs5g==", + "node_modules/@storybook/addon-themes": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-themes/-/addon-themes-7.6.5.tgz", + "integrity": "sha512-TzGCxwdYAAlgeYuTYhMnL5KLutLVHrAXjVtWsYCYV+SVx+JklqCTk4zNUod64Z+sIYRMYd+YVC1KsWF2LHirow==", "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-viewport": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.0.tgz", - "integrity": "sha512-1NGgoEnDYLWw0HuRTTrIHCj8I0Xtc76PgPtArj92HQu+ENu4Hy0Y8MypZ+ZmAFddykaInwmZeQo2CD0GXU9qUQ==", + "node_modules/@storybook/addon-toolbars": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.5.tgz", + "integrity": "sha512-/zqWbVNE/SHc8I5Prnd2Q8U57RGEIYvHfeXjfkuLcE2Quc4Iss4x/9eU7SKu4jm+IOO2s0wlN6HcqI3XEf2XxA==", "dev": true, - "dependencies": { - "memoizerific": "^1.11.3" - }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addons": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-7.6.0.tgz", - "integrity": "sha512-f1J9g/aROUx/fPPBGA2fn/Bw/r+D2J47KmKv5yZ5UyNnMZMoXKRZLh0pyY/hwPWlDPK7mdcJyjdqi4C8HND/Nw==", + "node_modules/@storybook/addon-viewport": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.5.tgz", + "integrity": "sha512-9ghKTaduIUvQ6oShmWLuwMeTjtMR4RgKeKHrTJ7THMqvE/ydDPCYeL7ugF65ocXZSEz/QmxdK7uL686ZMKsqNA==", "dev": true, - "peer": true, "dependencies": { - "@storybook/manager-api": "7.6.0", - "@storybook/preview-api": "7.6.0", - "@storybook/types": "7.6.0" + "memoizerific": "^1.11.3" }, "funding": { "type": "opencollective", @@ -4280,22 +4279,22 @@ } }, "node_modules/@storybook/blocks": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.0.tgz", - "integrity": "sha512-S5g0h9dJevngPXnsFAjxQryOU/rQuA4cet40hIH0qRKesFwaXDFrsYHMZfxg6uGKygpeGbIEBoXckL8DT3SofA==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.5.tgz", + "integrity": "sha512-/NjuYkPks5w9lKn47KLgVC5cBkwfc+ERAp0CY0Xe//BQJkP+bcI8lE8d9Qc9IXFbOTvYEULeQrFgCkesk5BmLg==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.0", - "@storybook/client-logger": "7.6.0", - "@storybook/components": "7.6.0", - "@storybook/core-events": "7.6.0", + "@storybook/channels": "7.6.5", + "@storybook/client-logger": "7.6.5", + "@storybook/components": "7.6.5", + "@storybook/core-events": "7.6.5", "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "7.6.0", + "@storybook/docs-tools": "7.6.5", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.0", - "@storybook/preview-api": "7.6.0", - "@storybook/theming": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/manager-api": "7.6.5", + "@storybook/preview-api": "7.6.5", + "@storybook/theming": "7.6.5", + "@storybook/types": "7.6.5", "@types/lodash": "^4.14.167", "color-convert": "^2.0.1", "dequal": "^2.0.2", @@ -4319,15 +4318,15 @@ } }, "node_modules/@storybook/builder-manager": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.6.0.tgz", - "integrity": "sha512-xbyc1aMdvJrmN6mk7GW1mv/+gGxDfk3Rrv6tZph5nAWUojibEEqVHv5k6IXq5yyTzwRU+IpCf8gzCUSEvORt7w==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.6.5.tgz", + "integrity": "sha512-FQyI+tfzMam2XKXq7k921YVafIJs9Vqvos5qx8vyRnRffo55UU8tgunwjGn0PswtbMm6sThVqE0C0ZzVr7RG8A==", "dev": true, "dependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", - "@storybook/core-common": "7.6.0", - "@storybook/manager": "7.6.0", - "@storybook/node-logger": "7.6.0", + "@storybook/core-common": "7.6.5", + "@storybook/manager": "7.6.5", + "@storybook/node-logger": "7.6.5", "@types/ejs": "^3.1.1", "@types/find-cache-dir": "^3.2.1", "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", @@ -4347,19 +4346,19 @@ } }, "node_modules/@storybook/builder-vite": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-7.6.0.tgz", - "integrity": "sha512-gYJbQxquk/REN0Gg3okC56gFsrWHvnNtvc5Dbs+WRN8Zm2hUr7Gyrp3BQGgqvbinLFy2oK4Hq+Z18UAptG4LGw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.0", - "@storybook/client-logger": "7.6.0", - "@storybook/core-common": "7.6.0", - "@storybook/csf-plugin": "7.6.0", - "@storybook/node-logger": "7.6.0", - "@storybook/preview": "7.6.0", - "@storybook/preview-api": "7.6.0", - "@storybook/types": "7.6.0", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-7.6.5.tgz", + "integrity": "sha512-VbAYTGr92lgCWTwO2Z7NgSW3f5/K4Vr0Qxa2IlTgMCymWdDbWdIQiREcmCP0vjAGM2ftq1+vxngohVgx/r7pUw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.6.5", + "@storybook/client-logger": "7.6.5", + "@storybook/core-common": "7.6.5", + "@storybook/csf-plugin": "7.6.5", + "@storybook/node-logger": "7.6.5", + "@storybook/preview": "7.6.5", + "@storybook/preview-api": "7.6.5", + "@storybook/types": "7.6.5", "@types/find-cache-dir": "^3.2.1", "browser-assert": "^1.2.1", "es-module-lexer": "^0.9.3", @@ -4392,13 +4391,13 @@ } }, "node_modules/@storybook/channels": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.0.tgz", - "integrity": "sha512-Zobr57AkPIE+cdQMrIC9FdgQZDJt8XmpCR+QCxzhrhz6zJLVbIDjf866vKmy3EGSzGrlajfAg/G1PK4v7FdAcw==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.5.tgz", + "integrity": "sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.0", - "@storybook/core-events": "7.6.0", + "@storybook/client-logger": "7.6.5", + "@storybook/core-events": "7.6.5", "@storybook/global": "^5.0.0", "qs": "^6.10.0", "telejson": "^7.2.0", @@ -4410,23 +4409,23 @@ } }, "node_modules/@storybook/cli": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.6.0.tgz", - "integrity": "sha512-bpkJDBrpdrwn4D0XlqpPHuKBplpTirAC6hAvYLE1yVXFYKcZjF/Xavd6uaSZ5IK8G8E6BWJ2cgNFHM/FtF510w==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.6.5.tgz", + "integrity": "sha512-w+Y8dx5oCLQVESOVmpsQuFksr/ewARKrnSKl9kwnVMN4sMgjOgoZ3zmV66J7SKexvwyuwlOjf840pmEglGdPPg==", "dev": true, "dependencies": { "@babel/core": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/types": "^7.23.0", "@ndelangen/get-tarball": "^3.0.7", - "@storybook/codemod": "7.6.0", - "@storybook/core-common": "7.6.0", - "@storybook/core-events": "7.6.0", - "@storybook/core-server": "7.6.0", - "@storybook/csf-tools": "7.6.0", - "@storybook/node-logger": "7.6.0", - "@storybook/telemetry": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/codemod": "7.6.5", + "@storybook/core-common": "7.6.5", + "@storybook/core-events": "7.6.5", + "@storybook/core-server": "7.6.5", + "@storybook/csf-tools": "7.6.5", + "@storybook/node-logger": "7.6.5", + "@storybook/telemetry": "7.6.5", + "@storybook/types": "7.6.5", "@types/semver": "^7.3.4", "@yarnpkg/fslib": "2.10.3", "@yarnpkg/libzip": "2.3.0", @@ -4524,9 +4523,9 @@ "dev": true }, "node_modules/@storybook/client-logger": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.0.tgz", - "integrity": "sha512-18XPPEWYHmmUav7i+PjZGwtImshNtay0xO2vh2DmQtzoCh2Lx/NVldqv9Li1eHCI88+4y7fyutmC5OIi0YASbg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.5.tgz", + "integrity": "sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -4537,18 +4536,18 @@ } }, "node_modules/@storybook/codemod": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.6.0.tgz", - "integrity": "sha512-86/7AH5qg5uOE5e4ymnXjykfzA29eSFRQnTYJN0pbI/xlnFnPnh1mLQtinV03S2DtdcZKAm04UntfNgSFrSJNA==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.6.5.tgz", + "integrity": "sha512-K5C9ltBClZ0aSyujGt3RJFtRicrUZy8nzhHrcADUj27rrQD26jH/p+Y05jWKj9JcI8SyMg978GN5X/1aw2Y31A==", "dev": true, "dependencies": { "@babel/core": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.0", - "@storybook/node-logger": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/csf-tools": "7.6.5", + "@storybook/node-logger": "7.6.5", + "@storybook/types": "7.6.5", "@types/cross-spawn": "^6.0.2", "cross-spawn": "^7.0.3", "globby": "^11.0.2", @@ -4578,18 +4577,18 @@ } }, "node_modules/@storybook/components": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.0.tgz", - "integrity": "sha512-yV2krJOGYHldThfFShl5jC5EySUYVOWnhomwwT2b0J5e7odp04TCBycKmLxZhYmaFawnf5BNbDaIXvxcnY518A==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.5.tgz", + "integrity": "sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw==", "dev": true, "dependencies": { "@radix-ui/react-select": "^1.2.2", "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.0", + "@storybook/client-logger": "7.6.5", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/theming": "7.6.5", + "@storybook/types": "7.6.5", "memoizerific": "^1.11.3", "use-resize-observer": "^9.1.0", "util-deprecate": "^1.0.2" @@ -4604,13 +4603,13 @@ } }, "node_modules/@storybook/core-client": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.6.0.tgz", - "integrity": "sha512-zc2v1WoyN64okcVWfZb1/fWasaZX3mdFVigqavUOIeXsxWFcywU5bcr0bWCCwwk19xKaNzJ2FvWVqXrkOUq22w==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.6.5.tgz", + "integrity": "sha512-6FtyJcz8MSl+JYwNJZ53FM6rkT27pFHWcJPdtw/9229Ec8as9RpkNeZ/NBZjRTeDkn9Ki0VOiVAefNie9tZ/8Q==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.0", - "@storybook/preview-api": "7.6.0" + "@storybook/client-logger": "7.6.5", + "@storybook/preview-api": "7.6.5" }, "funding": { "type": "opencollective", @@ -4618,14 +4617,14 @@ } }, "node_modules/@storybook/core-common": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.0.tgz", - "integrity": "sha512-Le11+Pcbi2D+i63utkhjHEAUIVO65CNiZiDFa/ZJI5aSajy209ece2eX0Z12wPecfYu5TXlqhqaeXAVBABAUow==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.5.tgz", + "integrity": "sha512-z4EgzZSIVbID6Ib0jhh3jimKeaDWU8OOhoZYfn3galFmgQWowWOv1oMgipWiXfRLWw9DaLFQiCHIdLANH+VO2g==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.0", - "@storybook/node-logger": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/core-events": "7.6.5", + "@storybook/node-logger": "7.6.5", + "@storybook/types": "7.6.5", "@types/find-cache-dir": "^3.2.1", "@types/node": "^18.0.0", "@types/node-fetch": "^2.6.4", @@ -4653,18 +4652,18 @@ } }, "node_modules/@storybook/core-common/node_modules/@types/node": { - "version": "18.18.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.13.tgz", - "integrity": "sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==", + "version": "18.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz", + "integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@storybook/core-events": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.0.tgz", - "integrity": "sha512-13d4YOcXPu0j5PDjqE2iy+mG68w2TLit408cF/ZbJ8d6V4QwuUiz6mUt34vTuTc3yB93q5moYXYo6a/AhrsPnQ==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.5.tgz", + "integrity": "sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ==", "dev": true, "dependencies": { "ts-dedent": "^2.0.0" @@ -4675,26 +4674,26 @@ } }, "node_modules/@storybook/core-server": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.6.0.tgz", - "integrity": "sha512-9DmUDcMKbeZDaTENjRoV3cFvqLOKq64RNIh/eDffeRyzaj8PvY4E7MOd7XXx6pTSO7CmSpgcaZ4OYbmvu2xI/A==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.6.5.tgz", + "integrity": "sha512-BfKzK/ObTjUcPvE5/r1pogCifM/4nLRhOUYJl7XekwHkOQwn19e6H3/ku1W3jDoYXBu642Dc9X7l/ERjKTqxFg==", "dev": true, "dependencies": { "@aw-web-design/x-default-browser": "1.4.126", "@discoveryjs/json-ext": "^0.5.3", - "@storybook/builder-manager": "7.6.0", - "@storybook/channels": "7.6.0", - "@storybook/core-common": "7.6.0", - "@storybook/core-events": "7.6.0", + "@storybook/builder-manager": "7.6.5", + "@storybook/channels": "7.6.5", + "@storybook/core-common": "7.6.5", + "@storybook/core-events": "7.6.5", "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.0", + "@storybook/csf-tools": "7.6.5", "@storybook/docs-mdx": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/manager": "7.6.0", - "@storybook/node-logger": "7.6.0", - "@storybook/preview-api": "7.6.0", - "@storybook/telemetry": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/manager": "7.6.5", + "@storybook/node-logger": "7.6.5", + "@storybook/preview-api": "7.6.5", + "@storybook/telemetry": "7.6.5", + "@storybook/types": "7.6.5", "@types/detect-port": "^1.3.0", "@types/node": "^18.0.0", "@types/pretty-hrtime": "^1.0.0", @@ -4728,9 +4727,9 @@ } }, "node_modules/@storybook/core-server/node_modules/@types/node": { - "version": "18.18.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.13.tgz", - "integrity": "sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==", + "version": "18.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz", + "integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -4779,12 +4778,12 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.0.tgz", - "integrity": "sha512-d/rcRcNad+tLGXV3GQiQdFJOBS4fj90fqa4joJIekgVh+LfRBS+KYuiPeukBxfnmz2AbhF9ezwXQMFIYYyHmzg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.5.tgz", + "integrity": "sha512-iQ8Y/Qq1IUhHRddjDVicWJA2sM7OZA1FR97OvWUT2240WjCuQSCfy32JD8TQlYjqXgEolJeLPv3zW4qH5om4LQ==", "dev": true, "dependencies": { - "@storybook/csf-tools": "7.6.0", + "@storybook/csf-tools": "7.6.5", "unplugin": "^1.3.1" }, "funding": { @@ -4793,9 +4792,9 @@ } }, "node_modules/@storybook/csf-tools": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.0.tgz", - "integrity": "sha512-JhGJeLgnE96JfBBXM1DIPVR/JLQH2OTGH+yZ3ohiTPGWjf+aShB3jKUxTkBl7Fjq0xu57tnky7kNUO690vYypg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.5.tgz", + "integrity": "sha512-1iaCh7nt+WE7Q5UwRhLLc5flMNoAV/vBr0tvDSCKiHaO+D3dZzlZOe/U+S6wegdyN2QNcvT2xs179CcrX6Qp6w==", "dev": true, "dependencies": { "@babel/generator": "^7.23.0", @@ -4803,7 +4802,7 @@ "@babel/traverse": "^7.23.2", "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.0", + "@storybook/types": "7.6.5", "fs-extra": "^11.1.0", "recast": "^0.23.1", "ts-dedent": "^2.0.0" @@ -4820,14 +4819,14 @@ "dev": true }, "node_modules/@storybook/docs-tools": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.0.tgz", - "integrity": "sha512-06M/Vo3AwOdr4VP1LbvnSih8eWT5zO6Mkm3ZZikMQVn+eDr5YJ9PzUeI2/SAymgCs4jH9qRf4lmKTPMl4bjGsQ==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.5.tgz", + "integrity": "sha512-UyHkHu5Af6jMpYsR4lZ69D32GQGeA0pLAn7jaBbQndgAjBdK1ykZcifiUC7Wz1hG7+YpuYspEGuDEddOh+X8FQ==", "dev": true, "dependencies": { - "@storybook/core-common": "7.6.0", - "@storybook/preview-api": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/core-common": "7.6.5", + "@storybook/preview-api": "7.6.5", + "@storybook/types": "7.6.5", "@types/doctrine": "^0.0.3", "assert": "^2.1.0", "doctrine": "^3.0.0", @@ -4845,9 +4844,9 @@ "dev": true }, "node_modules/@storybook/manager": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.6.0.tgz", - "integrity": "sha512-HJ1DCCf3GT+irAFCZg9WsPcGwSZlDyQiJHsaqxFVzuoPnz2lx10eHkXTnKa3t8x6hJeWK9BFHVyOXEFUV78ryg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.6.5.tgz", + "integrity": "sha512-y1KLH0O1PGPyMxGMvOhppzFSO7r4ibjTve5iqsI0JZwxUjNuBKRLYbrhXdAyC2iacvxYNrHgevae1k9XdD+FQw==", "dev": true, "funding": { "type": "opencollective", @@ -4855,19 +4854,19 @@ } }, "node_modules/@storybook/manager-api": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.0.tgz", - "integrity": "sha512-P2ISRw8cmIDPrsMwDTOZvFOH6P9GN6O9wC2cSrfMWYE/aaXHWf/7f5gk5pX/zILHuLQeVnDBguS/zXmMDxJj7g==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.5.tgz", + "integrity": "sha512-tE3OShOcs6A3XtI3NJd6hYQOZLaP++Fn0dCtowBwYh/vS1EN/AyroVmL97tsxn1DZTyoRt0GidwbB6dvLMBOwA==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.0", - "@storybook/client-logger": "7.6.0", - "@storybook/core-events": "7.6.0", + "@storybook/channels": "7.6.5", + "@storybook/client-logger": "7.6.5", + "@storybook/core-events": "7.6.5", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.0", - "@storybook/theming": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/router": "7.6.5", + "@storybook/theming": "7.6.5", + "@storybook/types": "7.6.5", "dequal": "^2.0.2", "lodash": "^4.17.21", "memoizerific": "^1.11.3", @@ -4921,9 +4920,9 @@ "dev": true }, "node_modules/@storybook/node-logger": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.0.tgz", - "integrity": "sha512-Z+wVmjnTMhMG2ydL4T8F+gf/awvuAv3IAzH6T4D5UgjmdABqxVqWNAAF+Mgp48TUAGxiJCowzI6sGDg3iNJx2w==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.5.tgz", + "integrity": "sha512-xKw6IH1wLkIssekdBv3bd13xYKUF1t8EwqDR8BYcN8AVjZlqJMTifssqG4bYV+G/B7J3tz4ugJ5nmtWg6RQ0Qw==", "dev": true, "funding": { "type": "opencollective", @@ -4931,9 +4930,9 @@ } }, "node_modules/@storybook/postinstall": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.0.tgz", - "integrity": "sha512-saxxLh6dXpNYA1WQ5KnOfMsJ1U+WtxBLSgYv8DWYujbFirmafgzPKslxgCjP6OlV3erQgnoO/xBUK4YVTfsuag==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.5.tgz", + "integrity": "sha512-12WxfpqGKsk7GQ3KWiZSbamsYK8vtRmhOTkavZ9IQkcJ/zuVfmqK80/Mds+njJMudUPzuREuSFGWACczo17EDA==", "dev": true, "funding": { "type": "opencollective", @@ -4941,9 +4940,9 @@ } }, "node_modules/@storybook/preview": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.6.0.tgz", - "integrity": "sha512-/zHTMl3aj1S3xxnffwaGzhMi1KySCKeln3xX15RBme014ZQ8cNYwnSDRAsiW/n3viDFFyZ6ybrtmw2HnpNBUhw==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.6.5.tgz", + "integrity": "sha512-zmLa7C7yFGTYhgGZXoecdww9rx0Z5HpNi/GDBRWoNSK+FEdE8Jj2jF5NJ2ncldtYIyegz9ku29JFMKbhMj9K5Q==", "dev": true, "funding": { "type": "opencollective", @@ -4951,17 +4950,17 @@ } }, "node_modules/@storybook/preview-api": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.0.tgz", - "integrity": "sha512-//8mYKM8gkSDkIRcG3kSozGEvPUurVhfjBXDtaF8Y8cOZLzwe8/AZy+mUYHShh9HWFUXx5QAj5oU0U0PflfMeg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.5.tgz", + "integrity": "sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.0", - "@storybook/client-logger": "7.6.0", - "@storybook/core-events": "7.6.0", + "@storybook/channels": "7.6.5", + "@storybook/client-logger": "7.6.5", + "@storybook/core-events": "7.6.5", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.0", + "@storybook/types": "7.6.5", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -4977,9 +4976,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.0.tgz", - "integrity": "sha512-DBsQ9OBwSjUEI2bvHcGqs+ucVy3UE8CjoWpD93kRcJZY913DCoNDrMSBWozhBHlcO65LhuBjrNm7oKdmwAKJsg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.5.tgz", + "integrity": "sha512-Qp3N3zENdvx20ikHmz5yI03z+mAWF8bUAwUofqXarVtZUkBNtvfTfUwgAezOAF0eClClH+ktIziIKd976tLSPw==", "dev": true, "funding": { "type": "opencollective", @@ -4991,12 +4990,12 @@ } }, "node_modules/@storybook/router": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.0.tgz", - "integrity": "sha512-661mO2JtO/wdWJEtVqyaUjQ8tsy56LrsKqz4suzO0L32Z7NHCBu0IzbZbLON6MXje3PWXksw0vFbd8jwH/i//w==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.5.tgz", + "integrity": "sha512-QiTC86gRuoepzzmS6HNJZTwfz/n27NcqtaVEIxJi1Yvsx2/kLa9NkRhylNkfTuZ1gEry9stAlKWanMsB2aKyjQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.0", + "@storybook/client-logger": "7.6.5", "memoizerific": "^1.11.3", "qs": "^6.10.0" }, @@ -5006,14 +5005,14 @@ } }, "node_modules/@storybook/telemetry": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.6.0.tgz", - "integrity": "sha512-xUYiWiXicYX6oneDqHx5bq3zViTuckLoXi1QTzKN+WPO98vt4NBr532XeVNJG+x+UE8ERSKazT6CHkZ9XeqyMA==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.6.5.tgz", + "integrity": "sha512-FiLRh9k9LoGphqgBqPYySWdGqplihiZyDwqdo+Qs19RcQ/eiKg0W7fdA09nStcdcsHmDl/1cMfRhz9KUiMtwOw==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.0", - "@storybook/core-common": "7.6.0", - "@storybook/csf-tools": "7.6.0", + "@storybook/client-logger": "7.6.5", + "@storybook/core-common": "7.6.5", + "@storybook/csf-tools": "7.6.5", "chalk": "^4.1.0", "detect-package-manager": "^2.0.1", "fetch-retry": "^5.0.2", @@ -5037,13 +5036,13 @@ } }, "node_modules/@storybook/theming": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.0.tgz", - "integrity": "sha512-F5PTGkaRQ0TWIWRrZgQ2dmVxVcjX77vDc6QfUYxvOfez9/zrduKRHP5lGqHoqJlugJc8i2zpRNEFbL99frdUKg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.5.tgz", + "integrity": "sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw==", "dev": true, "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.0", + "@storybook/client-logger": "7.6.5", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -5057,12 +5056,12 @@ } }, "node_modules/@storybook/types": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.0.tgz", - "integrity": "sha512-mrbL9qrRekaPCAV3d7jYpege5wOpsvBvNW6pmATG3UvNXpqz5BOWe6RWZJXbtkvjyt01b6HE9CbVUFJppplr6w==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.5.tgz", + "integrity": "sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.0", + "@storybook/channels": "7.6.5", "@types/babel__core": "^7.0.0", "@types/express": "^4.7.0", "file-system-cache": "2.3.0" @@ -5073,16 +5072,16 @@ } }, "node_modules/@storybook/vue3": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/vue3/-/vue3-7.6.0.tgz", - "integrity": "sha512-oAuRsb5rW9juiPDlmuy63Rw3tifHWmTNE8fzOpTndxoPpi1AGfsHMAFOr+dO5csm0sHKeUXBfyuTnE6++MtMCA==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/vue3/-/vue3-7.6.5.tgz", + "integrity": "sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==", "dev": true, "dependencies": { - "@storybook/core-client": "7.6.0", - "@storybook/docs-tools": "7.6.0", + "@storybook/core-client": "7.6.5", + "@storybook/docs-tools": "7.6.5", "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.6.0", - "@storybook/types": "7.6.0", + "@storybook/preview-api": "7.6.5", + "@storybook/types": "7.6.5", "lodash": "^4.17.21", "ts-dedent": "^2.0.0", "type-fest": "~2.19", @@ -5101,14 +5100,14 @@ } }, "node_modules/@storybook/vue3-vite": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@storybook/vue3-vite/-/vue3-vite-7.6.0.tgz", - "integrity": "sha512-Dt13QXqUUDQb28A9W4N0Zq5da3m0Der8r6F6k+ZGNZOxQmPRsVqdhK3XgKXcS1h+o4Plqt4GUI4nEcjwDKFJ5A==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@storybook/vue3-vite/-/vue3-vite-7.6.5.tgz", + "integrity": "sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==", "dev": true, "dependencies": { - "@storybook/builder-vite": "7.6.0", - "@storybook/core-server": "7.6.0", - "@storybook/vue3": "7.6.0", + "@storybook/builder-vite": "7.6.5", + "@storybook/core-server": "7.6.5", + "@storybook/vue3": "7.6.5", "@vitejs/plugin-vue": "^4.0.0", "magic-string": "^0.30.0", "vue-docgen-api": "^4.40.0" @@ -5192,9 +5191,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" @@ -5456,9 +5455,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.39", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz", - "integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==", + "version": "18.2.45", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz", + "integrity": "sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -5499,21 +5498,18 @@ "@types/node": "*" } }, - "node_modules/@types/set-cookie-parser": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.7.tgz", - "integrity": "sha512-+ge/loa0oTozxip6zmhRIk8Z/boU51wl9Q6QdLZcokIGMzY5lFXYy/x7Htj2HTC6/KZP1hUbZ1ekx8DYXICvWg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/sortablejs": { "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.7.tgz", "integrity": "sha512-PvgWCx1Lbgm88FdQ6S7OGvLIjWS66mudKPlfdrWil0TjsO5zmoZmzoKiiwRShs1dwPgrlkr0N4ewuy0/+QUXYQ==", "peer": true }, + "node_modules/@types/statuses": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.4.tgz", + "integrity": "sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==", + "dev": true + }, "node_modules/@types/unist": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", @@ -6124,15 +6120,6 @@ } } }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/@yarnpkg/esbuild-plugin-pnp": { "version": "3.0.0-rc.15", "resolved": "https://registry.npmjs.org/@yarnpkg/esbuild-plugin-pnp/-/esbuild-plugin-pnp-3.0.0-rc.15.tgz", @@ -6186,13 +6173,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/@zxing/text-encoding": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", - "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", - "dev": true, - "optional": true - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -6565,13 +6545,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", - "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", + "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.3", + "@babel/helper-define-polyfill-provider": "^0.4.4", "semver": "^6.3.1" }, "peerDependencies": { @@ -6579,12 +6559,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", - "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", + "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.3", + "@babel/helper-define-polyfill-provider": "^0.4.4", "core-js-compat": "^3.33.1" }, "peerDependencies": { @@ -6592,33 +6572,17 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", - "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", + "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.3" + "@babel/helper-define-polyfill-provider": "^0.4.4" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/babel-plugin-styled-components": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", - "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "lodash": "^4.17.21", - "picomatch": "^2.3.1" - }, - "peerDependencies": { - "styled-components": ">= 2" - } - }, "node_modules/babel-walk": { "version": "3.0.0-canary-5", "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", @@ -6832,9 +6796,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "dev": true, "funding": [ { @@ -6851,9 +6815,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, "bin": { @@ -6985,15 +6949,6 @@ "node": ">= 6" } }, - "node_modules/camelize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", - "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001565", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz", @@ -7602,12 +7557,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.33.3", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.3.tgz", - "integrity": "sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==", + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", + "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", "dev": true, "dependencies": { - "browserslist": "^4.22.1" + "browserslist": "^4.22.2" }, "funding": { "type": "opencollective", @@ -7643,26 +7598,6 @@ "node": ">=8" } }, - "node_modules/css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-to-react-native": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", - "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", - "dev": true, - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -8253,9 +8188,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.596", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.596.tgz", - "integrity": "sha512-zW3zbZ40Icb2BCWjm47nxwcFGYlIgdXkAx85XDO7cyky9J4QQfq8t0W19/TLZqq3JPQXtlv8BPIGmfa9Jb4scg==", + "version": "1.4.614", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz", + "integrity": "sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ==", "dev": true }, "node_modules/element-resize-event": { @@ -8771,15 +8706,6 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9309,9 +9235,9 @@ } }, "node_modules/flow-parser": { - "version": "0.222.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.222.0.tgz", - "integrity": "sha512-Fq5OkFlFRSMV2EOZW+4qUYMTE0uj8pcLsYJMxXYriSBDpHAF7Ofx3PibCTy3cs5P6vbsry7eYj7Z7xFD49GIOQ==", + "version": "0.224.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.224.0.tgz", + "integrity": "sha512-S1P78o0VLB1FZvkoGSIpaRiiTUQ3xDhm9I4Z1qc3lglmkjehfR2sjM0vhwKS7UC1G12VT4Leb/GGV/KlactqjA==", "dev": true, "engines": { "node": ">=0.4.0" @@ -9856,9 +9782,9 @@ } }, "node_modules/headers-polyfill": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-3.2.5.tgz", - "integrity": "sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.2.tgz", + "integrity": "sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==", "dev": true }, "node_modules/highlight.js": { @@ -9869,21 +9795,6 @@ "node": "*" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dev": true, - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -12649,29 +12560,31 @@ "dev": true }, "node_modules/msw": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.2.tgz", - "integrity": "sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.0.11.tgz", + "integrity": "sha512-dAXFS2DxZX0uFqMPhS3oUAu8S/5IQ5qKKSwtXl3/dMTeML0C8JfSvbeWtowYg6pu4Iehgp5L/pHLrlIcG++y/A==", "dev": true, "hasInstallScript": true, "dependencies": { - "@mswjs/cookies": "^0.2.2", - "@mswjs/interceptors": "^0.17.10", - "@open-draft/until": "^1.0.3", + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/js-levenshtein": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.25.13", + "@open-draft/until": "^2.1.0", "@types/cookie": "^0.4.1", "@types/js-levenshtein": "^1.1.1", - "chalk": "^4.1.1", + "@types/statuses": "^2.0.1", + "chalk": "^4.1.2", "chokidar": "^3.4.2", - "cookie": "^0.4.2", "graphql": "^16.8.1", - "headers-polyfill": "3.2.5", + "headers-polyfill": "^4.0.1", "inquirer": "^8.2.0", "is-node-process": "^1.2.0", "js-levenshtein": "^1.1.6", - "node-fetch": "^2.6.7", "outvariant": "^1.4.0", "path-to-regexp": "^6.2.0", - "strict-event-emitter": "^0.4.3", + "strict-event-emitter": "^0.5.0", "type-fest": "^2.19.0", "yargs": "^17.3.1" }, @@ -12679,14 +12592,14 @@ "msw": "cli/index.js" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mswjs" }, "peerDependencies": { - "typescript": ">= 4.4.x <= 5.2.x" + "typescript": ">= 4.7.x <= 5.2.x" }, "peerDependenciesMeta": { "typescript": { @@ -12695,24 +12608,15 @@ } }, "node_modules/msw-storybook-addon": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/msw-storybook-addon/-/msw-storybook-addon-1.10.0.tgz", - "integrity": "sha512-soCTMTf7DnLeaMnFHPrtVgbyeFTJALVvnDHpzzXpJad+HOzJgQdwU4EAzVfDs1q+X5cVEgxOdAhSMC7ljvnSXg==", + "version": "2.0.0--canary.122.b3ed3b1.0", + "resolved": "https://registry.npmjs.org/msw-storybook-addon/-/msw-storybook-addon-2.0.0--canary.122.b3ed3b1.0.tgz", + "integrity": "sha512-HZn9B6MCdfHpgm5wQ92A0K3moXDCDTxPRjWWH6C/4myg3KmsD0kwiaE14vWO49A4+TG10yXMGqIll2NjIMtnQg==", "dev": true, "dependencies": { "is-node-process": "^1.0.1" }, "peerDependencies": { - "msw": ">=0.35.0 <2.0.0" - } - }, - "node_modules/msw/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" + "msw": "^2.0.0" } }, "node_modules/msw/node_modules/path-to-regexp": { @@ -12861,9 +12765,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/normalize-package-data": { @@ -13146,9 +13050,9 @@ } }, "node_modules/outvariant": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", - "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", "dev": true }, "node_modules/p-limit": { @@ -13786,23 +13690,6 @@ "node": ">= 6" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -14869,12 +14756,6 @@ "node": ">= 0.8.0" } }, - "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", - "dev": true - }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -14921,12 +14802,6 @@ "node": ">=8" } }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "dev": true - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15177,12 +15052,12 @@ "dev": true }, "node_modules/storybook": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.6.0.tgz", - "integrity": "sha512-t844tajV8dcWiGmGV0zXUdmLzLnftTqQOfzX678AjJXh7ijhMkNi2dgSFLaAOLQqeljdGfyrgFrivveZxkaj2w==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.6.5.tgz", + "integrity": "sha512-uHPrL+g/0v6iIVtDA8J0uWd3jDZcdr51lCR/vPXTkrCY1uVaFjswzl8EMy5PR05I7jMpKUzkJWZtFbgbh9e1Bw==", "dev": true, "dependencies": { - "@storybook/cli": "7.6.0" + "@storybook/cli": "7.6.5" }, "bin": { "sb": "index.js", @@ -15193,22 +15068,6 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/storybook-addon-rtl": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/storybook-addon-rtl/-/storybook-addon-rtl-0.5.0.tgz", - "integrity": "sha512-C9wopwY6w/wznGubKD0gPHnQZkMk2oiSmpx1g7+68LXKVTDY2NJ8+kSdF+yITUvLNVX+UECAv4RPV1P685Wg4A==", - "dev": true, - "dependencies": { - "@storybook/core-events": "^7.0.0", - "prop-types": "^15.8.1", - "styled-components": "^5.3.9" - }, - "peerDependencies": { - "@storybook/addons": "< 8.0.0", - "react": "*", - "react-dom": "*" - } - }, "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -15216,9 +15075,9 @@ "dev": true }, "node_modules/strict-event-emitter": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", - "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", "dev": true }, "node_modules/string_decoder": { @@ -15364,57 +15223,6 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/styled-components": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", - "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", - "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/styled-components" - }, - "peerDependencies": { - "react": ">= 16.8.0", - "react-dom": ">= 16.8.0", - "react-is": ">= 16.8.0" - } - }, - "node_modules/styled-components/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/styled-components/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -17984,18 +17792,6 @@ "defaults": "^1.0.3" } }, - "node_modules/web-encoding": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", - "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", - "dev": true, - "dependencies": { - "util": "^0.12.3" - }, - "optionalDependencies": { - "@zxing/text-encoding": "0.9.0" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -18246,9 +18042,9 @@ } }, "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", + "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", "dev": true, "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index b3aaeb296..44d02504e 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "chromatic": "npx chromatic --project-token chpt_d560551f1e4c0c7", - "unittest": "vitest" - + "test": "vitest" }, "dependencies": { "@headlessui/vue": "^1.7.16", @@ -43,14 +42,15 @@ }, "devDependencies": { "@rushstack/eslint-patch": "^1.3.3", - "@storybook/addon-essentials": "^7.6.0", - "@storybook/addon-interactions": "^7.6.0", - "@storybook/addon-links": "^7.6.0", - "@storybook/addon-mdx-gfm": "^7.6.0", - "@storybook/blocks": "^7.6.0", + "@storybook/addon-essentials": "^7.6.5", + "@storybook/addon-interactions": "^7.6.5", + "@storybook/addon-links": "^7.6.5", + "@storybook/addon-mdx-gfm": "^7.6.5", + "@storybook/addon-themes": "^7.6.5", + "@storybook/blocks": "^7.6.5", "@storybook/testing-library": "^0.2.2", - "@storybook/vue3": "^7.6.0", - "@storybook/vue3-vite": "^7.6.0", + "@storybook/vue3": "^7.6.5", + "@storybook/vue3-vite": "^7.6.5", "@vitejs/plugin-vue": "^4.3.4", "@vue/eslint-config-prettier": "^8.0.0", "autoprefixer": "^10.4.14", @@ -61,15 +61,14 @@ "husky": "^8.0.3", "less": "^4.2.0", "lint-staged": "^14.0.1", - "msw": "^1.3.2", - "msw-storybook-addon": "^1.10.0", + "msw": "^2.0.11", + "msw-storybook-addon": "^2.0.0--canary.122.b3ed3b1.0", "postcss": "^8.4.27", "prettier": "^3.0.2", "prettier-plugin-tailwindcss": "^0.5.6", "react": "^18.2.0", "react-dom": "^18.2.0", - "storybook": "^7.6.0", - "storybook-addon-rtl": "^0.5.0", + "storybook": "^7.6.5", "tailwindcss": "^3.3.3", "vite": "^4.4.9", "vitest": "^1.0.4" diff --git a/src/components/ActionPanel/ActionPanel.stories.js b/src/components/ActionPanel/ActionPanel.stories.js index 254679114..d8a839dd1 100644 --- a/src/components/ActionPanel/ActionPanel.stories.js +++ b/src/components/ActionPanel/ActionPanel.stories.js @@ -1,5 +1,4 @@ import ActionPanel from './ActionPanel.vue'; -import PkpDialog from '@/components/Modal/Dialog.vue'; import './ActionPanelStories.less'; import {useTranslation} from '@/composables/useTranslation'; import {useDialogStore} from '@/stores/dialogStore'; @@ -10,13 +9,12 @@ export default { export const Default = { render: (args) => ({ - components: {ActionPanel, PkpDialog}, + components: {ActionPanel}, setup() { const {t} = useTranslation(); const dialogStore = useDialogStore(); function openDeleteDialog() { - console.log('open dialog!'); dialogStore.openDialog({ name: 'deleteDialog', title: 'Delete Incomplete Submissions', @@ -41,16 +39,10 @@ export const Default = { } return { args, - dialogStore, openDeleteDialog, }; }, template: ` -
diff --git a/src/components/Badge/Badge.stories.js b/src/components/Badge/Badge.stories.js index 3282fb0a9..b7ebfc514 100644 --- a/src/components/Badge/Badge.stories.js +++ b/src/components/Badge/Badge.stories.js @@ -2,7 +2,7 @@ import Badge from './Badge.vue'; import Icon from '@/components/Icon/Icon.vue'; export default { - title: 'Basic Components/Badge', + title: 'Components/Badge', component: Badge, render: (args) => ({ components: {Badge}, diff --git a/src/components/Button/Button.stories.js b/src/components/Button/Button.stories.js index 5df1a8840..55f6281c7 100644 --- a/src/components/Button/Button.stories.js +++ b/src/components/Button/Button.stories.js @@ -3,7 +3,7 @@ import Icon from '@/components/Icon/Icon.vue'; import {ref} from 'vue'; export default { - title: 'Basic Components/Button', + title: 'Components/Button', component: Button, render: (args) => ({ components: {Button}, diff --git a/src/components/Button/Button.vue b/src/components/Button/Button.vue index c09eaeebf..9bab20b80 100644 --- a/src/components/Button/Button.vue +++ b/src/components/Button/Button.vue @@ -82,7 +82,7 @@ export default { @import '../../styles/_import'; .pkpButton { - display: inline-flex; /* */ + display: inline-block; /* */ align-items: center; padding: 0 0.5em; min-width: 2.13rem; /**/ // Always at least as wide as it is tall diff --git a/src/components/Composer/Composer.stories.js b/src/components/Composer/Composer.stories.js index f7cbc1811..ea21af6c4 100644 --- a/src/components/Composer/Composer.stories.js +++ b/src/components/Composer/Composer.stories.js @@ -4,8 +4,6 @@ import Composer from './Composer.vue'; import fileAttachers from '@/docs/data/fileAttachers'; import insertContent from '@/docs/data/insertContent'; import emailTemplate from '@/docs/data/emailTemplate'; -import PkpDialog from '@/components/Modal/Dialog.vue'; -import {useDialogStore} from '@/stores/dialogStore'; export default { title: 'Components/Composer', @@ -14,10 +12,8 @@ export default { export const Default = { render: (args) => ({ - components: {Composer, PkpDialog}, + components: {Composer}, setup() { - const dialogStore = useDialogStore(); - const emailTemplates = [emailTemplate]; emailTemplates.push({ ...emailTemplate, @@ -106,14 +102,9 @@ export const Default = { composerState.value[key] = data[key]; }); } - return {args, composerState, composerChanged, dialogStore}; + return {args, composerState, composerChanged}; }, template: ` - + +# Decision Page + +## Data + +This is a root component. Learn about [page hydration](#/pages/pages). + +| Key | Description | +| --- | --- | +| `...` | All the data of [Page](#/component/Page). | +| `abandonDecisionLabel` | A localized string for the button to cancel recording a decision. | +| `cancelConfirmationPrompt` | A localized string for the confirmation prompt to cancel recording a decision. | +| `currentStep` | Tracks the currently open step. If not passed, the first step will be opened. Default: `{}` | +| `decision` | An integer representing the decision type. | +| `decisionCompleteLabel` | A localized string when a decision has been recorded. See: `DecisionType::getCompletedLabel()` | +| `decisionCompleteDescription` | A localized string when a decision has been recorded. See: `DecisionType::getCompletedLabel()` | +| `emailTemplatesApiUrl` | A URL to the REST API to get email templates. | +| `isSubmitting` | Whether or not the decision is being recorded. Default: `false` | +| `keepWorkingLabel` | A localized string for the button to keep working in the confirmation prompt to cancel recording a decision. | +| `reviewRoundId` | The id of the review round this decision should be recorded in. Only pass a review round for decisions in the review stage. Default: `0` | +| `skippedSteps` | An array of steps that have been skipped. Default: `[]` | +| `stageId` | The id of the workflow stage this decision should be recorded in. One of the `WORKFLOW_STAGE_ID_*` constants. | +| `startedSteps` | An array of the steps that have been started. Default: `[]` | +| `steps` | An array of steps to record this decision. See usage guidance below. Default: `[]` | +| `submissionUrl` | The URL to the editorial workflow of the submission. | +| `submissionApiUrl` | The URL to the submission in the REST API. | +| `submissionListUrl` | The URL to the current user's submissions list. | +| `viewAllSubmissionsLabel` | A localized string for the button to view all submissions. | +| `viewSubmissionLabel` | A localized string for the button to view this submission. | + +## Usage + +The `DecisionPage` extends the [`Page`](#/component/Page) component. This page controls the step-by-step workflow to record an editorial decision. Each step is defined in the `steps` array. Each step must be one of three recognized types: `form`, `email`, `promoteFiles`. + +The `form` type will display a [Form](#/component/Form) as a step in the workflow. Pass the `
` props in the `form` property. + +```json +{ + "id": "payment", + "type": "form", + "name": "Request Payment", + "description": "Ask the author to pay the Article Processing Charge (USD $150) now or waive the fee.", + "errors": {}, + "form": {...formComponentProps}, +} +``` + +The `email` type will display a [Composer](#/component/Composer) as a step in the workflow. Pass the `` props alongside the step props. + +```json +{ + "id": "notifyAuthors", + "type": "email", + "name": "Notify Authors", + "description": "Send an email to the authors to let them know that their submission has been accepted for publication.", + "errors": {}, + "to": [31], + "recipientOptions": [...], + "canChangeRecipients": false, + "canSkip": true, + "defaultEmailTemplateKey": "EDITOR_DECISION_ACCEPT", + "emailTemplates": [...], + "emailTemplatesApiUrl": "http://example.org", + "variables": {...}, + "locale": "en", + "locales": [...] +} +``` + +The `promoteFiles` type lets the user select one or more submission files to promote to a new file stage. Use a separate list for each file stage. Set the `to` property to the file stage which the selected files should be promoted to. Learn more about [Submission Files](https://docs.pkp.sfu.ca/dev/documentation/en/submission-files). + +```json +{ + "id": "promoteFiles", + "type": "promoteFiles", + "name": "Select Files", + "description": "Select files that should be sent to the copyediting stage.", + "errors": {}, + "lists": [ + { + "name": "Revisions", + "files": [...submissionFiles], + }, + ], + "selected": [], + "to": 6, +} +``` + +Helper classes to generate the step data from the server side can be found at `PKP\decision\steps`. To support new step types, edit the `submit()` method of `` and add a new conditional to the template like the following. + +```html + +``` + + diff --git a/src/components/Container/DecisionPage.stories.js b/src/components/Container/DecisionPage.stories.js new file mode 100644 index 000000000..cae125770 --- /dev/null +++ b/src/components/Container/DecisionPage.stories.js @@ -0,0 +1,495 @@ +import DecisionPage from './DecisionPage.vue'; + +import EmailTemplateMock from '@/mocks/emailTemplate'; +import SubmissionFilesMock from '@/mocks/submissionFiles'; +import InsertContentMock from '@/mocks/insertContent'; + +export default { + title: 'Pages/DecisionPage', + component: DecisionPage, +}; + +const DecisionPageWithDataAndTemplate = { + extends: DecisionPage, + template: `
+ +
+

+ Accept Submission: + +

+

+ This submission will be accepted for publication and sent for + copyediting. +

+ + + {{ error }} + + + + + + + +

{{ step.name }}

+

{{ step.description }}

+
+ + + +
+
+
+ + + + + + + +
+ +
+ + + {{ notification.message }} + + +
+ + + +
+`, + data() { + return { + abandonDecisionLabel: 'Cancel Decision', + cancelConfirmationPrompt: + 'Are you sure you want to cancel this decision?', + currentStep: {}, + decision: 1, + EmailTemplateMocksApiUrl: + 'https://httbin.org/publicknowledge/api/v1/EmailTemplateMocks', + keepWorkingLabel: 'Keep Working', + isComplete: false, + isSubmitting: false, + reviewRoundId: 2, + skippedSteps: [], + stageId: 3, + stepErrorMessage: 'There is a problem with the {$stepName} step.', + startedSteps: [], + submissionApiUrl: + 'https://httbin.org/publicknowledge/api/v1/submission/1', + steps: [ + { + id: 'payment', + type: 'form', + name: 'Request Payment', + description: + 'Ask the author to pay the Article Processing Charge (USD $150) now or waive the fee.', + errors: {}, + form: { + id: 'requestPayment', + method: '', + action: 'emit', + fields: [ + { + name: 'payment', + component: 'field-options', + label: 'Article Processing Charge', + value: [], + type: 'radio', + options: [ + { + value: true, + label: 'Request payment from the author (USD $150)', + }, + { + value: false, + label: 'Waive the fee', + }, + ], + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + }, + ], + primaryLocale: 'en', + visibleLocales: ['en'], + supportedFormLocales: [], + errors: {}, + }, + }, + { + id: 'notifyAuthors', + type: 'email', + name: 'Notify Authors', + description: + 'Send an email to the authors to let them know that their submission has been accepted for publication.', + errors: {}, + to: [31], + recipientOptions: [ + { + value: 31, + label: { + en: 'Rana Baiyewu', + fr_CA: 'Rana Fr Baiyewu', + }, + }, + ], + canChangeRecipients: false, + canSkip: true, + defaultEmailTemplateMockKey: 'EDITOR_DECISION_ACCEPT', + EmailTemplateMocks: [ + { + ...EmailTemplateMock, + name: { + en: 'Accept for Publication', + }, + subject: { + en: 'Accept for Publication', + }, + }, + { + ...EmailTemplateMock, + key: 'EDITOR_DECISION_ACCEPT_CONDITIONS', + name: { + en: 'Accept with Conditions', + }, + subject: { + en: 'Accept with Conditions', + }, + }, + { + ...EmailTemplateMock, + key: 'EDITOR_DECISION_ACCEPT_EARLY', + name: { + en: 'Accept for Early Publication', + }, + subject: { + en: 'Accept for Early Publication', + }, + }, + ], + EmailTemplateMocksApiUrl: 'http://example.org', + variables: { + en: [...InsertContentMock], + fr_CA: [...InsertContentMock], + }, + locale: 'en', + locales: [ + { + locale: 'en', + name: 'English', + }, + { + locale: 'fr_CA', + name: 'French', + }, + ], + }, + { + id: 'notifyReviewers', + type: 'email', + name: 'Notify Reviewers', + description: + 'Send an email to the reviewers to thank them for their review and let them know that a decision was taken.', + errors: { + body: ['This field is required.'], + subject: ['This field is required.'], + to: [ + 'You can not send an email to the following recipients: Alan Mwandenga.', + ], + }, + recipientOptions: [ + { + value: 10, + label: { + en: 'Adela Gallego', + fr_CA: 'Adela Gallego', + }, + }, + { + value: 9, + label: { + en: 'Aisla McCrae', + fr_CA: 'Aisla McCrae', + }, + }, + ], + canChangeRecipients: true, + canSkip: true, + defaultEmailTemplateMockKey: 'EDITOR_DECISION_THANK_REVIEWERS', + EmailTemplateMocks: [ + { + ...EmailTemplateMock, + key: 'EDITOR_DECISION_THANK_REVIEWERS', + subject: { + en: 'Thank Reviewer', + }, + }, + { + ...EmailTemplateMock, + key: 'EDITOR_DECISION_THANK_REVIEWERS_REQUEST', + subject: { + en: 'Thank Reviewer and Request Feedback', + }, + }, + ], + EmailTemplateMocksApiUrl: 'http://example.org', + variables: { + en: [...InsertContentMock], + fr_CA: [...InsertContentMock], + }, + locale: 'en', + locales: [ + { + locale: 'en', + name: 'English', + }, + { + locale: 'fr_CA', + name: 'French ', + }, + ], + }, + { + id: 'promoteFiles', + type: 'promoteFiles', + name: 'Select Files', + description: + 'Select files that should be sent to the copyediting stage.', + errors: {}, + lists: [ + { + name: 'Revisions', + files: [...SubmissionFilesMock], + }, + ], + selected: [], + to: 6, + }, + ], + }; + }, +}; + +export const Default = { + render: (args) => ({ + components: { + DecisionPageWithDataAndTemplate, + }, + setup() { + return {...args}; + }, + template: ` + + `, + }), + + args: {}, +}; diff --git a/src/components/Container/DecisionPage.vue b/src/components/Container/DecisionPage.vue index 5a2f0c9dc..9a972fae9 100644 --- a/src/components/Container/DecisionPage.vue +++ b/src/components/Container/DecisionPage.vue @@ -29,24 +29,43 @@ export default { mixins: [ajaxError, dialog, localizeSubmission], data() { return { + /** A localized string for the button to cancel recording a decision. */ abandonDecisionLabel: '', + /** A localized string for the confirmation prompt to cancel recording a decision. */ cancelConfirmationPrompt: '', + /** Tracks the currently open step. If not passed, the first step will be opened. Default: `{}` */ currentStep: {}, + /** An integer representing the decision type. */ decision: 0, + /** A localized string when a decision has been recorded. See: `DecisionType::getCompletedLabel()` */ decisionCompleteLabel: '', + /** A localized string when a decision has been recorded. See: `DecisionType::getCompletedLabel()` */ decisionCompleteDescription: '', + /** A URL to the REST API to get email templates. */ emailTemplatesApiUrl: '', + /** An array of the steps that have been started. Default: `[]` */ startedSteps: [], + /** Whether or not the decision is being recorded. Default: `false` */ isSubmitting: false, + /** A localized string for the button to keep working in the confirmation prompt to cancel recording a decision. */ keepWorkingLabel: '', + /** The id of the review round this decision should be recorded in. Only pass a review round for decisions in the review stage. Default: `0` */ reviewRoundId: 0, + /** An array of steps that have been skipped. Default: `[]` */ skippedSteps: [], + /** The id of the workflow stage this decision should be recorded in. One of the `WORKFLOW_STAGE_ID_*` constants. */ stageId: 0, + /** An array of steps to record this decision. See usage guidance below. Default: `[]` */ steps: [], + /** The URL to the editorial workflow of the submission. */ submissionUrl: '', + /** The URL to the submission in the REST API. */ submissionApiUrl: '', + /** The URL to the current user's submissions list. */ submissionListUrl: '', + /** A localized string for the button to view all submissions. */ viewAllSubmissionsLabel: '', + /** A localized string for the button to view this submission. */ viewSubmissionLabel: '', }; }, diff --git a/src/components/Container/ManageEmailsPage.mdx b/src/components/Container/ManageEmailsPage.mdx new file mode 100644 index 000000000..d79679b84 --- /dev/null +++ b/src/components/Container/ManageEmailsPage.mdx @@ -0,0 +1,35 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as ManageEmailsPageStories from './ManageEmailsPage.stories.js'; + + + +# ManageEmailsPage + +## Data + +This is a root component. Learn about [page hydration](#/pages/pages). + +| Key | Description | +| --- | --- | +| `...` | All the data of [Page](#/component/Page). | +| `activeFilters` | An object containing the currently active [Filters](#/component/Filter). | +| `currentMailable` | The `Mailable` currently open in a modal. | +| `currentTemplate` | The `EmailTemplate` currently open in a modal. | +| `currentTemplateForm` | A copy of the form to add or edit an `EmailTemplate` that is currently open in a modal. This form may have an email template's details loaded into it. | +| `i18nRemoveTemplate` | A localized string for the button to remove a template. | +| `i18nRemoveTemplateMessage` | A localized string for the confirmation message when removing a template. | +| `i18nResetAll` | A localized string for the button to reset all email templates. | +| `i18nResetAllMessage` | A localized string for the confirmation message when removing a template. | +| `mailables` | An array of all `Mailable`s active in this journal, press or preprint server. | +| `mailablesApiUrl` | The URL to the `/mailables` endpoint in the REST API. | +| `resetFocusTo` | Used to reset focus when a modal is closed. Default: `{}` | +| `searchPhrase` | The value of the search input. Default: `''` | +| `templateForm` | A "clean" copy of the form to add or edit an `EmailTemplate`. The `currentTemplateForm` is a copy of this form that has been modified to add or edit a specific template. | +| `templatesApiUrl` | The URL to the `/emailTemplates` endpoint in the REST API. | + +## Usage + +The `ManageEmailsPage` extends the [`Page`](#/component/Page) component. This page is where editors can edit email templates assigned to mailables. + + diff --git a/src/components/Container/ManageEmailsPage.stories.js b/src/components/Container/ManageEmailsPage.stories.js new file mode 100644 index 000000000..05277005c --- /dev/null +++ b/src/components/Container/ManageEmailsPage.stories.js @@ -0,0 +1,229 @@ +import {http, HttpResponse} from 'msw'; +import MailableMock from '@/mocks/mailable.json'; +import MailablesMock from '@/mocks/mailables.json'; + +import ManageEmailsPage from './ManageEmailsPage.vue'; + +import TemplateFormMock from '@/components/Form/mocks/form-email-template'; + +export default { + title: 'Pages/ManageEmailsPage', + component: ManageEmailsPage, +}; + +const ManageEmailsPageWithDataAndTemplate = { + extends: ManageEmailsPage, + template: `
+ + + + + + + + + + + + + +
+`, + data() { + return { + mailablesApiUrl: 'https://mock/index.php/publicknowledge/api/v1/mailable', + groupFilters: { + submission: 'Submission', + review: 'Review', + copyediting: 'Copyediting', + production: 'Production', + other: 'Other', + }, + fromFilters: { + 17: 'Editor', // ROLE_ID_SUB_EDITOR + 4096: 'Reviewer', // ROLE_ID_REVIEWER + 4097: 'Assistant', // ROLE_ID_ASSISTANT + 1048576: 'Reader', // ROLE_ID_READER + }, + toFilters: { + 17: 'Editor', // ROLE_ID_SUB_EDITOR + 4096: 'Reviewer', // ROLE_ID_REVIEWER + 4097: 'Assistant', // ROLE_ID_ASSISTANT + 65536: 'Author', // ROLE_ID_AUTHOR + 1048576: 'Reader', // ROLE_ID_READER + 2097152: 'Subscription Manager', // ROLE_ID_SUBSCRIPTION_MANAGER + }, + i18nRemoveTemplate: 'Remove Template', + i18nRemoveTemplateMessage: + 'Are you sure you want to delete the template {$template}?', + i18nResetAll: 'Reset All', + i18nResetAllMessage: + 'If you reset all templates, all modifications to the email templates will be lost. Do you want to confirm this operation?', + mailables: MailablesMock, + templateForm: {...TemplateFormMock}, + }; + }, +}; + +export const Default = { + render: (args) => ({ + components: { + ManageEmailsPageWithDataAndTemplate, + }, + setup() { + return {...args}; + }, + template: ` + + `, + }), + + args: {}, + parameters: { + msw: { + handlers: [ + http.get( + 'https://mock/index.php/publicknowledge/api/v1/mailable/*', + async (r) => { + return HttpResponse.json(MailableMock); + }, + ), + ], + }, + }, +}; diff --git a/src/components/Container/ManageEmailsPage.vue b/src/components/Container/ManageEmailsPage.vue index 52d3bd376..e6f8f9871 100644 --- a/src/components/Container/ManageEmailsPage.vue +++ b/src/components/Container/ManageEmailsPage.vue @@ -16,19 +16,33 @@ export default { mixins: [dialog], data() { return { + /** An object containing the currently active Filters */ activeFilters: {}, + /** The `Mailable` currently open in a modal. */ currentMailable: {}, + /** The `EmailTemplate` currently open in a modal. */ currentTemplate: {}, + /** A copy of the form to add or edit an `EmailTemplate` that is currently open in a modal. This form may have an email template's details loaded into it. */ currentTemplateForm: {}, + /** A localized string for the button to remove a template. */ i18nRemoveTemplate: '', + /** A localized string for the confirmation message when removing a template. */ i18nRemoveTemplateMessage: '', + /** A localized string for the button to reset all email templates. */ i18nResetAll: '', + /** A localized string for the confirmation message when removing a template. */ i18nResetAllMessage: '', + /** An array of all `Mailable`s active in this journal, press or preprint server. */ mailables: [], + /** The URL to the `/mailables` endpoint in the REST API. */ mailablesApiUrl: '', + /** Used to reset focus when a modal is closed. */ resetFocusTo: {}, + /** The value of the search input. */ searchPhrase: '', + /** A "clean" copy of the form to add or edit an `EmailTemplate`. The `currentTemplateForm` is a copy of this form that has been modified to add or edit a specific template. */ templateForm: {}, + /** The URL to the `/emailTemplates` endpoint in the REST API. */ templatesApiUrl: '', isModalOpenedMailable: false, isModalOpenedTemplate: false, diff --git a/src/components/Container/Page.mdx b/src/components/Container/Page.mdx new file mode 100644 index 000000000..2247acac6 --- /dev/null +++ b/src/components/Container/Page.mdx @@ -0,0 +1,48 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as PageStories from './Page.stories.js'; + + + +# Page + +## Data + +This is a root component. Learn about [page hydration](#/pages/pages). + +| Key | Description | +| --- | --- | +| `breadcrumbs` | An array of objects defining a breadcrumb to display above main page content. | +| `components` | A key/value object with component data to manage. See [Managing Components](#/component/Page#managing-components) | +| `isLoading` | Set this to `true` to show a full-screen loading [Spinner](#/component/Spinner). | +| `menu` | An array items for the main navigation menu | +| `notifications` | An array of toast-style notifications to display. Default: `[]`. See [Notify](#/utilities/Notify) | +| `notificationInterval` | Used internally to clear notifications. Do not set this manually. | +| `tasksUrl` | The URL to load the tasks grid modal. | +| `unreadTasksCount` | The current number of unread tasks. | + +## Usage + +This component is a root component which is used to render a page of the editorial backend. It manages the layout architecture, including the main navigation menu, tasks and user navigation dropdown. Nothing is needed to load the `Page` component. It is initialized automatically for every page in the editorial backend. Learn more about the [application's frontend](https://docs.pkp.sfu.ca/dev/documentation/en/frontend), including how to [initialize state](https://docs.pkp.sfu.ca/dev/documentation/en/frontend-ui-library#state). + +## URL Hash + +When the URL's `#hash` changes, this component will fire a method, `openUrlHash()`. By default, this will emit an event to open a tab. Override this method to implement other behavior, like changing the step in a [step-by-step wizard](#/component/Steps), in other page components. + +```html + diff --git a/src/components/Form/fields/FieldTextarea.mdx b/src/components/Form/fields/FieldTextarea.mdx new file mode 100644 index 000000000..2cc3907f1 --- /dev/null +++ b/src/components/Form/fields/FieldTextarea.mdx @@ -0,0 +1,17 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as FieldTextareaStories from './FieldTextarea.stories.js'; + + + +# FieldTextarea + +## Usage + +Use this component for entering multiple lines of text when you want to restrict the user's entry to plain text. If the user is allowed to use bold, italics and other rich text markup, use the [FieldRichTextarea](../?path=/docs/forms-fieldrichtextarea--docs) component. + +The `size` of the input area will signal to the user how much information they should enter into the field. Choose a size that is sufficient to display the expected input. + + + + diff --git a/src/components/Form/fields/FieldTextarea.stories.js b/src/components/Form/fields/FieldTextarea.stories.js new file mode 100644 index 000000000..b204aac34 --- /dev/null +++ b/src/components/Form/fields/FieldTextarea.stories.js @@ -0,0 +1,50 @@ +import FieldTextarea from './FieldTextarea.vue'; +import FieldBaseMock from '../mocks/field-base'; +import FieldTextareaMetatagsMock from '../mocks/field-textarea-metatags'; + +export default { + title: 'Forms/FieldTextarea', + component: FieldTextarea, + render: (args) => ({ + components: {FieldTextarea}, + setup() { + function change(name, prop, newValue, localeKey) { + if (localeKey) { + args[prop][localeKey] = newValue; + } else { + args[prop] = newValue; + } + } + + return {args, change}; + }, + template: ` + + `, + }), +}; + +export const Base = { + args: { + ...FieldBaseMock, + ...FieldTextareaMetatagsMock, + }, +}; + +export const Small = { + args: { + ...FieldBaseMock, + ...FieldTextareaMetatagsMock, + label: 'Mailing Address', + size: 'small', + }, +}; + +export const Large = { + args: { + ...FieldBaseMock, + ...FieldTextareaMetatagsMock, + label: 'References', + size: 'large', + }, +}; diff --git a/src/components/Form/fields/FieldTextarea.vue b/src/components/Form/fields/FieldTextarea.vue index 8eaad7c31..f92448202 100644 --- a/src/components/Form/fields/FieldTextarea.vue +++ b/src/components/Form/fields/FieldTextarea.vue @@ -67,6 +67,7 @@ export default { name: 'FieldTextarea', extends: FieldBase, props: { + /** One of `small`, `normal` or `large`. Default: `normal`. */ size: { type: String, default: 'normal', diff --git a/src/components/Form/fields/FieldUpload.mdx b/src/components/Form/fields/FieldUpload.mdx new file mode 100644 index 000000000..f043394bb --- /dev/null +++ b/src/components/Form/fields/FieldUpload.mdx @@ -0,0 +1,17 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as FieldUploadStories from './FieldUpload.stories.js'; + + + +# FieldUpload + +## Usage + +Use this component when you want the user to upload a file. If you want them to upload an image, use the [FieldUploadImage](#/component/Form/fields/FieldUploadImage) component instead. + +You _must_ pass a `url` with the `options` prop. In most cases, the URL should correspond to the application's [API endpoint for temporary files](https://docs.pkp.sfu.ca/dev/api). If you do not use this endpoint, the endpoint should respond to `OPTIONS` and `POST` requests that [Dropzone.js](https://www.dropzonejs.com) makes to upload the file. The response should match what is documented in the [API endpoint for temporary files](https://docs.pkp.sfu.ca/dev/api). + + + + diff --git a/src/components/Form/fields/FieldUpload.stories.js b/src/components/Form/fields/FieldUpload.stories.js new file mode 100644 index 000000000..b9f991447 --- /dev/null +++ b/src/components/Form/fields/FieldUpload.stories.js @@ -0,0 +1,34 @@ +import FieldUpload from './FieldUpload.vue'; +import FieldBaseMock from '../mocks/field-base'; +import FieldUploadCssMock from '../mocks/field-upload-css'; + +export default { + title: 'Forms/FieldUpload', + component: FieldUpload, + render: (args) => ({ + components: {FieldUpload}, + setup() { + function change(name, prop, newValue, localeKey) { + if (localeKey) { + args[prop][localeKey] = newValue; + } else { + args[prop] = newValue; + } + } + + return {args, change}; + }, + template: ` + + `, + }), +}; + +export const Base = { + args: { + ...FieldBaseMock, + ...FieldUploadCssMock, + uploadFileLabel: 'Add File', + restoreLabel: 'Restore', + }, +}; diff --git a/src/components/Form/fields/FieldUpload.vue b/src/components/Form/fields/FieldUpload.vue index 0915369bc..4c6acd250 100644 --- a/src/components/Form/fields/FieldUpload.vue +++ b/src/components/Form/fields/FieldUpload.vue @@ -116,6 +116,7 @@ export default { }, extends: FieldBase, props: { + /** Pass [options](https://www.dropzonejs.com/#configuration-options) to Dropzone.js. */ options: Object, restoreLabel: String, uploadFileLabel: String, diff --git a/src/components/Form/fields/FieldUploadImage.mdx b/src/components/Form/fields/FieldUploadImage.mdx new file mode 100644 index 000000000..0b8be5dd9 --- /dev/null +++ b/src/components/Form/fields/FieldUploadImage.mdx @@ -0,0 +1,15 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as FieldUploadImageStories from './FieldUploadImage.stories.js'; + + + +# FieldUploadImage + +## Usage + +Use this component when you want the user to upload an image. If you want to allow them to upload any file, use the [FieldUpload](#/component/Form/fields/FieldUpload) component instead. See the [FieldUpload](#/component/Form/fields/FieldUpload) component for guidance on passing an upload URL. + + + + diff --git a/src/components/Form/fields/FieldUploadImage.stories.js b/src/components/Form/fields/FieldUploadImage.stories.js new file mode 100644 index 000000000..c485b93df --- /dev/null +++ b/src/components/Form/fields/FieldUploadImage.stories.js @@ -0,0 +1,32 @@ +import FieldUploadImage from './FieldUploadImage.vue'; +import FieldBaseMock from '../mocks/field-base'; +import FieldUploadImageLogo from '../mocks/field-upload-image-logo'; + +export default { + title: 'Forms/FieldUploadImage', + component: FieldUploadImage, + render: (args) => ({ + components: {FieldUploadImage}, + setup() { + function change(name, prop, newValue, localeKey) { + if (localeKey) { + args[prop][localeKey] = newValue; + } else { + args[prop] = newValue; + } + } + + return {args, change}; + }, + template: ` + + `, + }), +}; + +export const Base = { + args: { + ...FieldBaseMock, + ...FieldUploadImageLogo, + }, +}; diff --git a/src/components/Form/fields/FieldUploadImage.vue b/src/components/Form/fields/FieldUploadImage.vue index cd40306fd..483a70762 100644 --- a/src/components/Form/fields/FieldUploadImage.vue +++ b/src/components/Form/fields/FieldUploadImage.vue @@ -135,6 +135,7 @@ export default { props: { altTextDescription: String, altTextLabel: String, + /** The `baseUrl` is prepended to the filename to display previews. For example, the `baseURL` for the logo of a journal with an ID of `2` would be `http://yoursite.com/public/journals/2/` */ baseUrl: String, thumbnailDescription: String, }, diff --git a/src/components/Form/mocks/field-archiving-pn.js b/src/components/Form/mocks/field-archiving-pn.js new file mode 100644 index 000000000..3cc0e9483 --- /dev/null +++ b/src/components/Form/mocks/field-archiving-pn.js @@ -0,0 +1,22 @@ +export default { + name: 'archiving-pn', + component: 'field-archiving-pn', + label: 'PKP Preservation Network', + description: + 'The PKP Preservation Network (PN) provides free preservation services for any OJS journal that meets a few basic criteria.', + value: false, + options: [ + { + value: true, + label: 'Enable PKP PN plugin', + }, + ], + terms: + 'View the to accept the terms of use for the PKP PN.', + enablePluginSuccess: 'The PKP PN plugin was successfully enabled.', + enablePluginUrl: 'https://example.com', + disablePluginSuccess: 'The PKP PN plugin has been disabled.', + disablePluginUrl: 'https://example.com', + settingsUrl: 'https://example.com', + groupId: 'test', +}; diff --git a/src/components/Form/mocks/field-autosuggest.js b/src/components/Form/mocks/field-autosuggest.js new file mode 100644 index 000000000..da5674f25 --- /dev/null +++ b/src/components/Form/mocks/field-autosuggest.js @@ -0,0 +1,9 @@ +export default { + name: 'autosuggest', + component: 'field-autosuggest', + label: '', + value: [], + deselectLabel: 'Remove {$item}', + selectedLabel: 'Selected', + apiUrl: '', +}; diff --git a/src/components/Form/mocks/field-base.js b/src/components/Form/mocks/field-base.js new file mode 100644 index 000000000..60e118cac --- /dev/null +++ b/src/components/Form/mocks/field-base.js @@ -0,0 +1,8 @@ +export default { + groupId: 'default', + formId: 'default', + isRequired: false, + primaryLocale: 'en', + locales: ['en'], + allErrors: {}, +}; diff --git a/src/components/Form/mocks/field-color.js b/src/components/Form/mocks/field-color.js new file mode 100644 index 000000000..ac0ed9338 --- /dev/null +++ b/src/components/Form/mocks/field-color.js @@ -0,0 +1,6 @@ +export default { + name: 'color', + component: 'field-color', + label: 'Color', + value: '#006798', +}; diff --git a/src/components/Form/mocks/field-controlled-vocab-keywords.js b/src/components/Form/mocks/field-controlled-vocab-keywords.js new file mode 100644 index 000000000..a9d62a34d --- /dev/null +++ b/src/components/Form/mocks/field-controlled-vocab-keywords.js @@ -0,0 +1,12 @@ +export default { + name: 'keywords', + component: 'field-controlled-vocab', + label: 'Keywords', + description: + 'Keywords are typically one- to three-word phrases that are used to indicate the main topics of a submission.', + value: [], + apiUrl: 'controlled-vocab.json', + deselectLabel: 'Remove {$item}', + noneLabel: 'None', + selectedLabel: 'Selected', +}; diff --git a/src/components/Form/mocks/field-html-lorem.js b/src/components/Form/mocks/field-html-lorem.js new file mode 100644 index 000000000..3872511bd --- /dev/null +++ b/src/components/Form/mocks/field-html-lorem.js @@ -0,0 +1,7 @@ +export default { + name: 'lorem', + component: 'field-html', + label: 'Lorem Ipsum', + description: + '

Dolor sit amet, consectetur adipiscing elit. Sed massa est, finibus vulputate justo ac, vestibulum gravida magna. Donec quis sagittis sapien. Ut tincidunt scelerisque nunc ut malesuada.

  • One
  • Two

Nullam quis eros quis justo blandit dignissim. Proin eget aliquet ligula, id ultrices elit.

', +}; diff --git a/src/components/Form/mocks/field-metadata.js b/src/components/Form/mocks/field-metadata.js new file mode 100644 index 000000000..845b4cb6e --- /dev/null +++ b/src/components/Form/mocks/field-metadata.js @@ -0,0 +1,31 @@ +export default { + name: 'metadata', + component: 'field-metadata-setting', + label: 'Keywords', + description: + 'Keywords are typically one- to three-word phrases that are used to indicate the main topics of a submission.', + value: 0, + disabledValue: 0, + enabledOnlyValue: 'enable', + options: [ + { + value: 'enable', + label: 'Enable keyword metadata', + }, + ], + submissionOptions: [ + { + value: 'enable', + label: 'Do not request keywords from the author during submission.', + }, + { + value: 'request', + label: 'Ask the author to suggest keywords during submission.', + }, + { + value: 'require', + label: + 'Require the author to suggest keywords before accepting their submission.', + }, + ], +}; diff --git a/src/components/Form/mocks/field-options-authors-role.js b/src/components/Form/mocks/field-options-authors-role.js new file mode 100644 index 000000000..88c6efa99 --- /dev/null +++ b/src/components/Form/mocks/field-options-authors-role.js @@ -0,0 +1,13 @@ +export default { + name: 'userGroupId', + component: 'field-options', + label: "Contributor's role", + type: 'radio', + isRequired: false, + value: 14, + groupId: 'default', + options: [ + {value: 14, label: 'Author'}, + {value: 15, label: 'Translator'}, + ], +}; diff --git a/src/components/Form/mocks/field-options-categories.js b/src/components/Form/mocks/field-options-categories.js new file mode 100644 index 000000000..5d34cff09 --- /dev/null +++ b/src/components/Form/mocks/field-options-categories.js @@ -0,0 +1,17 @@ +import categories from '../../../data/categories'; + +export default { + name: 'categories', + component: 'field-options', + label: 'Categories', + description: + 'Select only the categories that are appropriate for your submission.', + value: [], + options: Object.keys(categories).map((id) => { + return { + value: id, + label: categories[id], + }; + }), + groupId: 'default', +}; diff --git a/src/components/Form/mocks/field-options-confirmation.js b/src/components/Form/mocks/field-options-confirmation.js new file mode 100644 index 000000000..f8e4c4ed4 --- /dev/null +++ b/src/components/Form/mocks/field-options-confirmation.js @@ -0,0 +1,16 @@ +export default { + name: 'options-confirmation', + component: 'field-options', + label: 'Email Preferences', + description: '', + type: 'checkbox', + value: false, + options: [ + { + value: true, + label: + 'Yes, I would like to receive emails. Yes, I would like to receive emails. Yes, I would like to receive emails. Yes, I would like to receive emails. ', + }, + ], + groupId: 'preferences', +}; diff --git a/src/components/Form/mocks/field-options-copyright.js b/src/components/Form/mocks/field-options-copyright.js new file mode 100644 index 000000000..8e17d644e --- /dev/null +++ b/src/components/Form/mocks/field-options-copyright.js @@ -0,0 +1,20 @@ +import exampleText from '@/mocks/exampleText'; + +export default { + name: 'options-copyright', + component: 'field-options', + label: 'Copyright', + description: ` + Please read and understand the copyright terms for submissions to this journal. +
${exampleText[0]}
+ `, + type: 'checkbox', + value: false, + options: [ + { + value: true, + label: 'Yes, I agree to the copyright statement.', + }, + ], + groupId: 'default', +}; diff --git a/src/components/Form/mocks/field-options-emails-discussions.js b/src/components/Form/mocks/field-options-emails-discussions.js new file mode 100644 index 000000000..3d8ab0af0 --- /dev/null +++ b/src/components/Form/mocks/field-options-emails-discussions.js @@ -0,0 +1,15 @@ +export default { + name: 'email-options-discussions', + component: 'field-options', + label: 'Discussions and Replies', + showWhen: ['email-options', 'weekly'], + value: false, + options: [ + { + value: true, + label: + 'Yes, send me new discussions and replies by email as soon as they happen.', + }, + ], + groupId: 'default', +}; diff --git a/src/components/Form/mocks/field-options-emails.js b/src/components/Form/mocks/field-options-emails.js new file mode 100644 index 000000000..5c2ba9630 --- /dev/null +++ b/src/components/Form/mocks/field-options-emails.js @@ -0,0 +1,24 @@ +export default { + name: 'email-options', + component: 'field-options', + label: 'Email Options', + description: 'How would you like to receive email notifications?', + value: [], + type: 'radio', + showWhen: 'options-confirmation', + options: [ + { + value: 'asap', + label: 'As soon as possible', + }, + { + value: 'daily', + label: 'Once a day', + }, + { + value: 'weekly', + label: 'Once a week', + }, + ], + groupId: 'preferences', +}; diff --git a/src/components/Form/mocks/field-options-file-genre.js b/src/components/Form/mocks/field-options-file-genre.js new file mode 100644 index 000000000..4f3ff4b80 --- /dev/null +++ b/src/components/Form/mocks/field-options-file-genre.js @@ -0,0 +1,51 @@ +export default { + name: 'genreId', + component: 'field-options', + label: 'What kind of file is this?', + description: 'Choose the option that best describes this file.', + value: '', + type: 'radio', + options: [ + { + value: 1, + label: 'Book Manuscript', + }, + { + value: 2, + label: 'Chapter Manuscript', + }, + { + value: 3, + label: 'Preface', + }, + { + value: 4, + label: 'Index', + }, + { + value: 5, + label: 'Glossary', + }, + { + value: 7, + label: 'Prospectus', + }, + { + value: 11, + label: 'Table', + }, + { + value: 8, + label: 'Figure', + }, + { + value: 9, + label: 'Audio', + }, + { + value: 10, + label: 'Other', + }, + ], + groupId: 'default', +}; diff --git a/src/components/Form/mocks/field-options-orderable.js b/src/components/Form/mocks/field-options-orderable.js new file mode 100644 index 000000000..89d0e7457 --- /dev/null +++ b/src/components/Form/mocks/field-options-orderable.js @@ -0,0 +1,23 @@ +export default { + name: 'options-orderable', + component: 'field-options', + label: 'Sidebar', + description: 'Select which sidebar blocks to display and change their order.', + type: 'checkbox', + isOrderable: true, + value: ['languagetoggleblockplugin', 'developedbyblockplugin'], + options: [ + { + value: 'informationblockplugin', + label: 'Information Block', + }, + { + value: 'languagetoggleblockplugin', + label: 'Language Toggle Block', + }, + { + value: 'developedbyblockplugin', + label: '"Developed By" Block', + }, + ], +}; diff --git a/src/components/Form/mocks/field-options-submission-agreement.js b/src/components/Form/mocks/field-options-submission-agreement.js new file mode 100644 index 000000000..bc9fd6037 --- /dev/null +++ b/src/components/Form/mocks/field-options-submission-agreement.js @@ -0,0 +1,24 @@ +import exampleText from '../../../data/exampleText'; + +const $li = exampleText.map( + (paragraph) => `
  • ${paragraph.substring(0, 100)}
  • `, +); + +export default { + name: 'options-submission-agreement', + component: 'field-options', + label: 'Submission Agreement', + description: + 'By submitting to this journal, you agree to the following terms.
      ' + + $li.join('') + + '
    ', + type: 'checkbox', + value: false, + options: [ + { + value: true, + label: 'Yes, I agree to these terms.', + }, + ], + groupId: 'default', +}; diff --git a/src/components/Form/mocks/field-options-user-locales.js b/src/components/Form/mocks/field-options-user-locales.js new file mode 100644 index 000000000..862a26c32 --- /dev/null +++ b/src/components/Form/mocks/field-options-user-locales.js @@ -0,0 +1,25 @@ +export default { + name: 'user-locales', + component: 'field-options', + label: 'Working Languages', + description: + 'We use this to adjust your email notifications and the information we request from you.', + type: 'checkbox', + isOrderable: false, + value: [], + options: [ + { + value: 'en', + label: 'English', + }, + { + value: 'fr_CA', + label: 'French', + }, + { + value: 'ar', + label: 'عربى', + }, + ], + groupId: 'preferences', +}; diff --git a/src/components/Form/mocks/field-radio-input.js b/src/components/Form/mocks/field-radio-input.js new file mode 100644 index 000000000..02c5aa482 --- /dev/null +++ b/src/components/Form/mocks/field-radio-input.js @@ -0,0 +1,20 @@ +export default { + name: 'radio-input', + component: 'field-radio-input', + label: 'Select an option or input your own', + value: 'something', + options: [ + { + value: 'yes', + label: 'Yes', + }, + { + value: 'no', + label: 'No', + }, + { + label: 'Other', + isInput: true, + }, + ], +}; diff --git a/src/components/Form/mocks/field-rich-textarea-abstract.js b/src/components/Form/mocks/field-rich-textarea-abstract.js new file mode 100644 index 000000000..415a1489a --- /dev/null +++ b/src/components/Form/mocks/field-rich-textarea-abstract.js @@ -0,0 +1,17 @@ +export default { + name: 'abstract', + component: 'field-rich-textarea', + label: 'Abstract', + description: 'The abstract must be less than 300 words.', + isRequired: true, + plugins: 'paste,link,noneditable', + toolbar: 'bold italic superscript subscript | link', + value: { + en: '

    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. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    ', + fr_CA: '', + ar: '', + }, + isMultilingual: true, + wordCountLabel: 'Word Count: {$count}/{$limit}', + wordLimit: 200, +}; diff --git a/src/components/Form/mocks/field-rich-textarea-bio.js b/src/components/Form/mocks/field-rich-textarea-bio.js new file mode 100644 index 000000000..eceb5e5c5 --- /dev/null +++ b/src/components/Form/mocks/field-rich-textarea-bio.js @@ -0,0 +1,16 @@ +export default { + name: 'bio', + component: 'field-rich-textarea', + label: 'Bio Statement', + groupId: 'profile', + plugins: 'paste,link,noneditable', + toolbar: 'bold italic superscript subscript | link', + tooltip: + 'Your biographical statement will usually include your department and rank, and may include research interests or key publications.', + value: { + en: '', + fr_CA: '', + ar: '', + }, + isMultilingual: true, +}; diff --git a/src/components/Form/mocks/field-rich-textarea-description.js b/src/components/Form/mocks/field-rich-textarea-description.js new file mode 100644 index 000000000..c05a6f765 --- /dev/null +++ b/src/components/Form/mocks/field-rich-textarea-description.js @@ -0,0 +1,9 @@ +export default { + name: 'description', + component: 'field-rich-textarea', + label: 'Journal Description', + value: '', + plugins: 'paste,link,lists,image,code', + toolbar: + 'bold italic superscript subscript | link | blockquote bullist numlist | image | code', +}; diff --git a/src/components/Form/mocks/field-rich-textarea-signature.js b/src/components/Form/mocks/field-rich-textarea-signature.js new file mode 100644 index 000000000..fe8132927 --- /dev/null +++ b/src/components/Form/mocks/field-rich-textarea-signature.js @@ -0,0 +1,21 @@ +export default { + name: 'signature', + component: 'field-rich-textarea', + label: { + en: 'Signature', + fr_CA: 'Signature', + }, + description: { + en: 'Add a personal signature you would like to be included with any emails we send on your behalf.', + fr_CA: + 'Ajoutez une signature personnelle que vous souhaitez inclure dans les courriels que nous envoyons en votre nom.', + }, + groupId: 'contact', + isMultilingual: true, + plugins: 'paste,link,noneditable', + toolbar: 'bold italic superscript subscript | link', + value: { + en: '', + fr_CA: '', + }, +}; diff --git a/src/components/Form/mocks/field-select-country.js b/src/components/Form/mocks/field-select-country.js new file mode 100644 index 000000000..651e6ec42 --- /dev/null +++ b/src/components/Form/mocks/field-select-country.js @@ -0,0 +1,179 @@ +export default { + name: 'country', + component: 'field-select', + label: 'Country', + options: [ + { + value: '', + label: 'Select a country', + disabled: true, + }, + { + value: 'AF', + label: 'Afghanistan', + }, + { + value: 'AL', + label: 'Albania', + }, + { + value: 'DZ', + label: 'Algeria', + }, + { + value: 'AS', + label: 'American Samoa', + }, + { + value: 'AD', + label: 'Andorra', + }, + { + value: 'AO', + label: 'Angola', + }, + { + value: 'AI', + label: 'Anguilla', + }, + { + value: 'AQ', + label: 'Antarctica', + }, + { + value: 'AG', + label: 'Antigua and Barbuda', + }, + { + value: 'AR', + label: 'Argentina', + }, + { + value: 'AM', + label: 'Armenia', + }, + { + value: 'AW', + label: 'Aruba', + }, + { + value: 'AU', + label: 'Australia', + }, + { + value: 'AT', + label: 'Austria', + }, + { + value: 'AZ', + label: 'Azerbaijan', + }, + { + value: 'BS', + label: 'Bahamas', + }, + { + value: 'BH', + label: 'Bahrain', + }, + { + value: 'BD', + label: 'Bangladesh', + }, + { + value: 'BB', + label: 'Barbados', + }, + { + value: 'BY', + label: 'Belarus', + }, + { + value: 'BE', + label: 'Belgium', + }, + { + value: 'BZ', + label: 'Belize', + }, + { + value: 'BJ', + label: 'Benin', + }, + { + value: 'BM', + label: 'Bermuda', + }, + { + value: 'BT', + label: 'Bhutan', + }, + { + value: 'BO', + label: 'Bolivia, Plurinational State of', + }, + { + value: 'BQ', + label: 'Bonaire, Sint Eustatius and Saba', + }, + { + value: 'BA', + label: 'Bosnia and Herzegovina', + }, + { + value: 'BW', + label: 'Botswana', + }, + { + value: 'BV', + label: 'Bouvet Island', + }, + { + value: 'BR', + label: 'Brazil', + }, + { + value: 'IO', + label: 'British Indian Ocean Territory', + }, + { + value: 'BN', + label: 'Brunei Darussalam', + }, + { + value: 'BG', + label: 'Bulgaria', + }, + { + value: 'BF', + label: 'Burkina Faso', + }, + { + value: 'BI', + label: 'Burundi', + }, + { + value: 'CV', + label: 'Cabo Verde', + }, + { + value: 'KH', + label: 'Cambodia', + }, + { + value: 'CM', + label: 'Cameroon', + }, + { + value: 'CA', + label: 'Canada', + }, + { + value: '...', + label: '...', + }, + ], + value: '', + groupId: 'contact', + isRequired: true, +}; diff --git a/src/components/Form/mocks/field-select-issue.js b/src/components/Form/mocks/field-select-issue.js new file mode 100644 index 000000000..7e8d70b6d --- /dev/null +++ b/src/components/Form/mocks/field-select-issue.js @@ -0,0 +1,51 @@ +export default { + name: 'issueId', + component: 'field-select-issue', + label: 'Issue', + publicationStatus: 1, // PKPSubmission::STATUS_QUEUED + options: [ + { + value: '', + label: '', + }, + { + value: '', + label: '--- Future Issues ---', + }, + { + value: 5, + label: 'Vol 2 No 1 2019', + }, + { + value: 4, + label: 'Vol 1 No 4 2018', + }, + { + value: '', + label: '--- Back Issues ---', + }, + { + value: 3, + label: 'Vol 1 No 3 2019', + }, + { + value: 2, + label: 'Vol 1 No 2 2019', + }, + { + value: 1, + label: 'Vol 1 No 1 2019', + }, + ], + value: '', + assignLabel: 'Assign to Issue', + assignedNoticeBase: + 'This has been assigned to {$issueName} but it has not been scheduled for publication.', + changeIssue: 'Change Issue', + publishedNoticeBase: + 'Published in {$issueName}.', + scheduledNoticeBase: + 'Scheduled for publication in {$issueName}.', + unscheduledNotice: 'This has not been scheduled for publication in an issue.', + unscheduleLabel: 'Unschedule', +}; diff --git a/src/components/Form/mocks/field-show-ensuring-link.js b/src/components/Form/mocks/field-show-ensuring-link.js new file mode 100644 index 000000000..5b3ee9dc1 --- /dev/null +++ b/src/components/Form/mocks/field-show-ensuring-link.js @@ -0,0 +1,37 @@ +export default { + name: 'show-ensuring-link', + component: 'field-show-ensuring-link', + value: false, + label: 'Ensure Link', + options: [ + { + value: true, + label: + 'Present a link to during upload.', + }, + ], + message: ` +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    +

    Hello this is a long message that will require scrolling.

    • One
    • Two
    + `, + modalTitle: 'How to ensure all files are anonymized', +}; diff --git a/src/components/Form/mocks/field-text-affiliation.js b/src/components/Form/mocks/field-text-affiliation.js new file mode 100644 index 000000000..e3569ec9a --- /dev/null +++ b/src/components/Form/mocks/field-text-affiliation.js @@ -0,0 +1,13 @@ +export default { + name: 'affiliation', + component: 'field-text', + inputType: 'text', + label: 'Affiliation', + groupId: 'identity', + isMultilingual: true, + value: { + en: '', + fr_CA: '', + ar: '', + }, +}; diff --git a/src/components/Form/mocks/field-text-doi-prefix.js b/src/components/Form/mocks/field-text-doi-prefix.js new file mode 100644 index 000000000..d04cb45a9 --- /dev/null +++ b/src/components/Form/mocks/field-text-doi-prefix.js @@ -0,0 +1,8 @@ +export default { + name: 'doi-prefix', + component: 'field-text', + inputType: 'text', + label: 'DOI Prefix', + size: 'small', + value: '', +}; diff --git a/src/components/Form/mocks/field-text-email.js b/src/components/Form/mocks/field-text-email.js new file mode 100644 index 000000000..2e57791de --- /dev/null +++ b/src/components/Form/mocks/field-text-email.js @@ -0,0 +1,9 @@ +export default { + name: 'email', + component: 'field-text', + inputType: 'text', + label: 'Email', + groupId: 'identity', + isRequired: true, + value: '', +}; diff --git a/src/components/Form/mocks/field-text-family-name.js b/src/components/Form/mocks/field-text-family-name.js new file mode 100644 index 000000000..028e7ce24 --- /dev/null +++ b/src/components/Form/mocks/field-text-family-name.js @@ -0,0 +1,8 @@ +export default { + name: 'family-name', + component: 'field-text', + inputType: 'text', + label: 'Family Name', + isRequired: true, + value: '', +}; diff --git a/src/components/Form/mocks/field-text-given-name.js b/src/components/Form/mocks/field-text-given-name.js new file mode 100644 index 000000000..266ee423e --- /dev/null +++ b/src/components/Form/mocks/field-text-given-name.js @@ -0,0 +1,8 @@ +export default { + name: 'given-name', + component: 'field-text', + inputType: 'text', + label: 'Given Name', + isRequired: true, + value: '', +}; diff --git a/src/components/Form/mocks/field-text-orcid.js b/src/components/Form/mocks/field-text-orcid.js new file mode 100644 index 000000000..6c831916c --- /dev/null +++ b/src/components/Form/mocks/field-text-orcid.js @@ -0,0 +1,8 @@ +export default { + name: 'orcid', + component: 'field-text', + inputType: 'url', + label: 'ORCID iD', + groupId: 'profile', + value: '', +}; diff --git a/src/components/Form/mocks/field-text-phone.js b/src/components/Form/mocks/field-text-phone.js new file mode 100644 index 000000000..009ccc40e --- /dev/null +++ b/src/components/Form/mocks/field-text-phone.js @@ -0,0 +1,8 @@ +export default { + name: 'phone', + component: 'field-text', + inputType: 'tel', + label: 'Phone', + groupId: 'contact', + value: '', +}; diff --git a/src/components/Form/mocks/field-text-prefix.js b/src/components/Form/mocks/field-text-prefix.js new file mode 100644 index 000000000..017a5b099 --- /dev/null +++ b/src/components/Form/mocks/field-text-prefix.js @@ -0,0 +1,14 @@ +export default { + name: 'prefix', + component: 'field-text', + inputType: 'text', + label: 'Prefix', + description: 'Examples: A, The', + size: 'small', + value: { + en: 'The', + fr_CA: '', + ar: '', + }, + isMultilingual: true, +}; diff --git a/src/components/Form/mocks/field-text-subtitle.js b/src/components/Form/mocks/field-text-subtitle.js new file mode 100644 index 000000000..fcee1a048 --- /dev/null +++ b/src/components/Form/mocks/field-text-subtitle.js @@ -0,0 +1,13 @@ +export default { + name: 'subtitle', + component: 'field-text', + inputType: 'text', + label: 'Subtitle', + size: 'large', + value: { + en: 'A comparative analysis of functionality and roles', + fr_CA: '', + ar: '', + }, + isMultilingual: true, +}; diff --git a/src/components/Form/mocks/field-text-title.js b/src/components/Form/mocks/field-text-title.js new file mode 100644 index 000000000..70b834c3a --- /dev/null +++ b/src/components/Form/mocks/field-text-title.js @@ -0,0 +1,14 @@ +export default { + name: 'title', + component: 'field-text', + inputType: 'text', + label: 'Title', + isRequired: true, + size: 'large', + value: { + en: 'Effect of Data Modelling and Conceptual Modelling', + fr_CA: '', + ar: '', + }, + isMultilingual: true, +}; diff --git a/src/components/Form/mocks/field-text-url.js b/src/components/Form/mocks/field-text-url.js new file mode 100644 index 000000000..9f2e6e6d8 --- /dev/null +++ b/src/components/Form/mocks/field-text-url.js @@ -0,0 +1,11 @@ +export default { + name: 'url', + component: 'field-text', + inputType: 'url', + label: 'Website', + groupId: 'profile', + tooltip: + 'The link to your website or institutional profile, which should begin with http://.', + value: '', + prefix: 'https://', +}; diff --git a/src/components/Form/mocks/field-textarea-citations.js b/src/components/Form/mocks/field-textarea-citations.js new file mode 100644 index 000000000..a25c48edc --- /dev/null +++ b/src/components/Form/mocks/field-textarea-citations.js @@ -0,0 +1,7 @@ +export default { + name: 'citations', + component: 'field-textarea', + label: 'Citations', + value: '', + size: 'large', +}; diff --git a/src/components/Form/mocks/field-textarea-mailing-address.js b/src/components/Form/mocks/field-textarea-mailing-address.js new file mode 100644 index 000000000..b9c3c3300 --- /dev/null +++ b/src/components/Form/mocks/field-textarea-mailing-address.js @@ -0,0 +1,8 @@ +export default { + name: 'mailing-address', + component: 'field-textarea', + label: 'Mailing Address', + size: 'small', + value: '', + groupId: 'contact', +}; diff --git a/src/components/Form/mocks/field-textarea-metatags.js b/src/components/Form/mocks/field-textarea-metatags.js new file mode 100644 index 000000000..788f21bbb --- /dev/null +++ b/src/components/Form/mocks/field-textarea-metatags.js @@ -0,0 +1,6 @@ +export default { + name: 'metatags', + component: 'field-textarea', + label: 'Metatags', + value: '', +}; diff --git a/src/components/Form/mocks/field-upload-css.js b/src/components/Form/mocks/field-upload-css.js new file mode 100644 index 000000000..5272bcf0c --- /dev/null +++ b/src/components/Form/mocks/field-upload-css.js @@ -0,0 +1,28 @@ +export default { + name: 'css', + component: 'field-upload', + label: 'CSS', + description: + 'Upload a custom CSS file to change the look and feel of your site.', + options: { + url: 'https://httpbin.org/post', + acceptedFiles: '.css', + dropzoneDictDefaultMessage: 'Drop files here to upload', + dropzoneDictFallbackMessage: + "Your browser does not support drag'n'drop file uploads.", + dropzoneDictFallbackText: + 'Please use the fallback form below to upload your files.', + dropzoneDictFileTooBig: + 'File is too big ({{filesize}}mb). Files larger than {{maxFilesize}}mb can not be uploaded.', + dropzoneDictInvalidFileType: 'Files of this type can not be uploaded.', + dropzoneDictResponseError: + 'Server responded with {{statusCode}} code. Please contact the system administrator if this problem persists.', + dropzoneDictCancelUpload: 'Cancel upload', + dropzoneDictUploadCanceled: 'Upload canceled', + dropzoneDictCancelUploadConfirmation: + 'Are you sure you want to cancel this upload?', + dropzoneDictRemoveFile: 'Remove file', + dropzoneDictMaxFilesExceeded: 'You can not upload any more files.', + }, + value: '', +}; diff --git a/src/components/Form/mocks/field-upload-image-logo.js b/src/components/Form/mocks/field-upload-image-logo.js new file mode 100644 index 000000000..b39e40746 --- /dev/null +++ b/src/components/Form/mocks/field-upload-image-logo.js @@ -0,0 +1,15 @@ +export default { + name: 'logo', + component: 'field-upload-image', + label: 'Logo', + options: { + url: 'https://httpbin.org/post', + }, + baseUrl: 'https://httbin.org/public/journals/1/', + altTextDescription: + 'Describe this image for visitors viewing the site in a text-only browser or with assistive devices. Example: "Our editor speaking at the PKP conference."', + altTextLabel: 'Alt Text', + uploadFileLabel: 'Add File', + restoreLabel: 'Restore', + thumbnailDescription: 'Preview of the currently selected image.', +}; diff --git a/src/components/Form/mocks/field-urn.js b/src/components/Form/mocks/field-urn.js new file mode 100644 index 000000000..a9db55f76 --- /dev/null +++ b/src/components/Form/mocks/field-urn.js @@ -0,0 +1,20 @@ +export default { + name: 'urn', + component: 'field-pub-id', + label: 'URN', + value: '', + contextInitials: 'PKP', + issueNumber: '2', + issueVolume: '14', + pattern: 'v%vi%i.%a', + prefix: 'urn:pkp:1234', + separator: '-', + submissionId: 21, + year: '2019', + assignIdLabel: 'Assign URN', + clearIdLabel: 'Clear URN', + missingPartsLabel: + 'You can not generate a URN because one or more parts of the URN pattern are missing data. You may need to assign the publication to an issue, set a publisher ID or enter page numbers.', + missingIssueLabel: + 'You can not generate a URN until this publication has been assigned to an issue.', +}; diff --git a/src/components/Form/mocks/form-announcement.js b/src/components/Form/mocks/form-announcement.js new file mode 100644 index 000000000..b2940a875 --- /dev/null +++ b/src/components/Form/mocks/form-announcement.js @@ -0,0 +1,91 @@ +import Form from './form'; +import FieldTextTitle from './field-text-title'; + +export default { + ...Form, + id: 'example', + action: 'https://example.org/example', + fields: [ + { + ...FieldTextTitle, + value: { + en: '', + fr_CA: '', + ar: '', + }, + groupId: 'default', + }, + { + name: 'descriptionShort', + component: 'field-rich-textarea', + label: 'Short Description', + isMultilingual: true, + plugins: 'paste,link,noneditable', + toolbar: 'bold italic superscript subscript | link', + value: { + en: '', + fr_CA: '', + ar: '', + }, + groupId: 'default', + }, + { + name: 'description', + component: 'field-rich-textarea', + label: 'Description', + isMultilingual: true, + size: 'large', + plugins: 'paste,link,noneditable', + toolbar: 'bold italic superscript subscript | link', + value: { + en: '', + fr_CA: '', + ar: '', + }, + groupId: 'default', + }, + { + name: 'dateExpire', + component: 'field-text', + inputType: 'text', + label: 'Expiry Date', + description: + 'The announcement will be displayed to readers until this date. Leave blank if the announcement should be displayed indefinitely.', + value: '', + groupId: 'default', + }, + { + name: 'sendEmail', + component: 'field-options', + label: 'Send Email', + description: '', + type: 'checkbox', + value: false, + options: [ + { + value: true, + label: + 'Yes, I would like an email about this announcement to be sent to all registered users.', + }, + ], + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-base.js b/src/components/Form/mocks/form-base.js new file mode 100644 index 000000000..110e30c74 --- /dev/null +++ b/src/components/Form/mocks/form-base.js @@ -0,0 +1,35 @@ +import Form from './form'; +import FieldTextGivenName from './field-text-given-name'; +import FieldTextFamilyName from './field-text-family-name'; +import FieldTextEmail from './field-text-email'; +import FieldTextareaMailingAddress from './field-textarea-mailing-address'; + +export default { + ...Form, + id: 'example', + action: 'http://httpbin.org/put', + method: 'PUT', + fields: [ + {...FieldTextGivenName, groupId: 'default'}, + {...FieldTextFamilyName, groupId: 'default'}, + {...FieldTextEmail, groupId: 'default'}, + {...FieldTextareaMailingAddress, groupId: 'default'}, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-change-submission.js b/src/components/Form/mocks/form-change-submission.js new file mode 100644 index 000000000..26387cdb2 --- /dev/null +++ b/src/components/Form/mocks/form-change-submission.js @@ -0,0 +1,63 @@ +import Form from './form'; + +export default { + ...Form, + id: 'confirm', + action: 'emit', + fields: [ + { + name: 'section', + component: 'field-options', + label: 'Section', + value: 1, + type: 'radio', + options: [ + { + value: 1, + label: 'Articles', + }, + { + value: 2, + label: 'Reviews', + }, + { + value: 3, + label: 'Editorials', + }, + ], + groupId: 'default', + }, + { + name: 'language', + component: 'field-options', + label: 'Language', + value: 'en', + type: 'radio', + options: [ + { + value: 'en', + label: 'English', + }, + { + value: 'fr_CA', + label: 'French (Canadian)', + }, + ], + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], +}; diff --git a/src/components/Form/mocks/form-citations.js b/src/components/Form/mocks/form-citations.js new file mode 100644 index 000000000..aa2293225 --- /dev/null +++ b/src/components/Form/mocks/form-citations.js @@ -0,0 +1,34 @@ +import Form from './form'; +import FieldTextarea from './field-textarea-citations'; + +export default { + ...Form, + id: 'citations', + action: '/example/publications/1', + fields: [{...FieldTextarea, groupId: 'default'}], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-conditional-display.js b/src/components/Form/mocks/form-conditional-display.js new file mode 100644 index 000000000..67e82e1b4 --- /dev/null +++ b/src/components/Form/mocks/form-conditional-display.js @@ -0,0 +1,27 @@ +import Form from './form'; +import FieldOptionsConfirmation from './field-options-confirmation'; +import FieldOptionsEmails from './field-options-emails'; +import FieldOptionsEmailsDiscussions from './field-options-emails-discussions'; + +export default { + ...Form, + id: 'example', + action: 'https://example.org/example', + fields: [ + {...FieldOptionsConfirmation, groupId: 'default'}, + { + ...FieldOptionsEmails, + description: + FieldOptionsEmails.description + + ' (Select "Once a week" to see further options.)', + groupId: 'default', + }, + {...FieldOptionsEmailsDiscussions, groupId: 'default'}, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], +}; diff --git a/src/components/Form/mocks/form-confirm.js b/src/components/Form/mocks/form-confirm.js new file mode 100644 index 000000000..6ba513b06 --- /dev/null +++ b/src/components/Form/mocks/form-confirm.js @@ -0,0 +1,34 @@ +import Form from './form'; +import FieldOptionsCopyright from './field-options-copyright'; + +export default { + ...Form, + id: 'confirm', + action: '/example/submit/1', + fields: [{...FieldOptionsCopyright}], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-email-template.js b/src/components/Form/mocks/form-email-template.js new file mode 100644 index 000000000..f8c8dafce --- /dev/null +++ b/src/components/Form/mocks/form-email-template.js @@ -0,0 +1,68 @@ +import Form from './form'; + +export default { + ...Form, + id: 'example', + action: 'https://example.org/example', + hiddenFields: {}, + fields: [ + { + component: 'field-text', + description: 'Enter a brief name to help you find this template.', + label: 'Name', + name: 'name', + value: { + en: '', + fr_CA: '', + }, + isMultilingual: true, + groupId: 'default', + }, + { + name: 'subject', + component: 'field-text', + label: 'Subject', + isMultilingual: true, + value: { + en: '', + fr_CA: '', + }, + groupId: 'default', + }, + { + name: 'body', + component: 'field-prepared-content', + label: 'Body', + description: + 'Use the "Insert Content" button to use dynamic data in your template. These variables will be replaced with real data before the email is sent.', + isMultilingual: true, + size: 'large', + plugins: 'paste,link,lists', + toolbar: + 'bold italic superscript subscript | link | blockquote bullist numlist', + value: { + en: '', + fr_CA: '', + }, + insertLabel: 'Insert', + insertModalLabel: 'Insert Content', + preparedContentLabel: 'Content', + searchLabel: 'Find content to insert', + preparedContent: [], + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + ], +}; diff --git a/src/components/Form/mocks/form-for-the-editors.js b/src/components/Form/mocks/form-for-the-editors.js new file mode 100644 index 000000000..c906a2290 --- /dev/null +++ b/src/components/Form/mocks/form-for-the-editors.js @@ -0,0 +1,48 @@ +import Form from './form'; +import FieldCategories from './field-options-categories'; +import FieldRichTextareaAbstract from './field-rich-textarea-abstract'; + +export default { + ...Form, + id: 'forTheEditors', + action: '/example/publications/1', + fields: [ + {...FieldCategories, groupId: 'default'}, + { + ...FieldRichTextareaAbstract, + label: 'Comments for the Editors', + isMultilingual: false, + description: + 'Add any information that you think our editorial staff should know when evaluating your submission.', + value: '', + toolbar: 'bold italic superscript subscript | link', + plugins: 'paste,link', + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-groups.js b/src/components/Form/mocks/form-groups.js new file mode 100644 index 000000000..ffa53deca --- /dev/null +++ b/src/components/Form/mocks/form-groups.js @@ -0,0 +1,34 @@ +import Form from './form'; +import GroupIdentity from './group-identity'; +import GroupContact from './group-contact'; +import FieldTextAffiliation from './field-text-affiliation'; +import FieldTextEmail from './field-text-email'; +import FieldTextPhone from './field-text-phone'; +import FieldSelectCountry from './field-select-country'; + +export default { + ...Form, + id: 'example', + action: '/example/example', + fields: [ + FieldTextEmail, + FieldTextAffiliation, + FieldTextPhone, + FieldSelectCountry, + ], + groups: [ + {...GroupIdentity, pageId: 'default'}, + {...GroupContact, pageId: 'default'}, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-highlight.js b/src/components/Form/mocks/form-highlight.js new file mode 100644 index 000000000..24dcd5ea3 --- /dev/null +++ b/src/components/Form/mocks/form-highlight.js @@ -0,0 +1,70 @@ +import Form from './form'; +import FieldTextTitle from './field-text-title'; + +const FieldText = { + component: 'field-text', + groupId: 'default', + isMultilingual: true, + isRequired: true, + value: { + en: '', + fr_CA: '', + ar: '', + }, +}; + +export default { + ...Form, + id: 'example', + action: 'https://example.org/example', + fields: [ + { + ...FieldText, + name: 'title', + label: 'Title', + groupId: 'default', + }, + { + ...FieldText, + name: 'description', + label: 'Description', + required: false, + groupId: 'default', + }, + { + ...FieldTextTitle, + name: 'url', + label: 'URL', + description: + 'The full URL, including https://, to the page of the highlight.', + value: '', + groupId: 'default', + }, + { + ...FieldTextTitle, + name: 'urlText', + label: 'Button Text', + description: 'The text of the highlight button, such as Read More.', + isMultilingual: true, + size: 'small', + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-institution.js b/src/components/Form/mocks/form-institution.js new file mode 100644 index 000000000..8cf6dca39 --- /dev/null +++ b/src/components/Form/mocks/form-institution.js @@ -0,0 +1,52 @@ +import Form from './form'; + +export default { + ...Form, + id: 'example', + action: 'https://example.org/example', + fields: [ + { + name: 'name', + component: 'field-text', + inputType: 'text', + label: 'Name', + isRequired: true, + size: 'large', + value: { + en: '', + fr_CA: '', + ar: '', + }, + isMultilingual: true, + groupId: 'default', + }, + { + name: 'ipRanges', + component: 'field-textarea', + label: 'IP ranges', + description: + "Valid values include an IP address (e.g. 142.58.103.1), IP range (e.g. 142.58.103.1 - 142.58.103.4), IP range with wildcard '*' (e.g. 142.58.*.*), and an IP range with CIDR (e.g. 142.58.100.0/24).", + value: '', + size: 'large', + isMultilingual: false, + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-metadata.js b/src/components/Form/mocks/form-metadata.js new file mode 100644 index 000000000..c1b51082b --- /dev/null +++ b/src/components/Form/mocks/form-metadata.js @@ -0,0 +1,34 @@ +import Form from './form'; +import FieldControlledVocabKeywords from './field-controlled-vocab-keywords'; + +export default { + ...Form, + id: 'metadata', + action: '/example/publications/1', + fields: [{...FieldControlledVocabKeywords, groupId: 'default'}], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-multilingual.js b/src/components/Form/mocks/form-multilingual.js new file mode 100644 index 000000000..56088566a --- /dev/null +++ b/src/components/Form/mocks/form-multilingual.js @@ -0,0 +1,32 @@ +import Form from './form'; +import FieldTextOrcid from './field-text-orcid'; +import FieldTextAffiliation from './field-text-affiliation'; +import FieldRichTextareaBio from './field-rich-textarea-bio'; + +export default { + ...Form, + id: 'example', + action: 'https://example.org/example', + fields: [ + {...FieldTextOrcid, groupId: 'default'}, + {...FieldTextAffiliation, groupId: 'default'}, + {...FieldRichTextareaBio, groupId: 'default'}, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-relations.js b/src/components/Form/mocks/form-relations.js new file mode 100644 index 000000000..d49b65abb --- /dev/null +++ b/src/components/Form/mocks/form-relations.js @@ -0,0 +1,47 @@ +import Form from './form'; +import FieldTextPrefix from './field-text-prefix'; +import FieldTextTitle from './field-text-title'; +import FieldTextSubtitle from './field-text-subtitle'; +import FieldRichTextareaAbstract from './field-rich-textarea-abstract'; + +export default { + ...Form, + id: 'titleAbstract', + action: '/example/publications/1', + fields: [ + {...FieldTextPrefix, groupId: 'default'}, + {...FieldTextTitle, groupId: 'default'}, + {...FieldTextSubtitle, groupId: 'default'}, + { + ...FieldRichTextareaAbstract, + toolbar: 'bold italic superscript subscript | link', + plugins: 'paste,link', + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-submission-file.js b/src/components/Form/mocks/form-submission-file.js new file mode 100644 index 000000000..7ec30e714 --- /dev/null +++ b/src/components/Form/mocks/form-submission-file.js @@ -0,0 +1,23 @@ +import Form from './form'; +import FieldOptionsFileGenre from './field-options-file-genre'; + +export default { + ...Form, + id: 'submissionFile', + action: '/submissions/1/files', + fields: [{...FieldOptionsFileGenre}], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], +}; diff --git a/src/components/Form/mocks/form-test.js b/src/components/Form/mocks/form-test.js new file mode 100644 index 000000000..77f650498 --- /dev/null +++ b/src/components/Form/mocks/form-test.js @@ -0,0 +1,42 @@ +import Form from './form'; +import FieldColor from './field-color'; +import FieldHtmlLorem from './field-html-lorem'; +import FieldMetadataSetting from './field-metadata'; +import FieldOptionsOrderable from './field-options-orderable'; +import FieldRadioInput from './field-radio-input'; +import FieldShowEnsuringLink from './field-show-ensuring-link'; +import FieldUploadCss from './field-upload-css.js'; +import FieldUploadImageLogo from './field-upload-image-logo.js'; + +export default { + ...Form, + id: 'test', + action: '/example/default', + fields: [ + {...FieldColor, groupId: 'default'}, + {...FieldHtmlLorem, groupId: 'default'}, + {...FieldMetadataSetting, groupId: 'default'}, + {...FieldOptionsOrderable, groupId: 'default'}, + {...FieldRadioInput, groupId: 'default'}, + {...FieldShowEnsuringLink, groupId: 'default'}, + {...FieldUploadCss, groupId: 'default'}, + {...FieldUploadImageLogo, groupId: 'default'}, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-title-abstract.js b/src/components/Form/mocks/form-title-abstract.js new file mode 100644 index 000000000..d49b65abb --- /dev/null +++ b/src/components/Form/mocks/form-title-abstract.js @@ -0,0 +1,47 @@ +import Form from './form'; +import FieldTextPrefix from './field-text-prefix'; +import FieldTextTitle from './field-text-title'; +import FieldTextSubtitle from './field-text-subtitle'; +import FieldRichTextareaAbstract from './field-rich-textarea-abstract'; + +export default { + ...Form, + id: 'titleAbstract', + action: '/example/publications/1', + fields: [ + {...FieldTextPrefix, groupId: 'default'}, + {...FieldTextTitle, groupId: 'default'}, + {...FieldTextSubtitle, groupId: 'default'}, + { + ...FieldRichTextareaAbstract, + toolbar: 'bold italic superscript subscript | link', + plugins: 'paste,link', + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Save', + }, + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form-user.js b/src/components/Form/mocks/form-user.js new file mode 100644 index 000000000..4f8af08c6 --- /dev/null +++ b/src/components/Form/mocks/form-user.js @@ -0,0 +1,88 @@ +import Form from './form'; +import GroupIdentity from './group-identity'; +import GroupContact from './group-contact'; +import GroupPreferences from './group-preferences'; +import GroupProfile from './group-profile'; +import FieldOptionsUserLocales from './field-options-user-locales'; +import FieldOptionsConfirmation from './field-options-confirmation.js'; +import FieldOptionsEmails from './field-options-emails.js'; +import FieldRichTextareaBio from './field-rich-textarea-bio'; +import FieldTextAffiliation from './field-text-affiliation'; +import FieldTextareaMailingAddress from './field-textarea-mailing-address'; +import FieldTextEmail from './field-text-email'; +import FieldTextFamilyName from './field-text-family-name'; +import FieldTextGivenName from './field-text-given-name'; +import FieldTextOrcid from './field-text-orcid'; +import FieldTextPhone from './field-text-phone'; +import FieldTextUrl from './field-text-url'; +import FieldSelectCountry from './field-select-country'; + +export default { + ...Form, + id: 'example', + action: '/example/user', + fields: [ + FieldTextGivenName, + FieldTextFamilyName, + FieldTextEmail, + FieldTextAffiliation, + FieldTextPhone, + FieldSelectCountry, + FieldTextareaMailingAddress, + FieldOptionsUserLocales, + FieldOptionsConfirmation, + FieldOptionsEmails, + FieldTextUrl, + FieldRichTextareaBio, + FieldTextOrcid, + ], + groups: [ + {...GroupIdentity, pageId: 'account'}, + {...GroupContact, pageId: 'account'}, + {...GroupPreferences, pageId: 'preferences'}, + {...GroupProfile, pageId: 'profile'}, + ], + pages: [ + { + id: 'account', + label: 'Account Details', + submitButton: { + label: 'Next', + isPrimary: true, + }, + }, + { + id: 'preferences', + label: 'Preferences', + submitButton: { + label: 'Next', + isPrimary: true, + }, + previousButton: { + label: 'Previous', + }, + }, + { + id: 'profile', + label: 'Profile', + submitButton: { + label: 'Submit', + isPrimary: true, + }, + previousButton: { + label: 'Previous', + }, + }, + ], + supportedFormLocales: [ + ...Form.supportedFormLocales, + { + key: 'fr_CA', + label: 'Français (Canada)', + }, + { + key: 'ar', + label: 'عربى', + }, + ], +}; diff --git a/src/components/Form/mocks/form.js b/src/components/Form/mocks/form.js new file mode 100644 index 000000000..b095449be --- /dev/null +++ b/src/components/Form/mocks/form.js @@ -0,0 +1,25 @@ +export default { + method: 'PUT', + action: '/example/default', + errors: {}, + fields: [], + groups: [], + pages: [ + { + id: 'default', + submitButton: { + label: 'Submit', + isPrimary: true, + }, + }, + ], + primaryLocale: 'en', + visibleLocales: ['en'], + supportedFormLocales: [ + { + key: 'en', + label: 'English', + }, + ], + isSaving: false, +}; diff --git a/src/components/Form/mocks/group-contact.js b/src/components/Form/mocks/group-contact.js new file mode 100644 index 000000000..f485a0344 --- /dev/null +++ b/src/components/Form/mocks/group-contact.js @@ -0,0 +1,6 @@ +export default { + id: 'contact', + label: 'Contact', + description: + 'Help us reach you more easily and tailor how we handle sending emails on your behalf.', +}; diff --git a/src/components/Form/mocks/group-identity.js b/src/components/Form/mocks/group-identity.js new file mode 100644 index 000000000..fed699031 --- /dev/null +++ b/src/components/Form/mocks/group-identity.js @@ -0,0 +1,6 @@ +export default { + id: 'identity', + label: 'Identity', + description: + 'Your name, email and institution will be displayed alongside any published works. We also use these details to communicate with you.', +}; diff --git a/src/components/Form/mocks/group-preferences.js b/src/components/Form/mocks/group-preferences.js new file mode 100644 index 000000000..5c4381abc --- /dev/null +++ b/src/components/Form/mocks/group-preferences.js @@ -0,0 +1,5 @@ +export default { + id: 'preferences', + label: 'Preferences', + description: 'Tailor how you would like to interact with our journal.', +}; diff --git a/src/components/Form/mocks/group-profile.js b/src/components/Form/mocks/group-profile.js new file mode 100644 index 000000000..d1a470c00 --- /dev/null +++ b/src/components/Form/mocks/group-profile.js @@ -0,0 +1,6 @@ +export default { + id: 'profile', + label: 'Profile', + description: + 'Your public profile may be displayed alongside published articles.', +}; diff --git a/src/components/Form/mocks/i18n.js b/src/components/Form/mocks/i18n.js new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/src/components/Form/mocks/i18n.js @@ -0,0 +1 @@ +export default {}; diff --git a/src/components/Header/Header.stories.js b/src/components/Header/Header.stories.js index cdee4fe0d..0886b8972 100644 --- a/src/components/Header/Header.stories.js +++ b/src/components/Header/Header.stories.js @@ -1,7 +1,7 @@ import Header from './Header.vue'; export default { - title: 'Basic Components/Header', + title: 'Components/Header', component: Header, }; diff --git a/src/components/Icon/Icon.stories.js b/src/components/Icon/Icon.stories.js index 5c4fb856b..402ae02c7 100644 --- a/src/components/Icon/Icon.stories.js +++ b/src/components/Icon/Icon.stories.js @@ -1,7 +1,7 @@ import Icon from './Icon.vue'; export default { - title: 'Basic Components/Icon', + title: 'Components/Icon', component: Icon, render: (args) => ({ components: {Icon}, diff --git a/src/components/ListPanel/ListPanel.mdx b/src/components/ListPanel/ListPanel.mdx new file mode 100644 index 000000000..a5b7ae0a0 --- /dev/null +++ b/src/components/ListPanel/ListPanel.mdx @@ -0,0 +1,14 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as ListPanelStories from './ListPanel.stories.js'; + + + +# ListPanel + +## Usage + +Use this component to display anything from simple lists to complex management panels. A `ListPanel` will often interact with the REST API to get, search, filter and paginate items in the list. Use the [Fetch](#/mixins/fetch) mixin to provide these features. + + + diff --git a/src/components/ListPanel/ListPanel.stories.js b/src/components/ListPanel/ListPanel.stories.js new file mode 100644 index 000000000..4518b2ca6 --- /dev/null +++ b/src/components/ListPanel/ListPanel.stories.js @@ -0,0 +1,472 @@ +import {ref, computed, watch} from 'vue'; +import ListPanel from './ListPanel.vue'; +import PkpHeader from '@/components/Header/Header.vue'; +import PkpFilter from '@/components/Filter/Filter.vue'; +import Expander from '@/components/Expander/Expander.vue'; +import List from '@/components/List/List.vue'; +import ListItem from '@/components/List/ListItem.vue'; +import Pagination from '@/components/Pagination/Pagination.vue'; +import Search from '@/components/Search/Search.vue'; + +import ItemsMock from './mocks/items.js'; +import './ListPanel.stories.less'; +export default { + title: 'ListPanel/ListPanel', + component: ListPanel, +}; + +export const Base = { + render: (args) => ({ + components: {ListPanel}, + setup() { + return {args}; + }, + template: ` + + `, + }), + + args: {items: [...ItemsMock], title: 'List Panel'}, +}; + +export const WithActions = { + render: (args) => ({ + components: {ListPanel, PkpHeader}, + setup() { + return {args}; + }, + template: ` + + + + `, + }), + + args: {items: [...ItemsMock]}, +}; + +export const WithDescription = { + render: (args) => ({ + components: {ListPanel, PkpHeader}, + setup() { + return {args}; + }, + template: ` + + + + `, + }), + + args: {items: [...ItemsMock]}, +}; + +export const WithNoItems = { + render: (args) => ({ + components: {ListPanel}, + setup() { + return {args}; + }, + template: ` + + `, + }), + + args: {items: [], title: 'ListPanel with No Items'}, +}; + +export const WithFilter = { + render: (args) => ({ + components: {ListPanel, PkpHeader, PkpFilter}, + setup() { + const activeFilters = ref({}); + const colorFilters = ref([ + { + param: 'color', + title: 'Red', + value: 'red', + }, + { + param: 'color', + title: 'Green', + value: 'green', + }, + { + param: 'color', + title: 'Blue', + value: 'blue', + }, + ]); + const isSidebarVisible = ref(false); + + function toggleFilters() { + isSidebarVisible.value = !isSidebarVisible.value; + } + + function addFilter(param, value) { + activeFilters.value[param] = value; + } + function removeFilter(param, value) { + delete activeFilters.value[param]; + } + function isFilterActive(param, value) { + if (!Object.keys(activeFilters.value).includes(param)) { + return false; + } + if (Array.isArray(activeFilters.value[param])) { + return activeFilters[param].value.includes(value); + } + return activeFilters.value[param] === value; + } + + const items = ref([...ItemsMock]); + + return { + activeFilters, + colorFilters, + isSidebarVisible, + toggleFilters, + addFilter, + removeFilter, + isFilterActive, + items, + }; + }, + template: ` + + + + + `, + }), + + args: {}, +}; + +export const WithItemActions = { + render: (args) => ({ + components: {ListPanel}, + setup() { + function openModal(title) { + alert('You opened a modal for ' + title + '.'); + } + + return {args, openModal}; + }, + template: ` + + + + `, + }), + + args: {items: [...ItemsMock], title: 'List Panel with Item Actions'}, +}; + +export const WithExpandableItem = { + render: (args) => ({ + components: {ListPanel, Expander, List, ListItem}, + setup() { + function openModal(title) { + alert('Edit ' + title); + } + function toggleExpanded(id) { + if (args.expanded.includes(id)) { + args.expanded = args.expanded.filter((k) => k !== id); + } else { + args.expanded.push(id); + } + } + return {args, openModal, toggleExpanded}; + }, + template: ` + + + + + `, + }), + + args: { + items: [...ItemsMock], + title: 'List Panel with Item Expander', + expanded: [], + }, +}; + +export const WithPagination = { + render: (args) => ({ + components: {ListPanel, Pagination}, + setup() { + let manyItems = []; + for (let i = 1; i < 31; i++) { + const item = ItemsMock[i % 3]; + manyItems.push({ + id: i, + title: item.title + ' ' + i, + subtitle: item.subtitle, + }); + } + + const allItems = ref(manyItems); + const count = ref(5); + const isLoading = ref(false); + const items = ref(manyItems.slice(0, 5)); + const itemsMax = ref(manyItems.length); + const offset = ref(0); + + const currentPage = computed( + () => Math.floor(offset.value / count.value) + 1, + ); + const lastPage = computed(() => Math.ceil(itemsMax.value / count.value)); + + function setPage(page) { + // Mock a delay for a remote request + isLoading.value = true; + setTimeout(() => { + offset.value = page * count.value - count.value; + items.value = allItems.value.slice( + offset.value, + count.value + offset.value, + ); + isLoading.value = false; + }, 1000); + } + + return { + allItems, + count, + isLoading, + items, + itemsMax, + offset, + args, + currentPage, + lastPage, + setPage, + }; + }, + template: ` + + + + `, + }), + + args: { + items: [...ItemsMock], + title: 'List Panel with Pagination', + expanded: [], + }, +}; + +export const WithSearch = { + render: (args) => ({ + components: {ListPanel, Search, PkpHeader}, + setup() { + const items = ref([...ItemsMock]); + const originalItems = ref([...ItemsMock]); + const searchPhrase = ref(''); + + /** + * Normally you would send the search request to the server + */ + function setSearchPhrase(_searchPhrase) { + searchPhrase.value = _searchPhrase; + if (!searchPhrase.value.length) { + items.value = [...originalItems.value]; + } else { + items.value = originalItems.value.filter((item) => { + return ( + new RegExp(_searchPhrase, 'i').test(item.title) || + new RegExp(_searchPhrase, 'i').test(item.subtitle) + ); + }); + } + } + + return {args, items, originalItems, searchPhrase, setSearchPhrase}; + }, + template: ` + + + + `, + }), + + args: { + items: [...ItemsMock], + title: 'List Panel with Item Expander', + expanded: [], + }, +}; + +export const WithSelect = { + render: (args) => ({ + components: {ListPanel, PkpHeader}, + setup() { + const id = 'previewListPanelSelect'; + const canSelectAll = ref(true); + const isSelectAllOn = ref(false); + const items = ref([...ItemsMock]); + const selected = ref([]); + + watch(selected, (newVal, oldVal) => { + isSelectAllOn.value = + selected.value.length && selected.value.length === items.value.length; + }); + + function toggleSelectAll() { + if (isSelectAllOn.value) { + selected.value = []; + } else { + selected.value = items.value.map((i) => i.id); + } + } + + return { + args, + items, + id, + canSelectAll, + selected, + isSelectAllOn, + toggleSelectAll, + }; + }, + template: ` +
    + List Panel with Select + + + + +
    + `, + }), +}; diff --git a/src/components/ListPanel/ListPanel.stories.less b/src/components/ListPanel/ListPanel.stories.less new file mode 100644 index 000000000..d66674d31 --- /dev/null +++ b/src/components/ListPanel/ListPanel.stories.less @@ -0,0 +1,33 @@ +@import '../../styles/_import'; + +.previewListPanelSelect { + border: none; + padding: 0; + + .listPanel__selectAllWrapper { + display: flex; + align-items: center; + margin-top: 1rem; + margin-inline-start: -0.5rem; + line-height: 1.5rem; + + > input { + margin-inline-start: 0.5rem; + } + } + + .listPanel__selectAllLabel { + margin-inline-start: 0.5rem; + } + + .listPanel__selectWrapper { + display: flex; + align-items: center; + margin-inline-start: -1rem; + } + + .listPanel__selector { + width: 3rem; + padding-inline-start: 1rem; + } +} diff --git a/src/components/ListPanel/ListPanel.vue b/src/components/ListPanel/ListPanel.vue index 27ec5ebc2..2723bb410 100644 --- a/src/components/ListPanel/ListPanel.vue +++ b/src/components/ListPanel/ListPanel.vue @@ -73,40 +73,47 @@ export default { PkpHeader, }, props: { + /** An optional description of this `ListPanel`. */ description: { type: String, default() { return ''; }, }, + /** An optional message to show when no items exist. */ emptyLabel: { type: String, default() { return ''; }, }, + /** An array of item ids that are currently in expanded view. */ expanded: { type: Array, default() { return []; }, }, + /** The HTML tag to use for the title. */ headingLevel: { type: String, default() { return 'h2'; }, }, + /** Whether or not the sidebar is visible. */ isSidebarVisible: { type: Boolean, default() { return false; }, }, + /** Array containing items in the list. */ items: { type: Array, required: true, }, + /** The title to display for this `ListPanel`. */ title: { type: String, default() { diff --git a/src/components/ListPanel/announcements/AnnouncementsListPanel.mdx b/src/components/ListPanel/announcements/AnnouncementsListPanel.mdx new file mode 100644 index 000000000..8d31fbef8 --- /dev/null +++ b/src/components/ListPanel/announcements/AnnouncementsListPanel.mdx @@ -0,0 +1,14 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as AnnouncementsListPanelStories from './AnnouncementsListPanel.stories.js'; + + + +# AnnouncementsListPanel + +## Usage + +Use this component to view, add, edit and delete announcements. + + + diff --git a/src/components/ListPanel/announcements/AnnouncementsListPanel.stories.js b/src/components/ListPanel/announcements/AnnouncementsListPanel.stories.js new file mode 100644 index 000000000..a72b95e59 --- /dev/null +++ b/src/components/ListPanel/announcements/AnnouncementsListPanel.stories.js @@ -0,0 +1,99 @@ +import AnnouncementsListPanel from './AnnouncementsListPanel.vue'; + +import AnnouncementMock from '@/mocks/announcement'; +import FormAnnouncementMock from '@/components/Form/mocks/form-announcement'; +import {useDialogStore} from '@/stores/dialogStore'; + +export default { + title: 'ListPanel/AnnouncementsListPanel', + component: AnnouncementsListPanel, +}; + +const announcements = [ + { + ...AnnouncementMock, + dateExpire: '2022-02-03', + datePosted: '2020-01-29', + id: 1, + title: { + en: 'Eros in cursus turpis massa tincidunt dui ut ornare lectus', + fr_CA: 'Eros in cursus turpis massa tincidunt dui ut ornare lectus', + ar: 'Eros in cursus turpis massa tincidunt dui ut ornare lectus', + }, + }, + { + ...AnnouncementMock, + dateExpire: '2021-12-08', + datePosted: '2019-12-12', + id: 2, + title: { + en: 'Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at turpis massa tincidunt dui ut ornare lectus', + fr_CA: + 'Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at turpis massa tincidunt dui ut ornare lectus', + ar: 'Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at turpis massa tincidunt dui ut ornare lectus', + }, + }, + { + ...AnnouncementMock, + dateExpire: '2020-02-03', + datePosted: '2019-11-09', + id: 3, + title: { + en: 'Risus feugiat in ante metus dictum at', + fr_CA: 'Risus feugiat in ante metus dictum at', + ar: 'Risus feugiat in ante metus dictum at', + }, + }, + { + ...AnnouncementMock, + dateExpire: '2020-06-19', + datePosted: '2019-09-21', + id: 4, + title: { + en: 'Morbi tincidunt ornare massa eget egestas purus viverra accumsan', + fr_CA: 'Morbi tincidunt ornare massa eget egestas purus viverra accumsan', + ar: 'Morbi tincidunt ornare massa eget egestas purus viverra accumsan', + }, + }, + { + ...AnnouncementMock, + dateExpire: '2019-12-03', + datePosted: '2019-05-15', + id: 5, + title: { + en: 'Nibh praesent tristique magna sit amet purus gravida quis', + fr_CA: 'Nibh praesent tristique magna sit amet purus gravida quis', + ar: 'Nibh praesent tristique magna sit amet purus gravida quis', + }, + }, +]; + +export const Base = { + render: (args) => ({ + components: {AnnouncementsListPanel}, + setup() { + const dialogStore = useDialogStore(); + + return {args, dialogStore}; + }, + template: ` + + `, + }), + + args: { + form: {...FormAnnouncementMock}, + items: [...announcements], + itemsMax: announcements.length, + title: 'Announcements', + urlBase: 'https://example.com/announcement/view/__id__', + id: 'previewAnnouncementsListPanel', + addAnnouncementLabel: 'Add Announcement', + apiUrl: '', + confirmDeleteMessage: 'Are you sure you want to delete this announcement?', + deleteAnnouncementLabel: 'Delete Announcement', + editAnnouncementLabel: 'Edit Announcement', + }, +}; diff --git a/src/components/ListPanel/announcements/AnnouncementsListPanel.vue b/src/components/ListPanel/announcements/AnnouncementsListPanel.vue index 870b693ec..6c49ef018 100644 --- a/src/components/ListPanel/announcements/AnnouncementsListPanel.vue +++ b/src/components/ListPanel/announcements/AnnouncementsListPanel.vue @@ -81,47 +81,64 @@ export default { }, mixins: [dialog, fetch, ajaxError], props: { + /** A localized string for the button to add an announcement. */ addAnnouncementLabel: { type: String, required: true, }, + /** A localized string for the confirmation message before deleting an announcement. */ confirmDeleteMessage: { type: String, required: true, }, + /** A localized string for the button to delete an announcement. */ deleteAnnouncementLabel: { type: String, required: true, }, + /** A localized string for the button to edit an announcement. */ editAnnouncementLabel: { type: String, required: true, }, + /** The Form to edit an announcement. */ form: { type: Object, required: true, }, + /** A unique id for this component. */ id: { type: String, required: true, }, + /** An array of announcements. */ items: { type: Array, default() { return []; }, }, + /** A count of all announcements in the journal, press or preprint server. */ itemsMax: { type: Number, default() { return 0; }, }, + /** The title of the list panel. */ title: { type: String, required: true, }, }, + emits: [ + /** Emitted when an announcement is added. Payload: `(announcement)` */ + 'add:announcement', + /** Emitted when a prop should be changed. Payload: `(id, newProps)` */ + 'set', + /** Emitted when an announcement is updated. Payload: `(announcement)` */ + 'update:announcement', + ], data() { return { activeForm: null, diff --git a/src/components/ListPanel/highlights/HighlightsListPanel.mdx b/src/components/ListPanel/highlights/HighlightsListPanel.mdx new file mode 100644 index 000000000..ed8cc7b8b --- /dev/null +++ b/src/components/ListPanel/highlights/HighlightsListPanel.mdx @@ -0,0 +1,14 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as HighlightsListPanelStories from './HighlightsListPanel.stories.js'; + + + +# HighlightsListPanel + +## Usage + +Use this component to view, add, edit and delete highlights. + + + diff --git a/src/components/ListPanel/highlights/HighlightsListPanel.stories.js b/src/components/ListPanel/highlights/HighlightsListPanel.stories.js new file mode 100644 index 000000000..083db40d5 --- /dev/null +++ b/src/components/ListPanel/highlights/HighlightsListPanel.stories.js @@ -0,0 +1,96 @@ +import HighlightsListPanel from './HighlightsListPanel.vue'; +import HighlightMock from '@/mocks/highlight'; +import FormHighlightMock from '@/components/Form/mocks/form-highlight'; +import PkpDialog from '@/components/Modal/Dialog.vue'; + +export default { + title: 'ListPanel/HighlightsListPanel', + component: HighlightsListPanel, +}; + +const highlights = [ + { + ...HighlightMock, + }, + { + ...HighlightMock, + id: 2, + title: { + en: 'Eros in cursus turpis massa tincidunt dui ut ornare lectus', + fr_CA: 'Eros in cursus turpis massa tincidunt dui ut ornare lectus', + ar: 'Eros in cursus turpis massa tincidunt dui ut ornare lectus', + }, + description: { + en: 'Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at turpis massa tincidunt dui ut ornare lectus', + fr_CA: + 'Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at turpis massa tincidunt dui ut ornare lectus', + ar: 'Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at turpis massa tincidunt dui ut ornare lectus', + }, + }, + { + ...HighlightMock, + id: 3, + title: { + en: 'Risus feugiat in ante metus dictum at', + fr_CA: 'Risus feugiat in ante metus dictum at', + ar: 'Risus feugiat in ante metus dictum at', + }, + }, + { + ...HighlightMock, + id: 4, + title: { + en: 'Morbi tincidunt ornare massa eget egestas purus viverra accumsan', + fr_CA: 'Morbi tincidunt ornare massa eget egestas purus viverra accumsan', + ar: 'Morbi tincidunt ornare massa eget egestas purus viverra accumsan', + }, + }, + { + ...HighlightMock, + id: 5, + title: { + en: 'Nibh praesent tristique magna sit amet purus gravida quis', + fr_CA: 'Nibh praesent tristique magna sit amet purus gravida quis', + ar: 'Nibh praesent tristique magna sit amet purus gravida quis', + }, + }, +]; + +export const Base = { + render: (args) => ({ + components: {HighlightsListPanel, PkpDialog}, + setup() { + function setData(id, newData) { + Object.keys(newData).forEach((key) => { + if (args[key]) { + args[key] = newData[key]; + } + }); + } + + return {args, setData}; + }, + template: ` + + `, + }), + + args: { + id: 'previewHighlightsListPanel', + apiUrl: 'https://httpbin.org', + i18nAdd: 'Add Highlight', + i18nConfirmDelete: + 'Are you sure you want to delete {$title}? This action can not be undone.', + i18nDelete: 'Delete Highlight', + i18nEdit: 'Edit Highlight', + i18nSaveOrder: 'Save Order', + title: 'Highlights', + form: {...FormHighlightMock}, + items: [...highlights], + itemsMax: highlights.length, + }, +}; diff --git a/src/components/ListPanel/highlights/HighlightsListPanel.vue b/src/components/ListPanel/highlights/HighlightsListPanel.vue index 9761973ca..673bbd1ff 100644 --- a/src/components/ListPanel/highlights/HighlightsListPanel.vue +++ b/src/components/ListPanel/highlights/HighlightsListPanel.vue @@ -100,22 +100,29 @@ export default { }, mixins: [dialog, fetch, ajaxError], props: { + /** The URL to the REST API endpoint for highlights. */ + apiUrl: {type: String, required: true}, + /** The [Form](../?path=/docs/forms-form--docs) to edit a highlight. */ form: { type: Object, required: true, }, + /** A localized string for the button to add a highlight. */ i18nAdd: { type: String, required: true, }, + /** A localized string for the confirmation message before deleting a highlight. */ i18nConfirmDelete: { type: String, required: true, }, + /** A localized string for the button to delete a highlight. */ i18nDelete: { type: String, required: true, }, + /** A localized string for the button to edit a highlight. */ i18nEdit: { type: String, required: true, @@ -124,21 +131,34 @@ export default { type: String, required: true, }, + /** A unique id for this component. */ id: { type: String, required: true, }, + /** An array of highlights. */ items: { type: Array, default() { return []; }, }, + /** A count of all highlights in the journal, press or preprint server, or in the site for site-level highlights. */ + itemsMax: {type: Number, required: true}, + /** The title of the list panel. */ title: { type: String, required: true, }, }, + emits: [ + /** [Event Bus](http://localhost:8080/#/pages/event-bus) | Emitted when a highlight is added. Payload: `(highlight)` */ + 'add:highligh', + /** Component | Emitted when a prop should be changed. Payload: `(id, newProps)` */ + 'set', + /** [Event Bus](http://localhost:8080/#/pages/event-bus) | Emitted when a highlight is updated. Payload: `(highlight)` */ + 'update:highlight', + ], data() { return { activeForm: null, diff --git a/src/components/ListPanel/institutions/InstitutionsListPanel.mdx b/src/components/ListPanel/institutions/InstitutionsListPanel.mdx new file mode 100644 index 000000000..d2074879f --- /dev/null +++ b/src/components/ListPanel/institutions/InstitutionsListPanel.mdx @@ -0,0 +1,14 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as InstitutionsListPanelStories from './InstitutionsListPanel.stories.js'; + + + +# InstitutionsListPanel + +## Usage + +Use this component to view, add, edit and delete institutions. + + + diff --git a/src/components/ListPanel/institutions/InstitutionsListPanel.stories.js b/src/components/ListPanel/institutions/InstitutionsListPanel.stories.js new file mode 100644 index 000000000..87f178a96 --- /dev/null +++ b/src/components/ListPanel/institutions/InstitutionsListPanel.stories.js @@ -0,0 +1,81 @@ +import InstitutionsListPanel from './InstitutionsListPanel.vue'; + +import PkpDialog from '@/components/Modal/Dialog.vue'; +import FormInstitutionMock from '@/components/Form/mocks/form-institution'; + +export default { + title: 'ListPanel/InstitutionsListPanel', + component: InstitutionsListPanel, +}; + +const institutions = [ + { + id: 1, + name: { + en: 'Institution 1 Name EN', + fr_CA: 'Institution 1 Name FR', + ar: 'Institution 1 Name AR', + }, + ipRanges: ['142.60.*.*'], + }, + { + id: 2, + name: { + en: 'Institution 2 Name EN', + fr_CA: 'Institution 2 Name FR', + ar: 'Institution 2 Name AR', + }, + ipRanges: ['142.58.103.1 - 142.58.103.4', '142.59.*.*'], + }, + { + id: 3, + name: { + en: 'Institution 3 Name EN', + fr_CA: 'Institution 3 Name FR', + ar: 'Institution 3 Name AR', + }, + }, + { + id: 4, + name: { + en: 'Institution 4 Name EN', + fr_CA: 'Institution 4 Name FR', + ar: 'Institution 4 Name AR', + }, + }, +]; + +export const Base = { + render: (args) => ({ + components: {InstitutionsListPanel, PkpDialog}, + setup() { + function setData(id, newData) { + Object.keys(newData).forEach((key) => { + if (args[key]) { + args[key] = newData[key]; + } + }); + } + + return {args, setData}; + }, + template: ` + + `, + }), + + args: { + id: 'previewInstitutionsListPanel', + addInstitutionLabel: 'Add Institution', + apiUrl: '', + confirmDeleteMessage: 'Are you sure you want to delete this institution?', + deleteInstitutionLabel: 'Delete Institution', + editInstitutionLabel: 'Edit Institution', + title: 'Institutions', + form: {...FormInstitutionMock}, + items: [...institutions], + itemsMax: institutions.length, + }, +}; diff --git a/src/components/ListPanel/institutions/InstitutionsListPanel.vue b/src/components/ListPanel/institutions/InstitutionsListPanel.vue index a31274b50..dd991c2bb 100644 --- a/src/components/ListPanel/institutions/InstitutionsListPanel.vue +++ b/src/components/ListPanel/institutions/InstitutionsListPanel.vue @@ -87,47 +87,64 @@ export default { }, mixins: [dialog, fetch, ajaxError], props: { + /** A localized string for the button to add an institution. */ addInstitutionLabel: { type: String, required: true, }, + /** A localized string for the confirmation message before deleting an institution. */ confirmDeleteMessage: { type: String, required: true, }, + /** A localized string for the button to delete an institution. */ deleteInstitutionLabel: { type: String, required: true, }, + /** A localized string for the button to edit an institution. */ editInstitutionLabel: { type: String, required: true, }, + /** The [Form](../?path=/docs/forms-form--docs) to edit an institution */ form: { type: Object, required: true, }, + /** A unique id for this component. */ id: { type: String, required: true, }, + /** An array of institutions. */ items: { type: Array, default() { return []; }, }, + /** A count of all institutions in the journal, press or preprint server. */ itemsMax: { type: Number, default() { return 0; }, }, + /** The title of the list panel. */ title: { type: String, required: true, }, }, + emits: [ + /** Emitted when an institution is added. Payload: `(institution)` */ + 'add:institution', + /** Emitted when a prop should be changed. Payload: `(id, newProps)` */ + 'set', + /** Emitted when an institution is updated. Payload: `(institution)` */ + 'update:institution', + ], data() { return { activeForm: null, diff --git a/src/components/ListPanel/mocks/items.js b/src/components/ListPanel/mocks/items.js new file mode 100644 index 000000000..4195fd618 --- /dev/null +++ b/src/components/ListPanel/mocks/items.js @@ -0,0 +1,18 @@ +export default [ + { + id: 1, + title: 'C. Corino et. al', + subtitle: 'Lorem ipsum dolor sit amet', + }, + { + id: 2, + title: 'D. Barnes et. al', + subtitle: 'Amet sit dolor ipsum lorem', + }, + { + id: 3, + title: 'A Mwandenga', + subtitle: + 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur', + }, +]; diff --git a/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.mdx b/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.mdx new file mode 100644 index 000000000..f6d927683 --- /dev/null +++ b/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.mdx @@ -0,0 +1,28 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as SubmissionFilesListPanelStories from './SubmissionFilesListPanel.stories.js'; + + + +# SubmissionFilesListPanel + +## Usage + +Use this component to upload [Submission Files](https://docs.pkp.sfu.ca/dev/documentation/en/submission-files). Every submission file must be assigned a genre, one of the file types configured by the journal, press or preprint server. The `genre` prop expects an array of file type ids, names and whether or not the genre is one of the primary file types. See the following example. + +```json +[ + { + "id": 1, + "name": "Book Manuscript", + "isPrimary": true + }, + { + "id": 2, + "name": "Preface" + } +] +``` + + + diff --git a/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.stories.js b/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.stories.js new file mode 100644 index 000000000..067b63bbd --- /dev/null +++ b/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.stories.js @@ -0,0 +1,92 @@ +import SubmissionFilesListPanel from './SubmissionFilesListPanel.vue'; + +import FormSubmissionFileMock from '@/components/Form/mocks/form-submission-file'; +import DropzoneOptionsMock from '@/mocks/dropzoneOptions'; +import SubmissionFilesMock from '@/mocks/submissionFiles'; + +import GenresMock from '@/mocks/genres'; + +export default { + title: 'ListPanel/SubmissionFilesListPanel', + component: SubmissionFilesListPanel, +}; + +const yesterday = new Date(); +yesterday.setDate(new Date().getDate() - 1); + +export const Base = { + render: (args) => ({ + components: {SubmissionFilesListPanel}, + setup() { + function get(key) { + return args[key] ? args[key] : {}; + } + + function set(key, data) { + let component = {...get(key)}; + Object.keys(data).forEach(function (dataKey) { + args[dataKey] = data[dataKey]; + }); + args[key] = component; + } + + return {args, set}; + }, + template: ` + + `, + }), + + args: { + id: 'preview', + addFileLabel: 'Add File', + apiUrl: 'https://httbin.org/publicknowledge/api/v1/submissions/1/files', + cancelUploadLabel: 'Cancel Upload', + genrePromptLabel: 'What kind of file is this?', + emptyLabel: + 'Upload any files the editorial team will need to evaluate your submission.', + emptyAddLabel: 'Upload Files', + fileStage: 2, + otherLabel: 'Other', + primaryLocale: 'en', + removeConfirmLabel: 'Are you sure you want to remove this file?', + stageId: 1, + title: 'Files', + uploadProgressLabel: 'Uploading {$percent}% complete', + + items: [ + ...SubmissionFilesMock, + { + id: 235, + fileId: 1234, + name: { + en: 'file-with-no-genre-assignment-yet.docx', + fr_CA: 'french-file-with-no-genre-assignment-yet.docx', + }, + genre: null, + documentType: 'word', + url: 'https://example.com/publicknowledge/$$$call$$$/api/file/file-api/download-file?id=234&submissionId=22&stageId=2', + }, + { + id: '12345', + name: 'File In Progress.docx', + progress: 35, + error: [], + }, + { + id: '123456', + name: 'File that is too large.docx', + progress: 100, + error: [ + 'File is too big (20mb). Files larger than 2mb can not be uploaded.', + ], + }, + ], + options: {...DropzoneOptionsMock}, + form: {...FormSubmissionFileMock}, + genres: [...GenresMock], + }, +}; diff --git a/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.vue b/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.vue index 5fc65f1db..640ed14aa 100644 --- a/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.vue +++ b/src/components/ListPanel/submissionFiles/SubmissionFilesListPanel.vue @@ -82,82 +82,104 @@ export default { }, mixins: [dialog], props: { + /** A localized string for the button to add a file. */ addFileLabel: { type: String, required: true, }, + /** The URL to upload files to. */ apiUrl: { type: String, required: true, }, + /** A localized string for the button to cancel an upload while it is being uploaded. */ cancelUploadLabel: { type: String, required: true, }, + /** A localized string to show when there are no items in the list. */ emptyLabel: { type: String, required: true, }, + /** A localized string for the button to upload a file when there are no items in the list. */ emptyAddLabel: { type: String, required: true, }, + /** The file stage to upload the submission files to. See [Submission Files](https://docs.pkp.sfu.ca/dev/documentation/en/submission-files) */ fileStage: { type: Number, required: true, }, + /** The [Form](../?path=/docs/forms-form--docs) to select a file type. */ form: { type: Object, }, + /** A localized string for the prompt to select a file type. */ genrePromptLabel: { type: String, required: true, }, + /** An array of file types. See usage guidance below. */ genres: { type: Array, required: true, }, + /** A unique id for this component. */ id: { type: String, required: true, }, + /** An array of files uploaded or being uploaded. */ items: { type: Array, default() { return []; }, }, + /** Pass [options](https://www.dropzonejs.com/#configuration-options) to Dropzone.js. */ options: { type: Object, default() { return {}; }, }, + /** A localized string to describe files types other than the primary file types. */ otherLabel: { type: String, required: true, }, + /** The primary locale for the submission files uploaded. This should match the submission's locale. */ primaryLocale: { type: String, required: true, }, + /** A localized string to for the confirmation modal to remove a file. */ removeConfirmLabel: { type: String, required: true, }, + /** The workflow stage id to upload files to. One of the `WORKFLOW_STAGE_ID_*` constants. */ stageId: { type: Number, required: true, }, + /** The title of the list panel. */ title: { type: String, required: true, }, + /** A localized string to show the upload progress. */ uploadProgressLabel: { type: String, required: true, }, }, + emits: [ + /** Emitted when a prop should be changed. Payload: `(id, newProps)` */ + 'set', + ], data() { return { activeForm: {}, diff --git a/src/components/ListPanel/submissions/CatalogListPanel.mdx b/src/components/ListPanel/submissions/CatalogListPanel.mdx new file mode 100644 index 000000000..9f3bce492 --- /dev/null +++ b/src/components/ListPanel/submissions/CatalogListPanel.mdx @@ -0,0 +1,14 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as CatalogListPanelStories from './CatalogListPanel.stories.js'; + + + +# CatalogListPanel + +## Usage + +Use this component to manage the book catalog in OMP. + + + diff --git a/src/components/ListPanel/submissions/CatalogListPanel.stories.js b/src/components/ListPanel/submissions/CatalogListPanel.stories.js new file mode 100644 index 000000000..4241a933e --- /dev/null +++ b/src/components/ListPanel/submissions/CatalogListPanel.stories.js @@ -0,0 +1,201 @@ +import CatalogListPanel from './CatalogListPanel.vue'; + +import PkpDialog from '@/components/Modal/Dialog.vue'; +import SubmissionsMock from '@/mocks/submissions'; + +export default { + title: 'ListPanel/CatalogListPanel', + component: CatalogListPanel, +}; + +const getRandomInt = (min, max) => { + return Math.floor(Math.random() * (max - min) + min); +}; + +let items = SubmissionsMock.map((item, i) => { + const categoryId = i % 2 ? 1 : 2; + const seriesId = getRandomInt(1, 3); + item.series = { + id: '1', + position: '1', + title: '', + }; + item.category = [ + { + id: categoryId, + image: {}, + parent_id: 0, + path: 'example', + press_id: 1, + seq: 10000, + }, + ]; + item.featured = [ + { + assoc_id: categoryId, + assoc_type: pkp.const.ASSOC_TYPE_CATEGORY, + seq: 0, + }, + { + assoc_id: seriesId, + assoc_type: pkp.const.ASSOC_TYPE_SERIES, + seq: 0, + }, + ]; + if (i === 3) { + item.newRelease = [ + { + assoc_id: seriesId, + assoc_type: pkp.const.ASSOC_TYPE_SERIES, + }, + ]; + } else { + item.newRelease = []; + } + item.urlPublished = '/catalog/book/1'; + + return item; +}); + +export const Base = { + render: (args) => ({ + components: {CatalogListPanel, PkpDialog}, + setup() { + /** + * Add required locale keys + */ + pkp.localeKeys['submission.catalogEntry.new'] = 'Add Entry'; + pkp.localeKeys['submission.list.saveFeatureOrder'] = 'Save Order'; + pkp.localeKeys['submission.list.orderFeatures'] = 'Order Features'; + pkp.localeKeys['catalog.manage.categoryFeatured'] = + 'Featured in category'; + pkp.localeKeys['catalog.manage.seriesFeatured'] = 'Featured in series'; + pkp.localeKeys['catalog.manage.featured'] = 'Featured'; + pkp.localeKeys['catalog.manage.feature.categoryNewRelease'] = + 'New release in category'; + pkp.localeKeys['catalog.manage.feature.seriesNewRelease'] = + 'New release in series'; + pkp.localeKeys['catalog.manage.feature.newRelease'] = 'New Release'; + pkp.localeKeys['submission.list.orderingFeatures'] = + 'Drag-and-drop or tap the up and down buttons to change the order of features on the homepage.'; + pkp.localeKeys['submission.list.orderingFeaturesSection'] = + 'Drag-and-drop or tap the up and down buttons to change the order of features in {$title}.'; + pkp.localeKeys['catalog.manage.isFeatured'] = + 'This monograph is featured. Make this monograph not featured.'; + pkp.localeKeys['catalog.manage.isNotFeatured'] = + 'This monograph is not featured. Make this monograph featured.'; + pkp.localeKeys['catalog.manage.isNewRelease'] = + 'This monograph is a new release. Make this monograph not a new release.'; + pkp.localeKeys['catalog.manage.isNotNewRelease'] = + 'This monograph is not a new release. Make this monograph a new release.'; + pkp.localeKeys['submission.list.viewEntry'] = 'View Entry'; + pkp.localeKeys['submission.list.viewSubmission'] = 'View Submission'; + + return {args}; + }, + template: ` + + `, + }), + + args: { + id: 'previewCatalogListPanel', + apiUrl: 'http://httpbin.org/get', + catalogSortBy: 'datePublished', + catalogSortDir: '1', + csrfToken: '1234', + infoUrl: 'http://httbin.org/get', + title: 'catalog', + addEntryForm: { + method: 'PUT', + action: '/example/default', + errors: {}, + fields: [ + { + name: 'submissionIds', + component: 'field-select-submissions', + label: 'Find Submission', + isRequired: true, + selected: [], + value: [], + groupId: 'default', + }, + ], + groups: [ + { + id: 'default', + pageId: 'default', + }, + ], + pages: [ + { + id: 'default', + submitButton: { + label: 'Add Entry', + isPrimary: true, + }, + }, + ], + primaryLocale: 'en', + visibleLocales: ['en'], + supportedFormLocales: [ + { + key: 'en', + label: 'English', + }, + ], + isSaving: false, + }, + filters: [ + { + heading: 'Categories', + filters: [ + { + title: 'Example Category', + param: 'categoryIds', + value: 1, + sortBy: '', + sortDir: '', + }, + { + title: 'Another Category', + param: 'categoryIds', + value: 2, + sortBy: '', + sortDir: '', + }, + ], + }, + { + heading: 'Series', + filters: [ + { + title: 'Library & Information Studies', + param: 'seriesIds', + value: 1, + sortBy: 'seriesPosition', + sortDir: 'DESC', + }, + { + title: 'Political Economy', + param: 'seriesIds', + value: 2, + sortBy: '', + sortDir: 'ASC', + }, + { + title: 'History', + param: 'seriesIds', + value: 3, + sortBy: 'seriesIds', + sortDir: '', + }, + ], + }, + ], + items: [...items], + itemsMax: items.length, + }, +}; diff --git a/src/components/ListPanel/submissions/CatalogListPanel.vue b/src/components/ListPanel/submissions/CatalogListPanel.vue index 350b85e0c..45c5fba2f 100644 --- a/src/components/ListPanel/submissions/CatalogListPanel.vue +++ b/src/components/ListPanel/submissions/CatalogListPanel.vue @@ -166,48 +166,57 @@ export default { }, mixins: [ajaxError, fetch], props: { + /** The [Form](../?path=/docs/forms-form--docs) to add an entry to the catalog. */ addEntryForm: { type: Object, required: true, }, + /** One of the `ORDER_BY_*` constants. Default: `ORDERBY_DATE_PUBLISHED` */ catalogSortBy: { type: String, default() { return 'datePublished'; // ORDERBY_DATE_PUBLISHED }, }, + /** One of the `SORT_DIRECTION_*` constants. Default: `SORT_DIRECTION_ASC` */ catalogSortDir: { type: Number, default() { return 1; // SORT_DIRECTION_ASC }, }, + /** The id of the press. */ contextId: { type: Number, required: true, }, + /** The [Filters](../?path=/docs/components-filter-base--docs) to change the list view. */ filters: { type: Array, default() { return []; }, }, + /** A unique id for this component. */ id: { type: String, required: true, }, + /** An array of publishable submissions. */ items: { type: Array, default() { return []; }, }, + /** A count of all publishable submissions in the press. */ itemsMax: { type: Number, defaut() { return 0; }, }, + /** The title of the list panel. */ title: { type: String, default() { @@ -215,6 +224,10 @@ export default { }, }, }, + emits: [ + /** Emitted when a prop should be changed. Payload: `(id, newProps)` */ + 'set', + ], data() { return { newEntries: [], diff --git a/src/components/ListPanel/submissions/SubmissionsListPanel.mdx b/src/components/ListPanel/submissions/SubmissionsListPanel.mdx new file mode 100644 index 000000000..d33cea6a7 --- /dev/null +++ b/src/components/ListPanel/submissions/SubmissionsListPanel.mdx @@ -0,0 +1,14 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as SubmissionsListPanelStories from './SubmissionsListPanel.stories.js'; + + + +# SubmissionsListPanel + +## Usage + +Use this component to help editors, authors and reviewers track submissions and editorial assignments. + + + diff --git a/src/components/ListPanel/submissions/SubmissionsListPanel.stories.js b/src/components/ListPanel/submissions/SubmissionsListPanel.stories.js new file mode 100644 index 000000000..b68c2837f --- /dev/null +++ b/src/components/ListPanel/submissions/SubmissionsListPanel.stories.js @@ -0,0 +1,183 @@ +import SubmissionsListPanel from './SubmissionsListPanel.vue'; + +import SubmissionsMock from '@/mocks/submissions'; +import FieldBaseMock from '@/components/Form/mocks/field-base'; +import FieldBaseAutosuggestMock from '@/components/Form/mocks/field-autosuggest'; + +export default { + title: 'ListPanel/SubmissionsListPanel', + component: SubmissionsListPanel, +}; + +export const Base = { + render: (args) => ({ + components: {SubmissionsListPanel}, + setup() { + function setData(id, newData) { + Object.keys(newData).forEach((key) => { + if (args[key]) { + args[key] = newData[key]; + } + }); + } + + /** + * Add required locale keys + */ + pkp.localeKeys['common.lastActivity'] = + 'Last activity recorded on {$date}.'; + pkp.localeKeys['submission.list.empty'] = 'No submissions found.'; + pkp.localeKeys['submission.submit.newSubmissionSingle'] = + 'New Submission'; + pkp.localeKeys['submission.review'] = 'Review'; + pkp.localeKeys['submissions.incomplete'] = 'Incomplete'; + pkp.localeKeys['submission.list.assignEditor'] = 'Assign Editor'; + pkp.localeKeys['submission.list.copyeditsSubmitted'] = + 'Copyedited files submitted'; + pkp.localeKeys['submission.list.currentStage'] = + 'Currently in the {$stage} stage.'; + pkp.localeKeys['submission.list.discussions'] = 'Open discussions'; + pkp.localeKeys['submission.list.dualWorkflowLinks'] = + `You have been assigned multiple roles for this submission. Would you like to access the Author's workflow or the Editorial workflow?`; + pkp.localeKeys['submission.list.galleysCreated'] = + 'Copyedited files submitted'; + pkp.localeKeys['submission.list.infoCenter'] = 'Activity Log & Notes'; + pkp.localeKeys['submission.list.reviewAssignment'] = 'Review Assignment'; + pkp.localeKeys['submission.list.responseDue'] = 'Response Due: {$date}'; + pkp.localeKeys['submission.list.reviewCancelled'] = 'Review Cancelled'; + pkp.localeKeys['submission.list.reviewComplete'] = 'Review Submitted'; + pkp.localeKeys['submission.list.reviewDue'] = 'Review Due: {$date}'; + pkp.localeKeys['submission.list.reviewerWorkflowLink'] = + `You have been assigned an editorial role for this submission. Would you like to access the Editorial workflow?`; + pkp.localeKeys['submission.list.reviewsCompleted'] = + 'Assigned reviews completed'; + pkp.localeKeys['submission.list.revisionsSubmitted'] = + 'Production galleys created'; + pkp.localeKeys['submission.list.viewSubmission'] = 'View Submission'; + + return {args, setData}; + }, + template: ` + + `, + }), + + args: { + id: 'previewSubmissionsListPanel', + addUrl: 'https://httbin.org/publicknowledge/submission/wizard', + apiUrl: 'http://httpbin.org/get', + infoUrl: 'http://httbin.org/get', + title: 'Submissions', + filters: [ + { + filters: [ + { + title: 'Overdue', + param: 'isOverdue', + value: true, + }, + { + title: 'Incomplete', + param: 'isIncomplete', + value: true, + }, + ], + }, + { + heading: 'Stages', + filters: [ + { + title: 'Submission', + param: 'stageIds', + value: pkp.const.WORKFLOW_STAGE_ID_SUBMISSION, + }, + { + title: 'Review', + param: 'stageIds', + value: pkp.const.WORKFLOW_STAGE_ID_EXTERNAL_REVIEW, + }, + { + title: 'Copyediting', + param: 'stageIds', + value: pkp.const.WORKFLOW_STAGE_ID_EDITING, + }, + { + title: 'Production', + param: 'stageIds', + value: pkp.const.WORKFLOW_STAGE_ID_PRODUCTION, + }, + ], + }, + { + heading: 'Activity', + filters: [ + { + title: 'Days since last activity', + param: 'daysInactive', + value: 30, + min: 1, + max: 180, + filterType: 'pkp-filter-slider', + }, + ], + }, + { + heading: 'Sections', + filters: [ + { + title: 'Articles', + param: 'sectionIds', + value: 1, + }, + { + title: 'Reviews', + param: 'sectionIds', + value: 2, + }, + ], + }, + { + filters: [ + { + title: 'Editors', + param: 'assignedTo', + value: [], + component: 'field-select-users', + autosuggestProps: { + ...FieldBaseMock, + ...FieldBaseAutosuggestMock, + apiUrl: 'usernames.json', + name: 'assignedTo', + label: 'Assigned To Editors', + selectedLabel: 'Assigned', + }, + filterType: 'pkp-filter-autosuggest', + }, + ], + }, + { + filters: [ + { + title: 'Issues', + param: 'issueIds', + value: [], + component: 'field-select-issues', + autosuggestProps: { + ...FieldBaseMock, + ...FieldBaseAutosuggestMock, + apiUrl: 'issues.json', + name: 'issueIds', + label: 'Assigned To Issues', + selectedLabel: 'Assigned', + }, + filterType: 'pkp-filter-autosuggest', + }, + ], + }, + ], + items: [...SubmissionsMock], + itemsMax: SubmissionsMock.length, + }, +}; diff --git a/src/components/ListPanel/submissions/SubmissionsListPanel.vue b/src/components/ListPanel/submissions/SubmissionsListPanel.vue index a52f13b0b..fe3a9bf33 100644 --- a/src/components/ListPanel/submissions/SubmissionsListPanel.vue +++ b/src/components/ListPanel/submissions/SubmissionsListPanel.vue @@ -118,53 +118,66 @@ export default { }, mixins: [fetch], props: { + /** The URL to make a new submission. */ addUrl: { type: String, required: true, }, + /** Whether or not the journal, press or preprint server allows submissions. */ allowSubmissions: { type: Boolean, default() { return true; }, }, + /** The URL to the component handler to assign a participant to a submission. */ assignParticipantUrl: { type: String, default() { return ''; }, }, + /** An array of [Filters](../?path=/docs/components-filter-base--docs) to change the view of the submissions list. */ filters: { type: Array, default() { return []; }, }, + /** A unique id for this component. */ id: { type: String, required: true, }, + /** The URL to the component handler for the submission's activity log and notes. */ infoUrl: { type: String, required: true, }, + /** An array of submissions. */ items: { type: Array, default() { return []; }, }, + /** A count of all submissions the user can access in the journal, press or preprint server. */ itemsMax: { type: Number, defaut() { return 0; }, }, + /** The title of the list panel. */ title: { type: String, required: true, }, }, + emits: [ + /** Emitted when a prop should be changed. Payload: `(id, newProps) */ + 'set', + ], data() { return { isSidebarVisible: false, diff --git a/src/components/ListPanel/users/SelectReviewerListPanel.mdx b/src/components/ListPanel/users/SelectReviewerListPanel.mdx new file mode 100644 index 000000000..71069c8c7 --- /dev/null +++ b/src/components/ListPanel/users/SelectReviewerListPanel.mdx @@ -0,0 +1,14 @@ +import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks'; + +import * as SelectReviewerListPanelStories from './SelectReviewerListPanel.stories.js'; + + + +# SelectReviewerListPanel + +## Usage + +Use this component to select a reviewer for a review assignment. + + + diff --git a/src/components/ListPanel/users/SelectReviewerListPanel.stories.js b/src/components/ListPanel/users/SelectReviewerListPanel.stories.js new file mode 100644 index 000000000..b95352f85 --- /dev/null +++ b/src/components/ListPanel/users/SelectReviewerListPanel.stories.js @@ -0,0 +1,209 @@ +import SelectReviewerListPanel from './SelectReviewerListPanel.vue'; + +import ReviewerMock from '@/mocks/reviewer'; + +export default { + title: 'ListPanel/SelectReviewerListPanel', + component: SelectReviewerListPanel, +}; + +const yesterday = new Date('Tue Dec 19 2023 16:00:27 GMT+0100'); +yesterday.setDate(new Date('Tue Dec 19 2023 16:00:27 GMT+0100').getDate() - 1); + +const items = [ + ReviewerMock, + { + ...ReviewerMock, + id: 2, + fullName: 'Allison Ackerman', + reviewsActive: 0, + reviewsCompleted: 0, + dateLastReviewAssignment: '', + reviewerRating: null, + interests: [], + }, + { + ...ReviewerMock, + id: 3, + fullName: 'Aisla McCrae', + reviewsActive: 23, + reviewsCompleted: 32, + dateLastReviewAssignment: '2022-12-31 11:22:42', + reviewerRating: 5, + orcid: 'http://orcid.org/0000-0002-1825-0097', + biography: { + en: '

    Professor Aisla McCrae is a senior research fellow in the Publishing Studies Department in the School of Publishing at the University of Manitoba. She is the deputy chair of the Manitoba Publishing Institute and the author of Publishing Now and Forever: Recent Developments in Archival Permanence.

    ', + }, + gossip: + "

    Aisla is a terrific reviewer but she takes a while to confirm and complete a review. Recommend not using her if you're on a tight deadline.

    ", + }, + { + ...ReviewerMock, + id: 4, + fullName: 'Ahmed Maliki', + reviewsActive: 0, + reviewsCompleted: 2, + dateLastReviewAssignment: yesterday.toString(), + reviewerRating: 4, + interests: [ + { + id: 223, + interest: 'catapulting into space', + }, + { + id: 224, + interest: 'somersaults around the moon', + }, + { + id: 225, + interest: 'listening to taylor swift', + }, + { + id: 227, + interest: 'growing plants and stuff', + }, + { + id: 226, + interest: 'just kickin it', + }, + ], + }, + { + ...ReviewerMock, + id: 5, + fullName: 'Adela Gallego', + reviewsActive: 1, + reviewsCompleted: 0, + dateLastReviewAssignment: '2022-01-25 11:22:42', + reviewerRating: 5, + }, + { + ...ReviewerMock, + id: 6, + fullName: 'Habiba Yousef', + reviewsActive: 1, + reviewsCompleted: 0, + dateLastReviewAssignment: '2022-06-07 11:22:42', + reviewerRating: 3, + }, + { + ...ReviewerMock, + id: 7, + fullName: 'Matthew McMatherson', + reviewsActive: 0, + reviewsCompleted: 3, + dateLastReviewAssignment: '2022-10-21 11:22:42', + reviewerRating: 2, + }, +]; + +export const Base = { + render: (args) => ({ + components: {SelectReviewerListPanel}, + setup() { + function setData(id, newData) { + Object.keys(newData).forEach((key) => { + if (args[key]) { + args[key] = newData[key]; + } + }); + } + + return {args, setData}; + }, + template: ` + + `, + }), + + args: { + id: 'previewSelectReviewerListPanel', + apiUrl: 'http://httpbin.org/get', + activeReviewsCountLabel: '{$count} active', + activeReviewsLabel: 'Active reviews currently assigned', + assignedToLastRoundLabel: + 'This reviewer completed a review in the last round.', + averageCompletionLabel: 'Average days to complete review', + biographyLabel: 'Biography', + cancelledReviewsLabel: 'Reviews cancelled', + completedReviewsLabel: 'Reviews completed', + currentlyAssigned: [5], + currentlyAassignedLabel: + 'This reviewer has already been assigned to this review round.', + daySinceLastAssignmentLabel: 'Yesterday', + daysSinceLastAssignmentLabel: '{$days} days ago', + daysSinceLastAssignmentDescriptionLabel: 'Days since last review assigned', + declinedReviewsLabel: 'Reviews declined', + emptyLabel: 'No reviewers found', + gossipLabel: 'Editorial Notes', + items: [...items], + lastRoundReviewers: items.filter((reviewer) => + [3, 4].includes(reviewer.id), + ), + itemsMax: items.length, + neverAssignedLabel: 'Never assigned', + reassignLabel: 'Reassign', + reassignWithNameLabel: 'Reassign {$name}', + reviewerRatingLabel: 'Reviewer rating: {$rating}', + reviewInterestsLabel: 'Reviewing Interests', + selectReviewerLabel: 'Select Reviewer', + selectorName: 'reviewerId', + title: 'Locate a Reviewer', + warnOnAssignment: [7], + warnOnAssignmentLabel: + "This reviewer is locked because they have been assigned a role which allows them to view the author's identity. Anonymous peer review can not be guaranteed. Would you like to unlock this reviewer anyway?", + warnOnAssignmentUnlockLabel: 'Unlock', + + filters: [ + { + param: 'reviewerRating', + title: 'Rated at least', + value: 3, + min: 1, + max: 5, + useStars: true, + valueLabel: '{$value}/5', + }, + { + param: 'reviewsCompleted', + title: 'Reviews completed', + value: 10, + min: 0, + max: 20, + valueLabel: '{$value} or more', + }, + { + param: 'daysSinceLastAssignment', + title: 'Days since last review assigned', + filterType: 'filter-slider-multirange', + value: [0, 365], + min: 0, + max: 365, + valueLabel: '{$min}-{$max}', + moreThanLabel: 'More than', + lessThanLabel: 'Less than', + }, + { + param: 'reviewsActive', + title: 'Active reviews currently assigned', + filterType: 'filter-slider-multirange', + value: [0, 20], + min: 0, + max: 20, + valueLabel: '{$min}-{$max}', + moreThanLabel: 'More than', + lessThanLabel: 'Less than', + }, + { + param: 'averageCompletion', + title: 'Average days to complete review', + value: 75, + min: 0, + max: 75, + valueLabel: '{$value} or less', + }, + ], + }, +}; diff --git a/src/components/ListPanel/users/SelectReviewerListPanel.vue b/src/components/ListPanel/users/SelectReviewerListPanel.vue index e9af5dcd5..5d4995b68 100644 --- a/src/components/ListPanel/users/SelectReviewerListPanel.vue +++ b/src/components/ListPanel/users/SelectReviewerListPanel.vue @@ -128,14 +128,17 @@ export default { }, mixins: [fetch], props: { + /** A localized string for the [Badge](../?path=/docs/basic-components-badge--docs) showing the number of active reviews. */ activeReviewsCountLabel: { type: String, required: true, }, + /** A localized string for the active reviews row in the expanded table. */ activeReviewsLabel: { type: String, required: true, }, + /** A localized string that says this reviewer was assigned in the last review round. See `lastRoundReviewers` */ assignedToLastRoundLabel: { type: String, required: true, @@ -146,96 +149,117 @@ export default { return []; }, }, + /** A localized string for the average days to complete a review row in the expanded table. */ averageCompletionLabel: { type: String, required: true, }, + /** A localized string for the biography section. */ biographyLabel: { type: String, required: true, }, + /** A localized string for the canceled reviews row in the expanded table. */ cancelledReviewsLabel: { type: String, required: true, }, + /** A localized string for the completed reviews row in the expanded table. */ completedReviewsLabel: { type: String, required: true, }, + /** An array of user ids of reviewers who are already assigned to this review round. */ currentlyAssigned: { type: Array, default() { return []; }, }, + /** A localized string that says this reviewer is already assigned to this review round. */ currentlyAssignedLabel: { type: String, required: true, }, + /** A localized string to use when it has only been one day since the reviewer's last assignment. */ daySinceLastAssignmentLabel: { type: String, required: true, }, + /** A localized string to use when it has been 2 or more days since the reviewer's last assignment. Example: `{$number} days ago` */ daysSinceLastAssignmentLabel: { type: String, required: true, }, + /** A localized string for the days since last assignment row in the expanded table */ daysSinceLastAssignmentDescriptionLabel: { type: String, required: true, }, + /** A localized string for the declined reviews row in the expanded table. */ declinedReviewsLabel: { type: String, required: true, }, + /** A localized string to display when there are no reviewers to show in the list. */ emptyLabel: { type: String, required: true, }, + /** An array [Filter](../?path=/docs/components-filter-base--docs)s. */ filters: { type: Array, default() { return []; }, }, + /** A localized string for the gossip section. */ gossipLabel: { type: String, required: true, }, + /** A unique id for this component. */ id: { type: String, required: true, }, + /** An array of reviewers. */ items: { type: Array, default() { return []; }, }, + /** A count of all reviewers in the journal, press or preprint server. */ itemsMax: { type: Number, default() { return 0; }, }, + /** An array of reviewers that were assigned in the last review round. */ lastRoundReviewers: { type: Array, default() { return []; }, }, + /** A localized string to use when the reviewer has never been given a review assignment. */ neverAssignedLabel: { type: String, required: true, }, + /** A localized string to use for the button to reassign a reviewer who was assigned to the last review round. */ reassignLabel: { type: String, required: true, }, + /** A localized string to use in an accessible label for the button to reassign a reviewer who was assigned to the last review round. Example: `Reassign {$name}` */ reassignWithNameLabel: { type: String, required: true, }, + /** A localized string to use in an accessible label for the reviewer rating. */ reviewerRatingLabel: { type: String, required: true, @@ -244,6 +268,7 @@ export default { type: String, required: true, }, + /** A localized string to use for the reviewer interests section. */ reviewInterestsLabel: { type: String, required: true, @@ -252,29 +277,38 @@ export default { type: String, required: true, }, + /** A localized string to use for the button to select a reviewer. */ selectReviewerLabel: { type: String, required: true, }, + /** The title of the list panel. */ title: { type: String, required: true, }, + /** An array of user ids for reviewers that may not be able to conduct an anonymous review, because they have access to the submission details through another stage assignment. */ warnOnAssignment: { type: Array, default() { return []; }, }, + /** A localized string that describes why this user may be unable to conduct an anonymous review. */ warnOnAssignmentLabel: { type: String, required: true, }, + /** A localized string for the button to unlock a reviewer who may be unable to conduct an anonymous review. */ warnOnAssignmentUnlockLabel: { type: String, required: true, }, }, + emits: [ + /** Emitted when a prop should be changed. Payload: `(id, newProps)` */ + 'set', + ], data() { return { isLoading: false, diff --git a/src/components/Modal/Modal.stories.js b/src/components/Modal/Modal.stories.js index 53a098ec4..83a31096f 100644 --- a/src/components/Modal/Modal.stories.js +++ b/src/components/Modal/Modal.stories.js @@ -5,6 +5,7 @@ import cloneDeep from 'clone-deep'; import FormMock from '@/docs/components/Form/helpers/form-announcement'; import List from '@/components/List/List.vue'; import ListItem from '@/components/List/ListItem.vue'; +import {allModes} from '../../../.storybook/modes.js'; export default { title: 'Components/Modal', @@ -33,6 +34,12 @@ export const Base = { `, }), + decorators: [ + () => ({ + template: '
    ', + }), + ], + args: {}, }; @@ -44,7 +51,7 @@ export const WithForm = { const form = ref({ ...cloneDeep(FormMock), - aciton: 'https://httpbin.org', + action: 'https://httpbin.org', method: 'GET', }); @@ -64,6 +71,20 @@ export const WithForm = { `, }), + decorators: [ + () => ({ + template: '
    ', + }), + ], + parameters: { + chromatic: { + modes: { + desktop: {disable: true}, + desktopLargeHeight: allModes['desktopLargeHeight'], + }, + }, + }, + args: {}, }; @@ -114,6 +135,12 @@ export const WithTabs = { `, }), + decorators: [ + () => ({ + template: '
    ', + }), + ], + args: {}, }; @@ -180,5 +207,11 @@ export const WithActions = { `, }), + decorators: [ + () => ({ + template: '
    ', + }), + ], + args: {}, }; diff --git a/src/components/Notification/Notification.stories.js b/src/components/Notification/Notification.stories.js index 57679da53..4112632ee 100644 --- a/src/components/Notification/Notification.stories.js +++ b/src/components/Notification/Notification.stories.js @@ -3,7 +3,7 @@ import Icon from '@/components/Icon/Icon.vue'; import {ref} from 'vue'; export default { - title: 'Basic Components/Notification', + title: 'Components/Notification', component: Notification, }; diff --git a/src/components/Panel/Panel.stories.js b/src/components/Panel/Panel.stories.js index 6fb214edc..4b7da0dbd 100644 --- a/src/components/Panel/Panel.stories.js +++ b/src/components/Panel/Panel.stories.js @@ -5,7 +5,7 @@ import List from '@/components/List/List.vue'; import ListItem from '@/components/List/ListItem.vue'; export default { - title: 'Basic Components/Panel', + title: 'Components/Panel', component: Panel, }; diff --git a/src/components/Spinner/Spinner.stories.js b/src/components/Spinner/Spinner.stories.js index 2cecb0eb3..d918f05d5 100644 --- a/src/components/Spinner/Spinner.stories.js +++ b/src/components/Spinner/Spinner.stories.js @@ -1,7 +1,7 @@ import Spinner from './Spinner.vue'; export default { - title: 'Basic Components/Spinner', + title: 'Components/Spinner', component: Spinner, }; diff --git a/src/components/Steps/Steps.stories.js b/src/components/Steps/Steps.stories.js index 7a72a8568..6f541503c 100644 --- a/src/components/Steps/Steps.stories.js +++ b/src/components/Steps/Steps.stories.js @@ -8,7 +8,7 @@ import ButtonRow from '@/components/ButtonRow/ButtonRow.vue'; import './Steps.stories.less'; export default { - title: 'Basic Components/Steps', + title: 'Components/Steps', component: Steps, }; diff --git a/src/components/Table/mocks/articleStats.js b/src/components/Table/mocks/articleStats.js new file mode 100644 index 000000000..1815c5e19 --- /dev/null +++ b/src/components/Table/mocks/articleStats.js @@ -0,0 +1,893 @@ +function getRandomTitle(str) { + let title = str + .split(' ') + .sort((a, b) => (Math.random() > 0.5 ? -1 : 1)) + .join(' '); + const trimLength = Math.floor(Math.random() * 20); + title = title.charAt(0).toUpperCase() + title.slice(1); + return title.substr(0, title.length - trimLength); +} + +const baseTitle = + 'tortor ultrices dolor diam dignissim ante nulla et morbi imperdiet'; +const sectionIds = [1, 2, 3]; +const baseStat = { + total: 0, + views: 0, + downloads: 0, + object: { + id: 1, + fullTitle: { + en: getRandomTitle(baseTitle), + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, +}; + +let stats = []; +for (let i = 1; i < 61; i++) { + let stat = { + ...baseStat, + object: { + ...baseStat.object, + id: i, + fullTitle: { + en: getRandomTitle(baseTitle), + }, + sectionId: sectionIds[Math.floor(Math.random() * sectionIds.length)], + }, + views: Math.floor(Math.random() * 10000) + 1, + downloads: Math.floor(Math.random() * 1000) + 1, + }; + + stat.total = stat.views + stat.downloads; + + stats.push(stat); +} + +// use fixed stats to stabilize visual comparisons +stats = [ + { + total: 8564, + views: 7945, + downloads: 619, + object: { + id: 1, + fullTitle: { + en: 'Nulla et ultrices tortor imperdiet ante diam dignissim', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 1170, + views: 560, + downloads: 610, + object: { + id: 2, + fullTitle: { + en: 'Ante dignissim imperdiet diam dolor ultrices et null', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 9607, + views: 9392, + downloads: 215, + object: { + id: 3, + fullTitle: { + en: 'Nulla imperdiet ultrices et dignissim tortor morbi d', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3453, + views: 2732, + downloads: 721, + object: { + id: 4, + fullTitle: { + en: 'Et morbi nulla dignissim tortor ante diam dolor u', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2541, + views: 1782, + downloads: 759, + object: { + id: 5, + fullTitle: { + en: 'Nulla dignissim tortor ante et ultrices morbi dolor diam ', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5116, + views: 4512, + downloads: 604, + object: { + id: 6, + fullTitle: { + en: 'Ultrices nulla tortor morbi ante dignissim dolo', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 7468, + views: 6697, + downloads: 771, + object: { + id: 7, + fullTitle: { + en: 'Nulla ante dignissim diam imperdiet morbi dolor ultrices tortor e', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3678, + views: 3450, + downloads: 228, + object: { + id: 8, + fullTitle: { + en: 'Morbi ante tortor imperdiet ultrices nulla dignissim dolor et dia', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 1421, + views: 937, + downloads: 484, + object: { + id: 9, + fullTitle: { + en: 'Nulla tortor ultrices diam imperdiet et dignissim morbi dolor ', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 7490, + views: 6772, + downloads: 718, + object: { + id: 10, + fullTitle: { + en: 'Diam dolor imperdiet dignissim morbi et tortor ultrices nulla ante', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2440, + views: 1904, + downloads: 536, + object: { + id: 11, + fullTitle: { + en: 'Tortor nulla diam imperdiet dolor ante et ultrices mor', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3924, + views: 3714, + downloads: 210, + object: { + id: 12, + fullTitle: { + en: 'Dignissim diam ante morbi dolor ultrices nulla imperdiet et ', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2425, + views: 2329, + downloads: 96, + object: { + id: 13, + fullTitle: { + en: 'Nulla imperdiet et dolor ultrices diam dignissim tortor ant', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 10104, + views: 9617, + downloads: 487, + object: { + id: 14, + fullTitle: { + en: 'Dignissim ante diam tortor imperdiet ultrices nulla', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 1205, + views: 1053, + downloads: 152, + object: { + id: 15, + fullTitle: { + en: 'Diam dolor et nulla ultrices ante morbi imperdiet digni', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 1389, + views: 1345, + downloads: 44, + object: { + id: 16, + fullTitle: { + en: 'Ante dolor et imperdiet diam dignissim tortor ultr', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5861, + views: 5300, + downloads: 561, + object: { + id: 17, + fullTitle: { + en: 'Nulla tortor dolor et dignissim diam ultrices ante morbi i', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2914, + views: 2125, + downloads: 789, + object: { + id: 18, + fullTitle: { + en: 'Diam nulla tortor ante dolor et ultrices morbi imperdiet dig', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5439, + views: 4950, + downloads: 489, + object: { + id: 19, + fullTitle: { + en: 'Tortor nulla ultrices dolor ante dignissim morbi diam imperdiet', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 6822, + views: 6723, + downloads: 99, + object: { + id: 20, + fullTitle: { + en: 'Ultrices nulla et dolor ante dignissim morbi tortor imp', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2535, + views: 2262, + downloads: 273, + object: { + id: 21, + fullTitle: { + en: 'Tortor morbi nulla ante ultrices diam dolor et imperdiet', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3319, + views: 2988, + downloads: 331, + object: { + id: 22, + fullTitle: { + en: 'Diam ante dolor dignissim nulla morbi et imperdiet ultrices tort', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 891, + views: 475, + downloads: 416, + object: { + id: 23, + fullTitle: { + en: 'Tortor et morbi ante diam ultrices dolor dignissim imperdiet ', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5976, + views: 5882, + downloads: 94, + object: { + id: 24, + fullTitle: { + en: 'Et nulla imperdiet ante tortor morbi diam ultrices digniss', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 9086, + views: 9076, + downloads: 10, + object: { + id: 25, + fullTitle: { + en: 'Diam imperdiet ante nulla morbi tortor dignissim et ultrices d', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5547, + views: 4979, + downloads: 568, + object: { + id: 26, + fullTitle: { + en: 'Morbi ultrices dolor tortor dignissim ante et imperdiet d', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3019, + views: 2223, + downloads: 796, + object: { + id: 27, + fullTitle: { + en: 'Diam dolor ultrices tortor dignissim ante et nulla morbi imperdi', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3078, + views: 2595, + downloads: 483, + object: { + id: 28, + fullTitle: { + en: 'Ante nulla diam dolor morbi ultrices dignissim tortor et i', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 7242, + views: 6650, + downloads: 592, + object: { + id: 29, + fullTitle: { + en: 'Nulla dignissim tortor ultrices et ante diam do', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 10052, + views: 9717, + downloads: 335, + object: { + id: 30, + fullTitle: { + en: 'Tortor ultrices nulla ante dolor dignissim et morbi imperdiet d', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 6362, + views: 5870, + downloads: 492, + object: { + id: 31, + fullTitle: { + en: 'Tortor ultrices morbi ante imperdiet et dolor n', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 6481, + views: 5833, + downloads: 648, + object: { + id: 32, + fullTitle: { + en: 'Tortor ante morbi ultrices dolor diam et imperdiet nulla digni', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 1250, + views: 537, + downloads: 713, + object: { + id: 33, + fullTitle: { + en: 'Ante imperdiet diam dignissim dolor et ultrices tortor nulla mo', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2149, + views: 1463, + downloads: 686, + object: { + id: 34, + fullTitle: { + en: 'Ante diam dolor nulla ultrices dignissim et tortor imperdiet morbi', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2067, + views: 1855, + downloads: 212, + object: { + id: 35, + fullTitle: { + en: 'Tortor imperdiet morbi ultrices dignissim diam ', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 9446, + views: 8582, + downloads: 864, + object: { + id: 36, + fullTitle: { + en: 'Tortor ultrices dolor dignissim morbi diam nulla et', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 4692, + views: 4453, + downloads: 239, + object: { + id: 37, + fullTitle: { + en: 'Diam tortor imperdiet ante et ultrices dignissim morbi ', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 4901, + views: 4775, + downloads: 126, + object: { + id: 38, + fullTitle: { + en: 'Dolor ante nulla ultrices et diam tortor morbi dignissim imp', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 4945, + views: 4803, + downloads: 142, + object: { + id: 39, + fullTitle: { + en: 'Nulla ultrices morbi et dignissim tortor imperdiet dolor ante ', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5948, + views: 5088, + downloads: 860, + object: { + id: 40, + fullTitle: { + en: 'Ultrices dolor morbi diam ante tortor imperdiet nulla et dignis', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5239, + views: 4731, + downloads: 508, + object: { + id: 41, + fullTitle: { + en: 'Dignissim nulla imperdiet dolor morbi ante diam ultr', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 6611, + views: 6304, + downloads: 307, + object: { + id: 42, + fullTitle: { + en: 'Diam nulla dolor imperdiet tortor ante ultrices et morbi dignissi', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 6626, + views: 5827, + downloads: 799, + object: { + id: 43, + fullTitle: { + en: 'Ante et dignissim imperdiet tortor morbi ultrices diam ', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 8275, + views: 7685, + downloads: 590, + object: { + id: 44, + fullTitle: { + en: 'Dignissim et diam nulla dolor morbi ultrices imperdiet ante t', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 6467, + views: 5609, + downloads: 858, + object: { + id: 45, + fullTitle: { + en: 'Tortor ultrices et nulla dignissim morbi imperdiet diam a', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 680, + views: 349, + downloads: 331, + object: { + id: 46, + fullTitle: { + en: 'Dolor ante nulla dignissim morbi et diam imperdiet ', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 10596, + views: 9906, + downloads: 690, + object: { + id: 47, + fullTitle: { + en: 'Nulla diam dolor ultrices tortor imperdiet et digniss', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 4319, + views: 4233, + downloads: 86, + object: { + id: 48, + fullTitle: { + en: 'Et ultrices ante diam morbi tortor nulla digniss', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5177, + views: 4344, + downloads: 833, + object: { + id: 49, + fullTitle: { + en: 'Imperdiet dignissim morbi nulla diam dolor ultrices ante et', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3156, + views: 2966, + downloads: 190, + object: { + id: 50, + fullTitle: { + en: 'Ultrices ante morbi nulla dolor tortor diam imperdiet', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2808, + views: 2117, + downloads: 691, + object: { + id: 51, + fullTitle: { + en: 'Nulla tortor ante et ultrices imperdiet diam dolor dignis', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 2327, + views: 1581, + downloads: 746, + object: { + id: 52, + fullTitle: { + en: 'Dolor et ultrices diam dignissim tortor nulla ante imperdiet morb', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 711, + views: 310, + downloads: 401, + object: { + id: 53, + fullTitle: { + en: 'Nulla morbi ante tortor imperdiet et ultrices dignissim dolor ', + }, + sectionId: 2, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 5100, + views: 5015, + downloads: 85, + object: { + id: 54, + fullTitle: { + en: 'Diam et ultrices ante morbi dignissim tortor dolor imperdiet ', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 6647, + views: 6104, + downloads: 543, + object: { + id: 55, + fullTitle: { + en: 'Et diam tortor imperdiet morbi dolor dignissim ante nulla ultr', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 9420, + views: 9240, + downloads: 180, + object: { + id: 56, + fullTitle: { + en: 'Tortor ultrices imperdiet dolor nulla dignissim morbi d', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3323, + views: 2562, + downloads: 761, + object: { + id: 57, + fullTitle: { + en: 'Et tortor nulla imperdiet morbi ultrices dolor diam dig', + }, + sectionId: 3, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 1466, + views: 569, + downloads: 897, + object: { + id: 58, + fullTitle: { + en: 'Morbi dolor ultrices diam et imperdiet ante tortor dignissim', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3115, + views: 2861, + downloads: 254, + object: { + id: 59, + fullTitle: { + en: 'Tortor ultrices morbi imperdiet dolor diam nulla et ', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, + { + total: 3050, + views: 2500, + downloads: 550, + object: { + id: 60, + fullTitle: { + en: 'Ante nulla et tortor morbi ultrices dolor diam imperdiet digniss', + }, + sectionId: 1, + authorString: 'Carlo Corino', + urlPublished: '/example/1', + }, + }, +]; +export default stats; diff --git a/src/components/Table/mocks/articleStatsColumns.js b/src/components/Table/mocks/articleStatsColumns.js new file mode 100644 index 000000000..6f1186f3f --- /dev/null +++ b/src/components/Table/mocks/articleStatsColumns.js @@ -0,0 +1,54 @@ +export default [ + { + name: 'id', + label: 'ID', + value(row) { + return row.object.id; + }, + }, + { + name: 'title', + label: 'Title', + isRowHeader: true, + value(row) { + return row.object.fullTitle.en; + }, + }, + { + name: 'author', + label: 'Contributors', + value(row) { + return row.object.authorString; + }, + }, + { + name: 'abstractViews', + label: 'Abstract Views', + value: 'abstractViews', + }, + { + name: 'galleyViews', + label: 'Galley Views', + value: 'galleyViews', + }, + { + name: 'pdf', + label: 'PDF', + value: 'pdf', + }, + { + name: 'html', + label: 'HTML', + value: 'html', + }, + { + name: 'other', + label: 'Other', + value: 'other', + }, + { + name: 'total', + label: 'Total', + value: 'total', + }, +]; diff --git a/src/components/TableNext/Table.stories.js b/src/components/TableNext/Table.stories.js index de208db17..108c85342 100644 --- a/src/components/TableNext/Table.stories.js +++ b/src/components/TableNext/Table.stories.js @@ -5,7 +5,7 @@ import TableHeader from './TableHeader.vue'; import ButtonRow from '@/components/ButtonRow/ButtonRow.vue'; import Pagination from '@/components/Pagination/Pagination.vue'; -import articleStats from '@/docs/components/Table/helpers/articleStats.js'; +import articleStats from '@/components/Table/mocks/articleStats.js'; import {useSorting} from '@/composables/useSorting'; export default { title: 'Components/Table', diff --git a/src/components/Tabs/Tabs.stories.js b/src/components/Tabs/Tabs.stories.js index dbd6b4565..69a1a48cc 100644 --- a/src/components/Tabs/Tabs.stories.js +++ b/src/components/Tabs/Tabs.stories.js @@ -2,7 +2,7 @@ import Tabs from './Tabs.vue'; import Tab from './Tab.vue'; export default { - title: 'Basic Components/Tabs', + title: 'Components/Tabs', component: Tabs, }; diff --git a/src/composables/useContainerStateManager.js b/src/composables/useContainerStateManager.js new file mode 100644 index 000000000..b4294b63f --- /dev/null +++ b/src/composables/useContainerStateManager.js @@ -0,0 +1,34 @@ +import {ref} from 'vue'; + +export function useContainerStateManager() { + const components = ref({}); + + /** + * Get a component by its key + * + * @param {String} key + * @return {Object} + */ + function get(key) { + return components.value[key] ? components.value[key] : {}; + } + + /** + * Set data for a component + * + * Existing keys in the component that are not passed in the data + * argument will not be modified or removed. + * + * @param {String} key + * @param {Array} data Key/value object with new data + */ + function set(key, data) { + let component = {...get(key)}; + Object.keys(data).forEach(function (dataKey) { + component[dataKey] = data[dataKey]; + }); + components.value[key] = component; + } + + return {components, get, set}; +} diff --git a/src/composables/useFetch.js b/src/composables/useFetch.js index 65326d9f8..daddb47ef 100644 --- a/src/composables/useFetch.js +++ b/src/composables/useFetch.js @@ -1,7 +1,18 @@ import {ref, unref} from 'vue'; -import {ofetch} from 'ofetch'; +import {ofetch, createFetch} from 'ofetch'; import {useDialogStore} from '@/stores/dialogStore'; + +let ofetchInstance = ofetch; export function useFetch(url, options) { + /** + * Workaround for testing https://github.com/unjs/ofetch/issues/295 + * Can be removed once issue is addressed + * (likely getting fetch instance in runtime) + * */ + if (typeof process !== 'undefined' && process?.env?.VITEST == 'true') { + ofetchInstance = createFetch(); + } + const dialogStore = useDialogStore(); const isLoading = ref(false); const data = ref(null); @@ -17,11 +28,11 @@ export function useFetch(url, options) { const signal = lastRequestController.signal; - const opts = options; + const opts = {...options, signal}; isLoading.value = true; try { - const result = await ofetch(unref(url), opts); + const result = await ofetchInstance(unref(url), opts); data.value = result; } catch (e) { data.value = null; diff --git a/src/composables/useFetch.test.js b/src/composables/useFetch.test.js new file mode 100644 index 000000000..76b50a8be --- /dev/null +++ b/src/composables/useFetch.test.js @@ -0,0 +1,73 @@ +import { + describe, + test, + afterAll, + afterEach, + beforeAll, + beforeEach, + expect, +} from 'vitest'; +import {setupServer} from 'msw/node'; +import {ref} from 'vue'; +import {HttpResponse, http, delay} from 'msw'; + +import {setActivePinia, createPinia} from 'pinia'; + +import {useFetch} from './useFetch'; +import {useDialogStore} from '@/stores/dialogStore'; + +export const restHandlers = [ + http.get('http://mock/delayed', async ({request}) => { + const url = new URL(request.url); + + const id = parseInt(url.searchParams.get('id')); + await delay(id * 200); + + return HttpResponse.json({id}); + }), + + http.get('http://mock/status500', async ({request}) => { + return new HttpResponse(null, {status: 500}); + }), +]; + +const server = setupServer(...restHandlers); + +// Start server before all tests +beforeAll(() => server.listen({onUnhandledRequest: 'error'})); + +// Close server after all tests +afterAll(() => server.close()); + +// Reset handlers after each test `important for test isolation` +afterEach(() => server.resetHandlers()); + +beforeEach(() => { + setActivePinia(createPinia()); +}); + +describe('useFetch', () => { + test('last request data is used, previous are aborted', async () => { + const url = ref('http://mock/delayed?id=5'); + const {data, fetch} = useFetch(url); + const longFetch = fetch(); + + url.value = 'http://mock/delayed?id=1'; + const shortFetch = fetch(); + await Promise.all([longFetch, shortFetch]); + expect(data.value).toStrictEqual({id: 1}); + }); + + test('network dialog error is displayed if there is http code other than 2XX', async () => { + const url = ref('http://mock/status500'); + const dialogStore = useDialogStore(); + expect(dialogStore.dialogOpened).toBe(false); + + const {fetch} = useFetch(url); + await fetch(); + + expect(dialogStore.dialogOpened).toBe(true); + dialogStore.closeDialog(); + expect(dialogStore.dialogOpened).toBe(false); + }); +}); diff --git a/src/docs/components/ListPanel/previews/PreviewListPanelDescription.vue b/src/docs/components/ListPanel/previews/PreviewListPanelDescription.vue index bce8f92a3..93ea0a8ab 100644 --- a/src/docs/components/ListPanel/previews/PreviewListPanelDescription.vue +++ b/src/docs/components/ListPanel/previews/PreviewListPanelDescription.vue @@ -30,11 +30,3 @@ export default { }, }; - - diff --git a/src/docs/components/StatsPage/previews/PreviewStatsEditorialPage.vue b/src/docs/components/StatsPage/previews/PreviewStatsEditorialPage.vue index 6b817f753..b6a3c20e5 100644 --- a/src/docs/components/StatsPage/previews/PreviewStatsEditorialPage.vue +++ b/src/docs/components/StatsPage/previews/PreviewStatsEditorialPage.vue @@ -114,7 +114,7 @@ :row="row" :tabindex="!rowIndex && !columnIndex ? 0 : -1" > - - diff --git a/src/pages/submissions/SubmissionSummaryModal.vue b/src/pages/submissions/SubmissionSummaryModal.vue index bdc2b3abd..65b220999 100644 --- a/src/pages/submissions/SubmissionSummaryModal.vue +++ b/src/pages/submissions/SubmissionSummaryModal.vue @@ -3,6 +3,7 @@ diff --git a/src/pages/submissions/SubmissionsPage.mdx b/src/pages/submissions/SubmissionsPage.mdx index addba88af..47e06856c 100644 --- a/src/pages/submissions/SubmissionsPage.mdx +++ b/src/pages/submissions/SubmissionsPage.mdx @@ -5,3 +5,5 @@ import * as SubmissionsPage from './SubmissionsPage.stories.js'; # Submissions page + + diff --git a/src/pages/submissions/SubmissionsPage.stories.js b/src/pages/submissions/SubmissionsPage.stories.js index 433bdf71d..de0fa7c02 100644 --- a/src/pages/submissions/SubmissionsPage.stories.js +++ b/src/pages/submissions/SubmissionsPage.stories.js @@ -1,5 +1,5 @@ import SubmissionsPage from './SubmissionsPage.vue'; -import {rest} from 'msw'; +import {http, HttpResponse} from 'msw'; import SubmissionsMock25 from './mocks/submissions25.js'; import PageInitConfigMock from './mocks/pageInitConfig'; @@ -16,16 +16,14 @@ export const init = { parameters: { msw: { handlers: [ - rest.get( + http.get( 'https://mock/index.php/publicknowledge/api/v1/_submissions', - (req, res, ctx) => { - return res(ctx.json(SubmissionsMock25)); + () => { + return HttpResponse.json(SubmissionsMock25); }, ), ], }, }, - args: { - storeData: PageInitConfigMock, - }, + args: PageInitConfigMock, }; diff --git a/src/pages/submissions/SubmissionsPage.vue b/src/pages/submissions/SubmissionsPage.vue index de899b2da..9cc59bfca 100644 --- a/src/pages/submissions/SubmissionsPage.vue +++ b/src/pages/submissions/SubmissionsPage.vue @@ -54,7 +54,7 @@