diff --git a/api/js/etemplate/Et2Link/Et2LinkEntry.ts b/api/js/etemplate/Et2Link/Et2LinkEntry.ts
index 873bd01e28..6625b8ed19 100644
--- a/api/js/etemplate/Et2Link/Et2LinkEntry.ts
+++ b/api/js/etemplate/Et2Link/Et2LinkEntry.ts
@@ -163,7 +163,7 @@ export class Et2LinkEntry extends Et2InputWidget(FormControlMixin(SlotMixin(LitE
}
if(changedProperties.has("readonly"))
{
- this._appNode.readonly = this.readonly;
+ this._appNode.readonly = this._appNode.disabled = this.readonly;
this._searchNode.readonly = this.readonly;
}
// Pass some properties on to app selection
diff --git a/api/js/etemplate/Et2Link/Et2LinkTo.ts b/api/js/etemplate/Et2Link/Et2LinkTo.ts
index 9064a3095b..d1b480ae8e 100644
--- a/api/js/etemplate/Et2Link/Et2LinkTo.ts
+++ b/api/js/etemplate/Et2Link/Et2LinkTo.ts
@@ -139,6 +139,7 @@ export class Et2LinkTo extends Et2InputWidget(ScopedElementsMixin(FormControlMix
return html`
diff --git a/api/js/etemplate/Et2Select/Et2Select.ts b/api/js/etemplate/Et2Select/Et2Select.ts
index c0d63ec0db..08c43e2e9e 100644
--- a/api/js/etemplate/Et2Select/Et2Select.ts
+++ b/api/js/etemplate/Et2Select/Et2Select.ts
@@ -88,36 +88,51 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
.select__label {
display: block;
text-overflow: ellipsis;
- /* This is usually not used due to flex, but is the basis for ellipsis calculation */
- width: 10ex;
+ /* This is usually not used due to flex, but is the basis for ellipsis calculation */
+ width: 10ex;
}
- /** multiple=true uses tags for each value **/
- /* styling for icon inside tag (not option) */
- .tag_image {
+ /** multiple=true uses tags for each value **/
+ /* styling for icon inside tag (not option) */
+
+ .tag_image {
margin-right: var(--sl-spacing-x-small);
- }
- /* Maximum height + scrollbar on tags (+ other styling) */
- .select__tags {
+ }
+
+ /* Maximum height + scrollbar on tags (+ other styling) */
+
+ .select__tags {
margin-left: 0px;
max-height: initial;
overflow-y: auto;
gap: 0.1rem 0.5rem;
- }
- .select--medium .select__tags {
+ }
+
+ .select--medium .select__tags {
padding-top: 2px;
padding-bottom: 2px;
- }
- :host([rows]) .select__tags {
+ }
+
+ :host([rows]) .select__tags {
max-height: calc(var(--rows, 5) * 1.35rem);
- }
- /* Keep overflow tag right-aligned. It's the only sl-tag. */
- .select__tags sl-tag {
+ }
+
+ /* Keep overflow tag right-aligned. It's the only sl-tag. */
+
+ .select__tags sl-tag {
margin-left: auto;
- }
- select:hover {
+ }
+
+ select:hover {
box-shadow: 1px 1px 1px rgb(0 0 0 / 60%);
- }`
+ }
+
+ /* Hide dropdown trigger when multiple & readonly */
+
+ :host([readonly][multiple]) .select__icon {
+ display: none;
+ }
+ `
];
}
diff --git a/api/js/etemplate/Et2Select/SearchMixin.ts b/api/js/etemplate/Et2Select/SearchMixin.ts
index c56abbdee1..afc60a37ce 100644
--- a/api/js/etemplate/Et2Select/SearchMixin.ts
+++ b/api/js/etemplate/Et2Select/SearchMixin.ts
@@ -365,9 +365,14 @@ export const Et2WithSearchMixin = >(superclass
this._addNodes();
}
// Update any tags if edit mode changes
- if(changedProperties.has("editModeEnabled"))
+ if(changedProperties.has("editModeEnabled") || changedProperties.has("readonly"))
{
- this.shadowRoot.querySelectorAll(".select__tags > *").forEach(tag => tag.editable = this.editModeEnabled);
+ // Required because we explicitly create tags instead of doing it in render()
+ this.shadowRoot.querySelectorAll(".select__tags > *").forEach((tag : Et2Tag) =>
+ {
+ tag.editable = this.editModeEnabled && !this.readonly;
+ tag.removable = !this.readonly;
+ });
}
}
@@ -411,7 +416,7 @@ export const Et2WithSearchMixin = >(superclass
protected _createTagNode(item)
{
let tag = document.createElement(this.tagTag);
- tag.editable = this.editModeEnabled;
+ tag.editable = this.editModeEnabled && !this.readonly;
return tag;
}
diff --git a/api/js/etemplate/Et2Select/test/EditableTag.test.ts b/api/js/etemplate/Et2Select/test/EditableTag.test.ts
index 530340505d..5a3976d8e6 100644
--- a/api/js/etemplate/Et2Select/test/EditableTag.test.ts
+++ b/api/js/etemplate/Et2Select/test/EditableTag.test.ts
@@ -28,6 +28,9 @@ async function before(editable = true)
sinon.stub(element, "egw").returns(window.egw);
await element.updateComplete;
+ let tags = [];
+ element.shadowRoot.querySelectorAll(element.tagTag).forEach((t : Et2Tag) => tags.push(t.updateComplete));
+ await Promise.all(tags);
return element;
}
@@ -120,6 +123,21 @@ describe("Editable tag", () =>
assert.equal(element.value, "change select too", "Tag change did not cause value change in parent select (allowFreeEntries was on)");
});
+
+ it("Does not have edit button when readonly", async() =>
+ {
+ element.readonly = true;
+ await element.updateComplete;
+
+ let tag = element.shadowRoot.querySelectorAll(element.tagTag);
+ assert.isAbove(tag.length, 0, "No tags found");
+
+ let wait = [];
+ tag.forEach((t : Et2Tag) => wait.push(t.updateComplete))
+ await Promise.all(wait);
+
+ assert.isNull(tag[0].shadowRoot.querySelector("et2-button-icon[label='edit*']"), "Unexpected edit button");
+ });
});
describe("Select is not editable", () =>
{
@@ -130,6 +148,8 @@ describe("Select is not editable", () =>
{
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");
});
+
});
\ No newline at end of file