Skip to content

Commit

Permalink
TermInput: Mark invalid terms in read only mode as such
Browse files Browse the repository at this point in the history
The browser's validation API doesn't run for readonly
inputs, so we have to always check the constraint.
  • Loading branch information
nilmerg committed May 29, 2024
1 parent 7d0d73b commit b8d7b5b
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 2 deletions.
27 changes: 26 additions & 1 deletion asset/css/search-base.less
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

.search-bar,
.term-input-area {
[data-index] input:invalid {
[data-index] input:invalid,
[data-index] input.invalid {
background-color: var(--search-term-invalid-bg, @search-term-invalid-bg);
color: var(--search-term-invalid-color, @search-term-invalid-color);
}
Expand All @@ -41,6 +42,23 @@
}
}

.invalid-reason {
padding: .25em;
.rounded-corners(.25em);
border: 1px solid black;
font-weight: bold;
background: var(--search-term-invalid-reason-bg, @search-term-invalid-reason-bg);

opacity: 0;
visibility: hidden;
transition: opacity 2s, visibility 2s;
&.visible {
opacity: 1;
visibility: visible;
transition: none;
}
}

.search-suggestions {
background: var(--suggestions-bg, @suggestions-bg);
color: var(--suggestions-color, @suggestions-color);
Expand Down Expand Up @@ -235,6 +253,13 @@
display: revert;
}
}

.invalid-reason {
position: absolute;
z-index: 1;
top: 85%;
left: .5em;
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions asset/css/variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
@search-term-selected-bg: @base-disabled;
@search-term-invalid-bg: @state-critical;
@search-term-invalid-color: @default-text-color-inverted;
@search-term-invalid-reason-bg: @base-gray-lighter;
@search-term-disabled-bg: @base-disabled;
@search-term-selected-color: @base-gray-light;
@search-term-highlighted-bg: @base-primary-bg;
Expand Down Expand Up @@ -154,6 +155,7 @@
--search-term-selected-bg: var(--base-disabled);
--search-term-invalid-bg: var(--base-remove-bg);
--search-term-invalid-color: var(--default-text-color-inverted);
--search-term-invalid-reason-bg: var(--base-gray-lighter);
--search-term-disabled-bg: var(--base-gray-light);
--search-term-selected-color: var(--base-gray);
--search-term-highlighted-bg: var(--primary-button-bg);
Expand Down
43 changes: 43 additions & 0 deletions asset/js/widget/TermInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) {
this.ignoreSpaceUntil = null;
}

registerTerm(termData, termIndex = null) {
termIndex = super.registerTerm(termData, termIndex);

if (this.readOnly) {
const label = this.termContainer.querySelector(`[data-index="${ termIndex }"]`);
if (label) {
// The label only exists in DOM at this time if it was transmitted
// by the server. So it's safe to assume that it needs validation
this.validate(label.firstChild);
}
}

return termIndex;
}

readPartialTerm(input) {
let value = super.readPartialTerm(input);
if (value && this.ignoreSpaceUntil && value[0] === this.ignoreSpaceUntil) {
Expand Down Expand Up @@ -75,6 +90,33 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) {
return super.hasSyntaxError(input);
}

checkValidity(input) {
if (! this.readOnly) {
return super.checkValidity(input);
}

// Readonly terms don't participate in constraint validation, so we have to do it ourselves
return ! (input.pattern && ! input.value.match(input.pattern));
}

reportValidity(element) {
if (! this.readOnly) {
return super.reportValidity(element);
}

// Once invalid, it stays invalid since it's readonly
element.classList.add('invalid');
if (element.dataset.invalidMsg) {
const reason = element.parentNode.querySelector(':scope > .invalid-reason');
if (! reason.matches('.visible')) {
element.title = element.dataset.invalidMsg;
reason.textContent = element.dataset.invalidMsg;
reason.classList.add('visible');
setTimeout(() => reason.classList.remove('visible'), 5000);
}
}
}

termsToQueryString(terms) {
let quoted = [];
for (const termData of terms) {
Expand All @@ -101,6 +143,7 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) {
if (this.readOnly) {
label.firstChild.readOnly = true;
label.appendChild($.render('<i class="icon fa-trash fa"></i>'));
label.appendChild($.render('<span class="invalid-reason"></span>'));
}

return label;
Expand Down
5 changes: 4 additions & 1 deletion src/FormElement/TermInput/TermContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ protected function assemble()
)
);
if ($this->input->getReadOnly()) {
$label->addHtml(new Icon('trash'));
$label->addHtml(
new Icon('trash'),
new HtmlElement('span', Attributes::create(['class' => 'invalid-reason']))
);
}

$this->addHtml($label);
Expand Down

0 comments on commit b8d7b5b

Please sign in to comment.