Nc fix/rich perf (#10528)

* fix: rich text performance issue

* fix: rich text limit number of chars for canvas

* fix: flaky barcode tests

* fix: avoid duplicate trigger

* test: flaky kanban

* test: flaky meta sync

* test: flaky grid

* test: add flag to kanban calls

Signed-off-by: mertmit <mertmit99@gmail.com>

---------

Signed-off-by: mertmit <mertmit99@gmail.com>
Co-authored-by: mertmit <mertmit99@gmail.com>
This commit is contained in:
Raju Udava
2025-02-18 18:15:47 +05:30
committed by GitHub
parent bc6a5803d8
commit f1d4013d16
7 changed files with 35 additions and 24 deletions

View File

@@ -51,7 +51,7 @@ const onChange = (e: boolean, updateValue = false) => {
<slot />
</span>
<a-switch
v-model:checked="checked"
:checked="checked"
:disabled="disabled"
class="nc-switch"
:class="{

View File

@@ -1426,6 +1426,7 @@ const colSlice = ref({
const lastScrollTop = ref()
const lastScrollLeft = ref()
const lastTotalRows = ref()
const lastTotalFields = ref()
// Store the previous results for binary search to avoid redundant calculations
let prevScrollLeft = -1
@@ -1503,7 +1504,8 @@ const calculateSlices = () => {
lastScrollLeft.value &&
lastScrollLeft.value === scrollLeft.value &&
Math.abs(lastScrollTop.value - scrollTop.value) < 32 * (ROW_VIRTUAL_MARGIN - 2) &&
lastTotalRows.value === totalRows.value
lastTotalRows.value === totalRows.value &&
lastTotalFields.value === fields.value.length
) {
return
}
@@ -1511,6 +1513,7 @@ const calculateSlices = () => {
// Cache the current scroll positions
lastScrollLeft.value = scrollLeft.value
lastScrollTop.value = scrollTop.value
lastTotalFields.value = fields.value.length
// Determine visible column range using binary search
const { renderStart, renderEnd } = binarySearchForStart(scrollLeft.value, gridWrapper.value.clientWidth)

View File

@@ -394,8 +394,8 @@ export const wrapTextToLines = (
const ellipsis = '...'
const ellipsisWidth = ctx.measureText(ellipsis).width
while (width + ellipsisWidth > maxWidth && line.length > 0) {
line = line.slice(0, -1) // Remove one character at a time
if (width + ellipsisWidth > maxWidth && line.length > 0) {
line = truncateText(ctx, line, maxWidth - ellipsisWidth, false, false) // Truncate the line to fit within maxWidth
width = ctx.measureText(line).width
}
@@ -466,7 +466,7 @@ export const renderMarkdownBlocks = (
maxLines?: number
maxWidth: number
lineHeight: number
cellRenderStore: CellRenderStore
cellRenderStore?: CellRenderStore
fillStyle?: string
mousePosition?: { x: number; y: number }
},
@@ -519,8 +519,9 @@ export const renderMarkdownBlocks = (
let tokenWidth = ctx.measureText(tokenText).width
// Truncate the token if it exceeds the max width of the line
while (cursorX + tokenWidth > x + maxWidth && tokenText.length > 0) {
tokenText = tokenText.slice(0, -1)
if (cursorX + tokenWidth > x + maxWidth && tokenText.length > 0) {
// cursorX starts at x, so we need to subtract x to get used space
tokenText = truncateText(ctx, tokenText, maxWidth - (cursorX - x), false, false)
tokenWidth = ctx.measureText(tokenText).width
}
@@ -556,8 +557,9 @@ export const renderMarkdownBlocks = (
const ellipsisWidth = ctx.measureText(ellipsis).width
if (cursorX + tokenWidth + ellipsisWidth > x + maxWidth || tokenIndex === tokens.length - 1) {
while (cursorX + tokenWidth + ellipsisWidth > x + maxWidth && tokenText.length > 0) {
tokenText = tokenText.slice(0, -1)
if (cursorX + tokenWidth + ellipsisWidth > x + maxWidth && tokenText.length > 0) {
// cursorX starts at x, so we need to subtract x to get used space
tokenText = truncateText(ctx, tokenText, maxWidth - (cursorX - x) - ellipsisWidth, false, false)
tokenWidth = ctx.measureText(tokenText).width
}
@@ -586,7 +588,7 @@ export const renderMarkdownBlocks = (
renderedLineCount++
}
cellRenderStore.links = links
if (cellRenderStore) cellRenderStore.links = links
// Restore the original font
ctx.font = defaultFont
@@ -823,10 +825,8 @@ export const renderMarkdown = (
textAlign = 'left',
verticalAlign = 'middle',
render = true,
underline,
py = 10,
mousePosition = { x: 0, y: 0 },
spriteLoader,
cellRenderStore,
isTagLabel = false,
} = params
@@ -861,7 +861,10 @@ export const renderMarkdown = (
width = cachedText.width
blocks = cachedText.blocks
} else {
const renderText = NcMarkdownParser.preprocessMarkdown(text, true)
// Render 2000 characters of the text in the canvas
const processText = text.length > 2000 ? text.slice(0, 2000) : text
const renderText = NcMarkdownParser.preprocessMarkdown(processText, true)
width = maxWidth
blocks = parseMarkdown(renderText)
@@ -893,12 +896,9 @@ export const renderMarkdown = (
verticalAlign,
lineHeight,
maxLines,
fontSize,
fillStyle,
underline,
maxWidth,
mousePosition,
spriteLoader,
cellRenderStore,
})
} else {

View File

@@ -101,6 +101,7 @@ export class ColumnPageObject extends BasePage {
webhookIndex?: number;
}) {
if (insertBeforeColumnTitle) {
await this.grid.renderColumn(insertBeforeColumnTitle);
await this.grid.get().locator(`th[data-title="${insertBeforeColumnTitle}"]`).scrollIntoViewIfNeeded();
await this.grid.get().locator(`th[data-title="${insertBeforeColumnTitle}"] .nc-ui-dt-dropdown`).click();
if (isDisplayValue) {
@@ -109,6 +110,7 @@ export class ColumnPageObject extends BasePage {
}
await this.rootPage.locator('li[role="menuitem"]:has-text("Insert left"):visible').click();
} else if (insertAfterColumnTitle) {
await this.grid.renderColumn(insertAfterColumnTitle);
await this.grid.get().locator(`th[data-title="${insertAfterColumnTitle}"]`).scrollIntoViewIfNeeded();
await this.grid.get().locator(`th[data-title="${insertAfterColumnTitle}"] .nc-ui-dt-dropdown`).click();
await this.rootPage.locator('li[role="menuitem"]:has-text("Insert right"):visible').click();
@@ -386,6 +388,7 @@ export class ColumnPageObject extends BasePage {
timeFormat?: string;
selectType?: boolean;
}) {
await this.grid.renderColumn(title);
// when clicked on the dropdown cell header
await this.getColumnHeader(title).locator('.nc-ui-dt-dropdown').scrollIntoViewIfNeeded();
await this.getColumnHeader(title).locator('.nc-ui-dt-dropdown').click();
@@ -525,6 +528,7 @@ export class ColumnPageObject extends BasePage {
return await expect(this.getColumnHeader(title)).not.toBeVisible();
}
if (scroll) {
await this.grid.renderColumn(title);
await this.getColumnHeader(title).scrollIntoViewIfNeeded();
}
await expect(this.getColumnHeader(title)).toContainText(title);

View File

@@ -34,7 +34,7 @@ export class MetaDataPage extends BasePage {
await this.get().locator(`button:has-text("Sync Now")`).click();
await this.verifyToast({ message: 'Table metadata recreated successfully' });
// wait for clickability of the sync button
await this.get().locator(`.sync-completed`).waitFor({ state: 'visible' });
await this.get().locator(`.sync-completed`).waitFor({ state: 'visible', timeout: 10000 });
await this.get().locator(`.sync-completed`).click();
}

View File

@@ -87,7 +87,7 @@ export class ToolbarFieldsPage extends BasePage {
await this.toolbar.parent.waitLoading();
}
async toggleShowAllFields({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
async toggleShowAllFields({ isLocallySaved, isKanban }: { isLocallySaved?: boolean, isKanban?: boolean } = {}) {
await this.toolbar.clickFields();
await this.waitForResponse({
uiAction: () => this.get().locator(`.nc-fields-toggle-show-all-fields`).click(),
@@ -95,6 +95,10 @@ export class ToolbarFieldsPage extends BasePage {
httpMethodsToMatch: ['GET'],
timeout: 30000, // for Kanban, show all fields can take a long time
});
// TODO: fix this (Show all for kanban takes time to load)
if (isKanban) await new Promise((r) => setTimeout(r, 2000));
await this.toolbar.clickFields();
}

View File

@@ -86,8 +86,8 @@ test.describe('View', () => {
});
// hide fields
await toolbar.fields.toggleShowAllFields({ isLocallySaved: false });
await toolbar.fields.toggleShowAllFields({ isLocallySaved: false });
await toolbar.fields.toggleShowAllFields({ isLocallySaved: false, isKanban: true });
await toolbar.fields.toggleShowAllFields({ isLocallySaved: false, isKanban: true });
await toolbar.fields.toggle({ title: 'Title' });
await kanban.verifyCardCount({
count: [0, 25, 25, 25, 25, 25],
@@ -221,8 +221,8 @@ test.describe('View', () => {
});
await toolbar.clickFilter();
await toolbar.fields.toggleShowAllFields();
await toolbar.fields.toggleShowAllFields();
await toolbar.fields.toggleShowAllFields({ isKanban: true });
await toolbar.fields.toggleShowAllFields({ isKanban: true });
await toolbar.fields.toggle({ title: 'Title' });
await dashboard.viewSidebar.copyView({ title: 'Film Kanban' });
@@ -279,8 +279,8 @@ test.describe('View', () => {
await kanban.verifyCollapseStackCount({ count: 0 });
// add record to stack & verify
await toolbar.fields.toggleShowAllFields();
await toolbar.fields.toggleShowAllFields();
await toolbar.fields.toggleShowAllFields({ isKanban: true });
await toolbar.fields.toggleShowAllFields({ isKanban: true });
await toolbar.fields.toggleShowSystemFields();
await toolbar.fields.toggle({ title: 'LanguageId' });
await toolbar.fields.toggle({ title: 'Title' });