Skip to content

Commit

Permalink
Implement Show More icon in data table module
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 526089815
  • Loading branch information
Googler committed Apr 21, 2023
1 parent 1a9657e commit ee54333
Show file tree
Hide file tree
Showing 9 changed files with 395 additions and 8 deletions.
29 changes: 29 additions & 0 deletions lit_nlp/client/elements/showmore.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

/* Show More */
:host {
--show-more-max-width: 20px;
}

.lit-showmore {
background-color: var(--lit-cyea-400);
display: inline;
width: var(--show-more-max-width);
border-radius: 10px;
vertical-align: top;
}

/* TODO(lit-dev): Make this behavior keyed on host attribute selector once
show more functionality is expanded. */
.lit-showmore:hover {
background-color: var(--lit-mage-400);
}

.icon-button {
color: white;
font-size: 24px;
vertical-align: top;
}

.icon-button:hover {
color: white;
}
82 changes: 82 additions & 0 deletions lit_nlp/client/elements/showmore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* @fileoverview A reusable element for collapsing and expanding text for LIT
*
* @license
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// tslint:disable:no-new-decorators
import {html} from 'lit';
import {customElement, property} from 'lit/decorators';

import {ReactiveElement} from '../lib/elements';
import {styles as sharedStyles} from '../lib/shared_styles.css';

import {styles} from './showmore.css';

/**
* A show more element that denotes hidden text that can be expanded on click.
*
*
* Usage:
* <lit-showmore
* @showmore=${showMore}>
* </lit-showmore>
*/
@customElement('lit-showmore')
export class LitShowMore extends ReactiveElement {
static override get styles() {
return [sharedStyles, styles];
}

@property({type: Boolean}) visible = true;
@property({type: Number}) hiddenTextLength = 0;

/**
* Renders the Show More element with hidden or visible test.
*/
override render() {
const onChange = (e:Event) => {
const event = new CustomEvent('showmore');
this.dispatchEvent(event);
e.stopPropagation();
e.preventDefault();
this.visible = false;
};

/* Non-standard HTML formatting must be preserved to prevent newlines from
turning into spaces that expand the background of the Show More element.
*/
// clang-format off
const renderIcon = html`<span slot="tooltip-anchor"
class="material-icon-outlined icon-button"
@click=${onChange}>more_horiz</span>`;

const renderShowMore =
html`<lit-tooltip
content="Show ${this.hiddenTextLength} more characters">
${renderIcon}
</lit-tooltip>`;

return html`<span class='lit-showmore'>${renderShowMore}</span>`;
// clang-format on
}
}

declare global {
interface HTMLElementTagNameMap {
'lit-showmore': LitShowMore;
}
}
64 changes: 64 additions & 0 deletions lit_nlp/client/elements/showmore_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'jasmine';

import {html, render} from 'lit';
import {LitElement} from 'lit';

import {LitShowMore} from './showmore';

describe('showmore test', () => {
let showmore: LitShowMore;
const CHANGE_HANDLERS = {
showmore: (e: Event) => {},
};

beforeEach(async () => {
showmore = new LitShowMore();
document.body.appendChild(showmore);
await showmore.updateComplete;
});

it('should instantiate correctly', () => {
expect(showmore).toBeDefined();
expect(showmore).toBeInstanceOf(HTMLElement);
expect(showmore).toBeInstanceOf(LitElement);
});

it('is initially visible', async () => {
const content = html`
<lit-showmore
id="visible"
@show-more=${CHANGE_HANDLERS.showmore}>
</lit-showmore>`;
render(content, document.body);
const queryString = 'lit-showmore#visible';
const showMoreIcon =
document.body.querySelector<LitShowMore>(queryString)!;
expect(showMoreIcon).toBeDefined();
await showMoreIcon.updateComplete;
const contentDiv =
showMoreIcon.renderRoot.children[0] as HTMLDivElement;
expect(contentDiv).toBeDefined();
expect(contentDiv.innerHTML).toContain('more_horiz');
});

it('emits an event when clicked', async() => {
const spy = spyOn(CHANGE_HANDLERS, 'showmore');
const content = html`
<lit-showmore
id="event-when-clicked"
@showmore=${CHANGE_HANDLERS.showmore}>
</lit-showmore>`;
render(content, document.body);
const queryString = 'lit-showmore#event-when-clicked';
const showMoreIcon =
document.body.querySelector<LitShowMore>(queryString)!;
await showMoreIcon.updateComplete;
const icon = showMoreIcon.renderRoot.querySelector(
'span > lit-tooltip > span[slot="tooltip-anchor"]'
) as HTMLSpanElement;
icon.click();
await showMoreIcon.updateComplete;

expect(spy).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions lit_nlp/client/elements/table.css
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ tbody td {
max-height: 150px;
overflow: auto;
white-space: pre-wrap;
display: inline;
}

/* TODO(lit-dev): Make the table image width configurable. */
Expand Down
37 changes: 31 additions & 6 deletions lit_nlp/client/elements/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import './popup_container';

import {ascending, descending} from 'd3'; // array helpers.
import {html, TemplateResult} from 'lit';
import {customElement, property} from 'lit/decorators';
import {customElement, property, queryAll} from 'lit/decorators';
import {isTemplateResult} from 'lit/directive-helpers';
import {classMap} from 'lit/directives/class-map';
import {styleMap} from 'lit/directives/style-map';
Expand All @@ -40,6 +40,7 @@ import {styles as sharedStyles} from '../lib/shared_styles.css';
import {formatForDisplay} from '../lib/types';
import {isNumber, median, measureTextLength, randInt} from '../lib/utils';

import {LitTableTextCell} from './table_text_cell';
import {ColumnHeader, SortableTableEntry, SortableTemplateResult, TableData, TableEntry, TableRowInternal} from './table_types';
import {filterDataByQueries, getSortableEntry, itemMatchesText, parseSearchTextIntoQueries} from './table_utils';

Expand Down Expand Up @@ -115,6 +116,8 @@ export class DataTable extends ReactiveElement {
@observable @property({type: Boolean}) searchEnabled = false;
@observable @property({type: Boolean}) paginationEnabled = false;
@observable @property({type: Boolean}) exportEnabled = false;
@observable @property({type: Boolean}) showMoreEnabled = false;


/** Lowest row index of the continguous (i.e., shift-click) selection. */
@property({type: Number}) shiftSelectionStartIndex = 0;
Expand All @@ -126,6 +129,8 @@ export class DataTable extends ReactiveElement {
/** The maximum width of a <th> element's text before truncation. */
@property({type: Number}) headerTextMaxWidth: number|null = null;

@observable private hasExpandedCells = false;

// Callbacks
@property({type: Object}) onClick?: OnPrimarySelectCallback;
@property({type: Object}) onHover?: OnHoverCallback;
Expand Down Expand Up @@ -671,14 +676,16 @@ export class DataTable extends ReactiveElement {
this.onPrimarySelect(primaryIndex);
}

@queryAll('lit-table-text-cell') tableTextCells?: LitTableTextCell[];

/**
* Imperative controls, intended to be used by a containing module
* such as data_table_module.ts
*/
@computed
get isDefaultView() {
return this.sortName === undefined && this.columnFilterInfo.size === 0 &&
this.globalSearchText === '';
this.globalSearchText === '' && this.hasExpandedCells;
}

@computed
Expand All @@ -691,6 +698,8 @@ export class DataTable extends ReactiveElement {
this.sortName = undefined; // reset to input ordering
this.showColumnMenu = false; // hide search bar
this.requestUpdate();
this.hasExpandedCells = false;
this.tableTextCells?.forEach(t => {t.expanded = false;});
}

getVisibleDataIdxs(): number[] {
Expand Down Expand Up @@ -1037,7 +1046,11 @@ export class DataTable extends ReactiveElement {
}
};

function formatCellContents(d: TableEntry) {
const expand = (e:Event) => {
this.hasExpandedCells = true;
};

const formatCellContents = (d: TableEntry, maxWidth: number) => {
if (d == null) return null;

if (typeof d === 'string' && d.startsWith(IMAGE_PREFIX)) {
Expand Down Expand Up @@ -1065,13 +1078,25 @@ export class DataTable extends ReactiveElement {
// Text formatting uses pre-wrap, so be sure that this template
// doesn't add any extra whitespace inside the div.
// clang-format off
if (typeof d ==='string' && this.showMoreEnabled) {
return html`
<lit-table-text-cell
@showmore=${expand}
.content=${d}
.maxWidth=${maxWidth}>
</lit-table-text-cell>`;
}
return html`
<div class="text-cell">${formatForDisplay(d, undefined, true)}</div>`;
// clang-format on
}
};

const cellClasses = this.columnHeaders.map(
h => classMap({'cell-holder': true, 'right-align': h.rightAlign!}));
h => classMap({'cell-holder': true,
'right-align': h.rightAlign!}));

const cellMaxWidths = this.columnHeaders.map(h => h.maxWidth?? 0);

const cellStyles = styleMap(
{'vertical-align': this.verticalAlignMiddle ? 'middle' : 'top'});
// clang-format off
Expand All @@ -1080,7 +1105,7 @@ export class DataTable extends ReactiveElement {
@mouseleave=${mouseLeave} @mousedown=${mouseDown}>
${data.rowData.map((d, i) =>
html`<td style=${cellStyles}><div class=${cellClasses[i]}>${
formatCellContents(d)
formatCellContents(d, cellMaxWidths[i])
}</div></td>`)}
</tr>
`;
Expand Down

0 comments on commit ee54333

Please sign in to comment.