Browse Source

Merge pull request #5724 from nocodb/enhancement/hm-mm-cells

enhancement: hm / mm cells
pull/5820/head
Raju Udava 2 years ago committed by GitHub
parent
commit
d50f6d2a5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui/components/smartsheet/Cell.vue
  2. 17
      packages/nc-gui/components/virtual-cell/BelongsTo.vue
  3. 12
      packages/nc-gui/components/virtual-cell/HasMany.vue
  4. 12
      packages/nc-gui/components/virtual-cell/ManyToMany.vue
  5. 49
      packages/nc-gui/components/virtual-cell/components/ItemChip.vue
  6. 4
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  7. 8
      packages/nc-gui/components/virtual-cell/components/ListItems.vue
  8. 4
      tests/playwright/package-lock.json
  9. 5
      tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts
  10. 4
      tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts
  11. 2
      tests/playwright/pages/Dashboard/common/Cell/index.ts
  12. 1
      tests/playwright/tests/db/columnRelationalExtendedTests.spec.ts

2
packages/nc-gui/components/smartsheet/Cell.vue

@ -193,7 +193,7 @@ onUnmounted(() => {
`nc-cell-${(column?.uidt || 'default').toLowerCase()}`,
{ 'text-blue-600': isPrimary(column) && !props.virtual && !isForm },
{ 'nc-grid-numeric-cell': isGrid && !isForm && isNumericField },
{ 'h-[40px]': !props.editEnabled && isForm && !isSurveyForm && !isAttachment(column) },
{ 'h-[40px]': !props.editEnabled && isForm && !isSurveyForm && !isAttachment(column) && !props.virtual },
]"
@keydown.enter.exact="navigate(NavigateDir.NEXT, $event)"
@keydown.shift.enter.exact="navigate(NavigateDir.PREV, $event)"

17
packages/nc-gui/components/virtual-cell/BelongsTo.vue

@ -45,7 +45,7 @@ const listItemsDlg = ref(false)
const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow()
const { loadRelatedTableMeta, relatedTableDisplayValueProp, unlink } = useProvideLTARStore(
const { relatedTableMeta, loadRelatedTableMeta, relatedTableDisplayValueProp, unlink } = useProvideLTARStore(
column as Ref<Required<ColumnType>>,
row,
isNew,
@ -81,13 +81,24 @@ useSelectedCellKeyupListener(active, (e: KeyboardEvent) => {
break
}
})
const belongsToColumn = computed(
() =>
relatedTableMeta.value?.columns?.find((c: any) => c.title === relatedTableDisplayValueProp.value) as ColumnType | undefined,
)
</script>
<template>
<div class="flex w-full chips-wrapper items-center" :class="{ active }">
<div class="chips flex items-center flex-1">
<template v-if="value && relatedTableDisplayValueProp">
<VirtualCellComponentsItemChip :item="value" :value="value[relatedTableDisplayValueProp]" @unlink="unlinkRef(value)" />
<VirtualCellComponentsItemChip
:item="value"
:value="value[relatedTableDisplayValueProp]"
:column="belongsToColumn"
:show-unlink-button="true"
@unlink="unlinkRef(value)"
/>
</template>
</div>
@ -102,7 +113,7 @@ useSelectedCellKeyupListener(active, (e: KeyboardEvent) => {
/>
</div>
<LazyVirtualCellComponentsListItems v-model="listItemsDlg" @attach-record="listItemsDlg = true" />
<LazyVirtualCellComponentsListItems v-model="listItemsDlg" :column="belongsToColumn" @attach-record="listItemsDlg = true" />
</div>
</template>

12
packages/nc-gui/components/virtual-cell/HasMany.vue

@ -43,7 +43,7 @@ const { isUIAllowed } = useUIPermission()
const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow()
const { loadRelatedTableMeta, relatedTableDisplayValueProp, unlink } = useProvideLTARStore(
const { relatedTableMeta, loadRelatedTableMeta, relatedTableDisplayValueProp, unlink } = useProvideLTARStore(
column as Ref<Required<ColumnType>>,
row,
isNew,
@ -81,6 +81,11 @@ const unlinkRef = async (rec: Record<string, any>) => {
}
}
const hasManyColumn = computed(
() =>
relatedTableMeta.value?.columns?.find((c: any) => c.title === relatedTableDisplayValueProp.value) as ColumnType | undefined,
)
const onAttachRecord = () => {
childListDlg.value = false
listItemsDlg.value = true
@ -106,6 +111,8 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
:key="i"
:item="cell.item"
:value="cell.value"
:column="hasManyColumn"
:show-unlink-button="true"
@unlink="unlinkRef(cell.item)"
/>
@ -131,11 +138,12 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
</div>
</template>
<LazyVirtualCellComponentsListItems v-model="listItemsDlg" />
<LazyVirtualCellComponentsListItems v-model="listItemsDlg" :column="hasManyColumn" />
<LazyVirtualCellComponentsListChildItems
v-model="childListDlg"
:cell-value="localCellValue"
:column="hasManyColumn"
@attach-record="onAttachRecord"
/>
</div>

12
packages/nc-gui/components/virtual-cell/ManyToMany.vue

@ -45,7 +45,7 @@ const { isUIAllowed } = useUIPermission()
const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow()
const { loadRelatedTableMeta, relatedTableDisplayValueProp, unlink } = useProvideLTARStore(
const { relatedTableMeta, loadRelatedTableMeta, relatedTableDisplayValueProp, unlink } = useProvideLTARStore(
column as Ref<Required<ColumnType>>,
row,
isNew,
@ -96,6 +96,11 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
break
}
})
const m2mColumn = computed(
() =>
relatedTableMeta.value?.columns?.find((c: any) => c.title === relatedTableDisplayValueProp.value) as ColumnType | undefined,
)
</script>
<template>
@ -108,6 +113,8 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
:key="i"
:item="cell.item"
:value="cell.value"
:column="m2mColumn"
:show-unlink-button="true"
@unlink="unlinkRef(cell.item)"
/>
@ -133,11 +140,12 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
</div>
</template>
<LazyVirtualCellComponentsListItems v-model="listItemsDlg" />
<LazyVirtualCellComponentsListItems v-model="listItemsDlg" :column="m2mColumn" />
<LazyVirtualCellComponentsListChildItems
v-model="childListDlg"
:cell-value="localCellValue"
:column="m2mColumn"
@attach-record="onAttachRecord"
/>
</div>

49
packages/nc-gui/components/virtual-cell/components/ItemChip.vue

@ -1,4 +1,5 @@
<script lang="ts" setup>
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import {
ActiveCellInj,
IsFormInj,
@ -6,6 +7,7 @@ import {
ReadonlyInj,
iconMap,
inject,
isAttachment,
ref,
renderValue,
useExpandedFormDetached,
@ -15,9 +17,11 @@ import {
interface Props {
value?: string | number | boolean
item?: any
column: any
showUnlinkButton: boolean
}
const { value, item } = defineProps<Props>()
const { value, item, column, showUnlinkButton } = defineProps<Props>()
const emit = defineEmits(['unlink'])
@ -56,13 +60,46 @@ export default {
<template>
<div
class="chip group py-1 px-2 mr-1 my-1 flex items-center bg-blue-100/60 hover:bg-blue-100/40 rounded-[2px]"
:class="{ active }"
class="chip group mr-1 my-1 flex items-center rounded-[2px] flex-row"
:class="{ active, 'border-1 py-1 px-2': isAttachment(column) }"
@click="openExpandedForm"
>
<span class="name">{{ renderValue(value) }}</span>
<div v-show="active || isForm" v-if="!readOnly && !isLocked && isUIAllowed('xcDatatableEditable')" class="flex items-center">
<span class="name">
<!-- Render virtual cell -->
<div v-if="isVirtualCol(column)">
<template v-if="column.uidt === UITypes.LinkToAnotherRecord">
<LazySmartsheetVirtualCell :edit-enabled="false" :model-value="value" :column="column" :read-only="true" />
</template>
<LazySmartsheetVirtualCell v-else :edit-enabled="false" :read-only="true" :model-value="value" :column="column" />
</div>
<!-- Render normal cell -->
<template v-else>
<div v-if="isAttachment(column) && value && !Array.isArray(value) && typeof value === 'object'">
<LazySmartsheetCell :model-value="value" :column="column" :edit-enabled="false" :read-only="true" />
</div>
<!-- For attachment cell avoid adding chip style -->
<template v-else>
<div
class="min-w-max"
:class="{
'px-1 rounded-full flex-1': !isAttachment(column),
'border-gray-200 rounded border-1': ![UITypes.Attachment, UITypes.MultiSelect, UITypes.SingleSelect].includes(
column.uidt,
),
}"
>
<LazySmartsheetCell :model-value="value" :column="column" :edit-enabled="false" :virtual="true" :read-only="true" />
</div>
</template>
</template>
</span>
<div
v-show="active || isForm"
v-if="showUnlinkButton && !readOnly && !isLocked && isUIAllowed('xcDatatableEditable')"
class="flex items-center"
>
<component
:is="iconMap.closeThick"
class="nc-icon unlink-icon text-xs text-gray-500/50 group-hover:text-gray-500"

4
packages/nc-gui/components/virtual-cell/components/ListChildItems.vue

@ -19,7 +19,7 @@ import {
useVModel,
} from '#imports'
const props = defineProps<{ modelValue?: boolean; cellValue: any }>()
const props = defineProps<{ modelValue?: boolean; cellValue: any; column: any }>()
const emit = defineEmits(['update:modelValue', 'attachRecord'])
@ -148,7 +148,7 @@ const onClick = (row: Row) => {
>
<div class="flex items-center">
<div class="flex-1 overflow-hidden min-w-0">
{{ renderValue(row[relatedTableDisplayValueProp]) }}
<VirtualCellComponentsItemChip :value="row[relatedTableDisplayValueProp]" :column="props.column" />
<span class="text-gray-400 text-[11px] ml-1">(Primary key : {{ getRelatedTableRowId(row) }})</span>
</div>

8
packages/nc-gui/components/virtual-cell/components/ListItems.vue

@ -19,7 +19,7 @@ import {
useVModel,
} from '#imports'
const props = defineProps<{ modelValue: boolean }>()
const props = defineProps<{ modelValue: boolean; column: any }>()
const emit = defineEmits(['update:modelValue', 'addNewRecord'])
@ -229,7 +229,11 @@ watch(vModel, (nextVal) => {
:class="{ 'nc-selected-row': selectedRowIndex === i }"
@click="linkRow(refRow)"
>
{{ renderValue(refRow[relatedTableDisplayValueProp]) }}
<VirtualCellComponentsItemChip
:value="refRow[relatedTableDisplayValueProp]"
:column="props.column"
:show-unlink-button="false"
/>
<span class="hidden group-hover:(inline) text-gray-400 text-[11px] ml-1">
({{ $t('labels.primaryKey') }} : {{ getRelatedTableRowId(refRow) }})
</span>

4
tests/playwright/package-lock.json generated

@ -39,7 +39,7 @@
}
},
"../../packages/nocodb-sdk": {
"version": "0.107.0-beta.1",
"version": "0.107.5",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
@ -10702,4 +10702,4 @@
"dev": true
}
}
}
}

5
tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts

@ -31,7 +31,10 @@ export class ChildList extends BasePage {
const childCards = await childList.count();
await expect(childCards).toEqual(cardCount);
for (let i = 0; i < cardCount; i++) {
await expect(await childList.nth(i).textContent()).toContain(cardTitle[i]);
await childList.nth(i).locator('.name').waitFor({ state: 'visible' });
await childList.nth(i).locator('.name').scrollIntoViewIfNeeded();
await this.rootPage.waitForTimeout(100);
await expect(await childList.nth(i).locator('.name').textContent()).toContain(cardTitle[i]);
// icon: unlink
// icon: delete
await expect(

4
tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts

@ -29,7 +29,9 @@ export class LinkRecord extends BasePage {
const childCards = await childList.count();
await expect(childCards).toEqual(cardTitle.length);
for (let i = 0; i < cardTitle.length; i++) {
await expect(await childList.nth(i).textContent()).toContain(cardTitle[i]);
await childList.nth(i).locator('.name').scrollIntoViewIfNeeded();
await childList.nth(i).locator('.name').waitFor({ state: 'visible' });
await expect(await childList.nth(i).locator('.name').textContent()).toContain(cardTitle[i]);
}
}
}

2
tests/playwright/pages/Dashboard/common/Cell/index.ts

@ -286,6 +286,8 @@ export class CellPageObject extends BasePage {
// verify only the elements that are passed in
for (let i = 0; i < value.length; ++i) {
await chips.nth(i).locator('.name').waitFor({ state: 'visible' });
await chips.nth(i).locator('.name').scrollIntoViewIfNeeded();
await expect(await chips.nth(i).locator('.name')).toHaveText(value[i]);
}

1
tests/playwright/tests/db/columnRelationalExtendedTests.spec.ts

@ -1,7 +1,6 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { isPg } from '../../setup/db';
test.describe('Relational Columns', () => {
let dashboard: DashboardPage;

Loading…
Cancel
Save