mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Add tests and tooltips to new Add Column menu
Summary: Adds tooltips to the menu and tests for recently-added functionality. Test Plan: Browser tests. Reviewers: JakubSerafin Reviewed By: JakubSerafin Subscribers: JakubSerafin Differential Revision: https://phab.getgrist.com/D4087
This commit is contained in:
parent
69d5ee53a8
commit
de33c5a3c6
@ -3,6 +3,8 @@ import GridView from 'app/client/components/GridView';
|
|||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
import {ViewSectionRec} from 'app/client/models/DocModel';
|
import {ViewSectionRec} from 'app/client/models/DocModel';
|
||||||
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||||
|
import {GristTooltips} from 'app/client/ui/GristTooltips';
|
||||||
|
import {withInfoTooltip} from 'app/client/ui/tooltips';
|
||||||
import {testId, theme, vars} from 'app/client/ui2018/cssVars';
|
import {testId, theme, vars} from 'app/client/ui2018/cssVars';
|
||||||
import {icon} from 'app/client/ui2018/icons';
|
import {icon} from 'app/client/ui2018/icons';
|
||||||
import {
|
import {
|
||||||
@ -265,7 +267,11 @@ function buildUUIDMenuItem(gridView: GridView, index?: number) {
|
|||||||
await gridView.gristDoc.convertToTrigger(colRef, 'UUID()');
|
await gridView.gristDoc.convertToTrigger(colRef, 'UUID()');
|
||||||
}, {nestInActiveBundle: true});
|
}, {nestInActiveBundle: true});
|
||||||
},
|
},
|
||||||
t('UUID'),
|
withInfoTooltip(
|
||||||
|
t('UUID'),
|
||||||
|
GristTooltips.uuid(),
|
||||||
|
{variant: 'hover'}
|
||||||
|
),
|
||||||
testId('new-columns-menu-shortcuts-uuid'),
|
testId('new-columns-menu-shortcuts-uuid'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -522,8 +528,15 @@ function buildLookupSection(gridView: GridView, index?: number){
|
|||||||
: [lookupMenu, reverseLookupMenu];
|
: [lookupMenu, reverseLookupMenu];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
menuSubHeader(t("Lookups"), testId('new-columns-menu-lookups')),
|
menuSubHeader(
|
||||||
|
withInfoTooltip(
|
||||||
|
t('Lookups'),
|
||||||
|
GristTooltips.lookups(),
|
||||||
|
{variant: 'hover'}
|
||||||
|
),
|
||||||
|
testId('new-columns-menu-lookups'),
|
||||||
|
),
|
||||||
...menuContent
|
...menuContent
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,9 @@ export type Tooltip =
|
|||||||
| 'workOnACopy'
|
| 'workOnACopy'
|
||||||
| 'openAccessRules'
|
| 'openAccessRules'
|
||||||
| 'addRowConditionalStyle'
|
| 'addRowConditionalStyle'
|
||||||
| 'addColumnConditionalStyle';
|
| 'addColumnConditionalStyle'
|
||||||
|
| 'uuid'
|
||||||
|
| 'lookups';
|
||||||
|
|
||||||
export type TooltipContentFunc = (...domArgs: DomElementArg[]) => DomContents;
|
export type TooltipContentFunc = (...domArgs: DomElementArg[]) => DomContents;
|
||||||
|
|
||||||
@ -98,6 +100,21 @@ see or edit which parts of your document.')
|
|||||||
),
|
),
|
||||||
...args,
|
...args,
|
||||||
),
|
),
|
||||||
|
uuid: (...args: DomElementArg[]) => cssTooltipContent(
|
||||||
|
dom('div', t('A UUID is a randomly-generated string that is useful for unique identifiers and link keys.')),
|
||||||
|
dom('div',
|
||||||
|
cssLink({href: commonUrls.helpLinkKeys, target: '_blank'}, t('Learn more.')),
|
||||||
|
),
|
||||||
|
...args,
|
||||||
|
),
|
||||||
|
lookups: (...args: DomElementArg[]) => cssTooltipContent(
|
||||||
|
dom('div', t('Lookups return data from related tables.')),
|
||||||
|
dom('div', t('Use reference columns to relate data in different tables.')),
|
||||||
|
dom('div',
|
||||||
|
cssLink({href: commonUrls.helpColRefs, target: '_blank'}, t('Learn more.')),
|
||||||
|
),
|
||||||
|
...args,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface BehavioralPromptContent {
|
export interface BehavioralPromptContent {
|
||||||
|
@ -386,7 +386,7 @@ export class PageWidgetSelect extends Disposable {
|
|||||||
testId('selectby'))
|
testId('selectby'))
|
||||||
),
|
),
|
||||||
GristTooltips.selectBy(),
|
GristTooltips.selectBy(),
|
||||||
{tooltipMenuOptions: {attach: null}, domArgs: [
|
{popupOptions: {attach: null}, domArgs: [
|
||||||
this._behavioralPromptsManager.attachTip('pageWidgetPickerSelectBy', {
|
this._behavioralPromptsManager.attachTip('pageWidgetPickerSelectBy', {
|
||||||
popupOptions: {
|
popupOptions: {
|
||||||
attach: null,
|
attach: null,
|
||||||
|
@ -256,7 +256,7 @@ function menuWorkOnCopy(pageModel: DocPageModel) {
|
|||||||
withInfoTooltip(
|
withInfoTooltip(
|
||||||
t("Edit without affecting the original"),
|
t("Edit without affecting the original"),
|
||||||
GristTooltips.workOnACopy(),
|
GristTooltips.workOnACopy(),
|
||||||
{tooltipMenuOptions: {attach: null}}
|
{popupOptions: {attach: null}}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -12,7 +12,7 @@ import {makeLinks} from 'app/client/ui2018/links';
|
|||||||
import {menuCssClass} from 'app/client/ui2018/menus';
|
import {menuCssClass} from 'app/client/ui2018/menus';
|
||||||
import {dom, DomContents, DomElementArg, DomElementMethod, styled} from 'grainjs';
|
import {dom, DomContents, DomElementArg, DomElementMethod, styled} from 'grainjs';
|
||||||
import Popper from 'popper.js';
|
import Popper from 'popper.js';
|
||||||
import {cssMenu, defaultMenuOptions, IMenuOptions, setPopupToCreateDom} from 'popweasel';
|
import {cssMenu, cssMenuItem, defaultMenuOptions, IPopupOptions, setPopupToCreateDom} from 'popweasel';
|
||||||
import merge = require('lodash/merge');
|
import merge = require('lodash/merge');
|
||||||
|
|
||||||
export interface ITipOptions {
|
export interface ITipOptions {
|
||||||
@ -307,10 +307,41 @@ export function tooltipCloseButton(ctl: ITooltipControl): HTMLElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InfoTooltipOptions {
|
||||||
|
/** Defaults to `click`. */
|
||||||
|
variant?: InfoTooltipVariant;
|
||||||
|
/** Only applicable to the `click` variant. */
|
||||||
|
popupOptions?: IPopupOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InfoTooltipVariant = 'click' | 'hover';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an info icon that shows a tooltip with the specified `content` on click.
|
* Renders an info icon that shows a tooltip with the specified `content`.
|
||||||
*/
|
*/
|
||||||
export function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
|
export function infoTooltip(
|
||||||
|
content: DomContents,
|
||||||
|
options: InfoTooltipOptions = {},
|
||||||
|
...domArgs: DomElementArg[]
|
||||||
|
) {
|
||||||
|
const {variant = 'click'} = options;
|
||||||
|
switch (variant) {
|
||||||
|
case 'click': {
|
||||||
|
const {popupOptions} = options;
|
||||||
|
return buildClickableInfoTooltip(content, popupOptions, domArgs);
|
||||||
|
}
|
||||||
|
case 'hover': {
|
||||||
|
return buildHoverableInfoTooltip(content, domArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildClickableInfoTooltip(
|
||||||
|
content: DomContents,
|
||||||
|
popupOptions?: IPopupOptions,
|
||||||
|
...domArgs: DomElementArg[]
|
||||||
|
) {
|
||||||
return cssInfoTooltipButton('?',
|
return cssInfoTooltipButton('?',
|
||||||
(elem) => {
|
(elem) => {
|
||||||
setPopupToCreateDom(
|
setPopupToCreateDom(
|
||||||
@ -336,7 +367,7 @@ export function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ..
|
|||||||
testId('info-tooltip-popup'),
|
testId('info-tooltip-popup'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{...defaultMenuOptions, ...{placement: 'bottom-end'}, ...menuOptions},
|
{...defaultMenuOptions, ...{placement: 'bottom-end'}, ...popupOptions},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
testId('info-tooltip'),
|
testId('info-tooltip'),
|
||||||
@ -344,22 +375,41 @@ export function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ..
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildHoverableInfoTooltip(content: DomContents, ...domArgs: DomElementArg[]) {
|
||||||
|
return cssInfoTooltipIcon('?',
|
||||||
|
hoverTooltip(() => cssInfoTooltipTransientPopup(
|
||||||
|
content,
|
||||||
|
cssTooltipCorner(testId('tooltip-origin')),
|
||||||
|
{tabIndex: '-1'},
|
||||||
|
testId('info-tooltip-popup'),
|
||||||
|
), {closeOnClick: false}),
|
||||||
|
testId('info-tooltip'),
|
||||||
|
...domArgs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export interface WithInfoTooltipOptions {
|
export interface WithInfoTooltipOptions {
|
||||||
|
/** Defaults to `click`. */
|
||||||
|
variant?: InfoTooltipVariant;
|
||||||
domArgs?: DomElementArg[];
|
domArgs?: DomElementArg[];
|
||||||
tooltipButtonDomArgs?: DomElementArg[];
|
iconDomArgs?: DomElementArg[];
|
||||||
tooltipMenuOptions?: IMenuOptions;
|
/** Only applicable to the `click` variant. */
|
||||||
|
popupOptions?: IPopupOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps `domContent` with a info tooltip button that displays the provided
|
* Wraps `domContent` with a info tooltip icon that displays the provided
|
||||||
* `tooltipContent` on click, and returns the wrapped element.
|
* `tooltipContent` and returns the wrapped element.
|
||||||
*
|
*
|
||||||
* The tooltip button is displayed to the right of `domContents`, and displays
|
* The tooltip button is displayed to the right of `domContents`, and displays
|
||||||
* a popup on click. The popup can be dismissed by clicking away from it, clicking
|
* a popup on click by default. The popup can be dismissed by clicking away from
|
||||||
* the close button in the top-right corner, or pressing Enter or Escape.
|
* it; clicking the close button in the top-right corner; or pressing Enter or Escape.
|
||||||
|
*
|
||||||
|
* You may optionally specify `options.variant`, which controls whether the tooltip
|
||||||
|
* is shown on hover or on click.
|
||||||
*
|
*
|
||||||
* Arguments can be passed to both the top-level wrapped DOM element and the
|
* Arguments can be passed to both the top-level wrapped DOM element and the
|
||||||
* tooltip button element with `options.domArgs` and `options.tooltipButtonDomArgs`
|
* tooltip icon element with `options.domArgs` and `options.tooltipIconDomArgs`
|
||||||
* respectively.
|
* respectively.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
@ -374,10 +424,10 @@ export function withInfoTooltip(
|
|||||||
tooltipContent: DomContents,
|
tooltipContent: DomContents,
|
||||||
options: WithInfoTooltipOptions = {},
|
options: WithInfoTooltipOptions = {},
|
||||||
) {
|
) {
|
||||||
const {domArgs, tooltipButtonDomArgs, tooltipMenuOptions} = options;
|
const {variant = 'click', domArgs, iconDomArgs, popupOptions} = options;
|
||||||
return cssDomWithTooltip(
|
return cssDomWithTooltip(
|
||||||
domContents,
|
domContents,
|
||||||
infoTooltip(tooltipContent, tooltipMenuOptions, tooltipButtonDomArgs),
|
infoTooltip(tooltipContent, {variant, popupOptions}, iconDomArgs),
|
||||||
...(domArgs ?? [])
|
...(domArgs ?? [])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -395,7 +445,7 @@ export function descriptionInfoTooltip(
|
|||||||
key: 'columnDescription',
|
key: 'columnDescription',
|
||||||
openOnClick: true,
|
openOnClick: true,
|
||||||
};
|
};
|
||||||
const builder = () => cssDescriptionInfoTooltip(
|
const builder = () => cssInfoTooltipTransientPopup(
|
||||||
body,
|
body,
|
||||||
// Used id test to find the origin of the tooltip regardless webdriver implementation (some of them start)
|
// Used id test to find the origin of the tooltip regardless webdriver implementation (some of them start)
|
||||||
cssTooltipCorner(testId('tooltip-origin')),
|
cssTooltipCorner(testId('tooltip-origin')),
|
||||||
@ -422,7 +472,7 @@ const cssTooltipCorner = styled('div', `
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssDescriptionInfoTooltip = styled('div', `
|
const cssInfoTooltipTransientPopup = styled('div', `
|
||||||
position: relative;
|
position: relative;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@ -489,7 +539,7 @@ const cssTooltipCloseButton = styled('div', `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssInfoTooltipButton = styled('div', `
|
const cssInfoTooltipIcon = styled('div', `
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -499,9 +549,17 @@ const cssInfoTooltipButton = styled('div', `
|
|||||||
border: 1px solid ${theme.controlSecondaryFg};
|
border: 1px solid ${theme.controlSecondaryFg};
|
||||||
color: ${theme.controlSecondaryFg};
|
color: ${theme.controlSecondaryFg};
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
.${cssMenuItem.className}-sel & {
|
||||||
|
color: ${theme.menuItemSelectedFg};
|
||||||
|
border-color: ${theme.menuItemSelectedFg};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const cssInfoTooltipButton = styled(cssInfoTooltipIcon, `
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border: 1px solid ${theme.controlSecondaryHoverFg};
|
border: 1px solid ${theme.controlSecondaryHoverFg};
|
||||||
color: ${theme.controlSecondaryHoverFg};
|
color: ${theme.controlSecondaryHoverFg};
|
||||||
|
@ -80,6 +80,7 @@ export function searchableMenu(
|
|||||||
dom.autoDispose(searchValue),
|
dom.autoDispose(searchValue),
|
||||||
dom.on('input', (_ev, elem) => { setSearchValue(elem.value); }),
|
dom.on('input', (_ev, elem) => { setSearchValue(elem.value); }),
|
||||||
{placeholder: searchInputPlaceholder},
|
{placeholder: searchInputPlaceholder},
|
||||||
|
testId('searchable-menu-input'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -97,6 +98,7 @@ export function searchableMenu(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
testId('searchable-menu'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ export const commonUrls = {
|
|||||||
helpCustomWidgets: "https://support.getgrist.com/widget-custom",
|
helpCustomWidgets: "https://support.getgrist.com/widget-custom",
|
||||||
helpTelemetryLimited: "https://support.getgrist.com/telemetry-limited",
|
helpTelemetryLimited: "https://support.getgrist.com/telemetry-limited",
|
||||||
helpCalendarWidget: "https://support.getgrist.com/widget-calendar",
|
helpCalendarWidget: "https://support.getgrist.com/widget-calendar",
|
||||||
|
helpLinkKeys: "https://support.getgrist.com/examples/2021-04-link-keys",
|
||||||
plans: "https://www.getgrist.com/pricing",
|
plans: "https://www.getgrist.com/pricing",
|
||||||
sproutsProgram: "https://www.getgrist.com/sprouts-program",
|
sproutsProgram: "https://www.getgrist.com/sprouts-program",
|
||||||
contact: "https://www.getgrist.com/contact",
|
contact: "https://www.getgrist.com/contact",
|
||||||
|
@ -68,7 +68,7 @@ describe('GridViewNewColumnMenu', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('column creation', function () {
|
describe('column creation', function () {
|
||||||
it('should show rename menu after new column click', async function () {
|
it('should show rename menu after new column click', async function () {
|
||||||
const menu = await openAddColumnIcon();
|
const menu = await openAddColumnIcon();
|
||||||
await menu.findWait('.test-new-columns-menu-add-new', 100).click();
|
await menu.findWait('.test-new-columns-menu-add-new', 100).click();
|
||||||
await driver.findWait('.test-column-title-popup', 100, 'rename menu is not present');
|
await driver.findWait('.test-column-title-popup', 100, 'rename menu is not present');
|
||||||
@ -86,6 +86,39 @@ describe('GridViewNewColumnMenu', function () {
|
|||||||
assert.lengthOf(columns, 4, 'wrong number of columns');
|
assert.lengthOf(columns, 4, 'wrong number of columns');
|
||||||
await gu.undo();
|
await gu.undo();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support inserting before selected column', async function () {
|
||||||
|
await gu.openColumnMenu('A', 'Insert column to the left');
|
||||||
|
await driver.findWait(".test-new-columns-menu", 100);
|
||||||
|
await gu.sendKeys(Key.ENTER);
|
||||||
|
await gu.waitForServer();
|
||||||
|
await driver.findWait('.test-column-title-close', 100).click();
|
||||||
|
const columns = await gu.getColumnNames();
|
||||||
|
assert.deepEqual(columns, ['D', 'A', 'B', 'C']);
|
||||||
|
await gu.undo();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support inserting after selected column', async function () {
|
||||||
|
await gu.openColumnMenu('A', 'Insert column to the right');
|
||||||
|
await driver.findWait(".test-new-columns-menu", 100);
|
||||||
|
await gu.sendKeys(Key.ENTER);
|
||||||
|
await gu.waitForServer();
|
||||||
|
await driver.findWait('.test-column-title-close', 100).click();
|
||||||
|
const columns = await gu.getColumnNames();
|
||||||
|
assert.deepEqual(columns, ['A', 'D', 'B', 'C']);
|
||||||
|
await gu.undo();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support inserting after the last visible column', async function () {
|
||||||
|
await gu.openColumnMenu('C', 'Insert column to the right');
|
||||||
|
await driver.findWait(".test-new-columns-menu", 100);
|
||||||
|
await gu.sendKeys(Key.ENTER);
|
||||||
|
await gu.waitForServer();
|
||||||
|
await driver.findWait('.test-column-title-close', 100).click();
|
||||||
|
const columns = await gu.getColumnNames();
|
||||||
|
assert.deepEqual(columns, ['A', 'B', 'C', 'D']);
|
||||||
|
await gu.undo();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hidden columns', function () {
|
describe('hidden columns', function () {
|
||||||
@ -103,13 +136,13 @@ describe('GridViewNewColumnMenu', function () {
|
|||||||
await gu.addColumn('Add3');
|
await gu.addColumn('Add3');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('1 to 5 hidden columns, secion should be inline', async function () {
|
it('1 to 5 hidden columns, section should be inline', async function () {
|
||||||
const checkSection = async (...columns: string[]) => {
|
const checkSection = async (...columns: string[]) => {
|
||||||
const menu = await openAddColumnIcon();
|
const menu = await openAddColumnIcon();
|
||||||
await menu.findWait(".test-new-columns-menu-hidden-columns", 100,
|
await menu.findWait(".test-new-columns-menu-hidden-columns", 100,
|
||||||
'hidden section is not present');
|
'hidden section is not present');
|
||||||
for (const column of columns) {
|
for (const column of columns) {
|
||||||
const isColumnPresent = await menu.find(`.test-new-columns-menu-hidden-columns-${column}`).isPresent();
|
const isColumnPresent = await menu.findContent('li', column).isPresent();
|
||||||
assert.isTrue(isColumnPresent, `column ${column} is not present`);
|
assert.isTrue(isColumnPresent, `column ${column} is not present`);
|
||||||
}
|
}
|
||||||
await closeAddColumnMenu();
|
await closeAddColumnMenu();
|
||||||
@ -123,7 +156,7 @@ describe('GridViewNewColumnMenu', function () {
|
|||||||
await gu.moveToHidden('Add1');
|
await gu.moveToHidden('Add1');
|
||||||
await gu.moveToHidden('Add2');
|
await gu.moveToHidden('Add2');
|
||||||
await checkSection('A', 'B', 'C', 'Add1', 'Add2');
|
await checkSection('A', 'B', 'C', 'Add1', 'Add2');
|
||||||
await gu.undo(5);
|
await gu.undo(11);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inline button should show column at the end of the table', async function () {
|
it('inline button should show column at the end of the table', async function () {
|
||||||
@ -156,11 +189,7 @@ describe('GridViewNewColumnMenu', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('shortucts', function () {
|
describe('shortcuts', function () {
|
||||||
describe('Timestamp', function () {
|
|
||||||
it('created at - should create new column with date triggered on create');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Timestamp', function () {
|
describe('Timestamp', function () {
|
||||||
it('created at - should create new column with date triggered on create', function () {
|
it('created at - should create new column with date triggered on create', function () {
|
||||||
|
|
||||||
@ -178,6 +207,84 @@ describe('GridViewNewColumnMenu', function () {
|
|||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Detect Duplicates in...', function () {
|
||||||
|
it('should show columns in a searchable sub-menu', async function () {
|
||||||
|
const menu = await openAddColumnIcon();
|
||||||
|
await menu.findWait('.test-new-columns-menu-shortcuts-duplicates', 100).mouseMove();
|
||||||
|
await gu.waitToPass(async () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
await driver.findAll('.test-searchable-menu li', (el) => el.getText()),
|
||||||
|
['A', 'B', 'C']
|
||||||
|
);
|
||||||
|
}, 500);
|
||||||
|
await driver.find('.test-searchable-menu-input').click();
|
||||||
|
await gu.sendKeys('A');
|
||||||
|
await gu.waitToPass(async () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
await driver.findAll('.test-searchable-menu li', (el) => el.getText()),
|
||||||
|
['A']
|
||||||
|
);
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
await gu.sendKeys('BC');
|
||||||
|
await gu.waitToPass(async () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
await driver.findAll('.test-searchable-menu li', (el) => el.getText()),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
await gu.clearInput();
|
||||||
|
await gu.waitToPass(async () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
await driver.findAll('.test-searchable-menu li', (el) => el.getText()),
|
||||||
|
['A', 'B', 'C']
|
||||||
|
);
|
||||||
|
}, 250);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create new column that checks for duplicates in the specified column', async function () {
|
||||||
|
const menu = await openAddColumnIcon();
|
||||||
|
await menu.findWait('.test-new-columns-menu-shortcuts-duplicates', 100).mouseMove();
|
||||||
|
await driver.findContentWait('.test-searchable-menu li', 'A', 500).click();
|
||||||
|
await gu.waitForServer();
|
||||||
|
await gu.sendKeys(Key.ENTER);
|
||||||
|
|
||||||
|
// Just checking the formula looks plausible - correctness is best left to a python test.
|
||||||
|
assert.equal(
|
||||||
|
await driver.find('.test-formula-editor').getText(),
|
||||||
|
'True if len(Table1.lookupRecords(A=$A)) > 1 else False'
|
||||||
|
);
|
||||||
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
|
const columns = await gu.getColumnNames();
|
||||||
|
assert.deepEqual(columns, ['A', 'B', 'C', 'Duplicate in A']);
|
||||||
|
await gu.undo();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('UUID', function () {
|
||||||
|
it('should create new column that generates a UUID on new record', async function () {
|
||||||
|
await gu.getCell(2, 1).click();
|
||||||
|
await gu.sendKeys('A', Key.ENTER);
|
||||||
|
await gu.waitForServer();
|
||||||
|
const menu = await openAddColumnIcon();
|
||||||
|
await menu.findWait('.test-new-columns-menu-shortcuts-uuid', 100).click();
|
||||||
|
await gu.waitForServer();
|
||||||
|
const cells1 = await gu.getVisibleGridCells({col: 'UUID', rowNums: [1, 2]});
|
||||||
|
assert.match(cells1[0], /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
|
||||||
|
assert.equal(cells1[1], '');
|
||||||
|
await gu.getCell(2, 2).click();
|
||||||
|
await gu.sendKeys('B', Key.ENTER);
|
||||||
|
await gu.waitForServer();
|
||||||
|
const cells2 = await gu.getVisibleGridCells({col: 'UUID', rowNums: [1, 2, 3]});
|
||||||
|
assert.match(cells2[0], /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
|
||||||
|
assert.match(cells2[1], /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
|
||||||
|
assert.equal(cells2[2], '');
|
||||||
|
assert.equal(cells1[0], cells2[0]);
|
||||||
|
await gu.undo(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user