mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Add layout registration logic (#120)
* Move modules types into extensions file * Rename modules store to extensions store * Start on registering tabular * Add register component util * Register layouts * Build bundle for modern browsers Snuck this commit into the wrong branch, just because I can * Add tests for layout registration + add dummy tabular
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
"author": "Rijk van Zanten <rijk@rngr.org>",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"build": "vue-cli-service build --modern",
|
||||
"test": "vue-cli-service test:unit",
|
||||
"lint": "vue-cli-service lint",
|
||||
"lint:styles": "stylelint \"**/*.{vue,scss}\"",
|
||||
|
||||
84
src/layouts/register.test.ts
Normal file
84
src/layouts/register.test.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import Vue from 'vue';
|
||||
import VueCompositionAPI from '@vue/composition-api';
|
||||
import { LayoutConfig } from '@/types/extensions';
|
||||
import layoutRegistration from './register';
|
||||
import * as registerUtil from '@/utils/register-component';
|
||||
import { useExtensionsStore } from '@/stores/extensions';
|
||||
|
||||
describe('Layouts / Register', () => {
|
||||
beforeAll(() => {
|
||||
Vue.config.productionTip = false;
|
||||
Vue.config.devtools = false;
|
||||
Vue.use(VueCompositionAPI);
|
||||
});
|
||||
|
||||
it('Calls registerComponent util', () => {
|
||||
jest.spyOn(registerUtil, 'registerComponent');
|
||||
|
||||
const testLayout: LayoutConfig = {
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
icon: 'test',
|
||||
component: {
|
||||
render(h) {
|
||||
return h('div');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
layoutRegistration.registerLayout(testLayout);
|
||||
|
||||
expect(registerUtil.registerComponent).toHaveBeenCalledWith(
|
||||
'layout-test',
|
||||
testLayout.component
|
||||
);
|
||||
});
|
||||
|
||||
it('Adds the layout to the extensions store', () => {
|
||||
const extensionsStore = useExtensionsStore({});
|
||||
extensionsStore.state.layouts = [];
|
||||
|
||||
const testLayout: LayoutConfig = {
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
icon: 'test',
|
||||
component: {
|
||||
render(h) {
|
||||
return h('div');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
layoutRegistration.registerLayout(testLayout);
|
||||
|
||||
expect(extensionsStore.state.layouts).toEqual([
|
||||
{
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
icon: 'test'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('Calls the name function if its a method', () => {
|
||||
const testLayout: LayoutConfig = {
|
||||
id: 'test',
|
||||
name: jest.fn(),
|
||||
icon: 'test',
|
||||
component: {
|
||||
render(h) {
|
||||
return h('div');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
layoutRegistration.registerLayout(testLayout);
|
||||
expect(testLayout.name).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Registers all global layouts', () => {
|
||||
jest.spyOn(layoutRegistration, 'registerLayout');
|
||||
layoutRegistration.registerGlobalLayouts();
|
||||
expect(layoutRegistration.registerLayout).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
31
src/layouts/register.ts
Normal file
31
src/layouts/register.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import TabularLayout from './tabular/';
|
||||
import { LayoutConfig } from '@/types/extensions';
|
||||
import { registerComponent } from '@/utils/register-component';
|
||||
import { useExtensionsStore } from '@/stores/extensions';
|
||||
import { i18n } from '@/lang';
|
||||
|
||||
const lib = {
|
||||
registerLayout,
|
||||
registerGlobalLayouts
|
||||
};
|
||||
|
||||
export default lib;
|
||||
|
||||
export function registerLayout(config: LayoutConfig) {
|
||||
const extensionsStore = useExtensionsStore();
|
||||
registerComponent(`layout-${config.id}`, config.component);
|
||||
|
||||
const name = typeof config.name === 'function' ? config.name(i18n) : config.name;
|
||||
|
||||
const layoutForStore = {
|
||||
id: config.id,
|
||||
name: name,
|
||||
icon: config.icon
|
||||
};
|
||||
|
||||
extensionsStore.state.layouts = [...extensionsStore.state.layouts, layoutForStore];
|
||||
}
|
||||
|
||||
export function registerGlobalLayouts() {
|
||||
[TabularLayout].forEach(lib.registerLayout);
|
||||
}
|
||||
11
src/layouts/tabular/index.ts
Normal file
11
src/layouts/tabular/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { LayoutConfig } from '@/types/extensions';
|
||||
import Component from './tabular.vue';
|
||||
|
||||
const TabularLayout: LayoutConfig = {
|
||||
id: 'tabular',
|
||||
name: 'Tabular',
|
||||
icon: 'box',
|
||||
component: Component
|
||||
};
|
||||
|
||||
export default TabularLayout;
|
||||
22
src/layouts/tabular/tabular.vue
Normal file
22
src/layouts/tabular/tabular.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="tabular-layout">
|
||||
Tabular
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { createComponent } from '@vue/composition-api';
|
||||
|
||||
export default createComponent({
|
||||
props: {},
|
||||
setup() {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tabular-layout {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
@@ -6,10 +6,12 @@ import './directives/register';
|
||||
import './components/register';
|
||||
import './views/register';
|
||||
import { registerGlobalModules } from './modules/register';
|
||||
import { registerGlobalLayouts } from './layouts/register';
|
||||
import router from './router';
|
||||
import i18n from './lang/';
|
||||
|
||||
registerGlobalModules();
|
||||
registerGlobalLayouts();
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ModuleConfig } from '@/types/modules';
|
||||
import { ModuleConfig } from '@/types/extensions';
|
||||
import Collections from './collections.vue';
|
||||
|
||||
const config: ModuleConfig = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ModuleConfig } from '@/types/modules';
|
||||
import { ModuleConfig } from '@/types/extensions';
|
||||
import Files from './files.vue';
|
||||
|
||||
const config: ModuleConfig = {
|
||||
|
||||
@@ -3,8 +3,8 @@ import VueCompositionAPI from '@vue/composition-api';
|
||||
import { RouteConfig } from 'vue-router';
|
||||
import * as router from '@/router';
|
||||
import moduleRegistration from './register';
|
||||
import { useModulesStore } from '@/stores/modules';
|
||||
import { ModuleConfig } from '@/types/modules';
|
||||
import { useExtensionsStore } from '@/stores/extensions';
|
||||
import { ModuleConfig } from '@/types/extensions';
|
||||
|
||||
describe('Modules / Register', () => {
|
||||
beforeAll(() => {
|
||||
@@ -38,7 +38,7 @@ describe('Modules / Register', () => {
|
||||
});
|
||||
|
||||
it('Adds the modules to the store', () => {
|
||||
const modulesStore = useModulesStore({});
|
||||
const extensionsStore = useExtensionsStore({});
|
||||
|
||||
const testModules: ModuleConfig[] = [
|
||||
{
|
||||
@@ -55,7 +55,7 @@ describe('Modules / Register', () => {
|
||||
|
||||
moduleRegistration.registerModules(testModules);
|
||||
|
||||
expect(modulesStore.state.modules).toEqual([
|
||||
expect(extensionsStore.state.modules).toEqual([
|
||||
{
|
||||
id: 'test',
|
||||
icon: 'box',
|
||||
|
||||
@@ -3,9 +3,9 @@ import FilesModule from './files/';
|
||||
import SettingsModule from './settings/';
|
||||
import UsersModule from './users/';
|
||||
import { RouteConfig } from 'vue-router';
|
||||
import { ModuleConfig, Module } from '@/types/modules';
|
||||
import { ModuleConfig, Module } from '@/types/extensions';
|
||||
import { replaceRoutes } from '@/router';
|
||||
import useModulesStore from '@/stores/modules';
|
||||
import useExtensionsStore from '@/stores/extensions';
|
||||
import { i18n } from '@/lang';
|
||||
|
||||
const lib = {
|
||||
@@ -23,7 +23,7 @@ export function insertBeforeProjectWildcard(routes: RouteConfig[], moduleRoutes:
|
||||
}
|
||||
|
||||
export function registerModules(modules: ModuleConfig[]) {
|
||||
const modulesStore = useModulesStore();
|
||||
const extensionsStore = useExtensionsStore();
|
||||
|
||||
/** @todo
|
||||
* This is where we will download the module definitions for custom modules
|
||||
@@ -51,7 +51,7 @@ export function registerModules(modules: ModuleConfig[]) {
|
||||
};
|
||||
});
|
||||
|
||||
modulesStore.state.modules = modulesForStore;
|
||||
extensionsStore.state.modules = modulesForStore;
|
||||
}
|
||||
|
||||
export function registerGlobalModules() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ModuleConfig } from '@/types/modules';
|
||||
import { ModuleConfig } from '@/types/extensions';
|
||||
import Settings from './settings.vue';
|
||||
|
||||
const config: ModuleConfig = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ModuleConfig } from '@/types/modules';
|
||||
import { ModuleConfig } from '@/types/extensions';
|
||||
import Users from './users.vue';
|
||||
|
||||
const config: ModuleConfig = {
|
||||
|
||||
10
src/stores/extensions/extensions.ts
Normal file
10
src/stores/extensions/extensions.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { createStore } from 'pinia';
|
||||
import { Module, Layout } from '@/types/extensions';
|
||||
|
||||
export const useExtensionsStore = createStore({
|
||||
id: 'extensions',
|
||||
state: () => ({
|
||||
modules: [] as Module[],
|
||||
layouts: [] as Layout[]
|
||||
})
|
||||
});
|
||||
4
src/stores/extensions/index.ts
Normal file
4
src/stores/extensions/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { useExtensionsStore } from './extensions';
|
||||
|
||||
export { useExtensionsStore };
|
||||
export default useExtensionsStore;
|
||||
@@ -1,4 +0,0 @@
|
||||
import { useModulesStore } from './modules';
|
||||
|
||||
export { useModulesStore };
|
||||
export default useModulesStore;
|
||||
@@ -1,9 +0,0 @@
|
||||
import { createStore } from 'pinia';
|
||||
import { Module } from '@/types/modules';
|
||||
|
||||
export const useModulesStore = createStore({
|
||||
id: 'modules',
|
||||
state: () => ({
|
||||
modules: [] as Module[]
|
||||
})
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component } from 'vue';
|
||||
import Vuei18n from 'vue-i18n';
|
||||
import { RouteConfig } from 'vue-router';
|
||||
import { i18n } from '@/lang/';
|
||||
|
||||
export type Module = {
|
||||
id: string;
|
||||
@@ -14,3 +14,16 @@ export type ModuleConfig = {
|
||||
icon: string;
|
||||
name: string | ((i18n: Vuei18n) => Vuei18n.TranslateResult);
|
||||
};
|
||||
|
||||
export type Layout = {
|
||||
id: string;
|
||||
icon: string;
|
||||
name: string | Vuei18n.TranslateResult;
|
||||
};
|
||||
|
||||
export type LayoutConfig = {
|
||||
id: string;
|
||||
icon: string;
|
||||
name: string | ((i18n: Vuei18n) => Vuei18n.TranslateResult);
|
||||
component: Component;
|
||||
};
|
||||
15
src/utils/register-component.test.ts
Normal file
15
src/utils/register-component.test.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import Vue, { Component } from 'vue';
|
||||
import registerComponent from './register-component';
|
||||
|
||||
describe('Utils / Register Component', () => {
|
||||
it('Calls Vue.component with the given arguments', () => {
|
||||
const spy = jest.spyOn(Vue, 'component');
|
||||
const component: Component = {
|
||||
render(h) {
|
||||
return h('div');
|
||||
}
|
||||
};
|
||||
registerComponent('test', component);
|
||||
expect(spy).toHaveBeenCalledWith('test', component);
|
||||
});
|
||||
});
|
||||
10
src/utils/register-component.ts
Normal file
10
src/utils/register-component.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import Vue, { Component } from 'vue';
|
||||
|
||||
function registerComponent(id: string, component: Component): void;
|
||||
function registerComponent(id: string, component: Parameters<typeof Vue.component>[1]): void;
|
||||
function registerComponent(id: string, component: any) {
|
||||
Vue.component(id, component);
|
||||
}
|
||||
|
||||
export { registerComponent };
|
||||
export default registerComponent;
|
||||
@@ -10,7 +10,7 @@
|
||||
<script lang="ts">
|
||||
import { createComponent, computed } from '@vue/composition-api';
|
||||
import ModuleBarLogo from './_module-bar-logo.vue';
|
||||
import { useModulesStore } from '@/stores/modules/';
|
||||
import { useExtensionsStore } from '@/stores/extensions/';
|
||||
import { useProjectsStore } from '@/stores/projects';
|
||||
|
||||
export default createComponent({
|
||||
@@ -18,12 +18,12 @@ export default createComponent({
|
||||
ModuleBarLogo
|
||||
},
|
||||
setup() {
|
||||
const modulesStore = useModulesStore();
|
||||
const extensionsStore = useExtensionsStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const { currentProjectKey } = projectsStore.state;
|
||||
|
||||
const modules = computed(() =>
|
||||
modulesStore.state.modules.map(module => ({
|
||||
extensionsStore.state.modules.map(module => ({
|
||||
...module,
|
||||
to: `/${currentProjectKey}/${module.id}/`
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user