Skip to content

Improve error message for social media [WHIT-3156]#11360

Open
GDSNewt wants to merge 7 commits into
mainfrom
Improve-error-message-social-media-wip
Open

Improve error message for social media [WHIT-3156]#11360
GDSNewt wants to merge 7 commits into
mainfrom
Improve-error-message-social-media-wip

Conversation

@GDSNewt
Copy link
Copy Markdown
Contributor

@GDSNewt GDSNewt commented Apr 14, 2026

Overview

This update improves the clarity, consistency, and usability of validation errors when publishers add social media accounts.

Why

Previously, validation feedback for social media accounts was unclear and inconsistent:

  • Errors only appeared in the summary, not inline
  • It wasn’t obvious which field (Channel or URL) needed fixing
  • Terminology used “service” instead of “channel”
  • Only one error appeared even when multiple fields were invalid

What Changed

Validation behaviour and messaging have been enhanced:

  • Terminology updated from service → channel to match the UI
  • Errors are now attached to individual fields (Channel, URL) instead of a shared attribute
  • Errors display both inline and in the summary
  • Validation runs independently per field
  • Multiple errors are shown when applicable (e.g. both fields blank)
  • Invalid fields are highlighted with a red border

ACs

Meets the ACs as described in the Jira ticket


UI Error Examples

1. Both fields blank

Screenshot 2026-04-14 at 11 33 54

2. Channel selected, URL missing

Screenshot 2026-04-14 at 11 34 13

3. URL entered, channel missing

Screenshot 2026-04-14 at 11 34 25

4. Multiple accounts, mixed errors

Screenshot 2026-04-14 at 11 35 34

@GDSNewt GDSNewt changed the title Improve error message social media wip Improve error message for social media [WHIT-3156] Apr 14, 2026
@GDSNewt GDSNewt force-pushed the Improve-error-message-social-media-wip branch from e63ba07 to 5a70f75 Compare April 14, 2026 12:50
ryanb-gds

This comment was marked as outdated.

@GDSNewt GDSNewt force-pushed the Improve-error-message-social-media-wip branch 6 times, most recently from 1bd6059 to 6ba1fa3 Compare April 17, 2026 10:51
@GDSNewt GDSNewt closed this Apr 22, 2026
@GDSNewt GDSNewt reopened this Apr 27, 2026
@ChrisBAshton ChrisBAshton dismissed ryanb-gds’s stale review April 28, 2026 08:04

Alex attempted the nested validation approach in #11381.

It worked, but we discussed as a team in Sit Down recently. As per ADR 009, we recently made the decision to move away from 'nested' validation as it added a lot of complexity for little benefit. We felt that writing a custom validator and declaring it at the root "validations" property level was the right level of abstraction / complexity at this stage.

Therefore we're re-exploring #11360 as the solution to better error messages for the array block.

Copy link
Copy Markdown
Contributor

@ChrisBAshton ChrisBAshton left a comment

Choose a reason for hiding this comment

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

I'm as keen to get this over the line as you are :D let's see if we can remove some of the code smells first - comments below.

Comment thread test/unit/app/validators/social_media_links_validator_test.rb Outdated
Comment thread test/unit/app/validators/social_media_links_validator_test.rb Outdated
Comment thread app/models/configurable_document_type.rb
Comment thread app/models/standard_edition.rb
Comment thread app/validators/social_media_links_validator.rb Outdated
@GDSNewt GDSNewt force-pushed the Improve-error-message-social-media-wip branch 3 times, most recently from a828a6d to d5cd09f Compare May 5, 2026 10:27
Copy link
Copy Markdown
Contributor

@ChrisBAshton ChrisBAshton left a comment

Choose a reason for hiding this comment

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

Getting there 👏

private

def find_field_title(config, segments)
return config["title"] if segments.empty?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks like we're missing a test for this behaviour ^

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure we're testing this behaviour yet? segments is "field_attribute" in your first unit test. The only situation I see this line returning anything is if we basically call title_for_attribute(nil), which shouldn't ever be a thing, right? Can we just delete this line?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tried removing it, and it breaks several tests (including the one that expects "Test Attribute"), because the final recursive call with [] no longer returns the title.

For title_for_attribute("field_attribute.0"), we remove the index and get ["field_attribute"], match the field, then recurse with []. That recursive call hits this line because segments.empty? is true, and that’s where we return the title.

So it’s not just for nil input—it’s the stop condition for valid lookups as well. Without it, the recursion wouldn’t know when to stop and would end up returning nil.

I don’t think we can safely delete it for that reason, even though it’s not obvious from the tests. We could perhaps add a test for the empty case?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yep I'd add a test for the empty case 👍

Comment thread app/models/configurable_document_type.rb
Comment thread app/models/configurable_document_type.rb
Comment thread test/unit/app/validators/social_media_links_validator_test.rb
Comment thread test/components/admin/error_summary_component_test.rb Outdated
Comment thread test/support/with_nested_attribute_errors.rb
# Errors on array item fields use dotted attribute names like
# :"social_media_links.0.url" so they can be targeted inline per
# field. Rails calls these as methods during error processing;
# returning nil here prevents a NoMethodError.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Did you consider my suggestion that we switch from test_array_attribute.0.social_media_service_id to something like test_array_attribute__0__social_media_service_id so that we don't need to fight Rails's default method name construction? (Could do it in a refactor commit at the end if you want to avoid a painful rebase)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I suggested this on the assumption that it means you don't need to define these method_missing / respond_to_missing method definitions...? (On the basis that method names can't include a . in them, but can include a __).

If all this suggestion does is a find-and-replace across the code then it's not a useful suggestion 😅 Did you check what else could be cleaned up as a result? 🙏

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

From a quick look, I'm hoping the move to __ means we can delete the WithNestedAttributeErrors helper entirely?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So I don't think it actually eliminates the need for method_missing guard or WithNestedAttributeErrors — Rails calls the attribute as a method on the model during error processing regardless of whether the separator is . or __, so the guard is needed either way.

I think it ends up being a pure find-and-replace.

One small improvement we could make as a follow-up: BlockContent currently has its own inline copy of the method_missing/respond_to_missing? logic, while WithNestedAttributeErrors has an identical copy for use in test doubles. We could de-duplicate by moving the helper to concerns and having BlockContent include it, rather than defining it inline. Wanted to check you were happy with that first, given it's making WithNestedAttributeErrors even more needed than before 😬

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OK - thanks for removing the "." to "__" replacement.

De-duplicating feels like a good move 👍

@GDSNewt GDSNewt force-pushed the Improve-error-message-social-media-wip branch 6 times, most recently from 8cfd894 to c5cd04c Compare May 11, 2026 13:27
Comment thread app/models/configurable_document_type.rb
Comment thread app/models/configurable_document_type.rb
Comment thread test/unit/app/models/configurable_document_type_test.rb Outdated
# Errors on array item fields use dotted attribute names like
# :"social_media_links.0.url" so they can be targeted inline per
# field. Rails calls these as methods during error processing;
# returning nil here prevents a NoMethodError.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

From a quick look, I'm hoping the move to __ means we can delete the WithNestedAttributeErrors helper entirely?

Comment thread test/unit/app/models/standard_edition/block_content_test.rb Outdated
GDSNewt added 6 commits May 13, 2026 14:05
Translates dotted error keys like "social_media_links.0.url" into a
human-readable field title by stripping numeric index segments and
walking the form config.
Hooks into Rails error message generation to return human-readable
field titles for dotted attribute names on configurable document types.
Replaces inline method_missing definitions in test classes with a
shared module, avoiding repetition across the codebase.
Validates uniqueness of channel and URL across social media link array
entries, adding per-field errors with dotted attribute keys.
Covers the case where an error is added with a dotted attribute key
like "social_media_links.0.url", ensuring the component renders the
correct link text and href anchor.
Return nil for dotted attribute names in method_missing to prevent
NoMethodError during Rails error processing.
Without segments.first(path.length) != path, looking up an attribute
would incorrectly return the title of the first field encountered rather
than the one whose path actually matches.
@GDSNewt GDSNewt force-pushed the Improve-error-message-social-media-wip branch from c5cd04c to 63f5426 Compare May 13, 2026 13:07
@GDSNewt GDSNewt requested a review from ChrisBAshton May 13, 2026 13:08
Copy link
Copy Markdown
Contributor

@ChrisBAshton ChrisBAshton left a comment

Choose a reason for hiding this comment

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

Have spent half an hour spiking trying to remove the dot notation. It should be doable but wasn't doable in that time! Happy to proceed and maybe we can have a backlog item to explore refactoring it back out again later.

A couple of comments above where we've agreed to add a couple more things ^ but otherwise this is good to go.

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.

3 participants