Skip to content

Commit

Permalink
Use expansion panels and pop-out controls for salience module.
Browse files Browse the repository at this point in the history
- Expansion panels instead of checkbox system.
- Move settings for methods like IG and LIME to pop-up controls modal.
- Reusable <popup-container> element for sticky modals.
- Expansion panels now have a slot for additional bar content.

PiperOrigin-RevId: 482920471
  • Loading branch information
iftenney authored and LIT team committed Oct 21, 2022
1 parent e4ab24f commit 1994425
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 89 deletions.
15 changes: 13 additions & 2 deletions lit_nlp/client/elements/expansion_panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
padding: 2px 8px;
border-bottom: 1px solid var(--lit-neutral-300);
border-top: 1px solid var(--lit-neutral-100);
column-gap: 4px;

display: flex;
flex-direction: row;
Expand All @@ -19,8 +20,6 @@
justify-content: space-between;

background-color: var(--lit-neutral-100);
color: var(--lit-majtonal-nv-800);
font-weight: bold;
}

.expansion-label {
Expand All @@ -29,6 +28,18 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

color: var(--lit-majtonal-nv-800);
font-weight: bold;
}

.bar-spacer {
flex: 1; /* use available space */
}

/* Revert a pointer style from .expansion-header */
.bar-content {
cursor: initial;
}

.expansion-content {
Expand Down
12 changes: 11 additions & 1 deletion lit_nlp/client/elements/expansion_panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class ExpansionPanel extends ReactiveElement {
}

@observable @property({type: String}) label = '';
@observable @property({type: String}) description = '';
@observable @property({type: Boolean}) expanded = false;
@observable @property({type: Boolean}) padLeft = false;
@observable @property({type: Boolean}) padRight = false;
Expand All @@ -66,16 +67,25 @@ export class ExpansionPanel extends ReactiveElement {
this.dispatchEvent(event);
};

const description = this.description ?? this.label;

// clang-format off
return html`
<div class="expansion-header" @click=${toggle}>
<div class="expansion-label">${this.label}</div>
<div class="expansion-label" title=${description}>${this.label}</div>
<div class='bar-spacer'></div>
<div class='bar-content'
@click=${(e: Event) => { e.stopPropagation(); }}>
<slot name="bar-content"></slot>
</div>
<mwc-icon class="icon-button min-button">
${this.expanded ? 'expand_less' : 'expand_more'}
</mwc-icon>
</div>
${this.expanded ?
html`<div class=${classes} style=${styles}><slot></slot></div>` :
null}`;
// clang-format on
}
}

Expand Down
40 changes: 38 additions & 2 deletions lit_nlp/client/elements/expansion_panel_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ describe('expansion panel test', () => {
(firstChild as HTMLDivElement).click();
await panel.updateComplete;

const slot = panel.shadowRoot!.querySelector('slot');
const slot =
panel.shadowRoot!.querySelector('slot:not([name])') as HTMLSlotElement;
const slottedNodes = slot!.assignedNodes({flatten: true})
.filter(n => n instanceof HTMLDivElement);

Expand All @@ -181,7 +182,8 @@ describe('expansion panel test', () => {
(firstChild as HTMLDivElement).click();
await panel.updateComplete;

const slot = panel.shadowRoot!.querySelector('slot');
const slot =
panel.shadowRoot!.querySelector('slot:not([name])') as HTMLSlotElement;
const slottedNodes = slot!.assignedNodes({flatten: true})
.filter(n => n instanceof HTMLDivElement);

Expand All @@ -190,4 +192,38 @@ describe('expansion panel test', () => {
expect(node instanceof HTMLDivElement).toBeTrue();
}
});

it('should render bar content from a named slot from a template',
async () => {
const template = html`
<expansion-panel .label=${'test expansion panel'}>
<div slot="bar-content">This goes in the bar</div>
<div>This is a test div</div>
</expansion-panel>`;
render(template, document.body);
const panels = document.body.querySelectorAll('expansion-panel');
const panel = panels[panels.length - 1];
await panel.updateComplete;

const [firstChild] = panel.renderRoot.children;
(firstChild as HTMLDivElement).click();
await panel.updateComplete;

const namedSlot = panel.shadowRoot!.querySelector(
'slot[name=bar-content]') as HTMLSlotElement;
const namedSlotNodes = namedSlot.assignedNodes({flatten: true})
.filter(n => n instanceof HTMLDivElement);

expect(namedSlotNodes.length).toEqual(1);
expect(namedSlotNodes[0] instanceof HTMLDivElement).toBeTrue();

// Check the non-named slot is still handled correctly.
const slot = panel.shadowRoot!.querySelector('slot:not([name])') as
HTMLSlotElement;
const slottedNodes = slot.assignedNodes({flatten: true})
.filter(n => n instanceof HTMLDivElement);

expect(slottedNodes.length).toEqual(1);
expect(slottedNodes[0] instanceof HTMLDivElement).toBeTrue();
});
});
7 changes: 5 additions & 2 deletions lit_nlp/client/elements/interpreter_controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class InterpreterControls extends ReactiveElement {
@observable @property({type: String}) applyButtonText = 'Apply';
@property({type: Boolean, reflect: true}) applyButtonDisabled = false;
@observable settings: InterpreterSettings = {};
@property({type: Boolean, reflect: true}) noexpand = false;
@property({type: Boolean, reflect: true}) opened = false;

static override get styles() {
Expand All @@ -70,8 +71,10 @@ export class InterpreterControls extends ReactiveElement {
this.dispatchEvent(event);
};

const expandable =
Object.keys(this.spec).length > 0 || this.description.length > 0;
// TODO(b/254775471): revisit this logic, remove need for noexpand
// in favor of an explicit 'expandable' attribute.
const expandable = !this.noexpand &&
(Object.keys(this.spec).length > 0 || this.description.length > 0);

const descriptionHTML = getTemplateStringFromMarkdown(this.description);

Expand Down
30 changes: 30 additions & 0 deletions lit_nlp/client/elements/popup_container.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
:host {
--popup-left: unset;
--popup-right: unset;
--popup-top: unset;
--popup-bottom: unset;
}

.popup-toggle-anchor {
cursor: pointer;
}

.popup-outer-holder {
position: relative;
}

.popup-container {
padding: 4px;
background: white;
border: 1px solid var(--lit-neutral-300);
border-radius: 4px;
box-shadow: var(--lit-box-shadow);

position: absolute;
z-index: 9001; /* over 9000! */

left: var(--popup-left);
right: var(--popup-right);
top: var(--popup-top);
bottom: var(--popup-bottom);
}
125 changes: 125 additions & 0 deletions lit_nlp/client/elements/popup_container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* @fileoverview Element for a pop-up container.
*
* @license
* Copyright 2022 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 './popup_container.css';

/** Custom expansion event interface for ExpansionPanel */
export interface PopupToggle {
isExpanded: boolean;
}

/**
* Popup controls container, anchored by a clickable toggle.
*
* Use the 'toggle-anchor' slot for the control; this will be rendered
* where the <popup-container> element is placed.
*
* The remaining content will be rendered in the popup, which is positioned
* relative to this anchor following the usual rules for position: absolute,
* and the top, bottom, left, and right attributes specified in CSS as
* --popup-top, --popup-left, etc.
*
* Default position is below and left-aligned; to right-align
* set --popup-right: 0 in styling for the <popup-container> element.
*
* Usage:
* <popup-container style=${popupStyle}>
* <div slot="toggle-anchor">
* <mwc-icon>settings</mwc-icon>
* </div>
* <div>
* // Content goes here
* </div>
* </popup-container>
*
* TODO(b/254786211): add DOM and behavioral tests for this element.
*/
@customElement('popup-container')
export class PopupContainer extends ReactiveElement {
static override get styles() {
return [sharedStyles, styles];
}

@property({type: Boolean}) expanded = false;

toggle() {
this.expanded = !this.expanded;
const event = new CustomEvent<PopupToggle>(
'popup-toggle', {detail: {isExpanded: this.expanded}});
this.dispatchEvent(event);
}

private renderPopup() {
if (!this.expanded) return null;
// clang-format off
return html`
<div class='popup-outer-holder'>
<div class='popup-container'>
<slot></slot>
</div>
</div>
`;
// clang-format on
}

override render() {
// clang-format off
return html`
<div class='popup-toggle-anchor' @click=${() => { this.toggle(); }}>
<slot name="toggle-anchor"></slot>
</div>
${this.renderPopup()}
`;
// clang-format off
}

protected clickToClose(event: MouseEvent) {
const path = event.composedPath();
if (!path.some(elem => elem === this)) {
this.expanded = false;
}
}

override updated() {
const onBodyClick = (event: MouseEvent) => {
this.clickToClose(event);
};
if (this.expanded) {
document.body.addEventListener(
'click', onBodyClick, {passive: true, capture: true});
} else {
document.body.removeEventListener(
'click', onBodyClick, {capture: true});
}
}

}

declare global {
interface HTMLElementTagNameMap {
'popup-container': PopupContainer;
}
}
13 changes: 7 additions & 6 deletions lit_nlp/client/lib/shared_styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@
--lit-bold-label-color: var(--lit-majtonal-nv-800);
--lit-light-label-color: var(--lit-majtonal-nv-600);

--lit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
--lit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14),
0 3px 1px -2px rgba(0, 0, 0, .2),
0 1px 5px 0 rgba(0, 0, 0, .12);
}

button {
Expand Down Expand Up @@ -561,7 +563,7 @@ mwc-icon.mdi-outlined {

.module-footer {
align-items: center;
border-top: 1px solid #dadce0;
border-top: 1px solid var(--viz-color-grey-1);
box-sizing: border-box;
display: flex;
flex-direction: row;
Expand All @@ -574,7 +576,7 @@ mwc-icon.mdi-outlined {

/** Status text in a module footer. **/
.module-status {
color: #3c4043;
color: var(--lit-neutral-800);
font-style: italic;
font-weight: normal;
margin: 0 0.5em;
Expand Down Expand Up @@ -616,10 +618,9 @@ mwc-icon.outlined {
--mdc-icon-font: "Material Icons Outlined";
}

/* TODO(b/254783802): consolidate this with <popup-container> */
.popup-container {
background: white;
border: 1px solid var(--lit-neutral-600);
box-shadow: rgb(0 0 0 / 14%) 0px 2px 2px 0px,
rgb(0 0 0 / 20%) 0px 3px 1px -2px,
rgb(0 0 0 / 12%) 0px 1px 5px 0px;
box-shadow: var(--lit-box-shadow);
}
29 changes: 23 additions & 6 deletions lit_nlp/client/modules/salience_map_module.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,31 @@
visibility: hidden;
}

table {
border-collapse: collapse;
width: 100%;
.method-row-contents {
display: flex;
flex-direction: row;
}

.controls-toggle {
display: flex;
flex-direction: row;
align-items: center;
}

.controls-toggle mwc-icon {
--mdc-icon-size: 16px;
}

.controls-panel {
width: 295px;
}

.method-results {
padding: 4px;
}

.method-row {
border-top: 1px solid #e8eaed;
border-bottom: 1px solid #e8eaed;
.salience-placeholder {
color: var(--lit-neutral-800);
}

.loading {
Expand Down

0 comments on commit 1994425

Please sign in to comment.