Skip to content

Commit

Permalink
[Search][a11y] Add validation to extraction rules form (#202980)
Browse files Browse the repository at this point in the history
## Closes: #199154

This adds more validations to the Crawler extraction rules form.

The original issue of the error being at the top of the page is not
easily fixable, as it's a catch-all server error display. Ideally, we
shouldn't have server errors occurring at all, so it makes sense to me
to just add a front-end validation to the inputs in this field.

These validations cover the following previously-missed scenarios:

1. When a user has not added any rules
2. When rule is for a specific URL and the URL pattern field is empty,
or doesn't begin with `/`
3. When the value for "Source" is empty (covers both HTML element and
URL selectors)
4. When "Content" is "A fixed value" and the value field is empty
  • Loading branch information
navarone-feekery authored Dec 27, 2024
1 parent 1d1d265 commit 9865da3
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,34 @@ export const EditExtractionRule: React.FC<EditExtractionRuleProps> = ({
<Controller
control={control}
name={`url_filters.${index}.pattern`}
render={({ field }) => (
rules={{
validate: (rule) => {
if (!rule?.trim()) {
return i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.fieldInput.requiredError',
{
defaultMessage: 'A value is required.',
}
);
}

if (rule[0] !== '/') {
return i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.fieldInput.slashMissingError',
{
defaultMessage: 'Value must begin with a /.',
}
);
}

return true;
},
}}
render={({ field, fieldState: { error, isTouched } }) => (
<>
<EuiFormRow
isInvalid={!!error && isTouched}
error={error?.message}
label={i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editRule.url.urlFilter.',
{
Expand All @@ -311,6 +336,7 @@ export const EditExtractionRule: React.FC<EditExtractionRuleProps> = ({
<EuiFieldText
data-telemetry-id="entSearchContent-crawler-domainDetail-extractionRules-urlPattern"
fullWidth
isInvalid={!!error && isTouched}
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editRule.url.urlFilters.patternPlaceholder',
{
Expand Down Expand Up @@ -414,7 +440,7 @@ export const EditExtractionRule: React.FC<EditExtractionRuleProps> = ({
data-telemetry-id="entSearchContent-crawler-domainDetail-extractionRules-saveExtractionRule"
type="submit"
onClick={() => saveRule({ ...getValues() })}
disabled={!formState.isValid}
disabled={!formState.isValid || !rulesFields || rulesFields.length === 0}
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editRule.saveButtonLabel',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,28 +228,39 @@ export const EditFieldRuleFlyout: React.FC<EditFieldRuleFlyoutProps> = ({
{!!field.value && (
<>
<EuiSpacer />
<EuiFormRow
fullWidth
label={
field.value === FieldType.HTML
? i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.content.htmlLabel',
{
defaultMessage: 'CSS selector or XPath expression',
}
)
: i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.content.urlLabel',
{
defaultMessage: 'URL pattern',
}
)
}
>
<Controller
control={control}
name="selector"
render={({ field: selectorField, fieldState: { error, isTouched } }) => (
<Controller
control={control}
name="selector"
rules={{
validate: (rule) =>
!!rule?.trim() ||
i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.fieldInput.requiredError',
{
defaultMessage: 'A value is required.',
}
),
}}
render={({ field: selectorField, fieldState: { error, isTouched } }) => (
<EuiFormRow
isInvalid={!!error && isTouched}
error={error?.message}
label={
field.value === FieldType.HTML
? i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.content.htmlLabel',
{
defaultMessage: 'CSS selector or XPath expression',
}
)
: i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.content.urlLabel',
{
defaultMessage: 'URL pattern',
}
)
}
>
<EuiFieldText
data-telemetry-id="entSearchContent-crawler-domainDetail-extractionRules-editContentRuleSelector"
isInvalid={!!error && isTouched}
Expand All @@ -274,9 +285,9 @@ export const EditFieldRuleFlyout: React.FC<EditFieldRuleFlyoutProps> = ({
onChange={selectorField.onChange}
value={selectorField.value ?? ''}
/>
)}
/>
</EuiFormRow>
</EuiFormRow>
)}
/>
<EuiSpacer />
{field.value === FieldType.HTML ? (
<EuiLink
Expand Down Expand Up @@ -446,8 +457,23 @@ export const EditFieldRuleFlyout: React.FC<EditFieldRuleFlyoutProps> = ({
<Controller
control={control}
name="content_from.value"
render={({ field: valueField }) => (
rules={{
validate: (rule) =>
!!rule?.trim() ||
i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.contentFixedValue.requiredError',
{
defaultMessage: 'A value is required',
}
),
}}
render={({
field: valueField,
fieldState: { error: fieldError, isTouched: fieldIsTouched },
}) => (
<EuiFormRow
isInvalid={!!fieldError && fieldIsTouched}
error={fieldError?.message}
helpText={i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.fixedValue.helpText',
{
Expand All @@ -464,6 +490,7 @@ export const EditFieldRuleFlyout: React.FC<EditFieldRuleFlyoutProps> = ({
<EuiFieldText
data-telemetry-id="entSearchContent-crawler-domainDetail-extractionRules-editContentRuleFixedValue"
fullWidth
isInvalid={!!fieldError && fieldIsTouched}
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.fixedValue.placeHolder',
{
Expand Down

0 comments on commit 9865da3

Please sign in to comment.