diff --git a/packages/nc-gui/components/cell/Checkbox.vue b/packages/nc-gui/components/cell/Checkbox.vue index 1e533f3c05..6b1bd0e50c 100644 --- a/packages/nc-gui/components/cell/Checkbox.vue +++ b/packages/nc-gui/components/cell/Checkbox.vue @@ -17,9 +17,9 @@ const emits = defineEmits() const active = inject(ActiveCellInj, ref(false)) -let vModel = $computed({ +let vModel = $computed({ get: () => !!props.modelValue && props.modelValue !== '0' && props.modelValue !== 0, - set: (val) => emits('update:modelValue', val), + set: (val: boolean) => emits('update:modelValue', val), }) const column = inject(ColumnInj) @@ -39,7 +39,13 @@ const checkboxMeta = $computed(() => { } }) -function onClick(force?: boolean) { +function onClick(force?: boolean, event?: MouseEvent) { + if ( + (event?.target as HTMLElement)?.classList?.contains('nc-checkbox') || + (event?.target as HTMLElement)?.closest('.nc-checkbox') + ) { + return + } if (!readOnly?.value && (force || active.value)) { vModel = !vModel } @@ -64,16 +70,17 @@ useSelectedCellKeyupListener(active, (e) => { 'nc-cell-hover-show': !vModel && !readOnly, 'opacity-0': readOnly && !vModel, }" - @click="onClick(false)" + @click="onClick(false, $event)" >
diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index 406c869dda..0e8ac91571 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -180,7 +180,7 @@ useSelectedCellKeyupListener(active, (e) => { break default: // toggle only if char key pressed - if (e.key?.length === 1) { + if (!(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) && e.key?.length === 1) { e.stopPropagation() isOpen.value = true } diff --git a/packages/nc-gui/components/cell/SingleSelect.vue b/packages/nc-gui/components/cell/SingleSelect.vue index 0445193150..5258ffe327 100644 --- a/packages/nc-gui/components/cell/SingleSelect.vue +++ b/packages/nc-gui/components/cell/SingleSelect.vue @@ -106,7 +106,7 @@ useSelectedCellKeyupListener(active, (e) => { break default: // toggle only if char key pressed - if (e.key?.length === 1) { + if (!(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) && e.key?.length === 1) { e.stopPropagation() isOpen.value = true } @@ -153,7 +153,7 @@ async function addIfMissingAndSave() { ) vModel.value = newOptValue await getMeta(column.value.fk_model_id!, true) - } catch (e) { + } catch (e: any) { console.log(e) message.error(await extractSdkResponseErrorMsg(e)) } diff --git a/packages/nc-gui/components/smartsheet/Grid.vue b/packages/nc-gui/components/smartsheet/Grid.vue index 2eb66428c0..69f6a49dc7 100644 --- a/packages/nc-gui/components/smartsheet/Grid.vue +++ b/packages/nc-gui/components/smartsheet/Grid.vue @@ -169,99 +169,120 @@ const getContainerScrollForElement = ( } const { selectCell, startSelectRange, endSelectRange, clearSelectedRange, copyValue, isCellSelected, selectedCell } = - useMultiSelect(fields, data, $$(editEnabled), isPkAvail, clearCell, makeEditable, scrollToCell, (e: KeyboardEvent) => { - // ignore navigating if picker(Date, Time, DateTime, Year) - // or single/multi select options is open - const activePickerOrDropdownEl = document.querySelector( - '.nc-picker-datetime.active,.nc-dropdown-single-select-cell.active,.nc-dropdown-multi-select-cell.active,.nc-picker-date.active,.nc-picker-year.active,.nc-picker-time.active', - ) - if (activePickerOrDropdownEl) { - e.preventDefault() - return true - } - - // skip keyboard event handling if there is a drawer / modal - if (isDrawerOrModalExist()) { - return true - } - - const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey - const altOrOptionKey = e.altKey - if (e.key === ' ') { - if (selectedCell.row !== null && !editEnabled) { + useMultiSelect( + meta, + fields, + data, + $$(editEnabled), + isPkAvail, + clearCell, + makeEditable, + scrollToCell, + (e: KeyboardEvent) => { + // ignore navigating if picker(Date, Time, DateTime, Year) + // or single/multi select options is open + const activePickerOrDropdownEl = document.querySelector( + '.nc-picker-datetime.active,.nc-dropdown-single-select-cell.active,.nc-dropdown-multi-select-cell.active,.nc-picker-date.active,.nc-picker-year.active,.nc-picker-time.active', + ) + if (activePickerOrDropdownEl) { e.preventDefault() - const row = data.value[selectedCell.row] - expandForm(row) - return true - } - } else if (e.key === 'Escape') { - if (editEnabled) { - editEnabled = false - return true - } - } else if (e.key === 'Enter') { - if (e.shiftKey) { - // add a line break for types like LongText / JSON return true } - if (editEnabled) { - editEnabled = false + + // skip keyboard event handling if there is a drawer / modal + if (isDrawerOrModalExist()) { return true } - } - if (cmdOrCtrl) { - switch (e.key) { - case 'ArrowUp': + const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey + const altOrOptionKey = e.altKey + if (e.key === ' ') { + if (selectedCell.row !== null && !editEnabled) { e.preventDefault() - selectedCell.row = 0 - selectedCell.col = selectedCell.col ?? 0 - scrollToCell?.() - editEnabled = false + const row = data.value[selectedCell.row] + expandForm(row) return true - case 'ArrowDown': - e.preventDefault() - selectedCell.row = data.value.length - 1 - selectedCell.col = selectedCell.col ?? 0 - scrollToCell?.() + } + } else if (e.key === 'Escape') { + if (editEnabled) { editEnabled = false return true - case 'ArrowRight': - e.preventDefault() - selectedCell.row = selectedCell.row ?? 0 - selectedCell.col = fields.value?.length - 1 - scrollToCell?.() - editEnabled = false + } + } else if (e.key === 'Enter') { + if (e.shiftKey) { + // add a line break for types like LongText / JSON return true - case 'ArrowLeft': - e.preventDefault() - selectedCell.row = selectedCell.row ?? 0 - selectedCell.col = 0 - scrollToCell?.() + } + if (editEnabled) { editEnabled = false return true + } } - } - if (altOrOptionKey) { - switch (e.keyCode) { - case 82: { - // ALT + R - if (isAddingEmptyRowAllowed) { - addEmptyRow() - } - break + if (cmdOrCtrl) { + switch (e.key) { + case 'ArrowUp': + e.preventDefault() + selectedCell.row = 0 + selectedCell.col = selectedCell.col ?? 0 + scrollToCell?.() + editEnabled = false + return true + case 'ArrowDown': + e.preventDefault() + selectedCell.row = data.value.length - 1 + selectedCell.col = selectedCell.col ?? 0 + scrollToCell?.() + editEnabled = false + return true + case 'ArrowRight': + e.preventDefault() + selectedCell.row = selectedCell.row ?? 0 + selectedCell.col = fields.value?.length - 1 + scrollToCell?.() + editEnabled = false + return true + case 'ArrowLeft': + e.preventDefault() + selectedCell.row = selectedCell.row ?? 0 + selectedCell.col = 0 + scrollToCell?.() + editEnabled = false + return true } - case 67: { - // ALT + C - if (isAddingColumnAllowed) { - addColumnDropdown.value = true + } + + if (altOrOptionKey) { + switch (e.keyCode) { + case 82: { + // ALT + R + if (isAddingEmptyRowAllowed) { + addEmptyRow() + } + break + } + case 67: { + // ALT + C + if (isAddingColumnAllowed) { + addColumnDropdown.value = true + } + break } - break } } - } - }) + }, + async (ctx: { row: number; col?: number; updatedColumnTitle?: string }) => { + const rowObj = data.value[ctx.row] + const columnObj = ctx.col !== null && ctx.col !== undefined ? fields.value[ctx.col] : null + + if (!ctx.updatedColumnTitle && isVirtualCol(columnObj)) { + return + } + + // update/save cell value + await updateOrSaveRow(rowObj, ctx.updatedColumnTitle || columnObj.title) + }, + ) function scrollToCell(row?: number | null, col?: number | null) { row = row ?? selectedCell.row @@ -369,7 +390,7 @@ watch(contextMenu, () => { const rowRefs = $ref() -async function clearCell(ctx: { row: number; col: number } | null) { +async function clearCell(ctx: { row: number; col: number } | null, skipUpdate = false) { if ( !ctx || !hasEditPermission || @@ -386,8 +407,11 @@ async function clearCell(ctx: { row: number; col: number } | null) { } rowObj.row[columnObj.title] = null - // update/save cell value - await updateOrSaveRow(rowObj, columnObj.title) + + if (!skipUpdate) { + // update/save cell value + await updateOrSaveRow(rowObj, columnObj.title) + } } function makeEditable(row: Row, col: ColumnType) { diff --git a/packages/nc-gui/composables/useColumnCreateStore.ts b/packages/nc-gui/composables/useColumnCreateStore.ts index 047e127dd9..c86fe2aa4f 100644 --- a/packages/nc-gui/composables/useColumnCreateStore.ts +++ b/packages/nc-gui/composables/useColumnCreateStore.ts @@ -194,7 +194,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState const addOrUpdate = async (onSuccess: () => void, columnPosition?: Pick) => { try { if (!(await validate())) return - } catch (e) { + } catch (e: any) { const errorMsgs = e.errorFields ?.map((e: any) => e.errors?.join(', ')) .filter(Boolean) diff --git a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts new file mode 100644 index 0000000000..7e4626aeaf --- /dev/null +++ b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts @@ -0,0 +1,83 @@ +import dayjs from 'dayjs' +import { UITypes } from 'nocodb-sdk' + +export default function convertCellData(args: { from: UITypes; to: UITypes; value: any }, isMysql = false) { + const { from, to, value } = args + if (from === to && ![UITypes.Attachment, UITypes.Date, UITypes.DateTime, UITypes.Time, UITypes.Year].includes(to)) { + return value + } + + const dateFormat = isMysql ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ' + + switch (to) { + case UITypes.Number: { + const parsedNumber = Number(value) + if (isNaN(parsedNumber)) { + throw new TypeError(`Cannot convert '${value}' to number`) + } + return parsedNumber + } + case UITypes.Checkbox: + return Boolean(value) + case UITypes.Date: { + const parsedDate = dayjs(value) + if (!parsedDate.isValid()) throw new Error('Not a valid date') + return parsedDate.format('YYYY-MM-DD') + } + case UITypes.DateTime: { + const parsedDateTime = dayjs(value) + if (!parsedDateTime.isValid()) { + throw new Error('Not a valid datetime value') + } + return parsedDateTime.format(dateFormat) + } + case UITypes.Time: { + let parsedTime = dayjs(value) + + if (!parsedTime.isValid()) { + parsedTime = dayjs(value, 'HH:mm:ss') + } + if (!parsedTime.isValid()) { + parsedTime = dayjs(`1999-01-01 ${value}`) + } + if (!parsedTime.isValid()) { + throw new Error('Not a valid time value') + } + return parsedTime.format(dateFormat) + } + case UITypes.Year: { + if (/^\d+$/.test(value)) { + return +value + } + + const parsedDate = dayjs(value) + + if (parsedDate.isValid()) { + return parsedDate.format('YYYY') + } + + throw new Error('Not a valid year value') + } + case UITypes.Attachment: { + let parsedVal + try { + parsedVal = typeof value === 'string' ? JSON.parse(value) : value + parsedVal = Array.isArray(parsedVal) ? parsedVal : [parsedVal] + } catch (e) { + throw new Error('Invalid attachment data') + } + if (parsedVal.some((v: any) => v && !(v.url || v.data))) { + throw new Error('Invalid attachment data') + } + return JSON.stringify(parsedVal) + } + case UITypes.LinkToAnotherRecord: + case UITypes.Lookup: + case UITypes.Rollup: + case UITypes.Formula: + case UITypes.QrCode: + throw new Error(`Unsupported conversion from ${from} to ${to}`) + default: + return value + } +} diff --git a/packages/nc-gui/composables/useMultiSelect/index.ts b/packages/nc-gui/composables/useMultiSelect/index.ts index c27ea269ed..dd4e645609 100644 --- a/packages/nc-gui/composables/useMultiSelect/index.ts +++ b/packages/nc-gui/composables/useMultiSelect/index.ts @@ -1,14 +1,31 @@ import type { MaybeRef } from '@vueuse/core' -import type { ColumnType } from 'nocodb-sdk' +import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk' +import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk' import type { Cell } from './cellRange' import { CellRange } from './cellRange' -import { copyTable, message, reactive, ref, unref, useCopy, useEventListener, useI18n } from '#imports' +import convertCellData from './convertCellData' import type { Row } from '~/lib' +import { + copyTable, + extractPkFromRow, + extractSdkResponseErrorMsg, + isMac, + message, + reactive, + ref, + unref, + useCopy, + useEventListener, + useI18n, + useMetas, + useProject, +} from '#imports' /** * Utility to help with multi-selecting rows/cells in the smartsheet */ export function useMultiSelect( + _meta: MaybeRef, fields: MaybeRef, data: MaybeRef, _editEnabled: MaybeRef, @@ -17,11 +34,20 @@ export function useMultiSelect( makeEditable: Function, scrollToActiveCell?: (row?: number | null, col?: number | null) => void, keyEventHandler?: Function, + syncCellData?: Function, ) { + const meta = ref(_meta) + const { t } = useI18n() const { copy } = useCopy() + const { getMeta } = useMetas() + + const { isMysql } = useProject() + + let clipboardContext = $ref<{ value: any; uidt: UITypes } | null>(null) + const editEnabled = ref(_editEnabled) const selectedCell = reactive({ row: null, col: null }) @@ -49,6 +75,11 @@ export function useMultiSelect( const columnObj = unref(fields)[cpCol] let textToCopy = (columnObj.title && rowObj.row[columnObj.title]) || '' + + if (columnObj.uidt === UITypes.Checkbox) { + textToCopy = !!textToCopy + } + if (typeof textToCopy === 'object') { textToCopy = JSON.stringify(textToCopy) } @@ -217,12 +248,95 @@ export function useMultiSelect( const columnObj = unref(fields)[selectedCell.col] - if ((!unref(editEnabled) && e.metaKey) || e.ctrlKey) { + if ( + (!unref(editEnabled) || + [ + UITypes.DateTime, + UITypes.Date, + UITypes.Year, + UITypes.Time, + UITypes.Lookup, + UITypes.Rollup, + UITypes.Formula, + UITypes.Attachment, + UITypes.Checkbox, + UITypes.Rating, + ].includes(columnObj.uidt as UITypes)) && + (isMac() ? e.metaKey : e.ctrlKey) + ) { switch (e.keyCode) { // copy - ctrl/cmd +c case 67: + // set clipboard context only if single cell selected + if (rowObj.row[columnObj.title!]) { + clipboardContext = { + value: rowObj.row[columnObj.title!], + uidt: columnObj.uidt as UITypes, + } + } else { + clipboardContext = null + } await copyValue() break + case 86: + try { + // handle belongs to column + if ( + columnObj.uidt === UITypes.LinkToAnotherRecord && + (columnObj.colOptions as LinkToAnotherRecordType)?.type === RelationTypes.BELONGS_TO + ) { + if (!clipboardContext || typeof clipboardContext.value !== 'object') { + return message.info('Invalid data') + } + rowObj.row[columnObj.title!] = convertCellData( + { + value: clipboardContext.value, + from: clipboardContext.uidt, + to: columnObj.uidt as UITypes, + }, + isMysql.value, + ) + e.preventDefault() + + const foreignKeyColumn = meta.value?.columns?.find( + (column: ColumnType) => column.id === (columnObj.colOptions as LinkToAnotherRecordType)?.fk_child_column_id, + ) + + const relatedTableMeta = await getMeta((columnObj.colOptions as LinkToAnotherRecordType).fk_related_model_id!) + + if (!foreignKeyColumn) return + + rowObj.row[foreignKeyColumn.title!] = extractPkFromRow( + clipboardContext.value, + (relatedTableMeta as any)!.columns!, + ) + + return await syncCellData?.({ ...selectedCell, updatedColumnTitle: foreignKeyColumn.title }) + } + + // if it's a virtual column excluding belongs to cell type skip paste + if (isVirtualCol(columnObj)) { + return message.info(t('msg.info.pasteNotSupported')) + } + + if (clipboardContext) { + rowObj.row[columnObj.title!] = convertCellData( + { + value: clipboardContext.value, + from: clipboardContext.uidt, + to: columnObj.uidt as UITypes, + }, + isMysql.value, + ) + e.preventDefault() + syncCellData?.(selectedCell) + } else { + clearCell(selectedCell as { row: number; col: number }, true) + makeEditable(rowObj, columnObj) + } + } catch (error: any) { + message.error(await extractSdkResponseErrorMsg(error)) + } } } diff --git a/packages/nc-gui/lang/en.json b/packages/nc-gui/lang/en.json index 8338c832e4..09c4984f1c 100644 --- a/packages/nc-gui/lang/en.json +++ b/packages/nc-gui/lang/en.json @@ -497,6 +497,7 @@ } }, "info": { + "pasteNotSupported": "Paste operation is not supported on the active cell", "roles": { "orgCreator": "Creator can create new projects and access any invited project.", "orgViewer": "Viewer is not allowed to create new projects but they can access any invited project." diff --git a/tests/playwright/pages/Dashboard/Grid/Column/index.ts b/tests/playwright/pages/Dashboard/Grid/Column/index.ts index b34cacd235..1116e886e5 100644 --- a/tests/playwright/pages/Dashboard/Grid/Column/index.ts +++ b/tests/playwright/pages/Dashboard/Grid/Column/index.ts @@ -155,7 +155,7 @@ export class ColumnPageObject extends BasePage { await this.get().locator('.ant-select-selection-search-input[aria-expanded="true"]').fill(type); // Select column type - await this.rootPage.locator(`text=${type}`).nth(1).click(); + await this.rootPage.locator('.rc-virtual-list-holder-inner > div').locator(`text="${type}"`).click(); } async changeReferencedColumnForQrCode({ titleOfReferencedColumn }: { titleOfReferencedColumn: string }) { diff --git a/tests/playwright/pages/Dashboard/common/Cell/DateCell.ts b/tests/playwright/pages/Dashboard/common/Cell/DateCell.ts new file mode 100644 index 0000000000..d1996afca4 --- /dev/null +++ b/tests/playwright/pages/Dashboard/common/Cell/DateCell.ts @@ -0,0 +1,35 @@ +import { CellPageObject } from '.'; +import BasePage from '../../../Base'; + +export class DateCellPageObject extends BasePage { + readonly cell: CellPageObject; + + constructor(cell: CellPageObject) { + super(cell.rootPage); + this.cell = cell; + } + + get({ index, columnHeader }: { index?: number; columnHeader: string }) { + return this.cell.get({ index, columnHeader }); + } + + async open({ index, columnHeader }: { index: number; columnHeader: string }) { + await this.cell.dblclick({ + index, + columnHeader, + }); + } + + async selectDate({ + // date in format `YYYY-MM-DD` + date, + }: { + date: string; + }) { + await this.rootPage.locator(`td[title="${date}"]`).click(); + } + + async close() { + await this.rootPage.keyboard.press('Escape'); + } +} diff --git a/tests/playwright/pages/Dashboard/common/Cell/index.ts b/tests/playwright/pages/Dashboard/common/Cell/index.ts index 204375dcda..ca04a11b83 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/index.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/index.ts @@ -6,6 +6,7 @@ import { SelectOptionCellPageObject } from './SelectOptionCell'; import { SharedFormPage } from '../../../SharedForm'; import { CheckboxCellPageObject } from './CheckboxCell'; import { RatingCellPageObject } from './RatingCell'; +import { DateCellPageObject } from './DateCell'; export class CellPageObject extends BasePage { readonly parent: GridPage | SharedFormPage; @@ -13,6 +14,8 @@ export class CellPageObject extends BasePage { readonly attachment: AttachmentCellPageObject; readonly checkbox: CheckboxCellPageObject; readonly rating: RatingCellPageObject; + readonly date: DateCellPageObject; + constructor(parent: GridPage | SharedFormPage) { super(parent.rootPage); this.parent = parent; @@ -20,6 +23,7 @@ export class CellPageObject extends BasePage { this.attachment = new AttachmentCellPageObject(this); this.checkbox = new CheckboxCellPageObject(this); this.rating = new RatingCellPageObject(this); + this.date = new DateCellPageObject(this); } get({ index, columnHeader }: { index?: number; columnHeader: string }): Locator { @@ -30,8 +34,11 @@ export class CellPageObject extends BasePage { } } - async click({ index, columnHeader }: { index: number; columnHeader: string }) { - await this.get({ index, columnHeader }).click(); + async click( + { index, columnHeader }: { index: number; columnHeader: string }, + ...options: Parameters + ) { + await this.get({ index, columnHeader }).click(...options); await (await this.get({ index, columnHeader }).elementHandle()).waitForElementState('stable'); } @@ -179,4 +186,13 @@ export class CellPageObject extends BasePage { param.role === 'creator' || param.role === 'editor' ? 1 : 0 ); } + + async copyToClipboard( + { index, columnHeader }: { index: number; columnHeader: string }, + ...clickOptions: Parameters + ) { + await this.get({ index, columnHeader }).click(...clickOptions); + + await this.get({ index, columnHeader }).press('Control+C'); + } } diff --git a/tests/playwright/scripts/stressTestNewlyAddedTest.js b/tests/playwright/scripts/stressTestNewlyAddedTest.js index 5af08d6cb8..94eb9f627d 100644 --- a/tests/playwright/scripts/stressTestNewlyAddedTest.js +++ b/tests/playwright/scripts/stressTestNewlyAddedTest.js @@ -13,7 +13,7 @@ void (async () => { return; } - const { stdout } = await exec(`git diff origin/develop -- *.spec.ts **/*.spec.ts | grep test\\(`); + const { stdout } = await exec(`git diff origin/develop -- **/*.spec.ts | grep test\\( | cat`); // eslint-disable-next-line no-undef const dbType = process.env.E2E_DB_TYPE; diff --git a/tests/playwright/tests/gridOperations.spec.ts b/tests/playwright/tests/gridOperations.spec.ts new file mode 100644 index 0000000000..d05b9fcc54 --- /dev/null +++ b/tests/playwright/tests/gridOperations.spec.ts @@ -0,0 +1,117 @@ +import { expect, test } from '@playwright/test'; +import { DashboardPage } from '../pages/Dashboard'; +import setup from '../setup'; + +test.describe('Grid operations', () => { + let dashboard: DashboardPage; + let context: any; + + test.beforeEach(async ({ page }) => { + context = await setup({ page }); + dashboard = new DashboardPage(page, context.project); + }); + + test('Clipboard support', async () => { + // close 'Team & Auth' tab + await dashboard.closeTab({ title: 'Team & Auth' }); + + await dashboard.treeView.createTable({ title: 'Sheet1' }); + + await dashboard.grid.column.create({ + title: 'Number', + type: 'Number', + }); + await dashboard.grid.column.create({ + title: 'Checkbox', + type: 'Checkbox', + }); + await dashboard.grid.column.create({ + title: 'Date', + type: 'Date', + }); + await dashboard.grid.column.create({ + title: 'Attachment', + type: 'Attachment', + }); + + await dashboard.grid.addNewRow({ + index: 0, + }); + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Number', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'Number', + text: '123', + }); + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Checkbox', + }); + + const today = new Date().toISOString().slice(0, 10); + await dashboard.grid.cell.date.open({ + index: 0, + columnHeader: 'Date', + }); + await dashboard.grid.cell.date.selectDate({ + date: today, + }); + await dashboard.grid.cell.date.close(); + + await dashboard.grid.cell.attachment.addFile({ + index: 0, + columnHeader: 'Attachment', + filePath: `${process.cwd()}/fixtures/sampleFiles/1.json`, + }); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Title', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('Row 0'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Number', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('123'); + + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'Checkbox', + }, + { position: { x: 1, y: 1 } } + ); + await new Promise(resolve => setTimeout(resolve, 5000)); + expect(await dashboard.grid.cell.getClipboardText()).toBe('true'); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Checkbox', + }); + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'Checkbox', + }, + { position: { x: 1, y: 1 } } + ); + expect(await dashboard.grid.cell.getClipboardText()).toBe('false'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Date', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe(today); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Attachment', + }); + // expect(await dashboard.grid.cell.getClipboardText()).toBe('1.json'); + }); +});