Skip to content

Commit d18eeb5

Browse files
feat(popover): Add hint support (#2711)
* Add custom item * Remove customcontent parameter from popover * Tests * Cleanup * Cleanup * Lint * Cleanup * Rename custom to html, add enum with item types * Fix tests * Support hint * Rename hint content to hint * Align hint left * Move types and exports * Update changelog * Cleanup * Add todos * Change the way hint is disabled for mobile * Get rid of buildItems override * Update comment
1 parent 50f43bb commit d18eeb5

File tree

13 files changed

+202
-21
lines changed

13 files changed

+202
-21
lines changed

docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
- `Improvement` - The API `blocks.convert()` now returns the new block API
1717
- `Improvement` - The API `caret.setToBlock()` now can accept either BlockAPI or block index or block id
1818
- `New`*Menu Config* – New item type – HTML
19+
`Refactoring` – Switched to Vite as Cypress bundler
20+
`New`*Menu Config* – Default and HTML items now support hints
1921

2022
### 2.29.1
2123

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { bem } from '../../../bem';
2+
3+
/**
4+
* Hint block CSS class constructor
5+
*/
6+
const className = bem('ce-hint');
7+
8+
/**
9+
* CSS class names to be used in hint class
10+
*/
11+
export const css = {
12+
root: className(),
13+
alignedLeft: className(null, 'align-left'),
14+
title: className('title'),
15+
description: className('description'),
16+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.ce-hint {
2+
&--align-left {
3+
text-align: left;
4+
}
5+
6+
&__description {
7+
opacity: 0.6;
8+
margin-top: 3px;
9+
}
10+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import Dom from '../../../../dom';
2+
import { css } from './hint.const';
3+
import { HintParams } from './hint.types';
4+
5+
import './hint.css';
6+
7+
/**
8+
* Represents the hint content component
9+
*/
10+
export class Hint {
11+
/**
12+
* Html element used to display hint content on screen
13+
*/
14+
private nodes: {
15+
root: HTMLElement;
16+
title: HTMLElement;
17+
description?: HTMLElement;
18+
};
19+
20+
/**
21+
* Constructs the hint content instance
22+
*
23+
* @param params - hint content parameters
24+
*/
25+
constructor(params: HintParams) {
26+
this.nodes = {
27+
root: Dom.make('div', [css.root, css.alignedLeft]),
28+
title: Dom.make('div', css.title, { textContent: params.title }),
29+
};
30+
31+
this.nodes.root.appendChild(this.nodes.title);
32+
33+
if (params.description !== undefined) {
34+
this.nodes.description = Dom.make('div', css.description, { textContent: params.description });
35+
36+
this.nodes.root.appendChild(this.nodes.description);
37+
}
38+
}
39+
40+
/**
41+
* Returns the root element of the hint content
42+
*/
43+
public getElement(): HTMLElement {
44+
return this.nodes.root;
45+
}
46+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Hint parameters
3+
*/
4+
export interface HintParams {
5+
/**
6+
* Title of the hint
7+
*/
8+
title: string;
9+
10+
/**
11+
* Secondary text to be displayed below the title
12+
*/
13+
description?: string;
14+
}
15+
16+
/**
17+
* Possible hint positions
18+
*/
19+
export type HintPosition = 'top' | 'bottom' | 'left' | 'right';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './hint';
2+
export * from './hint.types';

src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import Dom from '../../../../../dom';
22
import { IconDotCircle, IconChevronRight } from '@codexteam/icons';
33
import {
44
PopoverItemDefaultParams as PopoverItemDefaultParams,
5-
PopoverItemParams as PopoverItemParams
5+
PopoverItemParams as PopoverItemParams,
6+
PopoverItemRenderParamsMap,
7+
PopoverItemType
68
} from '../popover-item.types';
79
import { PopoverItem } from '../popover-item';
810
import { css } from './popover-item-default.const';
@@ -11,8 +13,9 @@ import { css } from './popover-item-default.const';
1113
* Represents sigle popover item node
1214
*
1315
* @todo move nodes initialization to constructor
14-
* @todo replace multiple make() usages with constructing separate instaces
16+
* @todo replace multiple make() usages with constructing separate instances
1517
* @todo split regular popover item and popover item with confirmation to separate classes
18+
* @todo display icon on the right side of the item for rtl languages
1619
*/
1720
export class PopoverItemDefault extends PopoverItem {
1821
/**
@@ -72,10 +75,6 @@ export class PopoverItemDefault extends PopoverItem {
7275
icon: null,
7376
};
7477

75-
/**
76-
* Popover item params
77-
*/
78-
private params: PopoverItemDefaultParams;
7978

8079
/**
8180
* If item is in confirmation state, stores confirmation params such as icon, label, onActivate callback and so on
@@ -86,12 +85,13 @@ export class PopoverItemDefault extends PopoverItem {
8685
* Constructs popover item instance
8786
*
8887
* @param params - popover item construction params
88+
* @param renderParams - popover item render params.
89+
* The parameters that are not set by user via popover api but rather depend on technical implementation
8990
*/
90-
constructor(params: PopoverItemDefaultParams) {
91+
constructor(private readonly params: PopoverItemDefaultParams, renderParams?: PopoverItemRenderParamsMap[PopoverItemType.Default]) {
9192
super();
9293

93-
this.params = params;
94-
this.nodes.root = this.make(params);
94+
this.nodes.root = this.make(params, renderParams);
9595
}
9696

9797
/**
@@ -159,8 +159,9 @@ export class PopoverItemDefault extends PopoverItem {
159159
* Constructs HTML element corresponding to popover item params
160160
*
161161
* @param params - item construction params
162+
* @param renderParams - popover item render params
162163
*/
163-
private make(params: PopoverItemDefaultParams): HTMLElement {
164+
private make(params: PopoverItemDefaultParams, renderParams?: PopoverItemRenderParamsMap[PopoverItemType.Default]): HTMLElement {
164165
const el = Dom.make('div', css.container);
165166

166167
if (params.name) {
@@ -197,6 +198,13 @@ export class PopoverItemDefault extends PopoverItem {
197198
el.classList.add(css.disabled);
198199
}
199200

201+
if (params.hint !== undefined && renderParams?.hint?.enabled !== false) {
202+
this.addHint(el, {
203+
...params.hint,
204+
position: renderParams?.hint?.position || 'right',
205+
});
206+
}
207+
200208
return el;
201209
}
202210

src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PopoverItem } from '../popover-item';
2-
import { PopoverItemHtmlParams } from '../popover-item.types';
2+
import { PopoverItemHtmlParams, PopoverItemRenderParamsMap, PopoverItemType } from '../popover-item.types';
33
import { css } from './popover-item-html.const';
44
import Dom from '../../../../../dom';
55

@@ -16,15 +16,24 @@ export class PopoverItemHtml extends PopoverItem {
1616
* Constructs the instance
1717
*
1818
* @param params – instance parameters
19+
* @param renderParams – popover item render params.
20+
* The parameters that are not set by user via popover api but rather depend on technical implementation
1921
*/
20-
constructor(params: PopoverItemHtmlParams) {
22+
constructor(params: PopoverItemHtmlParams, renderParams?: PopoverItemRenderParamsMap[PopoverItemType.Html]) {
2123
super();
2224

2325
this.nodes = {
2426
root: Dom.make('div', css.root),
2527
};
2628

2729
this.nodes.root.appendChild(params.element);
30+
31+
if (params.hint !== undefined && renderParams?.hint?.enabled !== false) {
32+
this.addHint(this.nodes.root, {
33+
...params.hint,
34+
position: renderParams?.hint?.position || 'right',
35+
});
36+
}
2837
}
2938

3039
/**

src/components/utils/popover/components/popover-item/popover-item.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
1+
import * as tooltip from '../../../../utils/tooltip';
2+
import { type HintPosition, Hint } from '../hint';
3+
14
/**
25
* Popover item abstract class
36
*/
47
export abstract class PopoverItem {
8+
/**
9+
* Adds hint to the item element if hint data is provided
10+
*
11+
* @param itemElement - popover item root element to add hint to
12+
* @param hintData - hint data
13+
*/
14+
protected addHint(itemElement: HTMLElement, hintData: { title: string, description?: string; position: HintPosition }): void {
15+
const content = new Hint(hintData);
16+
17+
tooltip.onHover(itemElement, content.getElement(), {
18+
placement: hintData.position,
19+
hidingDelay: 100,
20+
});
21+
}
22+
523
/**
624
* Returns popover item root element
725
*/

src/components/utils/popover/components/popover-item/popover-item.types.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { HintParams, HintPosition } from '../hint';
2+
13
/**
24
* Popover item types
35
*/
@@ -35,7 +37,12 @@ export interface PopoverItemHtmlParams {
3537
/**
3638
* Custom html content to be displayed in the popover
3739
*/
38-
element: HTMLElement
40+
element: HTMLElement;
41+
42+
/**
43+
* Hint data to be displayed on item hover
44+
*/
45+
hint?: HintParams;
3946
}
4047

4148
/**
@@ -89,6 +96,11 @@ interface PopoverItemDefaultBaseParams {
8996
* In case of string, works like radio buttons group and highlights as inactive any other item that has same toggle key value.
9097
*/
9198
toggle?: boolean | string;
99+
100+
/**
101+
* Hint data to be displayed on item hover
102+
*/
103+
hint?: HintParams;
92104
}
93105

94106
/**
@@ -117,7 +129,6 @@ export interface PopoverItemWithoutConfirmationParams extends PopoverItemDefault
117129
* @param event - event that initiated item activation
118130
*/
119131
onActivate: (item: PopoverItemParams, event?: PointerEvent) => void;
120-
121132
}
122133

123134

@@ -152,3 +163,28 @@ export type PopoverItemParams =
152163
PopoverItemSeparatorParams |
153164
PopoverItemHtmlParams;
154165

166+
167+
/**
168+
* Popover item render params.
169+
* The parameters that are not set by user via popover api but rather depend on technical implementation
170+
*/
171+
export type PopoverItemRenderParamsMap = {
172+
[key in PopoverItemType.Default | PopoverItemType.Html]?: {
173+
/**
174+
* Hint render params
175+
*/
176+
hint?: {
177+
/**
178+
* Hint position relative to the item
179+
*/
180+
position?: HintPosition;
181+
182+
/**
183+
* If false, hint will not be rendered.
184+
* True by default.
185+
* Used to disable hints on mobile popover
186+
*/
187+
enabled: boolean;
188+
}
189+
};
190+
};

0 commit comments

Comments
 (0)