mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +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:
@@ -3,6 +3,8 @@ import GridView from 'app/client/components/GridView';
|
||||
import {makeT} from 'app/client/lib/localization';
|
||||
import {ViewSectionRec} from 'app/client/models/DocModel';
|
||||
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 {icon} from 'app/client/ui2018/icons';
|
||||
import {
|
||||
@@ -265,7 +267,11 @@ function buildUUIDMenuItem(gridView: GridView, index?: number) {
|
||||
await gridView.gristDoc.convertToTrigger(colRef, 'UUID()');
|
||||
}, {nestInActiveBundle: true});
|
||||
},
|
||||
t('UUID'),
|
||||
withInfoTooltip(
|
||||
t('UUID'),
|
||||
GristTooltips.uuid(),
|
||||
{variant: 'hover'}
|
||||
),
|
||||
testId('new-columns-menu-shortcuts-uuid'),
|
||||
);
|
||||
}
|
||||
@@ -522,8 +528,15 @@ function buildLookupSection(gridView: GridView, index?: number){
|
||||
: [lookupMenu, reverseLookupMenu];
|
||||
|
||||
return [
|
||||
menuDivider(),
|
||||
menuSubHeader(t("Lookups"), testId('new-columns-menu-lookups')),
|
||||
menuDivider(),
|
||||
menuSubHeader(
|
||||
withInfoTooltip(
|
||||
t('Lookups'),
|
||||
GristTooltips.lookups(),
|
||||
{variant: 'hover'}
|
||||
),
|
||||
testId('new-columns-menu-lookups'),
|
||||
),
|
||||
...menuContent
|
||||
];
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ export type Tooltip =
|
||||
| 'workOnACopy'
|
||||
| 'openAccessRules'
|
||||
| 'addRowConditionalStyle'
|
||||
| 'addColumnConditionalStyle';
|
||||
| 'addColumnConditionalStyle'
|
||||
| 'uuid'
|
||||
| 'lookups';
|
||||
|
||||
export type TooltipContentFunc = (...domArgs: DomElementArg[]) => DomContents;
|
||||
|
||||
@@ -98,6 +100,21 @@ see or edit which parts of your document.')
|
||||
),
|
||||
...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 {
|
||||
|
||||
@@ -386,7 +386,7 @@ export class PageWidgetSelect extends Disposable {
|
||||
testId('selectby'))
|
||||
),
|
||||
GristTooltips.selectBy(),
|
||||
{tooltipMenuOptions: {attach: null}, domArgs: [
|
||||
{popupOptions: {attach: null}, domArgs: [
|
||||
this._behavioralPromptsManager.attachTip('pageWidgetPickerSelectBy', {
|
||||
popupOptions: {
|
||||
attach: null,
|
||||
|
||||
@@ -256,7 +256,7 @@ function menuWorkOnCopy(pageModel: DocPageModel) {
|
||||
withInfoTooltip(
|
||||
t("Edit without affecting the original"),
|
||||
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 {dom, DomContents, DomElementArg, DomElementMethod, styled} from 'grainjs';
|
||||
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');
|
||||
|
||||
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('?',
|
||||
(elem) => {
|
||||
setPopupToCreateDom(
|
||||
@@ -336,7 +367,7 @@ export function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ..
|
||||
testId('info-tooltip-popup'),
|
||||
);
|
||||
},
|
||||
{...defaultMenuOptions, ...{placement: 'bottom-end'}, ...menuOptions},
|
||||
{...defaultMenuOptions, ...{placement: 'bottom-end'}, ...popupOptions},
|
||||
);
|
||||
},
|
||||
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 {
|
||||
/** Defaults to `click`. */
|
||||
variant?: InfoTooltipVariant;
|
||||
domArgs?: DomElementArg[];
|
||||
tooltipButtonDomArgs?: DomElementArg[];
|
||||
tooltipMenuOptions?: IMenuOptions;
|
||||
iconDomArgs?: DomElementArg[];
|
||||
/** Only applicable to the `click` variant. */
|
||||
popupOptions?: IPopupOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps `domContent` with a info tooltip button that displays the provided
|
||||
* `tooltipContent` on click, and returns the wrapped element.
|
||||
* Wraps `domContent` with a info tooltip icon that displays the provided
|
||||
* `tooltipContent` and returns the wrapped element.
|
||||
*
|
||||
* 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
|
||||
* the close button in the top-right corner, or pressing Enter or Escape.
|
||||
* a popup on click by default. The popup can be dismissed by clicking away from
|
||||
* 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
|
||||
* tooltip button element with `options.domArgs` and `options.tooltipButtonDomArgs`
|
||||
* tooltip icon element with `options.domArgs` and `options.tooltipIconDomArgs`
|
||||
* respectively.
|
||||
*
|
||||
* Usage:
|
||||
@@ -374,10 +424,10 @@ export function withInfoTooltip(
|
||||
tooltipContent: DomContents,
|
||||
options: WithInfoTooltipOptions = {},
|
||||
) {
|
||||
const {domArgs, tooltipButtonDomArgs, tooltipMenuOptions} = options;
|
||||
const {variant = 'click', domArgs, iconDomArgs, popupOptions} = options;
|
||||
return cssDomWithTooltip(
|
||||
domContents,
|
||||
infoTooltip(tooltipContent, tooltipMenuOptions, tooltipButtonDomArgs),
|
||||
infoTooltip(tooltipContent, {variant, popupOptions}, iconDomArgs),
|
||||
...(domArgs ?? [])
|
||||
);
|
||||
}
|
||||
@@ -395,7 +445,7 @@ export function descriptionInfoTooltip(
|
||||
key: 'columnDescription',
|
||||
openOnClick: true,
|
||||
};
|
||||
const builder = () => cssDescriptionInfoTooltip(
|
||||
const builder = () => cssInfoTooltipTransientPopup(
|
||||
body,
|
||||
// Used id test to find the origin of the tooltip regardless webdriver implementation (some of them start)
|
||||
cssTooltipCorner(testId('tooltip-origin')),
|
||||
@@ -422,7 +472,7 @@ const cssTooltipCorner = styled('div', `
|
||||
visibility: hidden;
|
||||
`);
|
||||
|
||||
const cssDescriptionInfoTooltip = styled('div', `
|
||||
const cssInfoTooltipTransientPopup = styled('div', `
|
||||
position: relative;
|
||||
white-space: pre-wrap;
|
||||
text-align: left;
|
||||
@@ -489,7 +539,7 @@ const cssTooltipCloseButton = styled('div', `
|
||||
}
|
||||
`);
|
||||
|
||||
const cssInfoTooltipButton = styled('div', `
|
||||
const cssInfoTooltipIcon = styled('div', `
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -499,9 +549,17 @@ const cssInfoTooltipButton = styled('div', `
|
||||
border: 1px solid ${theme.controlSecondaryFg};
|
||||
color: ${theme.controlSecondaryFg};
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
.${cssMenuItem.className}-sel & {
|
||||
color: ${theme.menuItemSelectedFg};
|
||||
border-color: ${theme.menuItemSelectedFg};
|
||||
}
|
||||
`);
|
||||
|
||||
const cssInfoTooltipButton = styled(cssInfoTooltipIcon, `
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid ${theme.controlSecondaryHoverFg};
|
||||
color: ${theme.controlSecondaryHoverFg};
|
||||
|
||||
@@ -80,6 +80,7 @@ export function searchableMenu(
|
||||
dom.autoDispose(searchValue),
|
||||
dom.on('input', (_ev, elem) => { setSearchValue(elem.value); }),
|
||||
{placeholder: searchInputPlaceholder},
|
||||
testId('searchable-menu-input'),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -97,6 +98,7 @@ export function searchableMenu(
|
||||
}
|
||||
});
|
||||
}),
|
||||
testId('searchable-menu'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user