1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-17 02:17:49 +00:00

🌈 5️⃣ - Row coloring v5 - Add color to Gallery and Kanban view types

This commit is contained in:
Jrmi 2022-05-17 06:49:29 +00:00
parent df48bf7986
commit 224d5dfee3
30 changed files with 2234 additions and 1102 deletions

View file

@ -290,6 +290,7 @@ class GalleryViewType(ViewType):
api_exceptions_map = { api_exceptions_map = {
FieldNotInTable: ERROR_FIELD_NOT_IN_TABLE, FieldNotInTable: ERROR_FIELD_NOT_IN_TABLE,
} }
can_decorate = True
def get_api_urls(self): def get_api_urls(self):
from baserow.contrib.database.api.views.gallery import urls as api_urls from baserow.contrib.database.api.views.gallery import urls as api_urls

View file

@ -2,6 +2,8 @@
## Unreleased ## Unreleased
* Added row coloring for Kanban and Gallery views
## Released (2022-10-05 1.10.0) ## Released (2022-10-05 1.10.0)
* Added batch create/update/delete rows endpoints. These endpoints make it possible to * Added batch create/update/delete rows endpoints. These endpoints make it possible to

View file

@ -52,6 +52,7 @@ class KanbanViewType(ViewType):
), ),
FieldNotInTable: ERROR_FIELD_NOT_IN_TABLE, FieldNotInTable: ERROR_FIELD_NOT_IN_TABLE,
} }
can_decorate = True
def get_api_urls(self): def get_api_urls(self):
from baserow_premium.api.views.kanban import urls as api_urls from baserow_premium.api.views.kanban import urls as api_urls

View file

@ -1,8 +1,10 @@
.left-border-decorator { .left-border-decorator {
width: 6px; width: 6px;
height: 24px; align-self: stretch;
margin: 0 4px; flex-shrink: 0;
flex-grow: 0;
border-radius: 6px; border-radius: 6px;
margin: 4px;
@include add-shadow-on-same-background(); @include add-shadow-on-same-background();
} }

View file

@ -76,6 +76,7 @@
v-show="slot.position != -1" v-show="slot.position != -1"
:key="'card-' + slot.id" :key="'card-' + slot.id"
:fields="cardFields" :fields="cardFields"
:decorations-by-place="decorationsByPlace"
:row="slot.row" :row="slot.row"
:cover-image-field="coverImageField" :cover-image-field="coverImageField"
:style="{ :style="{
@ -138,11 +139,12 @@ import InfiniteScroll from '@baserow/modules/core/components/helpers/InfiniteScr
import { populateRow } from '@baserow_premium/store/view/kanban' import { populateRow } from '@baserow_premium/store/view/kanban'
import KanbanViewStackContext from '@baserow_premium/components/views/kanban/KanbanViewStackContext' import KanbanViewStackContext from '@baserow_premium/components/views/kanban/KanbanViewStackContext'
import { getCardHeight } from '@baserow/modules/database/utils/card' import { getCardHeight } from '@baserow/modules/database/utils/card'
import viewDecoration from '@baserow/modules/database/mixins/viewDecoration'
export default { export default {
name: 'KanbanViewStack', name: 'KanbanViewStack',
components: { InfiniteScroll, RowCard, KanbanViewStackContext }, components: { InfiniteScroll, RowCard, KanbanViewStackContext },
mixins: [kanbanViewHelper], mixins: [kanbanViewHelper, viewDecoration],
props: { props: {
option: { option: {
validator: (prop) => typeof prop === 'object' || prop === null, validator: (prop) => typeof prop === 'object' || prop === null,

View file

@ -107,7 +107,11 @@ export class ConditionalColorValueProviderType extends DecoratorValueProviderTyp
getValue({ options, fields, row }) { getValue({ options, fields, row }) {
const { $registry } = this.app const { $registry } = this.app
for (const { color, filters, operator } of options.colors) { for (const { color, filters, operator } of options.colors) {
if (matchSearchFilters($registry, operator, filters, fields, row)) { if (
row.id !== -1 &&
row.id !== undefined &&
matchSearchFilters($registry, operator, filters, fields, row)
) {
return color return color
} }
} }

View file

@ -1,6 +1,13 @@
import { ViewDecoratorType } from '@baserow/modules/database/viewDecorators' import { ViewDecoratorType } from '@baserow/modules/database/viewDecorators'
import { PremiumPlugin } from '@baserow_premium/plugins' import { PremiumPlugin } from '@baserow_premium/plugins'
import {
GridViewType,
GalleryViewType,
} from '@baserow/modules/database/viewTypes'
import { KanbanViewType } from './viewTypes'
import leftBorderDecoratorImage from '@baserow_premium/assets/images/leftBorderDecorator.svg' import leftBorderDecoratorImage from '@baserow_premium/assets/images/leftBorderDecorator.svg'
import backgroundDecoratorImage from '@baserow_premium/assets/images/backgroundDecorator.svg' import backgroundDecoratorImage from '@baserow_premium/assets/images/backgroundDecorator.svg'
@ -71,7 +78,13 @@ export class LeftBorderColorViewDecoratorType extends ViewDecoratorType {
isCompatible(view) { isCompatible(view) {
const { store } = this.app const { store } = this.app
return ['grid'].includes(view.type) && !store.getters['view/grid/isPublic'] return (
[
GridViewType.getType(),
GalleryViewType.getType(),
KanbanViewType.getType(),
].includes(view.type) && !store.getters['view/grid/isPublic']
)
} }
} }
@ -114,7 +127,13 @@ export class BackgroundColorViewDecoratorType extends ViewDecoratorType {
isCompatible(view) { isCompatible(view) {
const { store } = this.app const { store } = this.app
return ['grid'].includes(view.type) && !store.getters['view/grid/isPublic'] return (
[
GridViewType.getType(),
GalleryViewType.getType(),
KanbanViewType.getType(),
].includes(view.type) && !store.getters['view/grid/isPublic']
)
} }
getDeactivatedText() { getDeactivatedText() {

View file

@ -68,8 +68,13 @@
background-position: center center; background-position: center center;
} }
.card__content {
display: flex;
}
.card__fields { .card__fields {
padding: 16px 0; padding: 16px 0;
overflow: auto;
} }
.card__field { .card__field {

View file

@ -6,4 +6,5 @@
@extend %ellipsis; @extend %ellipsis;
@include select-option-style(inline-block); @include select-option-style(inline-block);
@include add-shadow-on-same-background();
} }

View file

@ -6,41 +6,65 @@
@mousemove="$emit('mousemove', $event)" @mousemove="$emit('mousemove', $event)"
@mouseenter="$emit('mouseenter', $event)" @mouseenter="$emit('mouseenter', $event)"
> >
<div v-if="coverImageField !== null" class="card__cover"> <RecursiveWrapper
<div :components="
v-if="coverImageUrl !== null" wrapperDecorations.map((comp) => ({
class="card__cover-image" ...comp,
:style="{ props: comp.propsFn(row),
'background-image': 'url(' + coverImageUrl + ')', }))
}" "
></div> >
</div> <div v-if="coverImageField !== null" class="card__cover">
<div class="card__fields"> <div
<div v-for="field in fields" :key="field.id" class="card__field"> v-if="coverImageUrl !== null"
<div class="card__field-name">{{ field.name }}</div> class="card__cover-image"
<div class="card__field-value"> :style="{
<component 'background-image': 'url(' + coverImageUrl + ')',
:is="getCardComponent(field)" }"
v-if="!loading" ></div>
:field="field" </div>
:value="row['field_' + field.id]" <div class="card__content">
/> <component
:is="dec.component"
v-for="dec in firstCellDecorations"
:key="dec.decoration.id"
v-bind="dec.propsFn(row)"
/>
<div class="card__fields">
<div v-for="field in fields" :key="field.id" class="card__field">
<div class="card__field-name">{{ field.name }}</div>
<div class="card__field-value">
<component
:is="getCardComponent(field)"
v-if="!loading"
:field="field"
:value="row['field_' + field.id]"
/>
</div>
</div>
</div> </div>
</div> </div>
</div> </RecursiveWrapper>
</div> </div>
</template> </template>
<script> <script>
import { FileFieldType } from '@baserow/modules/database/fieldTypes' import { FileFieldType } from '@baserow/modules/database/fieldTypes'
import RecursiveWrapper from '@baserow/modules/database/components/RecursiveWrapper'
export default { export default {
name: 'RowCard', name: 'RowCard',
components: { RecursiveWrapper },
props: { props: {
fields: { fields: {
type: Array, type: Array,
required: true, required: true,
}, },
decorationsByPlace: {
type: Object,
required: false,
default: () => {},
},
row: { row: {
type: Object, type: Object,
required: false, required: false,
@ -80,6 +104,12 @@ export default {
return image.thumbnails.card_cover.url return image.thumbnails.card_cover.url
}, },
firstCellDecorations() {
return this.decorationsByPlace?.first_cell || []
},
wrapperDecorations() {
return this.decorationsByPlace?.wrapper || []
},
}, },
methods: { methods: {
getCardComponent(field) { getCardComponent(field) {

View file

@ -17,11 +17,11 @@
<ViewDecoratorItem :decorator-type="dec.decoratorType" /> <ViewDecoratorItem :decorator-type="dec.decoratorType" />
</div> </div>
<div <div
v-show="dec.decoration.value_provider_type" v-show="dec.valueProviderType"
class="decorator-context__decorator-header-select" class="decorator-context__decorator-header-select"
> >
<Picker <Picker
v-if="dec.decoration.value_provider_type" v-if="dec.valueProviderType"
:icon="dec.valueProviderType.getIconClass()" :icon="dec.valueProviderType.getIconClass()"
:name="dec.valueProviderType.getName()" :name="dec.valueProviderType.getName()"
@select="selectValueProvider(dec.decoration, $event)" @select="selectValueProvider(dec.decoration, $event)"
@ -104,6 +104,7 @@
<script> <script>
import context from '@baserow/modules/core/mixins/context' import context from '@baserow/modules/core/mixins/context'
import viewDecoration from '@baserow/modules/database/mixins/viewDecoration'
import ViewDecoratorList from '@baserow/modules/database/components/view/ViewDecoratorList' import ViewDecoratorList from '@baserow/modules/database/components/view/ViewDecoratorList'
import ViewDecoratorItem from '@baserow/modules/database/components/view/ViewDecoratorItem' import ViewDecoratorItem from '@baserow/modules/database/components/view/ViewDecoratorItem'
import SelectViewDecoratorContext from '@baserow/modules/database/components/view/SelectViewDecoratorContext' import SelectViewDecoratorContext from '@baserow/modules/database/components/view/SelectViewDecoratorContext'
@ -120,7 +121,7 @@ export default {
SelectViewDecoratorContext, SelectViewDecoratorContext,
DecoratorValueProviderList, DecoratorValueProviderList,
}, },
mixins: [context], mixins: [context, viewDecoration],
props: { props: {
primary: { primary: {
type: Object, type: Object,
@ -143,42 +144,6 @@ export default {
required: true, required: true,
}, },
}, },
computed: {
allFields() {
return [this.primary, ...this.fields]
},
augmentedDecorations() {
return this.view.decorations.map((decoration) => {
const deco = { decoration }
deco.decoratorType = this.$registry.get(
'viewDecorator',
decoration.type
)
if (decoration.value_provider_type) {
deco.valueProviderType = this.$registry.get(
'decoratorValueProvider',
decoration.value_provider_type
)
}
deco.availableValueProviderTypes = this.$registry
.getOrderedList('decoratorValueProvider')
.filter((valueProviderType) =>
valueProviderType.isCompatible(deco.decoratorType)
)
deco.isDeactivated = deco.decoratorType.isDeactivated({
view: this.view,
})
return deco
})
},
activeDecorations() {
return this.augmentedDecorations.filter((deco) => !deco.isDeactivated)
},
},
methods: { methods: {
async removeDecoration(decoration) { async removeDecoration(decoration) {
try { try {
@ -202,7 +167,7 @@ export default {
value_provider_type: valueProviderType.getType(), value_provider_type: valueProviderType.getType(),
value_provider_conf: valueProviderType.getDefaultConfiguration({ value_provider_conf: valueProviderType.getDefaultConfiguration({
view: this.view, view: this.view,
fields: this.allFields, fields: this.allTableFields,
}), }),
}, },
decoration, decoration,

View file

@ -33,6 +33,7 @@
:row="slot.item || {}" :row="slot.item || {}"
:loading="slot.item === null" :loading="slot.item === null"
:cover-image-field="coverImageField" :cover-image-field="coverImageField"
:decorations-by-place="decorationsByPlace"
class="gallery-view__card" class="gallery-view__card"
:style="{ :style="{
width: cardWidth + 'px', width: cardWidth + 'px',
@ -93,11 +94,12 @@ import RowCreateModal from '@baserow/modules/database/components/row/RowCreateMo
import RowEditModal from '@baserow/modules/database/components/row/RowEditModal' import RowEditModal from '@baserow/modules/database/components/row/RowEditModal'
import bufferedRowsDragAndDrop from '@baserow/modules/database/mixins/bufferedRowsDragAndDrop' import bufferedRowsDragAndDrop from '@baserow/modules/database/mixins/bufferedRowsDragAndDrop'
import viewHelpers from '@baserow/modules/database/mixins/viewHelpers' import viewHelpers from '@baserow/modules/database/mixins/viewHelpers'
import viewDecoration from '@baserow/modules/database/mixins/viewDecoration'
export default { export default {
name: 'GalleryView', name: 'GalleryView',
components: { RowCard, RowCreateModal, RowEditModal }, components: { RowCard, RowCreateModal, RowEditModal },
mixins: [viewHelpers, bufferedRowsDragAndDrop], mixins: [viewHelpers, bufferedRowsDragAndDrop, viewDecoration],
props: { props: {
primary: { primary: {
type: Object, type: Object,

View file

@ -16,7 +16,7 @@
ref="left" ref="left"
class="grid-view__left" class="grid-view__left"
:fields="leftFields" :fields="leftFields"
:all-table-fields="allTableFields" :decorations-by-place="decorationsByPlace"
:table="table" :table="table"
:view="view" :view="view"
:include-field-width-handles="false" :include-field-width-handles="false"
@ -66,7 +66,7 @@
ref="right" ref="right"
class="grid-view__right" class="grid-view__right"
:fields="visibleFields" :fields="visibleFields"
:all-table-fields="allTableFields" :decorations-by-place="decorationsByPlace"
:table="table" :table="table"
:view="view" :view="view"
:include-add-field="true" :include-add-field="true"
@ -188,6 +188,7 @@ import gridViewHelpers from '@baserow/modules/database/mixins/gridViewHelpers'
import { maxPossibleOrderValue } from '@baserow/modules/database/viewTypes' import { maxPossibleOrderValue } from '@baserow/modules/database/viewTypes'
import viewHelpers from '@baserow/modules/database/mixins/viewHelpers' import viewHelpers from '@baserow/modules/database/mixins/viewHelpers'
import { isElement } from '@baserow/modules/core/utils/dom' import { isElement } from '@baserow/modules/core/utils/dom'
import viewDecoration from '@baserow/modules/database/mixins/viewDecoration'
export default { export default {
name: 'GridView', name: 'GridView',
@ -197,7 +198,7 @@ export default {
GridViewRowDragging, GridViewRowDragging,
RowEditModal, RowEditModal,
}, },
mixins: [viewHelpers, gridViewHelpers], mixins: [viewHelpers, gridViewHelpers, viewDecoration],
props: { props: {
primary: { primary: {
type: Object, type: Object,
@ -280,9 +281,6 @@ export default {
leftWidth() { leftWidth() {
return this.leftFieldsWidth + this.gridViewRowDetailsWidth return this.leftFieldsWidth + this.gridViewRowDetailsWidth
}, },
allTableFields() {
return [this.primary, ...this.fields]
},
}, },
watch: { watch: {
fieldOptions: { fieldOptions: {

View file

@ -128,10 +128,9 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
decorations: { decorationsByPlace: {
type: Array, type: Object,
required: false, required: true,
default: () => [],
}, },
allFields: { allFields: {
type: Array, type: Array,
@ -212,10 +211,10 @@ export default {
return fields return fields
}, },
firstCellDecorations() { firstCellDecorations() {
return this.decorations.filter(({ place }) => place === 'first_cell') return this.decorationsByPlace?.first_cell || []
}, },
wrapperDecorations() { wrapperDecorations() {
return this.decorations.filter(({ place }) => place === 'wrapper') return this.decorationsByPlace?.wrapper || []
}, },
}, },
methods: { methods: {

View file

@ -13,7 +13,7 @@
:all-fields="allFields" :all-fields="allFields"
:field-widths="fieldWidths" :field-widths="fieldWidths"
:include-row-details="includeRowDetails" :include-row-details="includeRowDetails"
:decorations="augmentedDecorations" :decorations-by-place="decorationsByPlace"
:read-only="readOnly" :read-only="readOnly"
:can-drag="view.sortings.length === 0" :can-drag="view.sortings.length === 0"
:store-prefix="storePrefix" :store-prefix="storePrefix"
@ -41,8 +41,8 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
allTableFields: { decorationsByPlace: {
type: Array, type: Object,
required: true, required: true,
}, },
leftOffset: { leftOffset: {
@ -72,42 +72,6 @@ export default {
}) })
return fieldWidths return fieldWidths
}, },
augmentedDecorations() {
return this.view.decorations
.filter(({ value_provider_type: valPro }) => valPro)
.map((decoration) => {
const deco = { decoration }
deco.decorationType = this.$registry.get(
'viewDecorator',
decoration.type
)
deco.component = deco.decorationType.getComponent()
deco.place = deco.decorationType.getPlace()
deco.valueProviderType = this.$registry.get(
'decoratorValueProvider',
decoration.value_provider_type
)
deco.propsFn = (row) => {
return {
value: deco.valueProviderType.getValue({
row,
fields: this.allTableFields,
options: decoration.value_provider_conf,
}),
}
}
return deco
})
.filter(
({ decorationType }) =>
!decorationType.isDeactivated({ view: this.view })
)
},
}, },
beforeCreate() { beforeCreate() {
this.$options.computed = { this.$options.computed = {

View file

@ -52,7 +52,7 @@
:view="view" :view="view"
:fields="fieldsToRender" :fields="fieldsToRender"
:all-fields="fields" :all-fields="fields"
:all-table-fields="allTableFields" :decorations-by-place="decorationsByPlace"
:left-offset="fieldsLeftOffset" :left-offset="fieldsLeftOffset"
:include-row-details="includeRowDetails" :include-row-details="includeRowDetails"
:read-only="readOnly" :read-only="readOnly"
@ -127,8 +127,8 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
allTableFields: { decorationsByPlace: {
type: Array, type: Object,
required: true, required: true,
}, },
table: { table: {

View file

@ -0,0 +1,75 @@
/**
* A mixin that can be used in combination with the view filter input components. If
* contains the expected props and it has a computed property that finds the field
* object related to filter field id.
*/
export default {
props: {
view: {
type: Object,
required: false,
default: undefined,
},
fields: {
type: Array,
required: true,
},
primary: {
type: Object,
required: true,
},
},
computed: {
allTableFields() {
return [this.primary, ...this.fields]
},
activeDecorations() {
return this.view.decorations
.map((decoration) => {
const deco = { decoration }
deco.decoratorType = this.$registry.get(
'viewDecorator',
decoration.type
)
deco.component = deco.decoratorType.getComponent()
deco.place = deco.decoratorType.getPlace()
if (decoration.value_provider_type) {
deco.valueProviderType = this.$registry.get(
'decoratorValueProvider',
decoration.value_provider_type
)
deco.propsFn = (row) => {
return {
value: deco.valueProviderType.getValue({
row,
fields: this.allTableFields,
options: decoration.value_provider_conf,
}),
}
}
}
return deco
})
.filter(
({ decoratorType }) =>
!decoratorType.isDeactivated({ view: this.view })
)
},
decorationsByPlace() {
return this.activeDecorations
.filter(({ valueProviderType }) => valueProviderType)
.reduce((prev, deco) => {
if (deco.valueProviderType) {
const decType = deco.decoratorType.getPlace()
prev[decType] = [...(prev[decType] || []), deco]
}
return prev
}, {})
},
},
}

49
web-frontend/test/fixtures/gallery.js vendored Normal file
View file

@ -0,0 +1,49 @@
export function createGalleryView(
mock,
application,
table,
{
viewType = 'gallery',
viewId = 1,
filters = [],
sortings = [],
decorations = [],
}
) {
const tableId = table.id
const galleryView = {
id: viewId,
table_id: tableId,
name: `mock_view_${viewId}`,
order: 0,
type: viewType,
table: {
id: tableId,
name: table.name,
order: 0,
database_id: application.id,
},
filter_type: 'AND',
filters_disabled: false,
filters,
sortings,
decorations,
}
mock.onGet(`/database/views/table/${tableId}/`).reply(200, [galleryView])
return galleryView
}
export function createGalleryRows(mock, view, fields, rows = []) {
const fieldOptions = {}
for (let i = 1; i < fields.length; i++) {
fieldOptions[i] = {
hidden: false,
order: i,
}
}
mock.onGet(`/database/views/gallery/${view.id}/`).reply(200, {
count: rows.length,
results: rows,
field_options: fieldOptions,
})
}

View file

@ -1,4 +1,4 @@
export function createRows(mock, gridView, fields, rows = []) { export function createGridRows(mock, gridView, fields, rows = []) {
const fieldOptions = {} const fieldOptions = {}
for (let i = 1; i < fields.length; i++) { for (let i = 1; i < fields.length; i++) {
fieldOptions[i] = { fieldOptions[i] = {

View file

@ -7,8 +7,12 @@ import {
import { createFields } from '@baserow/test/fixtures/fields' import { createFields } from '@baserow/test/fixtures/fields'
import { import {
createPublicGridViewRows, createPublicGridViewRows,
createRows, createGridRows,
} from '@baserow/test/fixtures/grid' } from '@baserow/test/fixtures/grid'
import {
createGalleryRows,
createGalleryView,
} from '@baserow/test/fixtures/gallery'
/** /**
* MockServer is responsible for being the single place where we mock out calls to the * MockServer is responsible for being the single place where we mock out calls to the
@ -39,12 +43,13 @@ export class MockServer {
createGridView( createGridView(
application, application,
table, table,
{ filters = [], sortings = [], decorations = [] } { filters = [], sortings = [], decorations = [], ...rest }
) { ) {
return createGridView(this.mock, application, table, { return createGridView(this.mock, application, table, {
filters, filters,
sortings, sortings,
decorations, decorations,
...rest,
}) })
} }
@ -56,12 +61,29 @@ export class MockServer {
return createPublicGridViewRows(this.mock, viewSlug, fields, rows) return createPublicGridViewRows(this.mock, viewSlug, fields, rows)
} }
createGalleryView(
application,
table,
{ filters = [], sortings = [], decorations = [], ...rest }
) {
return createGalleryView(this.mock, application, table, {
filters,
sortings,
decorations,
...rest,
})
}
createFields(application, table, fields) { createFields(application, table, fields) {
return createFields(this.mock, application, table, fields) return createFields(this.mock, application, table, fields)
} }
createRows(gridView, fields, rows) { createGridRows(gridView, fields, rows) {
return createRows(this.mock, gridView, fields, rows) return createGridRows(this.mock, gridView, fields, rows)
}
createGalleryRows(gridView, fields, rows) {
return createGalleryRows(this.mock, gridView, fields, rows)
} }
nextSearchForTermWillReturn(searchTerm, gridView, results) { nextSearchForTermWillReturn(searchTerm, gridView, results) {

View file

@ -43,7 +43,7 @@ exports[`GridViewRows component with decoration Default component 1`] = `
</div> </div>
`; `;
exports[`GridViewRows component with decoration Should show can add decorator tooltip 1`] = ` exports[`GridViewRows component with decoration Should show cant add decorator tooltip 1`] = `
<body> <body>
<div <div
class="tooltip tooltip--body tooltip--center" class="tooltip tooltip--body tooltip--center"
@ -95,303 +95,6 @@ exports[`GridViewRows component with decoration Should show can add decorator to
</div> </div>
</div> </div>
</div> </div>
<div
class="tooltip tooltip--body tooltip--center"
style="top: 4px; left: 0px;"
>
<div
class="tooltip__content"
>
unavailability reason
</div>
</div>
<div
class="context"
>
<div
class="decorator-list"
>
<div
class="decorator-list__item decorator-list__item--disabled"
>
<div
class="decorator-item"
>
<img
class="decorator-item__image"
src="fake/url.png"
/>
<div
class="decorator-item__content"
>
<div
class="decorator-item__title"
>
Fake decorator
</div>
<div
class="decorator-item__description"
>
Fake decorator description
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="context"
>
<div
class="decorator-context"
>
<div
class="decorator-context__list"
>
<div
class="decorator-context__decorator"
>
<div
class="decorator-context__decorator-header"
>
<div
class="decorator-context__decorator-header-info"
>
<div
class="decorator-item"
>
<img
class="decorator-item__image"
src="fake/url.png"
/>
<div
class="decorator-item__content"
>
<div
class="decorator-item__title"
>
Fake decorator
</div>
<div
class="decorator-item__description"
>
Fake decorator description
</div>
</div>
</div>
</div>
<div
class="decorator-context__decorator-header-select"
>
<div
class="dropdown"
>
<a
class="dropdown__selected"
>
<i
class="dropdown__selected-icon fas fa-filter"
/>
Fake value provider
<i
class="dropdown__toggle-icon fas fa-caret-down"
/>
</a>
</div>
</div>
<div
class="decorator-context__decorator-header-trash"
>
<a
class="decorator-context__decorator-header-trash-link"
>
<i
class="fa fa-trash"
/>
</a>
</div>
</div>
<div>
fake_value_provider_form
</div>
</div>
<div
class="decorator-context__decorator"
>
<div
class="decorator-context__decorator-header"
>
<div
class="decorator-context__decorator-header-info"
>
<div
class="decorator-item"
>
<img
class="decorator-item__image"
src="fake/url.png"
/>
<div
class="decorator-item__content"
>
<div
class="decorator-item__title"
>
Fake decorator
</div>
<div
class="decorator-item__description"
>
Fake decorator description
</div>
</div>
</div>
</div>
<div
class="decorator-context__decorator-header-select"
style="display: none;"
>
<!---->
</div>
<div
class="decorator-context__decorator-header-trash"
>
<a
class="decorator-context__decorator-header-trash-link"
>
<i
class="fa fa-trash"
/>
</a>
</div>
</div>
<div
class="value-provider-list value-provider-list--row"
>
<div
class="value-provider-list__item"
>
<div
class="value-provider-item"
>
<div
class="value-provider-item__title"
>
<i
class="fa fa-fw fa-filter value-provider-item__icon"
/>
Fake value provider
</div>
<div
class="value-provider-item__description"
>
Fake value provider description.
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="decorator-context__footer"
>
<a
class="decorator-context__add"
>
<i
class="fas fa-plus"
/>
viewDecoratorContext.addDecorator
</a>
</div>
</div>
</div>
<div
class="context visibility-hidden"
>
<!---->
</div>
<div
class="context picker__context visibility-hidden"
>
<!---->
</div>
<div
class="context"
>
<div
class="decorator-list"
>
<div
class="decorator-list__item"
>
<div
class="decorator-item"
>
<img
class="decorator-item__image"
src="fake/url.png"
/>
<div
class="decorator-item__content"
>
<div
class="decorator-item__title"
>
Fake decorator
</div>
<div
class="decorator-item__description"
>
Fake decorator description
</div>
</div>
</div>
</div>
</div>
</div>
</body> </body>
`; `;
@ -447,253 +150,6 @@ exports[`GridViewRows component with decoration Should show unavailable decorato
</div> </div>
</div> </div>
</div> </div>
<div
class="context"
>
<div
class="decorator-context"
>
<div
class="decorator-context__list"
>
<div
class="decorator-context__decorator"
>
<div
class="decorator-context__decorator-header"
>
<div
class="decorator-context__decorator-header-info"
>
<div
class="decorator-item"
>
<img
class="decorator-item__image"
src="fake/url.png"
/>
<div
class="decorator-item__content"
>
<div
class="decorator-item__title"
>
Fake decorator
</div>
<div
class="decorator-item__description"
>
Fake decorator description
</div>
</div>
</div>
</div>
<div
class="decorator-context__decorator-header-select"
>
<div
class="dropdown"
>
<a
class="dropdown__selected"
>
<i
class="dropdown__selected-icon fas fa-filter"
/>
Fake value provider
<i
class="dropdown__toggle-icon fas fa-caret-down"
/>
</a>
</div>
</div>
<div
class="decorator-context__decorator-header-trash"
>
<a
class="decorator-context__decorator-header-trash-link"
>
<i
class="fa fa-trash"
/>
</a>
</div>
</div>
<div>
fake_value_provider_form
</div>
</div>
<div
class="decorator-context__decorator"
>
<div
class="decorator-context__decorator-header"
>
<div
class="decorator-context__decorator-header-info"
>
<div
class="decorator-item"
>
<img
class="decorator-item__image"
src="fake/url.png"
/>
<div
class="decorator-item__content"
>
<div
class="decorator-item__title"
>
Fake decorator
</div>
<div
class="decorator-item__description"
>
Fake decorator description
</div>
</div>
</div>
</div>
<div
class="decorator-context__decorator-header-select"
style="display: none;"
>
<!---->
</div>
<div
class="decorator-context__decorator-header-trash"
>
<a
class="decorator-context__decorator-header-trash-link"
>
<i
class="fa fa-trash"
/>
</a>
</div>
</div>
<div
class="value-provider-list value-provider-list--row"
>
<div
class="value-provider-list__item"
>
<div
class="value-provider-item"
>
<div
class="value-provider-item__title"
>
<i
class="fa fa-fw fa-filter value-provider-item__icon"
/>
Fake value provider
</div>
<div
class="value-provider-item__description"
>
Fake value provider description.
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="decorator-context__footer"
>
<a
class="decorator-context__add"
>
<i
class="fas fa-plus"
/>
viewDecoratorContext.addDecorator
</a>
</div>
</div>
</div>
<div
class="context visibility-hidden"
>
<!---->
</div>
<div
class="context picker__context visibility-hidden"
>
<!---->
</div>
<div
class="context"
>
<div
class="decorator-list"
>
<div
class="decorator-list__item"
>
<div
class="decorator-item"
>
<img
class="decorator-item__image"
src="fake/url.png"
/>
<div
class="decorator-item__content"
>
<div
class="decorator-item__title"
>
Fake decorator
</div>
<div
class="decorator-item__description"
>
Fake decorator description
</div>
</div>
</div>
</div>
</div>
</div>
</body> </body>
`; `;

View file

@ -0,0 +1,251 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GalleryView component with decoration Default component with first_cell decoration 1`] = `
<div
class="gallery-view"
>
<a
class="gallery-view__add"
>
<i
class="fas fa-plus"
/>
</a>
<div
class="gallery-view__scroll"
>
<div
class="gallery-view__cards"
style="height: 410px;"
>
<div
class="card gallery-view__card"
style="width: -60px; transform: translateX(30px) translateY(30px);"
>
<!---->
<div
class="card__content"
>
<div>
fake_decoration: fake_value
</div>
<div>
fake_decoration: fake_value
</div>
<div
class="card__fields"
>
<div
class="card__field"
>
<div
class="card__field-name"
>
Name
</div>
<div
class="card__field-value"
>
<div
class="card-text"
>
first
</div>
</div>
</div>
<div
class="card__field"
>
<div
class="card__field-name"
>
Surname
</div>
<div
class="card__field-value"
>
<div
class="card-text"
>
Bram
</div>
</div>
</div>
<div
class="card__field"
>
<div
class="card__field-name"
>
Address
</div>
<div
class="card__field-value"
>
<div
class="card-text"
>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`GalleryView component with decoration Default component with row wrapper decoration 1`] = `
<div
class="gallery-view"
>
<a
class="gallery-view__add"
>
<i
class="fas fa-plus"
/>
</a>
<div
class="gallery-view__scroll"
>
<div
class="gallery-view__cards"
style="height: 410px;"
>
<div
class="card gallery-view__card"
style="width: -60px; transform: translateX(30px) translateY(30px);"
>
<div
class="test-wrapper"
/>
</div>
</div>
</div>
</div>
`;
exports[`GalleryView component with decoration Default component with unavailable decoration 1`] = `
<div
class="gallery-view"
>
<a
class="gallery-view__add"
>
<i
class="fas fa-plus"
/>
</a>
<div
class="gallery-view__scroll"
>
<div
class="gallery-view__cards"
style="height: 410px;"
>
<div
class="card gallery-view__card"
style="width: -60px; transform: translateX(30px) translateY(30px);"
>
<!---->
<div
class="card__content"
>
<div
class="card__fields"
>
<div
class="card__field"
>
<div
class="card__field-name"
>
Name
</div>
<div
class="card__field-value"
>
<div
class="card-text"
>
first
</div>
</div>
</div>
<div
class="card__field"
>
<div
class="card__field-name"
>
Surname
</div>
<div
class="card__field-value"
>
<div
class="card-text"
>
Bram
</div>
</div>
</div>
<div
class="card__field"
>
<div
class="card__field-name"
>
Address
</div>
<div
class="card__field-value"
>
<div
class="card-text"
>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,252 @@
import { TestApp } from '@baserow/test/helpers/testApp'
import GalleryView from '@baserow/modules/database/components/view/gallery/GalleryView'
import { DecoratorValueProviderType } from '@baserow/modules/database/decoratorValueProviders'
import { ViewDecoratorType } from '@baserow/modules/database/viewDecorators'
export class FakeDecoratorType extends ViewDecoratorType {
static getType() {
return 'fake_decorator'
}
getPlace() {
return 'first_cell'
}
isDeactivated({ view }) {
return false
}
getComponent() {
const component = {
functional: true,
render(h, ctx) {
return h('div', `fake_decoration: ${ctx.props.value}`)
},
}
return component
}
}
export class FakeValueProviderType extends DecoratorValueProviderType {
static getType() {
return 'fake_value_provider_type'
}
getValue({ options, fields, row }) {
return 'fake_value'
}
getFormComponent() {
const component = {
functional: true,
render(h) {
return h('div', 'fake_value_provider_form')
},
}
return component
}
}
const fieldData = [
{
id: 1,
name: 'Name',
order: 0,
type: 'text',
primary: true,
text_default: '',
},
{
id: 2,
name: 'Surname',
type: 'text',
text_default: '',
primary: false,
},
{
id: 3,
name: 'Address',
type: 'text',
text_default: '',
primary: false,
},
]
const rows = [
{
id: 2,
order: '2.00000000000000000000',
field_1: 'first',
field_2: 'Bram',
},
{
id: 4,
order: '2.50000000000000000000',
field_1: 'second',
field_2: 'foo',
field_3: 'bar',
},
]
describe('GalleryView component with decoration', () => {
let testApp = null
let mockServer = null
let store = null
beforeAll(() => {
testApp = new TestApp()
store = testApp.store
mockServer = testApp.mockServer
})
afterEach((done) => {
testApp.afterEach().then(done)
// Clean up potentially registered stuff
try {
store.$registry.unregister('viewDecorator', 'fake_decorator')
} catch {}
try {
store.$registry.unregister(
'decoratorValueProvider',
'fake_value_provider_type'
)
} catch {}
})
const mountComponent = (props, slots = {}) => {
return testApp.mount(GalleryView, { propsData: props, slots })
}
const populateStore = async (decorations) => {
const table = mockServer.createTable()
const { application } = await mockServer.createAppAndGroup(table)
const view = mockServer.createGalleryView(application, table, {
decorations,
})
mockServer.createFields(application, table, fieldData)
await store.dispatch('field/fetchAll', table)
const primary = store.getters['field/getPrimary']
const fields = store.getters['field/getAll']
mockServer.createGalleryRows(view, fields, rows)
await store.dispatch('page/view/gallery/fetchInitial', {
viewId: 1,
fields,
primary,
})
await store.dispatch('view/fetchAll', { id: 1 })
return { table, primary, fields, view }
}
test('Default component with first_cell decoration', async () => {
const { table, primary, fields, view } = await populateStore([
{
type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type',
value_provider_conf: {},
},
{
type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type',
value_provider_conf: {},
},
])
const fakeDecorator = new FakeDecoratorType({ app: testApp })
const fakeValueProvider = new FakeValueProviderType({ app: testApp })
store.$registry.register('viewDecorator', fakeDecorator)
store.$registry.register('decoratorValueProvider', fakeValueProvider)
const wrapper1 = await mountComponent({
table,
view,
primary,
fields,
readOnly: false,
storePrefix: 'page/',
})
expect(wrapper1.element).toMatchSnapshot()
})
test('Default component with row wrapper decoration', async () => {
const { table, primary, fields, view } = await populateStore([
{
type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type',
value_provider_conf: {},
},
{
type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type',
value_provider_conf: {},
},
])
const fakeDecorator = new FakeDecoratorType({ app: testApp })
const fakeValueProvider = new FakeValueProviderType({ app: testApp })
fakeDecorator.getPlace = () => 'wrapper'
fakeDecorator.getComponent = () => {
const component = {
functional: true,
render(h, ctx) {
return h(
'div',
{ class: { 'test-wrapper': true } },
ctx.slots().default
)
},
}
return component
}
store.$registry.register('viewDecorator', fakeDecorator)
store.$registry.register('decoratorValueProvider', fakeValueProvider)
const wrapper1 = await mountComponent({
table,
view,
primary,
fields,
readOnly: false,
storePrefix: 'page/',
})
expect(wrapper1.element).toMatchSnapshot()
})
test('Default component with unavailable decoration', async () => {
const { table, primary, fields, view } = await populateStore([
{
type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type',
value_provider_conf: {},
},
])
const fakeDecorator = new FakeDecoratorType({ app: testApp })
const fakeValueProvider = new FakeValueProviderType({ app: testApp })
fakeDecorator.isDeactivated = () => true
store.$registry.register('viewDecorator', fakeDecorator)
store.$registry.register('decoratorValueProvider', fakeValueProvider)
const wrapper1 = await mountComponent({
table,
view,
primary,
fields,
readOnly: false,
storePrefix: 'page/',
})
expect(wrapper1.element).toMatchSnapshot()
})
})

View file

@ -1,303 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GridViewRows component with decoration Default component with firs_cell decoration 1`] = `
<div
class="grid-view__rows"
style="transform: translateY(0px) translateX(0px);"
>
<div
class="grid-view__row"
>
<!---->
<div
class="grid-view__column"
style="width: 70px;"
>
<div
class="grid-view__row-info"
>
<div
class="grid-view__row-count"
title="2"
>
2
</div>
<div
class="grid-view__row-drag"
/>
<a
class="grid-view__row-more"
>
<i
class="fas fa-expand"
/>
</a>
<div>
fake_decoration: fake_value
</div>
<div>
fake_decoration: fake_value
</div>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
Bram
</div>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
</div>
</div>
</div>
</div>
<div
class="grid-view__row"
>
<!---->
<div
class="grid-view__column"
style="width: 70px;"
>
<div
class="grid-view__row-info"
>
<div
class="grid-view__row-count"
title="4"
>
4
</div>
<div
class="grid-view__row-drag"
/>
<a
class="grid-view__row-more"
>
<i
class="fas fa-expand"
/>
</a>
<div>
fake_decoration: fake_value
</div>
<div>
fake_decoration: fake_value
</div>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
foo
</div>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
bar
</div>
</div>
</div>
</div>
</div>
`;
exports[`GridViewRows component with decoration Default component with row wrapper decoration 1`] = `
<div
class="grid-view__rows"
style="transform: translateY(0px) translateX(0px);"
>
<div
class="testWrapper"
/>
<div
class="testWrapper"
/>
</div>
`;
exports[`GridViewRows component with decoration Default component with unavailable decoration 1`] = `
<div
class="grid-view__rows"
style="transform: translateY(0px) translateX(0px);"
>
<div
class="grid-view__row"
>
<!---->
<div
class="grid-view__column"
style="width: 70px;"
>
<div
class="grid-view__row-info"
>
<div
class="grid-view__row-count"
title="2"
>
2
</div>
<div
class="grid-view__row-drag"
/>
<a
class="grid-view__row-more"
>
<i
class="fas fa-expand"
/>
</a>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
Bram
</div>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
</div>
</div>
</div>
</div>
<div
class="grid-view__row"
>
<!---->
<div
class="grid-view__column"
style="width: 70px;"
>
<div
class="grid-view__row-info"
>
<div
class="grid-view__row-count"
title="4"
>
4
</div>
<div
class="grid-view__row-drag"
/>
<a
class="grid-view__row-more"
>
<i
class="fas fa-expand"
/>
</a>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
foo
</div>
</div>
</div>
<div
class="grid-view__column"
style="width: 200px;"
>
<div
class="grid-view__cell"
>
<div
class="grid-field-text"
>
bar
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -1,5 +1,5 @@
import { TestApp } from '@baserow/test/helpers/testApp' import { TestApp } from '@baserow/test/helpers/testApp'
import GridViewRows from '@baserow/modules/database/components/view/grid/GridViewRows' import GridView from '@baserow/modules/database/components/view/grid/GridView'
import { DecoratorValueProviderType } from '@baserow/modules/database/decoratorValueProviders' import { DecoratorValueProviderType } from '@baserow/modules/database/decoratorValueProviders'
import { ViewDecoratorType } from '@baserow/modules/database/viewDecorators' import { ViewDecoratorType } from '@baserow/modules/database/viewDecorators'
@ -49,7 +49,48 @@ export class FakeValueProviderType extends DecoratorValueProviderType {
} }
} }
describe('GridViewRows component with decoration', () => { const fieldData = [
{
id: 1,
name: 'Name',
order: 0,
type: 'text',
primary: true,
text_default: '',
},
{
id: 2,
name: 'Surname',
type: 'text',
text_default: '',
primary: false,
},
{
id: 3,
name: 'Address',
type: 'text',
text_default: '',
primary: false,
},
]
const rows = [
{
id: 2,
order: '2.00000000000000000000',
field_1: 'first',
field_2: 'Bram',
},
{
id: 4,
order: '2.50000000000000000000',
field_1: 'second',
field_2: 'foo',
field_3: 'bar',
},
]
describe('GridView component with decoration', () => {
let testApp = null let testApp = null
let mockServer = null let mockServer = null
let store = null let store = null
@ -75,69 +116,32 @@ describe('GridViewRows component with decoration', () => {
}) })
const mountComponent = (props, slots = {}) => { const mountComponent = (props, slots = {}) => {
return testApp.mount(GridViewRows, { propsData: props, slots }) return testApp.mount(GridView, { propsData: props, slots })
} }
const primary = {
id: 1,
name: 'Name',
order: 0,
type: 'text',
primary: true,
text_default: '',
_: {
loading: false,
},
}
const fieldData = [
{
id: 2,
name: 'Surname',
type: 'text',
text_default: '',
primary: false,
_: {
loading: false,
},
},
{
id: 3,
name: 'Address',
type: 'text',
text_default: '',
primary: false,
_: {
loading: false,
},
},
]
const rows = [
{ id: 2, order: '2.00000000000000000000', field_2: 'Bram' },
{ id: 4, order: '2.50000000000000000000', field_2: 'foo', field_3: 'bar' },
]
const populateStore = async (decorations) => { const populateStore = async (decorations) => {
const table = mockServer.createTable() const table = mockServer.createTable()
const { application } = await mockServer.createAppAndGroup(table) const { application } = await mockServer.createAppAndGroup(table)
const view = mockServer.createGridView(application, table, { const view = mockServer.createGridView(application, table, {
decorations, decorations,
}) })
const fields = mockServer.createFields(application, table, fieldData) mockServer.createFields(application, table, fieldData)
await store.dispatch('field/fetchAll', table)
const primary = store.getters['field/getPrimary']
const fields = store.getters['field/getAll']
mockServer.createRows(view, fields, rows) mockServer.createGridRows(view, fields, rows)
await store.dispatch('page/view/grid/fetchInitial', { await store.dispatch('page/view/grid/fetchInitial', {
gridId: 1, gridId: 1,
fields, fields,
primary, primary,
}) })
await store.dispatch('view/fetchAll', { id: 1 }) await store.dispatch('view/fetchAll', { id: 1 })
return { table, fields, view } return { table, primary, fields, view }
} }
test('Default component with firs_cell decoration', async () => { test('Default component with first_cell decoration', async () => {
const { fields, view } = await populateStore([ const { table, primary, fields, view } = await populateStore([
{ {
type: 'fake_decorator', type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type', value_provider_type: 'fake_value_provider_type',
@ -157,12 +161,11 @@ describe('GridViewRows component with decoration', () => {
store.$registry.register('decoratorValueProvider', fakeValueProvider) store.$registry.register('decoratorValueProvider', fakeValueProvider)
const wrapper1 = await mountComponent({ const wrapper1 = await mountComponent({
table,
view, view,
primary,
fields, fields,
allFields: fields,
leftOffset: 0,
readOnly: false, readOnly: false,
includeRowDetails: true,
storePrefix: 'page/', storePrefix: 'page/',
}) })
@ -170,7 +173,7 @@ describe('GridViewRows component with decoration', () => {
}) })
test('Default component with row wrapper decoration', async () => { test('Default component with row wrapper decoration', async () => {
const { fields, view } = await populateStore([ const { table, primary, fields, view } = await populateStore([
{ {
type: 'fake_decorator', type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type', value_provider_type: 'fake_value_provider_type',
@ -192,7 +195,11 @@ describe('GridViewRows component with decoration', () => {
functional: true, functional: true,
render(h, ctx) { render(h, ctx) {
return h('div', { class: { testWrapper: true } }, ctx.slots().default) return h(
'div',
{ class: { 'test-wrapper': true } },
ctx.slots().default
)
}, },
} }
return component return component
@ -202,12 +209,11 @@ describe('GridViewRows component with decoration', () => {
store.$registry.register('decoratorValueProvider', fakeValueProvider) store.$registry.register('decoratorValueProvider', fakeValueProvider)
const wrapper1 = await mountComponent({ const wrapper1 = await mountComponent({
table,
view, view,
primary,
fields, fields,
allFields: fields,
leftOffset: 0,
readOnly: false, readOnly: false,
includeRowDetails: false,
storePrefix: 'page/', storePrefix: 'page/',
}) })
@ -215,7 +221,7 @@ describe('GridViewRows component with decoration', () => {
}) })
test('Default component with unavailable decoration', async () => { test('Default component with unavailable decoration', async () => {
const { fields, view } = await populateStore([ const { table, primary, fields, view } = await populateStore([
{ {
type: 'fake_decorator', type: 'fake_decorator',
value_provider_type: 'fake_value_provider_type', value_provider_type: 'fake_value_provider_type',
@ -232,12 +238,11 @@ describe('GridViewRows component with decoration', () => {
store.$registry.register('decoratorValueProvider', fakeValueProvider) store.$registry.register('decoratorValueProvider', fakeValueProvider)
const wrapper1 = await mountComponent({ const wrapper1 = await mountComponent({
table,
view, view,
primary,
fields, fields,
allFields: fields,
leftOffset: 0,
readOnly: false, readOnly: false,
includeRowDetails: true,
storePrefix: 'page/', storePrefix: 'page/',
}) })

View file

@ -67,7 +67,7 @@ describe('GridViewRows component', () => {
const view = mockServer.createGridView(application, table, {}) const view = mockServer.createGridView(application, table, {})
const fields = mockServer.createFields(application, table, fieldData) const fields = mockServer.createFields(application, table, fieldData)
mockServer.createRows(view, fields, rows) mockServer.createGridRows(view, fields, rows)
await store.dispatch('page/view/grid/fetchInitial', { await store.dispatch('page/view/grid/fetchInitial', {
gridId: 1, gridId: 1,
fields, fields,

View file

@ -32,6 +32,17 @@ export class FakeDecoratorType extends ViewDecoratorType {
return 'unavailability reason' return 'unavailability reason'
} }
getComponent() {
const component = {
functional: true,
render(h, ctx) {
return h('div', `fake_decoration: ${ctx.props.value}`)
},
}
return component
}
canAdd({ view }) { canAdd({ view }) {
return [true] return [true]
} }
@ -70,19 +81,51 @@ export class FakeValueProviderType extends DecoratorValueProviderType {
} }
} }
const fieldData = [
{
id: 1,
name: 'Name',
order: 0,
type: 'text',
primary: true,
text_default: '',
},
{
id: 2,
name: 'Surname',
type: 'text',
text_default: '',
primary: false,
},
{
id: 3,
name: 'Status',
type: 'single_select',
text_default: '',
primary: false,
},
]
const rows = [
{ id: 2, order: '2.00000000000000000000', field_2: 'Bram' },
{ id: 4, order: '2.50000000000000000000', field_2: 'foo', field_3: 'bar' },
]
describe('GridViewRows component with decoration', () => { describe('GridViewRows component with decoration', () => {
let testApp = null let testApp = null
let mockServer = null let mockServer = null
let store = null let store = null
let wrapperToDestroy = null
beforeAll(() => { beforeAll(() => {
testApp = new TestApp() testApp = new TestApp()
store = testApp.store store = testApp.store
mockServer = testApp.mockServer mockServer = testApp.mockServer
store.$registry.registerNamespace('viewDecorator')
store.$registry.registerNamespace('decoratorValueProvider')
}) })
afterEach((done) => { afterEach((done) => {
testApp.afterEach().then(done)
// Clean up potentially registered stuff // Clean up potentially registered stuff
try { try {
store.$registry.unregister('viewDecorator', 'fake_decorator') store.$registry.unregister('viewDecorator', 'fake_decorator')
@ -93,64 +136,34 @@ describe('GridViewRows component with decoration', () => {
'fake_value_provider_type' 'fake_value_provider_type'
) )
} catch {} } catch {}
if (wrapperToDestroy) {
wrapperToDestroy.destroy()
wrapperToDestroy = null
}
testApp.afterEach().then(done)
}) })
const primary = { const populateStore = async ({ viewId = 1, decorations } = {}) => {
id: 1,
name: 'Name',
order: 0,
type: 'text',
primary: true,
text_default: '',
_: {
loading: false,
},
}
const fieldData = [
{
id: 2,
name: 'Surname',
type: 'text',
text_default: '',
primary: false,
_: {
loading: false,
},
},
{
id: 3,
name: 'Status',
type: 'select',
text_default: '',
primary: false,
_: {
loading: false,
},
},
]
const rows = [
{ id: 2, order: '2.00000000000000000000', field_2: 'Bram' },
{ id: 4, order: '2.50000000000000000000', field_2: 'foo', field_3: 'bar' },
]
const populateStore = async (decorations) => {
const table = mockServer.createTable() const table = mockServer.createTable()
const { application } = await mockServer.createAppAndGroup(table) const { application } = await mockServer.createAppAndGroup(table)
const view = mockServer.createGridView(application, table, { const view = mockServer.createGridView(application, table, {
decorations, decorations,
viewId,
}) })
const fields = mockServer.createFields(application, table, fieldData)
mockServer.createRows(view, fields, rows) mockServer.createFields(application, table, fieldData)
await store.dispatch('field/fetchAll', table)
const primary = store.getters['field/getPrimary']
const fields = store.getters['field/getAll']
mockServer.createGridRows(view, fields, rows)
await store.dispatch('page/view/grid/fetchInitial', { await store.dispatch('page/view/grid/fetchInitial', {
gridId: 1, gridId: view.id,
fields, fields,
primary, primary,
}) })
await store.dispatch('view/fetchAll', { id: 1 }) await store.dispatch('view/fetchAll', { id: table.id })
return { table, fields, view } return { table, primary, fields, view }
} }
const mountComponent = async (props) => { const mountComponent = async (props) => {
@ -160,11 +173,15 @@ describe('GridViewRows component with decoration', () => {
await wrapper.vm.show(document.body) await wrapper.vm.show(document.body)
await wrapper.vm.$nextTick() await wrapper.vm.$nextTick()
// Allow to clean the DOM after the test
wrapperToDestroy = wrapper
return wrapper return wrapper
} }
test('Default component', async () => { test('Default component', async () => {
const { table, fields, view } = await populateStore() const { table, primary, fields, view } = await populateStore()
const fakeDecorator = new FakeDecoratorType({ app: testApp }) const fakeDecorator = new FakeDecoratorType({ app: testApp })
const fakeValueProvider = new FakeValueProviderType({ app: testApp }) const fakeValueProvider = new FakeValueProviderType({ app: testApp })
@ -184,20 +201,22 @@ describe('GridViewRows component with decoration', () => {
}) })
test('View with decoration configured', async () => { test('View with decoration configured', async () => {
const { table, fields, view } = await populateStore([ const { table, primary, fields, view } = await populateStore({
{ decorations: [
type: 'fake_decorator', {
value_provider_type: 'fake_value_provider_type', type: 'fake_decorator',
value_provider_conf: {}, value_provider_type: 'fake_value_provider_type',
_: { loading: false }, value_provider_conf: {},
}, _: { loading: false },
{ },
type: 'fake_decorator', {
value_provider_type: '', type: 'fake_decorator',
value_provider_conf: {}, value_provider_type: '',
_: { loading: false }, value_provider_conf: {},
}, _: { loading: false },
]) },
],
})
const fakeDecorator = new FakeDecoratorType({ app: testApp }) const fakeDecorator = new FakeDecoratorType({ app: testApp })
const fakeValueProvider = new FakeValueProviderType({ app: testApp }) const fakeValueProvider = new FakeValueProviderType({ app: testApp })
@ -217,7 +236,7 @@ describe('GridViewRows component with decoration', () => {
}) })
test('Should show unavailable decorator tooltip', async () => { test('Should show unavailable decorator tooltip', async () => {
const { table, fields, view } = await populateStore() const { table, primary, fields, view } = await populateStore()
const fakeDecorator = new FakeDecoratorType({ app: testApp }) const fakeDecorator = new FakeDecoratorType({ app: testApp })
const fakeValueProvider = new FakeValueProviderType({ app: testApp }) const fakeValueProvider = new FakeValueProviderType({ app: testApp })
@ -242,8 +261,8 @@ describe('GridViewRows component with decoration', () => {
expect(document.body).toMatchSnapshot() expect(document.body).toMatchSnapshot()
}) })
test('Should show can add decorator tooltip', async () => { test('Should show cant add decorator tooltip', async () => {
const { table, fields, view } = await populateStore() const { table, primary, fields, view } = await populateStore()
const fakeDecorator = new FakeDecoratorType({ app: testApp }) const fakeDecorator = new FakeDecoratorType({ app: testApp })
const fakeValueProvider = new FakeValueProviderType({ app: testApp }) const fakeValueProvider = new FakeValueProviderType({ app: testApp })

View file

@ -144,7 +144,7 @@ describe('Table Component Tests', () => {
}, },
]) ])
mockServer.createRows(gridView, fields, [ mockServer.createGridRows(gridView, fields, [
{ {
id: 1, id: 1,
order: 0, order: 0,

View file

@ -54,7 +54,7 @@ describe('View Filter Tests', () => {
}) })
const fields = mockServer.createFields(application, table, [field]) const fields = mockServer.createFields(application, table, [field])
mockServer.createRows(gridView, fields, [row]) mockServer.createGridRows(gridView, fields, [row])
await store.dispatch('page/view/grid/fetchInitial', { await store.dispatch('page/view/grid/fetchInitial', {
gridId: 1, gridId: 1,
fields: [field], fields: [field],