forked from extern/egroupware
Add edit button to freeEntry selectbox tags
This commit is contained in:
parent
dc3e8c5b7d
commit
7518278948
@ -443,7 +443,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
* @see createTagNode()
|
* @see createTagNode()
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
public get tagTag()
|
public get tagTag() : string
|
||||||
{
|
{
|
||||||
return "et2-tag";
|
return "et2-tag";
|
||||||
}
|
}
|
||||||
@ -458,7 +458,15 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
*/
|
*/
|
||||||
protected _createTagNode(item)
|
protected _createTagNode(item)
|
||||||
{
|
{
|
||||||
const tag = <Et2Tag>document.createElement(this.tagTag);
|
let tag;
|
||||||
|
if(typeof super._createTagNode == "function")
|
||||||
|
{
|
||||||
|
tag = super._createTagNode(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tag = <Et2Tag>document.createElement(this.tagTag);
|
||||||
|
}
|
||||||
tag.value = item.value;
|
tag.value = item.value;
|
||||||
tag.textContent = item.getTextLabel().trim();
|
tag.textContent = item.getTextLabel().trim();
|
||||||
tag.class = item.classList.value + " search_tag";
|
tag.class = item.classList.value + " search_tag";
|
||||||
|
@ -13,6 +13,7 @@ import {cleanSelectOptions, SelectOption} from "./FindSelectOptions";
|
|||||||
import {Validator} from "@lion/form-core";
|
import {Validator} from "@lion/form-core";
|
||||||
import {Et2Tag} from "./Tag/Et2Tag";
|
import {Et2Tag} from "./Tag/Et2Tag";
|
||||||
import {SlMenuItem} from "@shoelace-style/shoelace";
|
import {SlMenuItem} from "@shoelace-style/shoelace";
|
||||||
|
import {waitForEvent} from "@shoelace-style/shoelace/dist/internal/event";
|
||||||
|
|
||||||
// Otherwise import gets stripped
|
// Otherwise import gets stripped
|
||||||
let keep_import : Et2Tag;
|
let keep_import : Et2Tag;
|
||||||
@ -292,6 +293,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
|
|
||||||
this.handleMenuSelect = this.handleMenuSelect.bind(this);
|
this.handleMenuSelect = this.handleMenuSelect.bind(this);
|
||||||
this._handleChange = this._handleChange.bind(this);
|
this._handleChange = this._handleChange.bind(this);
|
||||||
|
this.handleTagEdit = this.handleTagEdit.bind(this);
|
||||||
this._handleAfterShow = this._handleAfterShow.bind(this);
|
this._handleAfterShow = this._handleAfterShow.bind(this);
|
||||||
this._handleSearchBlur = this._handleSearchBlur.bind(this);
|
this._handleSearchBlur = this._handleSearchBlur.bind(this);
|
||||||
this._handleClear = this._handleClear.bind(this);
|
this._handleClear = this._handleClear.bind(this);
|
||||||
@ -361,6 +363,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
// Normally this should be handled in render(), but we have to add our nodes in
|
// Normally this should be handled in render(), but we have to add our nodes in
|
||||||
this._addNodes();
|
this._addNodes();
|
||||||
}
|
}
|
||||||
|
// Update any tags if edit mode changes
|
||||||
|
if(changedProperties.has("editModeEnabled"))
|
||||||
|
{
|
||||||
|
this.shadowRoot.querySelectorAll(this.tagTag).forEach(tag => tag.editable = this.editModeEnabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -393,6 +400,21 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customise how tags are rendered.
|
||||||
|
* Override to add edit
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected _createTagNode(item)
|
||||||
|
{
|
||||||
|
let tag = <Et2Tag>document.createElement(this.tagTag);
|
||||||
|
tag.editable = this.editModeEnabled;
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
protected _searchInputTemplate()
|
protected _searchInputTemplate()
|
||||||
{
|
{
|
||||||
let edit = null;
|
let edit = null;
|
||||||
@ -581,6 +603,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
|
|
||||||
this._searchInputNode?.removeEventListener("change", this._searchInputNode.handleChange);
|
this._searchInputNode?.removeEventListener("change", this._searchInputNode.handleChange);
|
||||||
this._searchInputNode?.addEventListener("change", this._handleSearchChange);
|
this._searchInputNode?.addEventListener("change", this._handleSearchChange);
|
||||||
|
|
||||||
|
this.dropdown.querySelector('.select__label').addEventListener("change", this.handleTagEdit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +750,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
// Find the tag
|
// Find the tag
|
||||||
const path = event.composedPath();
|
const path = event.composedPath();
|
||||||
const tag = <Et2Tag>path.find((el) => el instanceof Et2Tag);
|
const tag = <Et2Tag>path.find((el) => el instanceof Et2Tag);
|
||||||
this.startEdit(tag);
|
this.dropdown.hide();
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
tag.startEdit(event);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1197,8 +1225,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
if(!this.querySelector("[value='" + text.replace(/'/g, "\\\'") + "']") && !this.__select_options.find(o => o.value == text))
|
if(!this.querySelector("[value='" + text.replace(/'/g, "\\\'") + "']") && !this.__select_options.find(o => o.value == text))
|
||||||
{
|
{
|
||||||
this.__select_options.push(<SelectOption>{
|
this.__select_options.push(<SelectOption>{
|
||||||
value: text,
|
value: text.trim(),
|
||||||
label: text,
|
label: text.trim(),
|
||||||
class: "freeEntry"
|
class: "freeEntry"
|
||||||
});
|
});
|
||||||
this.requestUpdate('select_options');
|
this.requestUpdate('select_options');
|
||||||
@ -1239,8 +1267,41 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
return validators.length > 0 && result.length == 0 || validators.length == 0;
|
return validators.length > 0 && result.length == 0 || validators.length == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public handleTagEdit(event)
|
||||||
|
{
|
||||||
|
let value = event.target.value;
|
||||||
|
let original = event.target.dataset.original_value;
|
||||||
|
|
||||||
|
if(!value || !this.allowFreeEntries || !this.validateFreeEntry(value))
|
||||||
|
{
|
||||||
|
// Not a good value, reset it.
|
||||||
|
event.target.variant = "danger"
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.target.variant = "success";
|
||||||
|
|
||||||
|
// Add to internal list
|
||||||
|
this.createFreeEntry(value);
|
||||||
|
|
||||||
|
// Remove original from value & DOM
|
||||||
|
if(value != original)
|
||||||
|
{
|
||||||
|
if(this.multiple)
|
||||||
|
{
|
||||||
|
this.value = this.value.filter(v => v !== original);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
this.querySelector("[value='" + original.replace(/'/g, "\\\'") + "']")?.remove();
|
||||||
|
this.__select_options = this.__select_options.filter(v => v.value !== original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start editing an existing (free) tag, or the current value if multiple=false
|
* Start editing the current value if multiple=false
|
||||||
*
|
*
|
||||||
* @param {Et2Tag} tag
|
* @param {Et2Tag} tag
|
||||||
*/
|
*/
|
||||||
@ -1249,23 +1310,21 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
const tag_value = tag ? tag.value : this.value;
|
const tag_value = tag ? tag.value : this.value;
|
||||||
|
|
||||||
// hide the menu
|
// hide the menu
|
||||||
//this.dropdown.hide()
|
this.dropdown.hide()
|
||||||
|
|
||||||
// Turn on edit UI
|
waitForEvent(this, "sl-after-hide").then(() =>
|
||||||
this._activeControls.classList.add("editing", "active");
|
|
||||||
|
|
||||||
// Pre-set value to tag value
|
|
||||||
this._editInputNode.style.display = "";
|
|
||||||
this._editInputNode.value = tag_value
|
|
||||||
this._editInputNode.focus();
|
|
||||||
|
|
||||||
if(tag)
|
|
||||||
{
|
{
|
||||||
tag.remove();
|
// Turn on edit UI
|
||||||
}
|
this._activeControls.classList.add("editing", "active");
|
||||||
|
|
||||||
// If they abort the edit, they'll want the original back.
|
// Pre-set value to tag value
|
||||||
this._editInputNode.dataset.initial = tag_value;
|
this._editInputNode.style.display = "";
|
||||||
|
this._editInputNode.value = tag_value
|
||||||
|
this._editInputNode.focus();
|
||||||
|
|
||||||
|
// If they abort the edit, they'll want the original back.
|
||||||
|
this._editInputNode.dataset.initial = tag_value;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected stopEdit(abort = false)
|
protected stopEdit(abort = false)
|
||||||
|
@ -21,36 +21,49 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
return [
|
return [
|
||||||
super.styles,
|
super.styles,
|
||||||
shoelace, css`
|
shoelace, css`
|
||||||
:host {
|
:host {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.tag--pill {
|
|
||||||
overflow: hidden;
|
.tag--pill {
|
||||||
}
|
overflow: hidden;
|
||||||
::slotted(et2-image)
|
}
|
||||||
{
|
|
||||||
height: 20px;
|
::slotted(et2-image) {
|
||||||
width: 20px;
|
height: 20px;
|
||||||
}
|
width: 20px;
|
||||||
.tag__content {
|
}
|
||||||
padding: 0px 0.2rem;
|
|
||||||
flex: 1 2 auto;
|
.tag__content {
|
||||||
overflow: hidden;
|
padding: 0px 0.2rem;
|
||||||
text-overflow: ellipsis;
|
flex: 1 2 auto;
|
||||||
}
|
overflow: hidden;
|
||||||
/* Avoid button getting truncated by right side of button */
|
text-overflow: ellipsis;
|
||||||
.tag__remove {
|
}
|
||||||
margin-right: 0;
|
|
||||||
margin-left: 0;
|
/* Avoid button getting truncated by right side of button */
|
||||||
}
|
|
||||||
`];
|
.tag__remove {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
et2-button-icon {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(:hover) et2-button-icon {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
`];
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties()
|
static get properties()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
...super.properties,
|
...super.properties,
|
||||||
|
editable: {type: Boolean, reflect: true},
|
||||||
value: {type: String, reflect: true}
|
value: {type: String, reflect: true}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,7 +73,11 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
super(...args);
|
super(...args);
|
||||||
this.value = "";
|
this.value = "";
|
||||||
this.pill = false;
|
this.pill = false;
|
||||||
|
this.editable = false;
|
||||||
this.removable = true;
|
this.removable = true;
|
||||||
|
|
||||||
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _styleTemplate() : TemplateResult
|
protected _styleTemplate() : TemplateResult
|
||||||
@ -70,12 +87,44 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
|
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
|
let content;
|
||||||
|
if(this.isEditing)
|
||||||
|
{
|
||||||
|
content = html`${this._editTemplate()}`
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
content = html`${this._contentTemplate()}
|
||||||
|
${this.editable ? html`
|
||||||
|
<et2-button-icon
|
||||||
|
label=${this.egw().lang("edit")}
|
||||||
|
name="pencil"
|
||||||
|
@click=${this.startEdit}
|
||||||
|
></et2-button-icon>` : ''
|
||||||
|
}
|
||||||
|
${this.removable
|
||||||
|
? html`
|
||||||
|
<sl-icon-button
|
||||||
|
part="remove-button"
|
||||||
|
exportparts="base:remove-button__base"
|
||||||
|
name="x"
|
||||||
|
library="system"
|
||||||
|
label=${this.egw().lang('remove')}
|
||||||
|
class="tag__remove"
|
||||||
|
@click=${this.handleRemoveClick}
|
||||||
|
></sl-icon-button>
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
|
`;
|
||||||
|
}
|
||||||
return html`
|
return html`
|
||||||
${this._styleTemplate()}
|
${this._styleTemplate()}
|
||||||
<span
|
<span
|
||||||
part="base"
|
part="base"
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
tag: true,
|
tag: true,
|
||||||
|
'tag--editable': this.editable,
|
||||||
|
'tag--editing': this.isEditing,
|
||||||
// Types
|
// Types
|
||||||
'tag--primary': this.variant === 'primary',
|
'tag--primary': this.variant === 'primary',
|
||||||
'tag--success': this.variant === 'success',
|
'tag--success': this.variant === 'success',
|
||||||
@ -95,20 +144,7 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
<span part="prefix" class="tag__prefix">
|
<span part="prefix" class="tag__prefix">
|
||||||
<slot name="prefix"></slot>
|
<slot name="prefix"></slot>
|
||||||
</span>
|
</span>
|
||||||
${this._contentTemplate()}
|
${content}
|
||||||
${this.removable
|
|
||||||
? html`
|
|
||||||
<sl-icon-button
|
|
||||||
part="remove-button"
|
|
||||||
exportparts="base:remove-button__base"
|
|
||||||
name="x"
|
|
||||||
library="system"
|
|
||||||
label=${this.egw().lang('remove')}
|
|
||||||
class="tag__remove"
|
|
||||||
@click=${this.handleRemoveClick}
|
|
||||||
></sl-icon-button>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -120,6 +156,74 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
</span>`;
|
</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_editTemplate() : TemplateResult
|
||||||
|
{
|
||||||
|
return html`
|
||||||
|
<span part="content">
|
||||||
|
<et2-textbox value="${this.value}"
|
||||||
|
@sl-change=${this.handleChange}
|
||||||
|
@blur=${this.stopEdit}
|
||||||
|
@click=${e => e.stopPropagation()}
|
||||||
|
@keydown=${this.handleKeyDown}
|
||||||
|
></et2-textbox>
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
startEdit(event? : MouseEvent)
|
||||||
|
{
|
||||||
|
if(event)
|
||||||
|
{
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
this.isEditing = true;
|
||||||
|
this.requestUpdate();
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
this._editNode.focus();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEdit()
|
||||||
|
{
|
||||||
|
this.isEditing = false;
|
||||||
|
this.dataset.original_value = this.value;
|
||||||
|
if(!this.editable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.value = this.textContent = this._editNode.value.trim();
|
||||||
|
this.requestUpdate();
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
let event = new Event("change", {
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get _editNode() : HTMLInputElement
|
||||||
|
{
|
||||||
|
return this.shadowRoot.querySelector('et2-textbox');
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown(event : KeyboardEvent)
|
||||||
|
{
|
||||||
|
// Consume event so it doesn't bubble up to select
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if(["Tab", "Enter"].indexOf(event.key) !== -1)
|
||||||
|
{
|
||||||
|
this._editNode.blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(event : CustomEvent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("et2-tag", Et2Tag);
|
customElements.define("et2-tag", Et2Tag);
|
135
api/js/etemplate/Et2Select/test/EditableTag.test.ts
Normal file
135
api/js/etemplate/Et2Select/test/EditableTag.test.ts
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import {assert, fixture, html, oneEvent} from '@open-wc/testing';
|
||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import * as sinon from 'sinon';
|
||||||
|
import {Et2Tag} from "../Tag/Et2Tag";
|
||||||
|
|
||||||
|
// Stub global egw for cssImage & widget.egw() to find
|
||||||
|
// @ts-ignore
|
||||||
|
window.egw = {
|
||||||
|
image: () => "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDMyIDMyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjNjk2OTY5IiBkPSJNNi45NDMsMjguNDUzDQoJYzAuOTA2LDAuNzY1LDIuMDk3LDEuMTI3LDMuMjg2LDEuMTA5YzAuNDMsMC4wMTQsMC44NTItMC4wNjgsMS4yNjUtMC4yMDdjMC42NzktMC4xOCwxLjMyOC0wLjQ1LDEuODY2LTAuOTAyTDI5LjQwMywxNC45DQoJYzEuNzcyLTEuNDk4LDEuNzcyLTMuOTI1LDAtNS40MjJjLTEuNzcyLTEuNDk3LTQuNjQ2LTEuNDk3LTYuNDE4LDBMMTAuMTE5LDIwLjM0OWwtMi4zODktMi40MjRjLTEuNDQtMS40NTctMy43NzItMS40NTctNS4yMTIsMA0KCWMtMS40MzgsMS40Ni0xLjQzOCwzLjgyNSwwLDUuMjgxQzIuNTE4LDIzLjIwNiw1LjQ3NCwyNi45NDcsNi45NDMsMjguNDUzeiIvPg0KPC9zdmc+DQo=",
|
||||||
|
lang: i => i + "*",
|
||||||
|
tooltipUnbind: () => {},
|
||||||
|
webserverUrl: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
let element : Et2Select;
|
||||||
|
|
||||||
|
async function before(editable = true)
|
||||||
|
{
|
||||||
|
// Create an element to test with, and wait until it's ready
|
||||||
|
// @ts-ignore
|
||||||
|
element = await fixture<Et2Select>(html`
|
||||||
|
<et2-select label="I'm a select" value="one" multiple="true" .editModeEnabled=${editable}>
|
||||||
|
<sl-menu-item value="one">One</sl-menu-item>
|
||||||
|
<sl-menu-item value="two">Two</sl-menu-item>
|
||||||
|
</et2-select>
|
||||||
|
`);
|
||||||
|
// Stub egw()
|
||||||
|
sinon.stub(element, "egw").returns(window.egw);
|
||||||
|
|
||||||
|
await element.updateComplete;
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Editable tag", () =>
|
||||||
|
{
|
||||||
|
// Setup run before each test
|
||||||
|
beforeEach(before);
|
||||||
|
|
||||||
|
// Make sure it works
|
||||||
|
it('is defined', () =>
|
||||||
|
{
|
||||||
|
assert.instanceOf(element, Et2Select);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Tag editable matches editModeEnabled", async() =>
|
||||||
|
{
|
||||||
|
let tag = element.shadowRoot.querySelectorAll(element.tagTag);
|
||||||
|
assert.isAbove(tag.length, 0, "No tags found");
|
||||||
|
assert.isTrue(tag[0].editable);
|
||||||
|
|
||||||
|
// Change it to false & force immediate update
|
||||||
|
element.editModeEnabled = false;
|
||||||
|
element.syncItemsFromValue();
|
||||||
|
element.requestUpdate();
|
||||||
|
await element.updateComplete;
|
||||||
|
|
||||||
|
tag = element.shadowRoot.querySelectorAll(element.tagTag);
|
||||||
|
assert.isAbove(tag.length, 0, "No tags found");
|
||||||
|
assert.isFalse(tag[0].editable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Has edit button when editable ", async() =>
|
||||||
|
{
|
||||||
|
let tag = element.shadowRoot.querySelectorAll(element.tagTag);
|
||||||
|
assert.isAbove(tag.length, 0, "No tags found");
|
||||||
|
assert.exists(tag[0].shadowRoot.querySelector("et2-button-icon[label='edit*']"), "No edit button");
|
||||||
|
});
|
||||||
|
it("Shows input when edit button is clicked", async() =>
|
||||||
|
{
|
||||||
|
let tag = element.shadowRoot.querySelectorAll(element.tagTag)[0];
|
||||||
|
|
||||||
|
let edit_button = tag.shadowRoot.querySelector("et2-button-icon");
|
||||||
|
edit_button.click();
|
||||||
|
|
||||||
|
await tag.updateComplete;
|
||||||
|
assert.exists(tag.shadowRoot.querySelector("et2-textbox"), "No input to edit");
|
||||||
|
});
|
||||||
|
it("Changes value when edited", async() =>
|
||||||
|
{
|
||||||
|
let tag = <Et2Tag>element.shadowRoot.querySelectorAll(element.tagTag)[0];
|
||||||
|
tag.isEditing = true;
|
||||||
|
tag.requestUpdate();
|
||||||
|
await tag.updateComplete;
|
||||||
|
|
||||||
|
const listener = oneEvent(tag, "change");
|
||||||
|
let textbox = tag.shadowRoot.querySelector('et2-textbox');
|
||||||
|
textbox.value = "changed";
|
||||||
|
tag.stopEdit();
|
||||||
|
|
||||||
|
await listener;
|
||||||
|
|
||||||
|
// Value changes
|
||||||
|
assert.equal(tag.value, "changed");
|
||||||
|
|
||||||
|
// Haven't turned on allow free entries, so no change here
|
||||||
|
assert.equal(element.value, "one", "Tag change caused a value change in parent select, but allowFreeEntries was off");
|
||||||
|
|
||||||
|
// Shown as invalid
|
||||||
|
assert.equal(tag.variant, "danger");
|
||||||
|
|
||||||
|
// Turn it on, check again
|
||||||
|
element.allowFreeEntries = true;
|
||||||
|
|
||||||
|
// Re-set to original value
|
||||||
|
tag.value = "one"
|
||||||
|
|
||||||
|
// Change again, this time select should change value too
|
||||||
|
tag.isEditing = true;
|
||||||
|
tag.requestUpdate();
|
||||||
|
await tag.updateComplete;
|
||||||
|
const listener2 = oneEvent(tag, "change");
|
||||||
|
textbox = tag.shadowRoot.querySelector('et2-textbox');
|
||||||
|
textbox.value = "change select too";
|
||||||
|
tag.stopEdit();
|
||||||
|
await listener2;
|
||||||
|
assert.equal(tag.value, "change select too");
|
||||||
|
|
||||||
|
// Haven't turned on allow free entries, so no change here
|
||||||
|
assert.equal(element.value, "change select too", "Tag change did not cause value change in parent select (allowFreeEntries was on)");
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("Select is not editable", () =>
|
||||||
|
{
|
||||||
|
|
||||||
|
beforeEach(() => before(false));
|
||||||
|
|
||||||
|
it("Does not have edit button when not editable", async() =>
|
||||||
|
{
|
||||||
|
let tag = element.shadowRoot.querySelectorAll(element.tagTag);
|
||||||
|
assert.isAbove(tag.length, 0, "No tags found");
|
||||||
|
assert.isNull(tag[0].shadowRoot.querySelector("et2-button-icon[label='edit*']"), "Unexpected edit button");
|
||||||
|
});
|
||||||
|
});
|
@ -1304,7 +1304,6 @@ const Et2WidgetMixin = <T extends Constructor>(superClass : T) =>
|
|||||||
{
|
{
|
||||||
return (<et2_widget>this.getParent()).egw();
|
return (<et2_widget>this.getParent()).egw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the window this object belongs to
|
// Get the window this object belongs to
|
||||||
let wnd = null;
|
let wnd = null;
|
||||||
// @ts-ignore Technically this doesn't have implements(), but it's mixed in
|
// @ts-ignore Technically this doesn't have implements(), but it's mixed in
|
||||||
@ -1318,7 +1317,7 @@ const Et2WidgetMixin = <T extends Constructor>(superClass : T) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we're the root object, return the phpgwapi API instance
|
// If we're the root object, return the phpgwapi API instance
|
||||||
return typeof egw === "function" ? egw('phpgwapi', wnd) : null;
|
return typeof egw === "function" ? egw('phpgwapi', wnd) : (window['egw'] ? window['egw'] : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user