mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Collections module (#158)
* Update folder structure of module * Add barebones form structure * Add basic tabular layout implementation * Add test placeholders for collections module
This commit is contained in:
4
src/modules/collections/components/navigation/index.ts
Normal file
4
src/modules/collections/components/navigation/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import CollectionsNavigation from './navigation.vue';
|
||||
|
||||
export { CollectionsNavigation };
|
||||
export default CollectionsNavigation;
|
||||
@@ -1,10 +1,10 @@
|
||||
import CollectionsNavigation from './collections-navigation.vue';
|
||||
import CollectionsNavigation from './navigation.vue';
|
||||
import VueCompositionAPI from '@vue/composition-api';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import useNavigation from '../compositions/use-navigation';
|
||||
import useNavigation from '../../compositions/use-navigation';
|
||||
import VList, { VListItem, VListItemContent } from '@/components/v-list';
|
||||
|
||||
jest.mock('../compositions/use-navigation');
|
||||
jest.mock('../../compositions/use-navigation');
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(VueCompositionAPI);
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from '@vue/composition-api';
|
||||
import useNavigation from '../compositions/use-navigation';
|
||||
import useNavigation from '../../compositions/use-navigation';
|
||||
|
||||
export default defineComponent({
|
||||
props: {},
|
||||
@@ -1,5 +1,7 @@
|
||||
import CollectionsOverview from './routes/collections-overview.vue';
|
||||
import { defineModule } from '@/modules/define';
|
||||
import CollectionsOverview from './routes/overview/';
|
||||
import CollectionsBrowse from './routes/browse/';
|
||||
import CollectionsDetail from './routes/detail/';
|
||||
|
||||
export default defineModule({
|
||||
id: 'collections',
|
||||
@@ -9,6 +11,16 @@ export default defineModule({
|
||||
{
|
||||
path: '/',
|
||||
component: CollectionsOverview
|
||||
},
|
||||
{
|
||||
path: '/:collection',
|
||||
component: CollectionsBrowse,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/:collection/:primaryKey',
|
||||
component: CollectionsDetail,
|
||||
props: true
|
||||
}
|
||||
],
|
||||
icon: 'box'
|
||||
|
||||
21
src/modules/collections/routes/browse/browse.test.ts
Normal file
21
src/modules/collections/routes/browse/browse.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import VueCompositionAPI from '@vue/composition-api';
|
||||
import CollectionsBrowse from './browse.vue';
|
||||
import PrivateView from '@/views/private-view';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(VueCompositionAPI);
|
||||
localVue.component('private-view', PrivateView);
|
||||
|
||||
describe('Modules / Collections / Browse', () => {
|
||||
it('Renders', () => {
|
||||
const component = shallowMount(CollectionsBrowse, {
|
||||
localVue,
|
||||
propsData: {
|
||||
collection: 'my-test',
|
||||
primaryKey: 'id'
|
||||
}
|
||||
});
|
||||
expect(component.isVueInstance()).toBe(true);
|
||||
});
|
||||
});
|
||||
51
src/modules/collections/routes/browse/browse.vue
Normal file
51
src/modules/collections/routes/browse/browse.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<private-view v-if="currentCollection" :title="currentCollection.name">
|
||||
<template #actions>
|
||||
<v-button rounded icon style="--v-button-background-color: var(--success);">
|
||||
<v-icon name="add" />
|
||||
</v-button>
|
||||
<v-button rounded icon style="--v-button-background-color: var(--warning);">
|
||||
<v-icon name="delete" />
|
||||
</v-button>
|
||||
<v-button rounded icon style="--v-button-background-color: var(--danger);">
|
||||
<v-icon name="favorite" />
|
||||
</v-button>
|
||||
</template>
|
||||
|
||||
<template #navigation>
|
||||
<collections-navigation />
|
||||
</template>
|
||||
|
||||
<layout-tabular :collection="collection" />
|
||||
</private-view>
|
||||
<!-- @TODO: Render real 404 view here -->
|
||||
<p v-else>Not found</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from '@vue/composition-api';
|
||||
import useCollectionsStore from '@/stores/collections';
|
||||
import { Collection } from '@/stores/collections/types';
|
||||
import CollectionsNavigation from '../../components/navigation/';
|
||||
|
||||
export default defineComponent({
|
||||
components: { CollectionsNavigation },
|
||||
props: {
|
||||
collection: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const currentCollection = computed<Collection | null>(() => {
|
||||
return (
|
||||
collectionsStore.state.collections.find(
|
||||
collection => collection.collection === props.collection
|
||||
) || null
|
||||
);
|
||||
});
|
||||
return { currentCollection };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
4
src/modules/collections/routes/browse/index.ts
Normal file
4
src/modules/collections/routes/browse/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import CollectionsBrowse from './browse.vue';
|
||||
|
||||
export { CollectionsBrowse };
|
||||
export default CollectionsBrowse;
|
||||
21
src/modules/collections/routes/detail/detail.test.ts
Normal file
21
src/modules/collections/routes/detail/detail.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import VueCompositionAPI from '@vue/composition-api';
|
||||
import CollectionsDetail from './detail.vue';
|
||||
import PrivateView from '@/views/private-view';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(VueCompositionAPI);
|
||||
localVue.component('private-view', PrivateView);
|
||||
|
||||
describe('Modules / Collections / Detail', () => {
|
||||
it('Renders', () => {
|
||||
const component = shallowMount(CollectionsDetail, {
|
||||
localVue,
|
||||
propsData: {
|
||||
collection: 'my-test',
|
||||
primaryKey: 'id'
|
||||
}
|
||||
});
|
||||
expect(component.isVueInstance()).toBe(true);
|
||||
});
|
||||
});
|
||||
72
src/modules/collections/routes/detail/detail.vue
Normal file
72
src/modules/collections/routes/detail/detail.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<private-view title="Edit">
|
||||
<template v-if="item">
|
||||
<div style="margin-bottom: 20px" v-for="field in visibleFields" :key="field.field">
|
||||
<p>{{ field.name }}</p>
|
||||
<interface-text-input
|
||||
v-if="field.type === 'string'"
|
||||
:value="item[field.field]"
|
||||
:options="{}"
|
||||
/>
|
||||
<span v-else style="font-family: monospace;">{{ item[field.field] }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #navigation>
|
||||
<collections-navigation />
|
||||
</template>
|
||||
</private-view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref } from '@vue/composition-api';
|
||||
import useProjectsStore from '@/stores/projects';
|
||||
import useFieldsStore from '@/stores/fields';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
import api from '@/api';
|
||||
import CollectionsNavigation from '../../components/navigation/';
|
||||
|
||||
export default defineComponent({
|
||||
components: { CollectionsNavigation },
|
||||
props: {
|
||||
collection: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
primaryKey: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { currentProjectKey } = useProjectsStore().state;
|
||||
const fieldsStore = useFieldsStore();
|
||||
const fieldsInCurrentCollection = computed<Field[]>(() => {
|
||||
return fieldsStore.state.fields.filter(field => field.collection === props.collection);
|
||||
});
|
||||
const visibleFields = computed<Field[]>(() => {
|
||||
return fieldsInCurrentCollection.value
|
||||
.filter(field => field.hidden_browse === false)
|
||||
.sort((a, b) => (a.sort || Infinity) - (b.sort || Infinity));
|
||||
});
|
||||
const item = ref(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
fetchItem();
|
||||
return { visibleFields, item, loading, error };
|
||||
async function fetchItem() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await api.get(
|
||||
`/${currentProjectKey}/items/${props.collection}/${props.primaryKey}`
|
||||
);
|
||||
item.value = response.data.data;
|
||||
} catch (error) {
|
||||
error.value = error;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
4
src/modules/collections/routes/detail/index.ts
Normal file
4
src/modules/collections/routes/detail/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import CollectionsDetail from './detail.vue';
|
||||
|
||||
export { CollectionsDetail };
|
||||
export default CollectionsDetail;
|
||||
4
src/modules/collections/routes/overview/index.ts
Normal file
4
src/modules/collections/routes/overview/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import CollectionsOverview from './overview.vue';
|
||||
|
||||
export { CollectionsOverview };
|
||||
export default CollectionsOverview;
|
||||
@@ -1,12 +1,12 @@
|
||||
import CollectionsOverview from './collections-overview.vue';
|
||||
import CollectionsOverview from './overview.vue';
|
||||
import VueCompositionAPI from '@vue/composition-api';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import useNavigation from '../compositions/use-navigation';
|
||||
import useNavigation from '../../compositions/use-navigation';
|
||||
import VTable from '@/components/v-table';
|
||||
import PrivateView from '@/views/private-view/';
|
||||
import router from '@/router';
|
||||
|
||||
jest.mock('../compositions/use-navigation');
|
||||
jest.mock('../../compositions/use-navigation');
|
||||
jest.mock('@/router');
|
||||
|
||||
const localVue = createLocalVue();
|
||||
@@ -4,18 +4,6 @@
|
||||
<v-button rounded disabled icon secondary><v-icon name="box" /></v-button>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<v-button rounded icon style="--v-button-background-color: var(--success);">
|
||||
<v-icon name="add" />
|
||||
</v-button>
|
||||
<v-button rounded icon style="--v-button-background-color: var(--warning);">
|
||||
<v-icon name="delete" />
|
||||
</v-button>
|
||||
<v-button rounded icon style="--v-button-background-color: var(--danger);">
|
||||
<v-icon name="favorite" />
|
||||
</v-button>
|
||||
</template>
|
||||
|
||||
<template #navigation>
|
||||
<collections-navigation />
|
||||
</template>
|
||||
@@ -30,9 +18,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from '@vue/composition-api';
|
||||
import CollectionsNavigation from '../components/collections-navigation.vue';
|
||||
import CollectionsNavigation from '../../components/navigation/';
|
||||
import { i18n } from '@/lang';
|
||||
import useNavigation, { NavItem } from '../compositions/use-navigation';
|
||||
import useNavigation, { NavItem } from '../../compositions/use-navigation';
|
||||
import router from '@/router';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -58,11 +46,8 @@ export default defineComponent({
|
||||
value: 'note'
|
||||
}
|
||||
];
|
||||
|
||||
const { navItems } = useNavigation();
|
||||
|
||||
return { tableHeaders, navItems, navigateToCollection };
|
||||
|
||||
function navigateToCollection(navItem: NavItem) {
|
||||
router.push(navItem.to);
|
||||
}
|
||||
Reference in New Issue
Block a user