Browse Source

Nc fix/UI flaws p2 (#8394)

* fix: avoid data reload on field hide & rearrange

* fix: load rollup_function on edit

* fix: hide field if no rollup fn is available

* fix: allowed rollup fns

* fix: multiselect blur on escape

* test: skip wait for update from server for hide field ops

* fix: skip wait for response for field toggle

* fix: disable validation only for hide field

* fix: error handling

* fix: base name missing on dashboard

* fix: replace slash with arrow

* fix: table expand icon & animation

* fix: view chevron

* fix: chevron for base and chevron sizes

---------

Co-authored-by: mertmit <mertmit99@gmail.com>
pull/8408/head
Raju Udava 6 months ago committed by GitHub
parent
commit
af2c426c9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      packages/nc-gui/components/cell/MultiSelect.vue
  2. 6
      packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
  3. 6
      packages/nc-gui/components/dashboard/TreeView/TableNode.vue
  4. 4
      packages/nc-gui/components/project/View.vue
  5. 32
      packages/nc-gui/components/smartsheet/column/RollupOptions.vue
  6. 3
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  7. 4
      packages/nc-gui/components/smartsheet/toolbar/OpenedViewAction.vue
  8. 4
      packages/nc-gui/components/smartsheet/toolbar/ViewInfo.vue
  9. 2
      packages/nc-gui/composables/useViewColumns.ts
  10. 4
      packages/nocodb-sdk/src/lib/helperFunctions.ts
  11. 32
      tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts

4
packages/nc-gui/components/cell/MultiSelect.vue

@ -221,7 +221,9 @@ watch(isOpen, (n, _o) => {
if (!n) searchVal.value = '' if (!n) searchVal.value = ''
if (editAllowed.value) { if (editAllowed.value) {
if (n) { if (!n) {
aselect.value?.$el?.querySelector('input')?.blur()
} else {
aselect.value?.$el?.querySelector('input')?.focus() aselect.value?.$el?.querySelector('input')?.focus()
} }
} }

6
packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue

@ -632,9 +632,9 @@ const onTableIdCopy = async () => {
@click="onProjectClick(base, true, true)" @click="onProjectClick(base, true, true)"
> >
<GeneralIcon <GeneralIcon
icon="chevronDown" icon="chevronRight"
class="group-hover:visible cursor-pointer transform transition-transform duration-500 rotate-270" class="group-hover:visible cursor-pointer transform transition-transform duration-200 text-[20px]"
:class="{ '!rotate-180': base.isExpanded }" :class="{ '!rotate-90': base.isExpanded }"
/> />
</NcButton> </NcButton>
</template> </template>

6
packages/nc-gui/components/dashboard/TreeView/TableNode.vue

@ -395,9 +395,9 @@ const deleteTable = () => {
@click.stop="onExpand" @click.stop="onExpand"
> >
<GeneralIcon <GeneralIcon
icon="chevronDown" icon="chevronRight"
class="nc-sidebar-source-node-btns cursor-pointer transform transition-transform duration-500 !text-gray-600 rotate-270" class="nc-sidebar-source-node-btns cursor-pointer transform transition-transform duration-200 !text-gray-600 text-[20px]"
:class="{ '!rotate-180': isExpanded }" :class="{ '!rotate-90': isExpanded }"
/> />
</NcButton> </NcButton>
</div> </div>

4
packages/nc-gui/components/project/View.vue

@ -22,7 +22,7 @@ const route = router.currentRoute
const { $e, $api } = useNuxtApp() const { $e, $api } = useNuxtApp()
const currentBase = computed(async () => { const currentBase = computedAsync(async () => {
let base let base
if (props.baseId) { if (props.baseId) {
base = bases.value.get(props.baseId) base = bases.value.get(props.baseId)
@ -161,7 +161,7 @@ watch(
</div> </div>
</div> </div>
</template> </template>
<ProjectAccessSettings :base-id="currentBase.id" /> <ProjectAccessSettings :base-id="currentBase?.id" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane v-if="isUIAllowed('sourceCreate')" key="data-source"> <a-tab-pane v-if="isUIAllowed('sourceCreate')" key="data-source">
<template #tab> <template #tab>

32
packages/nc-gui/components/smartsheet/column/RollupOptions.vue

@ -56,13 +56,16 @@ const refTables = computed(() => {
const _refTables = meta.value.columns const _refTables = meta.value.columns
.filter( .filter(
(c) => (c: ColumnType) =>
isLinksOrLTAR(c) && isLinksOrLTAR(c) &&
![RelationTypes.BELONGS_TO, RelationTypes.ONE_TO_ONE].includes((c.colOptions as LinkToAnotherRecordType).type) && (c.colOptions as LinkToAnotherRecordType).type &&
![RelationTypes.BELONGS_TO, RelationTypes.ONE_TO_ONE].includes(
(c.colOptions as LinkToAnotherRecordType).type as RelationTypes,
) &&
!c.system && !c.system &&
c.source_id === meta.value?.source_id, c.source_id === meta.value?.source_id,
) )
.map((c) => ({ .map((c: ColumnType) => ({
col: c.colOptions, col: c.colOptions,
column: c, column: c,
...tables.value.find((t) => t.id === (c.colOptions as any)?.fk_related_model_id), ...tables.value.find((t) => t.id === (c.colOptions as any)?.fk_related_model_id),
@ -118,16 +121,29 @@ const allFunctions = [
{ text: t('general.avgDistinct'), value: 'avgDistinct' }, { text: t('general.avgDistinct'), value: 'avgDistinct' },
] ]
const availableRollupPerColumn = computed(() => {
const fnMap: Record<string, { text: string; value: string }[]> = {}
columns.value?.forEach((column) => {
if (!column?.id) return
fnMap[column.id] = allFunctions.filter((func) => getAvailableRollupForUiType(column.uidt as UITypes).includes(func.value))
})
return fnMap
})
const filteredColumns = computed(() => {
return columns.value?.filter((column) => {
return column.id && availableRollupPerColumn.value[column.id as string]?.length
})
})
watch( watch(
() => vModel.value.fk_rollup_column_id, () => vModel.value.fk_rollup_column_id,
() => { () => {
const childFieldColumn = columns.value?.find((column: ColumnType) => column.id === vModel.value.fk_rollup_column_id) const childFieldColumn = columns.value?.find((column: ColumnType) => column.id === vModel.value.fk_rollup_column_id)
aggFunctionsList.value = allFunctions.filter((func) => aggFunctionsList.value = availableRollupPerColumn.value[childFieldColumn?.id as string] || []
getAvailableRollupForUiType(childFieldColumn?.uidt as UITypes).includes(func.value),
)
if (!aggFunctionsList.value.includes(vModel.value.rollup_function)) { if (aggFunctionsList.value.length && !aggFunctionsList.value.find((func) => func.value === vModel.value.rollup_function)) {
// when the previous roll up function was numeric type and the current child field is non-numeric // when the previous roll up function was numeric type and the current child field is non-numeric
// reset rollup function with a non-numeric type // reset rollup function with a non-numeric type
vModel.value.rollup_function = aggFunctionsList.value[0].value vModel.value.rollup_function = aggFunctionsList.value[0].value
@ -176,7 +192,7 @@ watch(
dropdown-class-name="nc-dropdown-relation-column !rounded-xl" dropdown-class-name="nc-dropdown-relation-column !rounded-xl"
@change="onDataTypeChange" @change="onDataTypeChange"
> >
<a-select-option v-for="(column, index) of columns" :key="index" :value="column.id"> <a-select-option v-for="(column, index) of filteredColumns" :key="index" :value="column.id">
<div class="flex gap-2 truncate items-center"> <div class="flex gap-2 truncate items-center">
<div class="flex items-center flex-1 truncate font-semibold"> <div class="flex items-center flex-1 truncate font-semibold">
<component :is="cellIcon(column)" :column-meta="column" /> <component :is="cellIcon(column)" :column-meta="column" />

3
packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue

@ -133,9 +133,6 @@ const onMove = async (_event: { moved: { newIndex: number; oldIndex: number } },
) )
await loadViewColumns() await loadViewColumns()
await reloadViewDataHook?.trigger({
shouldShowLoading: false,
})
$e('a:fields:reorder') $e('a:fields:reorder')
} catch (e) { } catch (e) {

4
packages/nc-gui/components/smartsheet/toolbar/OpenedViewAction.vue

@ -173,7 +173,7 @@ function openDeleteDialog() {
> >
<div <div
v-e="['c:breadcrumb:view-actions']" v-e="['c:breadcrumb:view-actions']"
class="truncate nc-active-view-title flex gap-0.5 items-center !hover:(bg-gray-100 text-gray-800) ml-1 pl-1 pr-0.25 rounded-md py-1 cursor-pointer" class="truncate nc-active-view-title flex gap-1 items-center !hover:(bg-gray-100 text-gray-800) ml-1 pl-1 pr-0.25 rounded-md py-1 cursor-pointer"
:class="{ :class="{
'max-w-2/5': !isSharedBase && !isMobileMode && activeView?.is_default, 'max-w-2/5': !isSharedBase && !isMobileMode && activeView?.is_default,
'max-w-3/5': !isSharedBase && !isMobileMode && !activeView?.is_default, 'max-w-3/5': !isSharedBase && !isMobileMode && !activeView?.is_default,
@ -192,7 +192,7 @@ function openDeleteDialog() {
{{ activeView?.is_default ? $t('title.defaultView') : activeView?.title }} {{ activeView?.is_default ? $t('title.defaultView') : activeView?.title }}
</span> </span>
</NcTooltip> </NcTooltip>
<GeneralIcon icon="arrowDown" class="ml-1" /> <GeneralIcon icon="chevronDown" class="!text-gray-500 mt-0.5" />
</div> </div>
<template #overlay> <template #overlay>
<SmartsheetToolbarViewActionMenu <SmartsheetToolbarViewActionMenu

4
packages/nc-gui/components/smartsheet/toolbar/ViewInfo.vue

@ -69,7 +69,7 @@ const openedBaseUrl = computed(() => {
</div> </div>
</NcTooltip> </NcTooltip>
</NuxtLink> </NuxtLink>
<div class="px-1.75 text-gray-500">/</div> <div class="px-1.75 text-gray-500">></div>
</template> </template>
<template v-if="!(isMobileMode && !activeView?.is_default)"> <template v-if="!(isMobileMode && !activeView?.is_default)">
<LazyGeneralEmojiPicker v-if="isMobileMode" :emoji="activeTable?.meta?.icon" readonly size="xsmall"> <LazyGeneralEmojiPicker v-if="isMobileMode" :emoji="activeTable?.meta?.icon" readonly size="xsmall">
@ -122,7 +122,7 @@ const openedBaseUrl = computed(() => {
</div> </div>
</template> </template>
<div v-if="!isMobileMode" class="pl-1.25 text-gray-500">/</div> <div v-if="!isMobileMode" class="pl-1.25 text-gray-500">></div>
<template v-if="!(isMobileMode && activeView?.is_default)"> <template v-if="!(isMobileMode && activeView?.is_default)">
<LazyGeneralEmojiPicker v-if="isMobileMode" :emoji="activeView?.meta?.icon" readonly size="xsmall"> <LazyGeneralEmojiPicker v-if="isMobileMode" :emoji="activeView?.meta?.icon" readonly size="xsmall">

2
packages/nc-gui/composables/useViewColumns.ts

@ -286,7 +286,7 @@ const [useProvideViewColumns, useViewColumns] = useInjectionState(
}, },
scope: defineViewScope({ view: view.value }), scope: defineViewScope({ view: view.value }),
}) })
saveOrUpdate(field, fieldIndex) saveOrUpdate(field, fieldIndex, !checked)
} }
const toggleFieldStyles = (field: any, style: 'underline' | 'bold' | 'italic', status: boolean) => { const toggleFieldStyles = (field: any, style: 'underline' | 'bold' | 'italic', status: boolean) => {

4
packages/nocodb-sdk/src/lib/helperFunctions.ts

@ -87,9 +87,13 @@ const getAvailableRollupForUiType = (type: string) => {
UITypes.Email, UITypes.Email,
UITypes.PhoneNumber, UITypes.PhoneNumber,
UITypes.URL, UITypes.URL,
UITypes.Checkbox,
UITypes.JSON,
].includes(type as UITypes) ].includes(type as UITypes)
) { ) {
return ['count']; return ['count'];
} else if ([UITypes.Attachment].includes(type as UITypes)) {
return [];
} else { } else {
return [ return [
'sum', 'sum',

32
tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts

@ -31,22 +31,24 @@ export class ToolbarFieldsPage extends BasePage {
// hack // hack
await this.rootPage.waitForTimeout(100); await this.rootPage.waitForTimeout(100);
// toggle only if input checked value is not equal to given checked value
await this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').scrollIntoViewIfNeeded();
const isChecked = await this.get()
.locator(`[data-testid="nc-fields-menu-${title}"]`)
.locator('.nc-switch')
.isChecked();
if (checked !== undefined) { if (checked !== undefined) {
// toggle only if input checked value is not equal to given checked value
await this.get()
.locator(`[data-testid="nc-fields-menu-${title}"]`)
.locator('.nc-switch')
.scrollIntoViewIfNeeded();
const isChecked = await this.get()
.locator(`[data-testid="nc-fields-menu-${title}"]`)
.locator('.nc-switch')
.isChecked();
if ((checked && isChecked) || (!checked && !isChecked)) { if ((checked && isChecked) || (!checked && !isChecked)) {
await this.toolbar.clickFields(); await this.toolbar.clickFields();
return; return;
} }
} }
if (isChecked === true) {
// disable response validation for hide field
validateResponse = false;
}
const toggleColumn = () => const toggleColumn = () =>
this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click(); this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click();
@ -75,11 +77,13 @@ export class ToolbarFieldsPage extends BasePage {
} }
async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) { async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.waitForResponse({ // hide field doesn't trigger an un-solicited update from backend
uiAction: () => this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click(), // await this.waitForResponse({
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/', // uiAction: () => this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click(),
httpMethodsToMatch: ['GET'], // requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
}); // httpMethodsToMatch: ['GET'],
// });
await this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click();
await this.toolbar.parent.waitLoading(); await this.toolbar.parent.waitLoading();
} }

Loading…
Cancel
Save