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/7587/merge
Raju Udava 2 weeks ago committed by GitHub
parent 3747a5fc3c
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

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

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

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

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

@ -56,13 +56,16 @@ const refTables = computed(() => {
const _refTables = meta.value.columns
.filter(
(c) =>
(c: ColumnType) =>
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.source_id === meta.value?.source_id,
)
.map((c) => ({
.map((c: ColumnType) => ({
col: c.colOptions,
column: c,
...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' },
]
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(
() => 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) =>
getAvailableRollupForUiType(childFieldColumn?.uidt as UITypes).includes(func.value),
)
aggFunctionsList.value = availableRollupPerColumn.value[childFieldColumn?.id as string] || []
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
// reset rollup function with a non-numeric type
vModel.value.rollup_function = aggFunctionsList.value[0].value
@ -176,7 +192,7 @@ watch(
dropdown-class-name="nc-dropdown-relation-column !rounded-xl"
@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 items-center flex-1 truncate font-semibold">
<component :is="cellIcon(column)" :column-meta="column" />

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

@ -173,7 +173,7 @@ function openDeleteDialog() {
>
<div
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="{
'max-w-2/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 }}
</span>
</NcTooltip>
<GeneralIcon icon="arrowDown" class="ml-1" />
<GeneralIcon icon="chevronDown" class="!text-gray-500 mt-0.5" />
</div>
<template #overlay>
<SmartsheetToolbarViewActionMenu

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

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

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

@ -31,22 +31,24 @@ export class ToolbarFieldsPage extends BasePage {
// hack
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) {
// 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)) {
await this.toolbar.clickFields();
return;
}
}
if (isChecked === true) {
// disable response validation for hide field
validateResponse = false;
}
const toggleColumn = () =>
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 }) {
await this.waitForResponse({
uiAction: () => this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click(),
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
});
// hide field doesn't trigger an un-solicited update from backend
// await this.waitForResponse({
// uiAction: () => this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click(),
// 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();
}

Loading…
Cancel
Save