diff --git a/packages/ui/src/server/configs/e2e/local.ts b/packages/ui/src/server/configs/e2e/local.ts index 01dc0fc0d..93514e98e 100644 --- a/packages/ui/src/server/configs/e2e/local.ts +++ b/packages/ui/src/server/configs/e2e/local.ts @@ -47,6 +47,11 @@ const e2eConfig: Partial = { reUseEffectiveAclForPath: '//sys/access_control_object_namespaces[^/+]{0,}', }, + userColumnPresets: { + cluster: 'ui', + dynamicTablePath: '//tmp/userColumnPresets', + }, + defaultUserSettingsOverrides: { ...common.defaultUserSettingsOverrides, ['global::navigation::useSmartSort']: false, diff --git a/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnSelectorButton.tsx b/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnSelectorButton.tsx index 98e906646..1b4a07f6f 100644 --- a/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnSelectorButton.tsx +++ b/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnSelectorButton.tsx @@ -60,6 +60,7 @@ function ColumnSelectorButton({ view={view} pin={allowUserColumnPresets ? 'round-brick' : 'round-round'} style={showAllColumns ? undefined : actionStyle} + qa="table-columns-button" > Columns diff --git a/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnsPresetButton.tsx b/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnsPresetButton.tsx index 69c1f46d2..54550dd45 100644 --- a/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnsPresetButton.tsx +++ b/packages/ui/src/ui/pages/navigation/content/Table/TableOverview/ColumnsPresetButton.tsx @@ -21,7 +21,12 @@ function SharePresetButton(props: Props) { return ( - diff --git a/packages/ui/src/ui/store/actions/navigation/content/table/table.js b/packages/ui/src/ui/store/actions/navigation/content/table/table.js index 8ce253f03..0659829e4 100644 --- a/packages/ui/src/ui/store/actions/navigation/content/table/table.js +++ b/packages/ui/src/ui/store/actions/navigation/content/table/table.js @@ -571,6 +571,9 @@ export function openTableWithPresetOfColumns() { saveColumnPreset(map_(visibleColumns, 'name'), cluster).then((hash) => { const {href} = window.location; const url = `${href}&columns=${hash}`; + try { + navigator.clipboard.writeText(url); + } catch {} openInNewTab(url); metrics.countEvent({ diff --git a/packages/ui/tests/init-cluster-e2e.sh b/packages/ui/tests/init-cluster-e2e.sh index 9f11b2bad..a47383a89 100755 --- a/packages/ui/tests/init-cluster-e2e.sh +++ b/packages/ui/tests/init-cluster-e2e.sh @@ -1,9 +1,8 @@ #!/bin/bash +set -xe EXPIRATION_TIMEOUT=${EXPIRATION_TIMEOUT:-3600000} -set -xe - if ! which yt >/dev/null; then echo You have to install YT CLI manually, please 1>&2 exit 1 @@ -14,6 +13,17 @@ if [ -z "${YT_PROXY}" ]; then exit 2 fi +function createAndMountDynamicTable { + path=$1 + schema=$2 + yt create -i --attributes "{dynamic=%true;schema=$schema}" table $path + yt mount-table $path +} + +# userColumnPresets +createAndMountDynamicTable "//tmp/userColumnPresets" "[{name=hash;sort_order=ascending;type=string};{name=columns_json;type=string}]" + + suffix=E=$(mktemp -u XXXXXX) # to lower case E2E_SUFFIX=$(mktemp -u XXXXXX | tr '[:upper:]' '[:lower:]') @@ -132,8 +142,7 @@ createAccountForQuotaEditor e2e-overcommit yt set //sys/accounts/e2e-overcommit-${E2E_SUFFIX}/@allow_children_limit_overcommit %true DYN_TABLE=${E2E_DIR}/dynamic-table -yt create --attributes "{dynamic=%true;schema=[{name=key;sort_order=ascending;type=string};{name=value;type=string};{name=empty;type=any}]}" table ${DYN_TABLE} -yt mount-table ${DYN_TABLE} +createAndMountDynamicTable "$DYN_TABLE" "[{name=key;sort_order=ascending;type=string};{name=value;type=string};{name=empty;type=any}]" ( set +x for ((i = 0; i < 300; i++)); do diff --git a/packages/ui/tests/playwright.config.ts b/packages/ui/tests/playwright.config.ts index 816f76d4b..a646a9ac2 100644 --- a/packages/ui/tests/playwright.config.ts +++ b/packages/ui/tests/playwright.config.ts @@ -40,6 +40,8 @@ export default defineConfig({ screenshot: 'only-on-failure', video: 'retain-on-failure', + + permissions: ['clipboard-read', 'clipboard-write'], }, expect: { toHaveScreenshot: { diff --git a/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts index a537bf3a4..f07f88bd8 100644 --- a/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts +++ b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts @@ -2,6 +2,7 @@ import {Page, expect, test} from '@playwright/test'; import {E2E_DIR, makeClusterUrl} from '../../../utils'; import {replaceInnerHtml} from '../../../utils/dom'; import {TablePage} from './TablePage'; +import {waitForClipboardText} from '../../../utils/clipboard'; function tablePage(page: Page) { return new TablePage({page}); @@ -90,3 +91,49 @@ test('Navigation: static-table - rowselector', async ({page}) => { await expect(page).toHaveScreenshot(); }); + +test('Navigation: table - userColumnPresets', async ({page}) => { + await page.goto(makeClusterUrl(`navigation?path=${E2E_DIR}/static-table`)); + + await tablePage(page).waitForTablContent('.navigation-table', 10); + await tablePage(page).replaceStaticTableMeta(); + + await test.step('select only the "key" column', async () => { + await page.getByTestId('table-columns-button').click(); + await page.getByText('Remove all').click(); + await page.click( + '.column-selector__list-item:nth-child(1) .column-selector__list-item-check', + { + force: true, + }, + ); + await page.mouse.move(0, 0); + await expect(page).toHaveScreenshot(); + await page.click('button :text("Apply")'); + }); + + const shareBtn = page.getByTestId('table-columns-share-button'); + + await test.step('share button should be visible', async () => { + await shareBtn.waitFor(); + + await expect(page).toHaveScreenshot(); + }); + + await test.step('open url from clipboard', async () => { + await shareBtn.click(); + + await page.waitForTimeout(2000); + + const url = await waitForClipboardText(page); + + await page.goto(url!); + + await tablePage(page).waitForTablContent('.navigation-table', 10); + await tablePage(page).replaceStaticTableMeta(); + + await page.getByText('key0').waitFor(); + + await expect(page).toHaveScreenshot(); + }); +}); diff --git a/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-1-chromium-linux.png b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-1-chromium-linux.png new file mode 100644 index 000000000..995dbce5f Binary files /dev/null and b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-1-chromium-linux.png differ diff --git a/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-2-chromium-linux.png b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-2-chromium-linux.png new file mode 100644 index 000000000..91a78422f Binary files /dev/null and b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-2-chromium-linux.png differ diff --git a/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-3-chromium-linux.png b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-3-chromium-linux.png new file mode 100644 index 000000000..fd2146a67 Binary files /dev/null and b/packages/ui/tests/screenshots/pages/navigation/navigation.table.base.screen.ts-snapshots/Navigation-table---userColumnPresets-3-chromium-linux.png differ diff --git a/packages/ui/tests/utils/clipboard.ts b/packages/ui/tests/utils/clipboard.ts new file mode 100644 index 000000000..9db69a4c1 --- /dev/null +++ b/packages/ui/tests/utils/clipboard.ts @@ -0,0 +1,29 @@ +import {Page} from '@playwright/test'; + +let counter = -1; + +export async function waitForClipboardText(page: Page) { + const key = `__##__clipboard_value_${++counter}`; + + const res = await page.waitForFunction((key) => { + const storage = window as unknown as Record; + + console.log({k: key, v: storage[key]}); + + if (storage[key]) { + return storage[key]; + } + + async function fileClipboard() { + if (storage[key]) { + return; + } else { + storage[key] = await navigator.clipboard.readText(); + } + } + + requestIdleCallback(() => fileClipboard()); + }, key); + + return await res.jsonValue(); +}