Skip to content

Commit

Permalink
feat(item): show tooltip when text ellipsis (#394)
Browse files Browse the repository at this point in the history
* feat(item): show tooltip when text ellipsis

* style(item): fix lint error

* fix(item): remove condition to register tooltip

* chore(item): use isElementOverflown and remove isTruncated

* fix(item): add tooltip for label and sub-label

* fix(item): change way to call methods

* test(item): add more test cases

* fix(item): update tooltip content

* fix(item): update tooltip follow feedback

* fix(item): update subLabel show content logic

* chore(color-picker): add new slot ref

* fix(item): update test snapshot

* chore(color-picker): simplify label and subLabel logic template

* fix(item): remove unnecessary code

* chore(item): update code comment

Co-authored-by: Tremayne Christ <[email protected]>

* chore(item): remove unnecessary casting type

* chore(color-picker): remove empty line

* fix(item): handle empty slot when assign to nodes

Co-authored-by: Tremayne Christ <[email protected]>

Co-authored-by: Tremayne Christ <[email protected]>
  • Loading branch information
Nantawat-Poothong and TremayneChrist authored Sep 6, 2022
1 parent 7cb97f0 commit 8943eb6
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 79 deletions.
60 changes: 12 additions & 48 deletions packages/elements/src/item/__snapshots__/Item.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand All @@ -35,10 +32,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand All @@ -61,10 +55,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand All @@ -82,10 +73,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
tiger
<slot>
</slot>
Expand All @@ -107,10 +95,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
tiger
<slot>
</slot>
Expand All @@ -132,10 +117,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand All @@ -153,10 +135,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
<div part="sub-label">
Expand All @@ -177,10 +156,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
tiger
<slot>
</slot>
Expand Down Expand Up @@ -225,10 +201,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand All @@ -255,10 +228,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand All @@ -282,10 +252,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand All @@ -301,10 +268,7 @@
<slot name="left">
</slot>
</div>
<div
id="label"
part="center"
>
<div part="center">
<slot>
</slot>
</div>
Expand Down
30 changes: 19 additions & 11 deletions packages/elements/src/item/__test__/item.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { elementUpdated, expect, fixture, nextFrame } from '@refinitiv-ui/test-helpers';
import { elementUpdated, expect, fixture } from '@refinitiv-ui/test-helpers';
// import element and theme
import '@refinitiv-ui/elements/item';
import '@refinitiv-ui/elemental-theme/light/ef-item';
Expand All @@ -17,6 +17,10 @@ const createFixture = (type = '') => {
return fixture('<ef-item label="tiger">Test Not Highlightable</ef-item>');
case 'is_truncated':
return fixture('<div style="width: 100px; overflow: hidden;"><ef-item>Super vary long string that need to be truncated by parent</ef-item></div>');
case 'is_truncated_label':
return fixture('<div style="width: 100px; overflow: hidden;"><ef-item label="Super vary long string that need to be truncated by parent"></ef-item></div>');
case 'is_truncated_subLabel':
return fixture('<div style="width: 100px; overflow: hidden;"><ef-item sub-label="Super vary long string that need to be truncated by parent"></ef-item></div>');
case 'with_icon':
return fixture('<ef-item icon="tick">With settings icon</ef-item>');
case 'with_empty_icon':
Expand Down Expand Up @@ -130,23 +134,27 @@ describe('item/Item', () => {
expect(el.highlightable).to.equal(false);
});

it('Should truncate text', async () => {
it('Should truncate item text', async () => {
const div = await createFixture('is_truncated');
const el = div.querySelector('ef-item');
expect(el.isItemOverflown(), 'Should truncate text').to.equal(true);
});

await elementUpdated(el);
await nextFrame();
it('Should truncate label', async () => {
const div = await createFixture('is_truncated_label');
const el = div.querySelector('ef-item');
expect(el.isItemOverflown(), 'Should truncate text').to.equal(true);
});

expect(el.isTruncated, 'Should truncate text').to.equal(true);
it('Should truncate subLabel', async () => {
const div = await createFixture('is_truncated_subLabel');
const el = div.querySelector('ef-item');
expect(el.isItemOverflown(), 'Should truncate text').to.equal(true);
});

it('Should not truncate text', async () => {
it('Should not truncate item text', async () => {
const el = await createFixture();

await elementUpdated(el);
await nextFrame();

expect(el.isTruncated, 'Should not truncate text').to.equal(false);
expect(el.isItemOverflown(), 'Should not truncate text').to.equal(false);
});
});

Expand Down
98 changes: 78 additions & 20 deletions packages/elements/src/item/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import {
} from '@refinitiv-ui/core';
import { customElement } from '@refinitiv-ui/core/decorators/custom-element.js';
import { property } from '@refinitiv-ui/core/decorators/property.js';
import { query } from '@refinitiv-ui/core/decorators/query.js';
import { VERSION } from '../version.js';
import '../icon/index.js';
import '../checkbox/index.js';

import { registerOverflowTooltip } from '../tooltip/index.js';
import { isElementOverflown } from '@refinitiv-ui/utils/element.js';
import { createRef, ref, Ref } from '@refinitiv-ui/core/directives/ref.js';
import type { ItemType, ItemText, ItemHeader, ItemDivider, ItemData } from './helpers/types';
export type { ItemType, ItemText, ItemHeader, ItemDivider, ItemData };

Expand Down Expand Up @@ -126,10 +127,19 @@ export class Item extends ControlElement {
public for: string | null = null;

/**
* Cache label element
* Reference to the label element
*/
private labelRef: Ref<HTMLDivElement> = createRef();

/**
* Reference to the subLabel element
*/
private subLabelRef: Ref<HTMLDivElement> = createRef();

/**
* Reference to the slot element
*/
@query('#label')
private labelEl?: HTMLElement;
private slotRef: Ref<HTMLSlotElement> = createRef();

/**
* True, if there is no slotted content
Expand Down Expand Up @@ -187,6 +197,16 @@ export class Item extends ControlElement {
}
}

/**
* Called after the component is first rendered
* @param changedProperties Properties which have changed
* @returns {void}
*/
protected firstUpdated (changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
registerOverflowTooltip(this, () => this.getItemContent(), () => this.isItemOverflown());
}

/**
* Invoked before update() to compute values needed during the update.
* @param changedProperties changed properties
Expand All @@ -204,6 +224,45 @@ export class Item extends ControlElement {
}
}


/**
* Get Item content
* @returns return item content from slot or label and sub-label
*/
private getItemContent (): string {

if (this.isSlotEmpty) {
let text = '';
if (this.label) {
text += this.label;
}
if (this.subLabel) {
text += text ? ` (${this.subLabel})` : this.subLabel;
}
return text;
}
else {
return this.slotContent;
}
}

/**
* Get element overflown
* @param element Target element
* @returns return true if element is overflown.
*/
private isItemElementOverflown (element?: HTMLElement): boolean {
return element ? isElementOverflown(element) : false;
}

/**
* Get item overflown
* @returns return true if an item is overflown.
*/
private isItemOverflown (): boolean {
return this.isItemElementOverflown(this.labelRef.value) || this.isItemElementOverflown(this.subLabelRef.value);
}

/**
* Get icon template if icon attribute is defined
*/
Expand All @@ -215,14 +274,22 @@ export class Item extends ControlElement {
* Get subLabel template if it is defined and no slot content present
*/
private get subLabelTemplate (): TemplateResult | undefined {
return this.subLabel && this.isSlotEmpty ? html`<div part="sub-label">${this.subLabel}</div>` : undefined;
return html`<div part="sub-label" ${ref(this.subLabelRef)}>${this.subLabel}</div>`;
}

/**
* Get label template if it is defined and no slot content present
*/
private get labelTemplate (): TemplateResult | undefined {
return this.label && this.isSlotEmpty ? html`${this.label}` : undefined;
return html`${this.label}`;
}

/**
* Get slot content
*/
private get slotContent (): string {
const nodes = this.slotRef.value?.assignedNodes() || [];
return nodes.map(node => node.textContent).join(' ').trim();
}

/**
Expand Down Expand Up @@ -251,15 +318,6 @@ export class Item extends ControlElement {
return !this.disabled && this.type !== 'header' && this.type !== 'divider';
}

/**
* Getter returning if the label is truncated
* @prop {boolean} isTruncated
* @returns whether element is truncated or not
*/
public get isTruncated (): boolean {
return !!(this.labelEl && (this.labelEl.offsetWidth < this.labelEl.scrollWidth));
}

/**
* A `TemplateResult` that will be used
* to render the updated internal template.
Expand All @@ -272,10 +330,10 @@ export class Item extends ControlElement {
${this.multipleTemplate}
<slot name="left"></slot>
</div>
<div part="center" id="label">
${this.labelTemplate}
<slot @slotchange="${this.checkSlotChildren}"></slot>
${this.subLabelTemplate}
<div part="center" ${ref(this.labelRef)}>
${this.label && this.isSlotEmpty ? this.labelTemplate : undefined}
<slot ${ref(this.slotRef)} @slotchange="${this.checkSlotChildren}"></slot>
${this.subLabel && this.isSlotEmpty ? this.subLabelTemplate : undefined}
</div>
<div part="right">
<slot name="right"></slot>
Expand Down

0 comments on commit 8943eb6

Please sign in to comment.