Skip to content

Commit 2c019ac

Browse files
bennypowerseyevana
andauthored
feat(text-input): validation attrs (#2606)
* feat(text-input): validation attrs * fix(text-input): formStateRestor * style: whitespace * fix(text-input): update elements/pf-text-input/pf-text-input.ts Co-authored-by: Ivana Rodriguez <55894965+eyevana@users.noreply.github.com> * docs: update elements/pf-text-input/demo/validation.html Co-authored-by: Ivana Rodriguez <55894965+eyevana@users.noreply.github.com> --------- Co-authored-by: Ivana Rodriguez <55894965+eyevana@users.noreply.github.com>
1 parent a194ba7 commit 2c019ac

File tree

4 files changed

+95
-4
lines changed

4 files changed

+95
-4
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@patternfly/elements": minor
3+
---
4+
`<pf-text-input>`: adds `helper-text`, `error-text`, and `validate-on` attributes. Forwards `pattern` attribute
5+
6+
```html
7+
<pf-text-input id="validated"
8+
error-text="Enter a three digit integer"
9+
helper-text="How much wood could a woodchuck chuck?"
10+
validate-on="blur"
11+
pattern="\d{3}"
12+
required></pf-text-input>
13+
<pf-button id="validate">Validate</pf-button>
14+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<fieldset>
2+
<legend>Invalid</legend>
3+
<label>Validate on Blur? <pf-switch id="onblur"></pf-switch></label>
4+
<pf-text-input id="validated"
5+
error-text="Enter a three digit integer"
6+
pattern="\d{3}"
7+
required></pf-text-input>
8+
<pf-button id="validate">Validate</pf-button>
9+
</fieldset>
10+
11+
<script type="module">
12+
import '@patternfly/elements/pf-button/pf-button.js';
13+
import '@patternfly/elements/pf-switch/pf-switch.js';
14+
import '@patternfly/elements/pf-text-input/pf-text-input.js';
15+
const onblur = document.getElementById('onblur');
16+
const input = document.getElementById('validated');
17+
const validate = document.getElementById('validate');
18+
onblur.addEventListener('change', function() {
19+
validated.validateOn = this.checked ? 'blur' : null;
20+
});
21+
validate.addEventListener('click', function() {
22+
validated.checkValidity();
23+
});
24+
</script>
25+
26+
<link rel="stylesheet" href="demo.css">

elements/pf-text-input/pf-text-input.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@
124124
--pf-c-form-control--m-icon-sprite__select--success--BackgroundPosition: calc(100% - var(--pf-global--spacer--md, 1rem) + 1px - var(--pf-global--spacer--lg, 1.5rem));
125125
--pf-c-form-control--m-icon-sprite__select--m-warning--BackgroundPosition: calc(100% - var(--pf-global--spacer--md, 1rem) - var(--pf-global--spacer--lg, 1.5rem) + 0.0625rem);
126126
--pf-c-form-control--m-icon-sprite__select--invalid--BackgroundPosition: calc(100% - var(--pf-global--spacer--md, 1rem) - var(--pf-global--spacer--lg, 1.5rem));
127+
/* NB: this var doesn't exist in pf react */
128+
--pf-c-form-control__error-text--m-status--Color: var(--pf-global--danger-color--100, #c9190b);
127129

128130
display: inline-block;
129131
/* adjust the host to fit the input */
@@ -163,6 +165,10 @@ input::placeholder {
163165
color: var(--pf-c-form-control--placeholder--Color);
164166
}
165167

168+
#error-text {
169+
color: var(--pf-c-form-control__error-text--m-status--Color);
170+
}
171+
166172
:host([left-truncated]) {
167173
position: relative;
168174
}

elements/pf-text-input/pf-text-input.ts

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ function getLabelText(label: HTMLElement) {
143143
* @cssprop --pf-c-form-control--m-icon-sprite__select--success--BackgroundPosition - {@default calc(100% - var(--pf-global--spacer--md, 1rem) + 1px - var(--pf-global--spacer--lg, 1.5rem))}
144144
* @cssprop --pf-c-form-control--m-icon-sprite__select--m-warning--BackgroundPosition - {@default calc(100% - var(--pf-global--spacer--md, 1rem) - var(--pf-global--spacer--lg, 1.5rem) + 0.0625rem)}
145145
* @cssprop --pf-c-form-control--m-icon-sprite__select--invalid--BackgroundPosition - {@default calc(100% - var(--pf-global--spacer--md, 1rem) - var(--pf-global--spacer--lg, 1.5rem))}
146+
* @cssprop --pf-c-form-control__error-text--m-status--Color - {@default var(--pf-global--danger-color--100, #c9190b)}
146147
*/
147148
@customElement('pf-text-input')
148149
export class PfTextInput extends LitElement {
@@ -185,9 +186,21 @@ export class PfTextInput extends LitElement {
185186
/** Flag to show if the input is required. */
186187
@property({ type: Boolean, reflect: true }) required = false;
187188

189+
/** Validation pattern, like `<input>` */
190+
@property() pattern?: string;
191+
188192
/** Flag to show if the input is read only. */
189193
@property({ type: Boolean, reflect: true }) readonly = false;
190194

195+
/** Helper text is text below a form field that helps a user provide the right information, like "Enter a unique name". */
196+
@property({ attribute: 'helper-text' }) helperText?: string;
197+
198+
/** If set to 'blur', will validate when focus leaves the input */
199+
@property({ attribute: 'validate-on' }) validateOn?: 'blur';
200+
201+
/** Displayed when validation fails */
202+
@property({ attribute: 'error-text' }) errorText?: string;
203+
191204
/** Input placeholder. */
192205
@property() placeholder?: string;
193206

@@ -198,6 +211,8 @@ export class PfTextInput extends LitElement {
198211

199212
#derivedLabel = '';
200213

214+
#touched = false;
215+
201216
get #input() {
202217
return this.shadowRoot?.getElementById('input') as HTMLInputElement ?? null;
203218
}
@@ -213,48 +228,78 @@ export class PfTextInput extends LitElement {
213228
}
214229

215230
override render() {
231+
const { valid } = this.#internals.validity;
216232
return html`
217233
<input id="input"
234+
.placeholder="${this.placeholder ?? ''}"
235+
.value="${this.value}"
236+
.pattern="${this.pattern as string}"
218237
@input="${this.#onInput}"
238+
@blur="${this.#onBlur}"
219239
?disabled="${this.matches(':disabled') || this.disabled}"
220240
?readonly="${this.readonly}"
221241
?required="${this.required}"
222242
aria-label="${this.#derivedLabel}"
223-
placeholder="${ifDefined(this.placeholder)}"
224243
type="${ifDefined(this.type)}"
225-
.value="${this.value}"
226244
style="${ifDefined(this.customIconUrl && styleMap({
227245
backgroundImage: `url('${this.customIconUrl}')`,
228246
backgroundSize: this.customIconDimensions,
229247
}))}">
248+
<span id="helper-text" ?hidden="${!this.helperText ?? valid}">${this.helperText}</span>
249+
<span id="error-text" ?hidden="${valid}">${this.#internals.validationMessage}</span>
230250
`;
231251
}
232252

233253
#onInput(event: Event & { target: HTMLInputElement }) {
234254
const { value } = event.target;
235255
this.value = value;
236256
this.#internals.setFormValue(value);
257+
if (this.#touched && !this.#internals.validity.valid) {
258+
this.#onBlur();
259+
}
260+
this.#touched = true;
261+
}
262+
263+
#onBlur() {
264+
if (this.validateOn === 'blur') {
265+
this.checkValidity();
266+
}
237267
}
238268

239269
#setValidityFromInput() {
240270
this.#internals.setValidity(
241271
this.#input?.validity,
242-
this.#input.validationMessage,
272+
this.errorText ?? this.#input.validationMessage,
243273
);
274+
this.requestUpdate();
275+
}
276+
277+
async formStateRestoreCallback(state: string, mode: string) {
278+
if (mode === 'restore') {
279+
const [controlMode, value] = state.split('/');
280+
this.value = value ?? controlMode;
281+
this.requestUpdate();
282+
await this.updateComplete;
283+
this.#setValidityFromInput();
284+
}
244285
}
245286

287+
246288
async formDisabledCallback() {
247289
await this.updateComplete;
248290
this.requestUpdate();
249291
}
250292

251293
setCustomValidity(message: string) {
252294
this.#internals.setValidity({}, message);
295+
this.requestUpdate();
253296
}
254297

255298
checkValidity() {
256299
this.#setValidityFromInput();
257-
return this.#internals.checkValidity();
300+
const validity = this.#internals.checkValidity();
301+
this.requestUpdate();
302+
return validity;
258303
}
259304

260305
reportValidity() {

0 commit comments

Comments
 (0)