@@ -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' )
148149export 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