From 22cf6504376f5e6724a227c1953d2e9aebbc5069 Mon Sep 17 00:00:00 2001 From: Tomas Vondracek Date: Sun, 25 Oct 2026 21:54:42 +0100 Subject: [PATCH 1/5] refactor: replace Vuex with Pinia Pinia 2 runs on both Vue 2.7 and Vue 3, so the stores can move ahead of the Vue 3 upgrade. The stores take the shape they have in the Vue 3 branch: plain state that components read with mapState and assign directly, with actions only where there is logic (select/deSelect). Wherever a component calls a store directly, it passes its own $pinia: the sidebar, toolbar and listing are separate Vue roots on one page, and outside setup() and the lifecycle hooks Vue 2.7 has no current instance, so a bare useStore() would fall back to whichever root booted last. vue-demi, through which Pinia 2 reaches Vue, ships its Vue 3 build as the default and only switches to the 2.7 build in its postinstall. webpack and Vitest resolve it to the 2.7 build directly, so an install that skips scripts still builds. Both of those are Vue 2 only and go with the move to Vue 3. --- assets/js/app/editor/index.js | 8 +- assets/js/app/editor/store/index.js | 11 -- assets/js/app/editor/store/index.ts | 11 ++ .../editor/store/modules/general/_actions.js | 7 - .../editor/store/modules/general/_getters.js | 5 - .../store/modules/general/_mutations.js | 7 - .../editor/store/modules/general/_state.js | 5 - .../app/editor/store/modules/general/index.js | 12 -- assets/js/app/listing/Components/Filter.vue | 23 ++- assets/js/app/listing/Components/Records.vue | 6 +- .../js/app/listing/Components/SelectBox.vue | 15 +- .../Components/Table/Row/_Checkbox.vue | 15 +- .../listing/Components/Table/Row/_Sorting.vue | 7 +- .../listing/Components/Table/Row/index.vue | 9 +- .../js/app/listing/Components/Table/index.vue | 11 +- assets/js/app/listing/index.js | 8 +- assets/js/app/listing/mixins/type.js | 7 +- assets/js/app/listing/store/index.js | 18 --- assets/js/app/listing/store/index.ts | 51 ++++++ .../listing/store/modules/general/_actions.js | 13 -- .../listing/store/modules/general/_getters.js | 7 - .../store/modules/general/_mutations.js | 13 -- .../listing/store/modules/general/_state.js | 7 - .../listing/store/modules/general/index.js | 12 -- .../listing/store/modules/listing/_actions.js | 7 - .../listing/store/modules/listing/_getters.js | 10 -- .../store/modules/listing/_mutations.js | 7 - .../listing/store/modules/listing/_state.js | 5 - .../listing/store/modules/listing/index.js | 12 -- .../store/modules/selecting/_actions.js | 13 -- .../store/modules/selecting/_getters.js | 7 - .../store/modules/selecting/_mutations.js | 16 -- .../listing/store/modules/selecting/_state.js | 7 - .../listing/store/modules/selecting/index.js | 12 -- assets/js/app/listing/types.ts | 33 ++++ assets/js/app/sidebar/Components/Sidebar.vue | 3 +- .../sidebar/Components/_SidebarToggler.vue | 7 +- assets/js/app/sidebar/index.js | 6 +- assets/js/app/sidebar/store/index.js | 11 -- assets/js/app/sidebar/store/index.ts | 11 ++ .../sidebar/store/modules/general/_actions.js | 7 - .../sidebar/store/modules/general/_getters.js | 5 - .../store/modules/general/_mutations.js | 7 - .../sidebar/store/modules/general/_state.js | 5 - .../sidebar/store/modules/general/index.js | 12 -- assets/js/app/toolbar/Components/Toolbar.vue | 8 +- assets/js/app/toolbar/index.js | 8 +- assets/js/app/toolbar/store/index.js | 11 -- assets/js/app/toolbar/store/index.ts | 11 ++ .../toolbar/store/modules/general/_actions.js | 7 - .../toolbar/store/modules/general/_getters.js | 5 - .../store/modules/general/_mutations.js | 7 - .../toolbar/store/modules/general/_state.js | 5 - .../toolbar/store/modules/general/index.js | 12 -- package-lock.json | 67 ++++++-- package.json | 2 +- templates/pages/about.html.twig | 2 +- tests/unit/README.md | 17 +- tests/unit/components/listing/Filter.spec.ts | 45 +++--- tests/unit/components/listing/Records.spec.ts | 16 +- tests/unit/components/listing/Row.spec.ts | 10 +- .../components/listing/RowCheckbox.spec.ts | 40 ++--- .../components/listing/RowSorting.spec.ts | 2 +- .../unit/components/listing/SelectBox.spec.ts | 4 +- tests/unit/components/listing/Table.spec.ts | 14 +- tests/unit/components/sidebar/Sidebar.spec.ts | 26 +-- .../components/sidebar/SidebarToggler.spec.ts | 19 +-- tests/unit/components/toolbar/Toolbar.spec.ts | 10 +- tests/unit/helpers/store.ts | 149 ++++++++++-------- tests/unit/mixins/type.spec.ts | 14 +- tests/unit/shims-vuex.d.ts | 17 -- tests/unit/store/listing.spec.ts | 115 +++++--------- tests/unit/store/multi-root.spec.ts | 69 ++++++++ tests/unit/store/sidebar-toolbar.spec.ts | 34 ++-- tsconfig.json | 5 +- vitest.config.ts | 24 ++- webpack.config.js | 12 ++ 77 files changed, 598 insertions(+), 680 deletions(-) delete mode 100644 assets/js/app/editor/store/index.js create mode 100644 assets/js/app/editor/store/index.ts delete mode 100644 assets/js/app/editor/store/modules/general/_actions.js delete mode 100644 assets/js/app/editor/store/modules/general/_getters.js delete mode 100644 assets/js/app/editor/store/modules/general/_mutations.js delete mode 100644 assets/js/app/editor/store/modules/general/_state.js delete mode 100644 assets/js/app/editor/store/modules/general/index.js delete mode 100644 assets/js/app/listing/store/index.js create mode 100644 assets/js/app/listing/store/index.ts delete mode 100644 assets/js/app/listing/store/modules/general/_actions.js delete mode 100644 assets/js/app/listing/store/modules/general/_getters.js delete mode 100644 assets/js/app/listing/store/modules/general/_mutations.js delete mode 100644 assets/js/app/listing/store/modules/general/_state.js delete mode 100644 assets/js/app/listing/store/modules/general/index.js delete mode 100644 assets/js/app/listing/store/modules/listing/_actions.js delete mode 100644 assets/js/app/listing/store/modules/listing/_getters.js delete mode 100644 assets/js/app/listing/store/modules/listing/_mutations.js delete mode 100644 assets/js/app/listing/store/modules/listing/_state.js delete mode 100644 assets/js/app/listing/store/modules/listing/index.js delete mode 100644 assets/js/app/listing/store/modules/selecting/_actions.js delete mode 100644 assets/js/app/listing/store/modules/selecting/_getters.js delete mode 100644 assets/js/app/listing/store/modules/selecting/_mutations.js delete mode 100644 assets/js/app/listing/store/modules/selecting/_state.js delete mode 100644 assets/js/app/listing/store/modules/selecting/index.js create mode 100644 assets/js/app/listing/types.ts delete mode 100644 assets/js/app/sidebar/store/index.js create mode 100644 assets/js/app/sidebar/store/index.ts delete mode 100644 assets/js/app/sidebar/store/modules/general/_actions.js delete mode 100644 assets/js/app/sidebar/store/modules/general/_getters.js delete mode 100644 assets/js/app/sidebar/store/modules/general/_mutations.js delete mode 100644 assets/js/app/sidebar/store/modules/general/_state.js delete mode 100644 assets/js/app/sidebar/store/modules/general/index.js delete mode 100644 assets/js/app/toolbar/store/index.js create mode 100644 assets/js/app/toolbar/store/index.ts delete mode 100644 assets/js/app/toolbar/store/modules/general/_actions.js delete mode 100644 assets/js/app/toolbar/store/modules/general/_getters.js delete mode 100644 assets/js/app/toolbar/store/modules/general/_mutations.js delete mode 100644 assets/js/app/toolbar/store/modules/general/_state.js delete mode 100644 assets/js/app/toolbar/store/modules/general/index.js delete mode 100644 tests/unit/shims-vuex.d.ts create mode 100644 tests/unit/store/multi-root.spec.ts diff --git a/assets/js/app/editor/index.js b/assets/js/app/editor/index.js index 62330e7be9..65b85265f9 100644 --- a/assets/js/app/editor/index.js +++ b/assets/js/app/editor/index.js @@ -1,8 +1,8 @@ import Vue from 'vue'; /** - * VueX Store + * Pinia */ -import store from './store'; +import { createPinia, PiniaVuePlugin } from 'pinia'; /** * Components */ @@ -26,6 +26,8 @@ import Filelist from './Components/Filelist'; import Collection from './Components/Collection'; import Checkbox from './Components/Checkbox'; +Vue.use(PiniaVuePlugin); + Vue.component('EditorCheckbox', Checkbox); Vue.component('EditorDate', Date); Vue.component('EditorEmbed', Embed); @@ -52,7 +54,7 @@ const editorSelector = '#' + id; if (document.getElementById(id)) { new Vue({ - store, + pinia: createPinia(), el: editorSelector, name: 'BoltEditor', }); diff --git a/assets/js/app/editor/store/index.js b/assets/js/app/editor/store/index.js deleted file mode 100644 index f6bf86fb29..0000000000 --- a/assets/js/app/editor/store/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import general from './modules/general/'; - -Vue.use(Vuex); - -export default new Vuex.Store({ - modules: { - general, - }, -}); diff --git a/assets/js/app/editor/store/index.ts b/assets/js/app/editor/store/index.ts new file mode 100644 index 0000000000..e2e89f635a --- /dev/null +++ b/assets/js/app/editor/store/index.ts @@ -0,0 +1,11 @@ +import { defineStore } from 'pinia'; + +interface GeneralState { + slimSidebar: boolean; +} + +export const useGeneralStore = defineStore('editor-general', { + state: (): GeneralState => ({ + slimSidebar: false, + }), +}); diff --git a/assets/js/app/editor/store/modules/general/_actions.js b/assets/js/app/editor/store/modules/general/_actions.js deleted file mode 100644 index ac57ae0a67..0000000000 --- a/assets/js/app/editor/store/modules/general/_actions.js +++ /dev/null @@ -1,7 +0,0 @@ -const actions = { - slimSidebar({ commit }, arg) { - commit('slimSidebar', arg); - }, -}; - -export default actions; diff --git a/assets/js/app/editor/store/modules/general/_getters.js b/assets/js/app/editor/store/modules/general/_getters.js deleted file mode 100644 index 6200410724..0000000000 --- a/assets/js/app/editor/store/modules/general/_getters.js +++ /dev/null @@ -1,5 +0,0 @@ -const getters = { - slimSidebar: state => state.slimSidebar, -}; - -export default getters; diff --git a/assets/js/app/editor/store/modules/general/_mutations.js b/assets/js/app/editor/store/modules/general/_mutations.js deleted file mode 100644 index faa8f04445..0000000000 --- a/assets/js/app/editor/store/modules/general/_mutations.js +++ /dev/null @@ -1,7 +0,0 @@ -const mutations = { - slimSidebar(state, arg) { - state.slimSidebar = arg; - }, -}; - -export default mutations; diff --git a/assets/js/app/editor/store/modules/general/_state.js b/assets/js/app/editor/store/modules/general/_state.js deleted file mode 100644 index fee92b329c..0000000000 --- a/assets/js/app/editor/store/modules/general/_state.js +++ /dev/null @@ -1,5 +0,0 @@ -const state = { - slimSidebar: false, -}; - -export default state; diff --git a/assets/js/app/editor/store/modules/general/index.js b/assets/js/app/editor/store/modules/general/index.js deleted file mode 100644 index 8f2132fec7..0000000000 --- a/assets/js/app/editor/store/modules/general/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import state from './_state'; -import actions from './_actions'; -import mutations from './_mutations'; -import getters from './_getters'; - -export default { - namespaced: true, - state, - getters, - actions, - mutations, -}; diff --git a/assets/js/app/listing/Components/Filter.vue b/assets/js/app/listing/Components/Filter.vue index fc3182d317..ce97172e7e 100644 --- a/assets/js/app/listing/Components/Filter.vue +++ b/assets/js/app/listing/Components/Filter.vue @@ -60,7 +60,9 @@ diff --git a/assets/js/app/listing/Components/SelectBox.vue b/assets/js/app/listing/Components/SelectBox.vue index be90b8ef20..72801b8263 100644 --- a/assets/js/app/listing/Components/SelectBox.vue +++ b/assets/js/app/listing/Components/SelectBox.vue @@ -47,7 +47,9 @@ diff --git a/assets/js/app/listing/Components/Table/Row/_Checkbox.vue b/assets/js/app/listing/Components/Table/Row/_Checkbox.vue index 01651848f9..6b3e1f14ab 100644 --- a/assets/js/app/listing/Components/Table/Row/_Checkbox.vue +++ b/assets/js/app/listing/Components/Table/Row/_Checkbox.vue @@ -8,6 +8,9 @@ diff --git a/assets/js/app/listing/Components/Table/Row/index.vue b/assets/js/app/listing/Components/Table/Row/index.vue index 11264a0177..f5ab6adb1b 100644 --- a/assets/js/app/listing/Components/Table/Row/index.vue +++ b/assets/js/app/listing/Components/Table/Row/index.vue @@ -45,7 +45,9 @@ diff --git a/assets/js/app/listing/index.js b/assets/js/app/listing/index.js index 9bc140bff2..3b67d85dea 100644 --- a/assets/js/app/listing/index.js +++ b/assets/js/app/listing/index.js @@ -1,8 +1,8 @@ import Vue from 'vue'; /** - * VueX Store + * Pinia */ -import store from './store'; +import { createPinia, PiniaVuePlugin } from 'pinia'; /** * Components */ @@ -11,6 +11,8 @@ import Filter from './Components/Filter'; import Table from './Components/Table'; import SelectBox from './Components/SelectBox'; +Vue.use(PiniaVuePlugin); + /** * Register Components */ @@ -18,7 +20,7 @@ const id = 'listing'; if (document.getElementById(id)) { new Vue({ - store, + pinia: createPinia(), el: '#' + id, name: 'BoltListing', components: { diff --git a/assets/js/app/listing/mixins/type.js b/assets/js/app/listing/mixins/type.js index 2bb5e96e23..5d06abca4c 100644 --- a/assets/js/app/listing/mixins/type.js +++ b/assets/js/app/listing/mixins/type.js @@ -1,7 +1,8 @@ +import { mapState } from 'pinia'; +import { useGeneralStore } from '../store'; + export default { computed: { - type() { - return this.$store.getters['general/getType']; - }, + ...mapState(useGeneralStore, ['type']), }, }; diff --git a/assets/js/app/listing/store/index.js b/assets/js/app/listing/store/index.js deleted file mode 100644 index fefb9d7312..0000000000 --- a/assets/js/app/listing/store/index.js +++ /dev/null @@ -1,18 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -/** - * modules - */ -import general from './modules/general/'; -import listing from './modules/listing/'; -import selecting from './modules/selecting/'; - -Vue.use(Vuex); - -export default new Vuex.Store({ - modules: { - general, - listing, - selecting, - }, -}); diff --git a/assets/js/app/listing/store/index.ts b/assets/js/app/listing/store/index.ts new file mode 100644 index 0000000000..c5b58841fd --- /dev/null +++ b/assets/js/app/listing/store/index.ts @@ -0,0 +1,51 @@ +import { defineStore } from 'pinia'; +import type { ListingRecord } from '../types'; + +interface GeneralState { + type: string | null; + rowSize: string; + sorting: boolean; +} + +export const useGeneralStore = defineStore('listing-general', { + state: (): GeneralState => ({ + type: null, + rowSize: 'normal', + sorting: false, + }), +}); + +interface ListingState { + records: ListingRecord[]; +} + +export const useListingStore = defineStore('listing', { + state: (): ListingState => ({ + records: [], + }), +}); + +interface SelectingState { + selectAll: boolean; + selectedCount: number; + selected: number[]; +} + +export const useSelectingStore = defineStore('selecting', { + state: (): SelectingState => ({ + selectAll: false, + selectedCount: 0, + selected: [], + }), + actions: { + select(id: number) { + this.selectedCount++; + this.selected.push(id); + }, + deSelect(id: number) { + this.selectedCount--; + const index = this.selected.indexOf(id); + if (index > -1) this.selected.splice(index, 1); + }, + }, +}); diff --git a/assets/js/app/listing/store/modules/general/_actions.js b/assets/js/app/listing/store/modules/general/_actions.js deleted file mode 100644 index b2e9bfaea7..0000000000 --- a/assets/js/app/listing/store/modules/general/_actions.js +++ /dev/null @@ -1,13 +0,0 @@ -const actions = { - setType({ commit }, data) { - commit('setType', data); - }, - setRowSize({ commit }, data) { - commit('setRowSize', data); - }, - setSorting({ commit }, arg) { - commit('setSorting', arg); - }, -}; - -export default actions; diff --git a/assets/js/app/listing/store/modules/general/_getters.js b/assets/js/app/listing/store/modules/general/_getters.js deleted file mode 100644 index 0c3a835f78..0000000000 --- a/assets/js/app/listing/store/modules/general/_getters.js +++ /dev/null @@ -1,7 +0,0 @@ -const getters = { - getType: state => state.type, - getRowSize: state => state.rowSize, - getSorting: state => state.sorting, -}; - -export default getters; diff --git a/assets/js/app/listing/store/modules/general/_mutations.js b/assets/js/app/listing/store/modules/general/_mutations.js deleted file mode 100644 index 0c6650d8b7..0000000000 --- a/assets/js/app/listing/store/modules/general/_mutations.js +++ /dev/null @@ -1,13 +0,0 @@ -const mutations = { - setType(state, data) { - state.type = data; - }, - setRowSize(state, data) { - state.rowSize = data; - }, - setSorting(state, arg) { - state.sorting = arg; - }, -}; - -export default mutations; diff --git a/assets/js/app/listing/store/modules/general/_state.js b/assets/js/app/listing/store/modules/general/_state.js deleted file mode 100644 index 0a5536271b..0000000000 --- a/assets/js/app/listing/store/modules/general/_state.js +++ /dev/null @@ -1,7 +0,0 @@ -const state = { - type: null, - rowSize: 'normal', - sorting: false, -}; - -export default state; diff --git a/assets/js/app/listing/store/modules/general/index.js b/assets/js/app/listing/store/modules/general/index.js deleted file mode 100644 index 8f2132fec7..0000000000 --- a/assets/js/app/listing/store/modules/general/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import state from './_state'; -import actions from './_actions'; -import mutations from './_mutations'; -import getters from './_getters'; - -export default { - namespaced: true, - state, - getters, - actions, - mutations, -}; diff --git a/assets/js/app/listing/store/modules/listing/_actions.js b/assets/js/app/listing/store/modules/listing/_actions.js deleted file mode 100644 index ade7fa96c3..0000000000 --- a/assets/js/app/listing/store/modules/listing/_actions.js +++ /dev/null @@ -1,7 +0,0 @@ -const actions = { - setRecords({ commit }, data) { - commit('setRecords', data); - }, -}; - -export default actions; diff --git a/assets/js/app/listing/store/modules/listing/_getters.js b/assets/js/app/listing/store/modules/listing/_getters.js deleted file mode 100644 index 0f8b04ba8f..0000000000 --- a/assets/js/app/listing/store/modules/listing/_getters.js +++ /dev/null @@ -1,10 +0,0 @@ -const getters = { - getRecords: state => state.records, - getOrder(state) { - return state.records.map(record => { - return record.id; - }); - }, -}; - -export default getters; diff --git a/assets/js/app/listing/store/modules/listing/_mutations.js b/assets/js/app/listing/store/modules/listing/_mutations.js deleted file mode 100644 index 6f43fad4f9..0000000000 --- a/assets/js/app/listing/store/modules/listing/_mutations.js +++ /dev/null @@ -1,7 +0,0 @@ -const mutations = { - setRecords(state, data) { - state.records = data; - }, -}; - -export default mutations; diff --git a/assets/js/app/listing/store/modules/listing/_state.js b/assets/js/app/listing/store/modules/listing/_state.js deleted file mode 100644 index 493859e792..0000000000 --- a/assets/js/app/listing/store/modules/listing/_state.js +++ /dev/null @@ -1,5 +0,0 @@ -const state = { - records: [], -}; - -export default state; diff --git a/assets/js/app/listing/store/modules/listing/index.js b/assets/js/app/listing/store/modules/listing/index.js deleted file mode 100644 index 8f2132fec7..0000000000 --- a/assets/js/app/listing/store/modules/listing/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import state from './_state'; -import actions from './_actions'; -import mutations from './_mutations'; -import getters from './_getters'; - -export default { - namespaced: true, - state, - getters, - actions, - mutations, -}; diff --git a/assets/js/app/listing/store/modules/selecting/_actions.js b/assets/js/app/listing/store/modules/selecting/_actions.js deleted file mode 100644 index a9ca605829..0000000000 --- a/assets/js/app/listing/store/modules/selecting/_actions.js +++ /dev/null @@ -1,13 +0,0 @@ -const actions = { - selectAll({ commit }, arg) { - commit('selectAll', arg); - }, - select({ commit }, id) { - commit('select', id); - }, - deSelect({ commit }, id) { - commit('deSelect', id); - }, -}; - -export default actions; diff --git a/assets/js/app/listing/store/modules/selecting/_getters.js b/assets/js/app/listing/store/modules/selecting/_getters.js deleted file mode 100644 index 6389257de8..0000000000 --- a/assets/js/app/listing/store/modules/selecting/_getters.js +++ /dev/null @@ -1,7 +0,0 @@ -const getters = { - selectAll: state => state.selectAll, - selectedCount: state => state.selectedCount, - selected: state => state.selected, -}; - -export default getters; diff --git a/assets/js/app/listing/store/modules/selecting/_mutations.js b/assets/js/app/listing/store/modules/selecting/_mutations.js deleted file mode 100644 index 6a55895240..0000000000 --- a/assets/js/app/listing/store/modules/selecting/_mutations.js +++ /dev/null @@ -1,16 +0,0 @@ -const mutations = { - selectAll(state, arg) { - state.selectAll = arg; - }, - select(state, id) { - state.selectedCount++; - state.selected.push(id); - }, - deSelect(state, id) { - state.selectedCount--; - let index = state.selected.indexOf(id); - if (index > -1) state.selected.splice(index, 1); - }, -}; - -export default mutations; diff --git a/assets/js/app/listing/store/modules/selecting/_state.js b/assets/js/app/listing/store/modules/selecting/_state.js deleted file mode 100644 index 8a0405f032..0000000000 --- a/assets/js/app/listing/store/modules/selecting/_state.js +++ /dev/null @@ -1,7 +0,0 @@ -const state = { - selectAll: false, - selectedCount: 0, - selected: [], -}; - -export default state; diff --git a/assets/js/app/listing/store/modules/selecting/index.js b/assets/js/app/listing/store/modules/selecting/index.js deleted file mode 100644 index 8f2132fec7..0000000000 --- a/assets/js/app/listing/store/modules/selecting/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import state from './_state'; -import actions from './_actions'; -import mutations from './_mutations'; -import getters from './_getters'; - -export default { - namespaced: true, - state, - getters, - actions, - mutations, -}; diff --git a/assets/js/app/listing/types.ts b/assets/js/app/listing/types.ts new file mode 100644 index 0000000000..99677acc00 --- /dev/null +++ b/assets/js/app/listing/types.ts @@ -0,0 +1,33 @@ +export type LocalizedString = string | Record; + +export type ListingImage = { + thumbnail: string; + alt?: string | null; +}; + +export type ListingRecord = { + id?: number; + title?: string; + status?: string; + authorName?: string; + createdAt?: string | null; + publishedAt?: string | null; + modifiedAt?: string | null; + fieldValues?: { + slug?: LocalizedString | null; + }; + extras?: { + editLink?: string; + title?: string; + feature?: string | null; + excerpt?: string; + image?: ListingImage | null; + icon?: string; + link?: string; + statusLink?: string; + duplicateLink?: string; + deleteLink?: string; + singular_name?: string; + contentTypeOverviewLink?: string; + }; +}; diff --git a/assets/js/app/sidebar/Components/Sidebar.vue b/assets/js/app/sidebar/Components/Sidebar.vue index 4e2a5c1b96..f0c34e8138 100644 --- a/assets/js/app/sidebar/Components/Sidebar.vue +++ b/assets/js/app/sidebar/Components/Sidebar.vue @@ -11,6 +11,7 @@ diff --git a/assets/js/app/toolbar/index.js b/assets/js/app/toolbar/index.js index ae690d9d95..fb90909443 100644 --- a/assets/js/app/toolbar/index.js +++ b/assets/js/app/toolbar/index.js @@ -1,13 +1,15 @@ import Vue from 'vue'; /** - * VueX Store + * Pinia */ -import store from './store'; +import { createPinia, PiniaVuePlugin } from 'pinia'; /** * Components */ import Toolbar from './Components/Toolbar.vue'; +Vue.use(PiniaVuePlugin); + /** * Register Components */ @@ -15,7 +17,7 @@ const id = 'toolbar'; if (document.getElementById(id)) { new Vue({ - store, + pinia: createPinia(), el: '#' + id, name: 'BoltToolbar', components: { diff --git a/assets/js/app/toolbar/store/index.js b/assets/js/app/toolbar/store/index.js deleted file mode 100644 index f6bf86fb29..0000000000 --- a/assets/js/app/toolbar/store/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import general from './modules/general/'; - -Vue.use(Vuex); - -export default new Vuex.Store({ - modules: { - general, - }, -}); diff --git a/assets/js/app/toolbar/store/index.ts b/assets/js/app/toolbar/store/index.ts new file mode 100644 index 0000000000..b0799bb102 --- /dev/null +++ b/assets/js/app/toolbar/store/index.ts @@ -0,0 +1,11 @@ +import { defineStore } from 'pinia'; + +interface GeneralState { + toolbarColor: string; +} + +export const useGeneralStore = defineStore('toolbar-general', { + state: (): GeneralState => ({ + toolbarColor: '', + }), +}); diff --git a/assets/js/app/toolbar/store/modules/general/_actions.js b/assets/js/app/toolbar/store/modules/general/_actions.js deleted file mode 100644 index 1135f81f78..0000000000 --- a/assets/js/app/toolbar/store/modules/general/_actions.js +++ /dev/null @@ -1,7 +0,0 @@ -const actions = { - toolbarColor({ commit }, data) { - commit('toolbarColor', data); - }, -}; - -export default actions; diff --git a/assets/js/app/toolbar/store/modules/general/_getters.js b/assets/js/app/toolbar/store/modules/general/_getters.js deleted file mode 100644 index 4d001ba273..0000000000 --- a/assets/js/app/toolbar/store/modules/general/_getters.js +++ /dev/null @@ -1,5 +0,0 @@ -const getters = { - toolbarColor: state => state.toolbarColor, -}; - -export default getters; diff --git a/assets/js/app/toolbar/store/modules/general/_mutations.js b/assets/js/app/toolbar/store/modules/general/_mutations.js deleted file mode 100644 index 26efe7ac28..0000000000 --- a/assets/js/app/toolbar/store/modules/general/_mutations.js +++ /dev/null @@ -1,7 +0,0 @@ -const mutations = { - toolbarColor(state, data) { - state.toolbarColor = data; - }, -}; - -export default mutations; diff --git a/assets/js/app/toolbar/store/modules/general/_state.js b/assets/js/app/toolbar/store/modules/general/_state.js deleted file mode 100644 index 20a7e0794f..0000000000 --- a/assets/js/app/toolbar/store/modules/general/_state.js +++ /dev/null @@ -1,5 +0,0 @@ -const state = { - toolbarColor: null, -}; - -export default state; diff --git a/assets/js/app/toolbar/store/modules/general/index.js b/assets/js/app/toolbar/store/modules/general/index.js deleted file mode 100644 index 8f2132fec7..0000000000 --- a/assets/js/app/toolbar/store/modules/general/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import state from './_state'; -import actions from './_actions'; -import mutations from './_mutations'; -import getters from './_getters'; - -export default { - namespaced: true, - state, - getters, - actions, - mutations, -}; diff --git a/package-lock.json b/package-lock.json index 5a6cb22d23..822f7fe842 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "luxon": "^1.28.1", "no-scroll": "^2.1.1", "node-vibrant": "^4.0.4", + "pinia": "^2.3.1", "popper.js": "^1.16.1", "register-service-worker": "^1.7.2", "selectize": "^0.12.6", @@ -35,7 +36,6 @@ "vue-multiselect": "^2.1.8", "vue-trumbowyg": "^3.6.2", "vuedraggable": "^2.24.3", - "vuex": "^3.6.2", "zxcvbn": "^4.4.2" }, "devDependencies": { @@ -5786,6 +5786,12 @@ "dev": true, "license": "ISC" }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, "node_modules/@vue/test-utils": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.3.6.tgz", @@ -15017,6 +15023,54 @@ "node": ">=6" } }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", @@ -20207,7 +20261,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -20949,15 +21003,6 @@ "sortablejs": "1.10.2" } }, - "node_modules/vuex": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", - "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", - "license": "MIT", - "peerDependencies": { - "vue": "^2.0.0" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", diff --git a/package.json b/package.json index fb0653e72e..50fc60c881 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "luxon": "^1.28.1", "no-scroll": "^2.1.1", "node-vibrant": "^4.0.4", + "pinia": "^2.3.1", "popper.js": "^1.16.1", "register-service-worker": "^1.7.2", "selectize": "^0.12.6", @@ -60,7 +61,6 @@ "vue-multiselect": "^2.1.8", "vue-trumbowyg": "^3.6.2", "vuedraggable": "^2.24.3", - "vuex": "^3.6.2", "zxcvbn": "^4.4.2" }, "devDependencies": { diff --git a/templates/pages/about.html.twig b/templates/pages/about.html.twig index 1f62346b67..595a7f031d 100644 --- a/templates/pages/about.html.twig +++ b/templates/pages/about.html.twig @@ -72,7 +72,7 @@
  • Twig - flexible, fast, and secure template engine.
  • Bootstrap 4 and Font Awesome.
  • -
  • Vue and VueX.
  • +
  • Vue and Pinia.
  • CodeMirror.
  • Webpack and Symfony Encore.
  • diff --git a/tests/unit/README.md b/tests/unit/README.md index f0d4e7970a..a06f40ee1e 100644 --- a/tests/unit/README.md +++ b/tests/unit/README.md @@ -1,6 +1,6 @@ # Frontend unit tests -Vitest + `@vue/test-utils` over the Vue 2.7 admin components, the Vuex stores, +Vitest + `@vue/test-utils` over the Vue 2.7 admin components, the Pinia stores, the filters and the app-level scripts. `npm run test:unit` to run, `npm run test:unit:watch` while working, `npm run test:unit:coverage` for the gate CI enforces. @@ -31,8 +31,8 @@ regression baseline rather than a round of manual clicking. mount against Twig-rendered markup. The specs under `app/` are the exception: those scripts expect a whole page, so each lays out the markup it needs. 4. **Seed store state through `helpers/store.ts`**, then assert through the - rendered DOM or the spied `dispatch`. The factories build a real store from - the app's own modules, so real getters and mutations run. + rendered DOM or the recorded `writes()` and `calls()`. The factories build a + real Pinia from the app's own stores, so real getters and actions run. 5. **Vue warnings fail the test.** Two known defects are allowed for in `helpers/warnings.ts`; a spec that deliberately provokes another declares it with `expectVueWarning()`, which also asserts the warning happened. @@ -50,7 +50,7 @@ regression baseline rather than a round of manual clicking. setup.ts global setup: jQuery globals, jsdom gaps, per-test teardown helpers/ - store.ts Vuex store factories over the real modules + store.ts Pinia factories over the real stores stubs.ts third-party component stubs, keyed by template tag dom.ts the Twig-rendered surroundings components query warnings.ts Vue warning policy @@ -69,9 +69,9 @@ layer, so the upgrade edits them. Most of it is mechanical: | change | scope | |---|---| | `propsData:` → `props:` | every mount | -| `stubs:` / `localVue` / `store:` → `global: { stubs, plugins }` | every mount that has them | +| `stubs:` / `localVue` / `pinia:` → `global: { stubs, plugins }` | every mount that has them | | `wrapper.destroy()` → `unmount()`, `findAll(...).at(i)` → `[i]` | mechanical | -| Vuex → Pinia | `helpers/store.ts` and the specs that seed state | +| pinia 2 → 3, `PiniaVuePlugin` → `app.use(pinia)` | `helpers/store.ts` and the boot files | | `$root.$emit`/`$on` → the mitt bus | `helpers/bus.ts`, plus Text and Slug | | vue-multiselect 2→3, vuedraggable 2→4, trumbowyg 3→4, easymde | `helpers/stubs.ts` | | `flushPromises` | delete `helpers/async.ts`, import from test-utils | @@ -81,6 +81,5 @@ is why rule 1 matters. Keeping assertions at the DOM means the specs survive components becoming `