Skip to content

Add errorMessage and hasError props to the TextField component. - #13334

Open
shervElmi wants to merge 14 commits into
developfrom
enhancement/12935-text-field-error-message-prop
Open

Add errorMessage and hasError props to the TextField component.#13334
shervElmi wants to merge 14 commits into
developfrom
enhancement/12935-text-field-error-message-prop

Conversation

@shervElmi

@shervElmi shervElmi commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Related issue(s):

Relevant technical choices

  • assets/js/googlesitekit/components-gm2/TextField.tsx
    • Renders a hidden errorMessage in a VisuallyHidden sibling, instead of adding screen-reader-text to the helper text.
  • Converts TextField, CreateAccountField, and VisuallyHidden to TypeScript.
  • assets/js/modules/sign-in-with-google/components/common/ClientIDTextField.js
    • Shows the invalid Client ID message in place of the existing Client ID recommendation, which the setup flow alone renders.

PR Author Checklist

  • My code is tested and passes existing unit tests.
  • My code has an appropriate set of unit tests which all pass.
  • My code is backward-compatible with WordPress 5.2 and PHP 7.4.
  • My code follows the WordPress coding standards.
  • My code has proper inline documentation.
  • I have added a QA Brief on the issue linked above.
  • I have signed the Contributor License Agreement (see https://cla.developers.google.com/).

Do not alter or remove anything below. The following sections will be managed by moderators only.

Code Reviewer Checklist

  • Run the code.
  • Ensure the acceptance criteria are satisfied.
  • Reassess the implementation with the IB.
  • Ensure no unrelated changes are included.
  • Ensure CI checks pass.
  • Check Storybook where applicable.
  • Ensure there is a QA Brief.
  • Ensure there are no unexpected significant changes to file sizes.

Merge Reviewer Checklist

  • Ensure the PR has the correct target branch.
  • Double-check that the PR is okay to be merged.
  • Ensure the corresponding issue has a ZenHub release assigned.
  • Add a changelog message to the issue.

`TextField` is now a TypeScript file, and it renders `VisuallyHidden`
for the error message it keeps off the screen. The plain JavaScript
`VisuallyHidden` left `className` required for a TypeScript caller,
since a prop with no default value reads as required.
Every `TextField` caller built the error state by hand, setting
`mdc-text-field--error`, a warning trailing icon, and `helperText` at
the field. `TextField` now takes `errorMessage` for a message it shows
under the field, and `hasError` for the error outline and the warning
icon alone. Either prop sets `aria-invalid` on the input, and a message
the field keeps off the screen still reaches a screen reader through
`aria-errormessage`.
The tests cover `errorMessage` alone, `hasError` alone, and the two
together. Each test reads the error outline, the warning icon, the error
message, and the `aria-invalid` and `aria-errormessage` attributes.
The two error examples set `errorMessage` and `hasError` in place of the
`mdc-text-field--error` class and the trailing icon they built by hand.
Both examples take a new label, so the three Text Fields reference
images need approving again.
The Ads conversion ID field passes the invalid ID message as
`errorMessage`, so the error outline and the warning icon come from
`TextField`.
The web data stream name field passes each name error as `errorMessage`,
so the error outline and the warning icon come from `TextField`. The
tests read `aria-invalid` and `aria-errormessage` on the input.
The invalid Client ID message moves to `errorMessage`. The
recommendation to use an existing Client ID stays in `helperText`, so
that recommendation shows with no error outline.
An empty container name now holds the message a screen reader reads. The
container name field shows the error outline and the warning icon alone.
A duplicate name keeps its message on the screen.
`CreateAccountField` takes an `errorMessage` prop and passes it to
`TextField` with `hasError`. An empty field then names its error for a
screen reader, while the screen shows the error outline and the warning
icon alone. Dropping the `className` prop also drops a duplicate
`mdc-text-field`.
The account, property, and web data stream fields each pass the message
a screen reader reads while that field is empty.
The tests read `aria-invalid`, `aria-errormessage`, and the hidden error
message on each of the three fields. A field with a name holds none of
the three.
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 This comment is automatically updated by CI workflows. Each section is managed independently.

📚 Storybook for 47dd401:

📦 Build files for 47dd401:

🎭 Playwright reports for 47dd401:

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 3.33 MB

ℹ️ View Unchanged
Filename Size Change
dist/assets/blocks/reader-revenue-manager/block-editor-plugin/editor-styles.css 124 B 0 B
dist/assets/blocks/reader-revenue-manager/block-editor-plugin/editor-styles.js 0 B 0 B 🆕
dist/assets/blocks/reader-revenue-manager/block-editor-plugin/index.js 43.9 kB 0 B
dist/assets/blocks/reader-revenue-manager/common/editor-styles.css 307 B 0 B
dist/assets/blocks/reader-revenue-manager/common/editor-styles.js 0 B 0 B 🆕
dist/assets/blocks/reader-revenue-manager/contribute-with-google/index.js 7.03 kB 0 B
dist/assets/blocks/reader-revenue-manager/contribute-with-google/non-site-kit-user.js 6.21 kB 0 B
dist/assets/blocks/reader-revenue-manager/subscribe-with-google/index.js 7.03 kB 0 B
dist/assets/blocks/reader-revenue-manager/subscribe-with-google/non-site-kit-user.js 6.21 kB 0 B
dist/assets/blocks/sign-in-with-google/editor-styles.css 84 B 0 B
dist/assets/blocks/sign-in-with-google/editor-styles.js 0 B 0 B 🆕
dist/assets/blocks/sign-in-with-google/index.js 18.5 kB 0 B
dist/assets/css/googlesitekit-admin-css-********************.min.css 74.6 kB 0 B
dist/assets/css/googlesitekit-adminbar-css-********************.min.css 12.7 kB 0 B
dist/assets/css/googlesitekit-authorize-application-css-********************.min.css 851 B 0 B
dist/assets/css/googlesitekit-wp-dashboard-css-********************.min.css 9.07 kB 0 B
dist/assets/js/46-********************.js 3.84 kB 0 B
dist/assets/js/65-********************.js 1.03 kB 0 B
dist/assets/js/187-********************.js 101 kB 0 B
dist/assets/js/308-********************.js 3 kB 0 B
dist/assets/js/315-********************.js 3.08 kB 0 B
dist/assets/js/397-********************.js 477 kB 0 B
dist/assets/js/403-********************.js 2.26 kB 0 B
dist/assets/js/509-********************.js 970 B 0 B
dist/assets/js/658-********************.js 52.7 kB 0 B
dist/assets/js/917-********************.js 2.41 kB 0 B
dist/assets/js/analytics-advanced-tracking-********************.js 404 B 0 B
dist/assets/js/googlesitekit-activation-********************.js 29 kB -41 B (-0.14%)
dist/assets/js/googlesitekit-ad-blocking-recovery-********************.js 66.7 kB -21 B (-0.03%)
dist/assets/js/googlesitekit-admin-pointers-tracking-********************.js 5.36 kB 0 B
dist/assets/js/googlesitekit-adminbar-********************.js 41.4 kB +24 B (+0.06%)
dist/assets/js/googlesitekit-api-********************.js 8.04 kB 0 B
dist/assets/js/googlesitekit-block-tracking-********************.js 5.56 kB 0 B
dist/assets/js/googlesitekit-components-********************.js 6.78 kB +501 B (+7.98%) 🔍
dist/assets/js/googlesitekit-consent-mode-********************.js 26 kB 0 B
dist/assets/js/googlesitekit-data-********************.js 1.84 kB 0 B
dist/assets/js/googlesitekit-datastore-forms-********************.js 7.21 kB 0 B
dist/assets/js/googlesitekit-datastore-location-********************.js 1.6 kB 0 B
dist/assets/js/googlesitekit-datastore-pdf-********************.js 1.22 kB 0 B
dist/assets/js/googlesitekit-datastore-site-********************.js 19.5 kB +30 B (+0.15%)
dist/assets/js/googlesitekit-datastore-ui-********************.js 7.37 kB 0 B
dist/assets/js/googlesitekit-datastore-user-********************.js 23.8 kB +29 B (+0.12%)
dist/assets/js/googlesitekit-entity-dashboard-********************.js 89.4 kB +17 B (+0.02%)
dist/assets/js/googlesitekit-events-provider-contact-form-7-********************.js 2.35 kB 0 B
dist/assets/js/googlesitekit-events-provider-content-events-********************.js 43 B 0 B
dist/assets/js/googlesitekit-events-provider-easy-digital-downloads-********************.js 1.12 kB 0 B
dist/assets/js/googlesitekit-events-provider-mailchimp-********************.js 2.34 kB 0 B
dist/assets/js/googlesitekit-events-provider-ninja-forms-********************.js 2.3 kB 0 B
dist/assets/js/googlesitekit-events-provider-optin-monster-********************.js 2.22 kB 0 B
dist/assets/js/googlesitekit-events-provider-popup-maker-********************.js 2.44 kB 0 B
dist/assets/js/googlesitekit-events-provider-woocommerce-********************.js 1.08 kB 0 B
dist/assets/js/googlesitekit-events-provider-wpforms-********************.js 2.44 kB 0 B
dist/assets/js/googlesitekit-i18n-********************.js 4.43 kB 0 B
dist/assets/js/googlesitekit-key-metrics-setup-********************.js 61.8 kB +226 B (+0.37%)
dist/assets/js/googlesitekit-main-dashboard-********************.js 213 kB +11 B (+0.01%)
dist/assets/js/googlesitekit-metric-selection-********************.js 65.3 kB -8 B (-0.01%)
dist/assets/js/googlesitekit-modules-********************.js 28.2 kB +246 B (+0.88%)
dist/assets/js/googlesitekit-modules-ads-********************.js 49.7 kB -175 B (-0.35%)
dist/assets/js/googlesitekit-modules-adsense-********************.js 161 kB -190 B (-0.12%)
dist/assets/js/googlesitekit-modules-analytics-4-********************.js 283 kB +34 B (+0.01%)
dist/assets/js/googlesitekit-modules-pagespeed-insights-********************.js 25.3 kB 0 B
dist/assets/js/googlesitekit-modules-reader-revenue-manager-********************.js 113 kB +214 B (+0.19%)
dist/assets/js/googlesitekit-modules-search-console-********************.js 75.5 kB +21 B (+0.03%)
dist/assets/js/googlesitekit-modules-sign-in-with-google-********************.js 35.4 kB +8 B (+0.02%)
dist/assets/js/googlesitekit-modules-tagmanager-********************.js 31.8 kB -232 B (-0.72%)
dist/assets/js/googlesitekit-notifications-********************.js 85.1 kB +37 B (+0.04%)
dist/assets/js/googlesitekit-polyfills-********************.js 227 B 0 B
dist/assets/js/googlesitekit-settings-********************.js 170 kB +9 B (+0.01%)
dist/assets/js/googlesitekit-splash-********************.js 92.9 kB +214 B (+0.23%)
dist/assets/js/googlesitekit-user-input-********************.js 57.7 kB -9 B (-0.02%)
dist/assets/js/googlesitekit-vendor-********************.js 314 kB 0 B
dist/assets/js/googlesitekit-vendor-lazy-pdf-********************.js 21.6 kB 0 B
dist/assets/js/googlesitekit-widgets-********************.js 179 kB -170 B (-0.1%)
dist/assets/js/googlesitekit-wp-dashboard-********************.js 68.9 kB -301 B (-0.44%)
dist/assets/js/runtime-********************.js 1.94 kB 0 B
dist/assets/js/sign-in-with-google-********************.js 1.14 kB 0 B

compressed-size-action

@zutigrm zutigrm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THanks @shervElmi looks good, left you few comments

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since TextField is now TS, should this be renamed to TextField.stories.tsx per https://github.com/google/site-kit-wp/blob/develop/docs/context/js/storybook.md?

);
expect(
getByText(
'Tracking for your Ads campaigns won’t work until you insert a valid ID'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few of these (across different test files that were added/updated) assert the full error sentence. Worth matching on a fragment/regex instead (e.q /tracking for your ads campaigns/) so a copy tweak doesn't break the test?

Comment on lines +139 to +141
[ 'Account', 'An account name is required.' ],
[ 'Property', 'A property name is required.' ],
[ 'Web data stream', 'A web data stream name is required.' ],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same array is copy-pasted for both it.each calls below. Lets pull it into one const FIELD_ERRORS = [...] and reuse.

value={ value }
hasError={ ! value }
errorMessage={ __(
'A web data stream name is required.',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message is identical to the one in

Can we pull it into a shared constant both files import, so it can't drift?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add errorMessage field to TextField component that encapsulates error message and icon pattern

2 participants