Fix isRequired not passed to React Aria field components
isRequired was consumed locally for the secondary label but never forwarded to the underlying AriaTextField/AriaSearchField, resulting in missing aria-required attribute and no built-in validation. Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Fixed `isRequired` prop not being passed to the underlying React Aria field components in TextField, SearchField, and PasswordField. Previously, `isRequired` was consumed locally for the secondary label text but never forwarded, which meant the input elements lacked `aria-required="true"` and React Aria's built-in required validation was not activated.
|
||||
|
||||
**Affected components:** TextField, SearchField, PasswordField
|
||||
@@ -1642,7 +1642,6 @@ export const PasswordFieldDefinition: {
|
||||
readonly label: {};
|
||||
readonly description: {};
|
||||
readonly secondaryLabel: {};
|
||||
readonly isRequired: {};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1655,12 +1654,11 @@ export type PasswordFieldOwnProps = {
|
||||
label?: FieldLabelProps['label'];
|
||||
description?: FieldLabelProps['description'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
isRequired?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface PasswordFieldProps
|
||||
extends Omit<TextFieldProps_2, 'className' | 'isRequired' | 'description'>,
|
||||
extends Omit<TextFieldProps_2, 'className' | 'description'>,
|
||||
PasswordFieldOwnProps {}
|
||||
|
||||
// @public
|
||||
@@ -1843,7 +1841,6 @@ export const SearchFieldDefinition: {
|
||||
readonly label: {};
|
||||
readonly description: {};
|
||||
readonly secondaryLabel: {};
|
||||
readonly isRequired: {};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1857,12 +1854,11 @@ export type SearchFieldOwnProps = {
|
||||
label?: FieldLabelProps['label'];
|
||||
description?: FieldLabelProps['description'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
isRequired?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface SearchFieldProps
|
||||
extends Omit<SearchFieldProps_2, 'className' | 'isRequired' | 'description'>,
|
||||
extends Omit<SearchFieldProps_2, 'className' | 'description'>,
|
||||
SearchFieldOwnProps {}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -2318,7 +2314,6 @@ export const TextFieldDefinition: {
|
||||
readonly label: {};
|
||||
readonly description: {};
|
||||
readonly secondaryLabel: {};
|
||||
readonly isRequired: {};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2331,12 +2326,11 @@ export type TextFieldOwnProps = {
|
||||
label?: FieldLabelProps['label'];
|
||||
description?: FieldLabelProps['description'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
isRequired?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TextFieldProps
|
||||
extends Omit<TextFieldProps_2, 'className' | 'isRequired' | 'description'>,
|
||||
extends Omit<TextFieldProps_2, 'className' | 'description'>,
|
||||
TextFieldOwnProps {
|
||||
type?: 'text' | 'email' | 'tel' | 'url';
|
||||
}
|
||||
|
||||
@@ -35,15 +35,8 @@ export const PasswordField = forwardRef<HTMLDivElement, PasswordFieldProps>(
|
||||
PasswordFieldDefinition,
|
||||
props,
|
||||
);
|
||||
const {
|
||||
classes,
|
||||
label,
|
||||
icon,
|
||||
isRequired,
|
||||
secondaryLabel,
|
||||
placeholder,
|
||||
description,
|
||||
} = ownProps;
|
||||
const { classes, label, icon, secondaryLabel, placeholder, description } =
|
||||
ownProps;
|
||||
|
||||
useEffect(() => {
|
||||
if (!label && !restProps['aria-label'] && !restProps['aria-labelledby']) {
|
||||
@@ -55,7 +48,7 @@ export const PasswordField = forwardRef<HTMLDivElement, PasswordFieldProps>(
|
||||
|
||||
// If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required.
|
||||
const secondaryLabelText =
|
||||
secondaryLabel || (isRequired ? 'Required' : null);
|
||||
secondaryLabel || (restProps.isRequired ? 'Required' : null);
|
||||
|
||||
// Manage secret visibility toggle
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
@@ -40,7 +40,6 @@ export const PasswordFieldDefinition = defineComponent<PasswordFieldOwnProps>()(
|
||||
label: {},
|
||||
description: {},
|
||||
secondaryLabel: {},
|
||||
isRequired: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -42,10 +42,9 @@ export type PasswordFieldOwnProps = {
|
||||
label?: FieldLabelProps['label'];
|
||||
description?: FieldLabelProps['description'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
isRequired?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface PasswordFieldProps
|
||||
extends Omit<AriaTextFieldProps, 'className' | 'isRequired' | 'description'>,
|
||||
extends Omit<AriaTextFieldProps, 'className' | 'description'>,
|
||||
PasswordFieldOwnProps {}
|
||||
|
||||
@@ -39,7 +39,6 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
||||
classes,
|
||||
label,
|
||||
icon,
|
||||
isRequired,
|
||||
secondaryLabel,
|
||||
placeholder,
|
||||
startCollapsed,
|
||||
@@ -59,7 +58,7 @@ export const SearchField = forwardRef<HTMLDivElement, SearchFieldProps>(
|
||||
|
||||
// If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required.
|
||||
const secondaryLabelText =
|
||||
secondaryLabel || (isRequired ? 'Required' : null);
|
||||
secondaryLabel || (restProps.isRequired ? 'Required' : null);
|
||||
|
||||
const handleFocusChange = (isFocused: boolean) => {
|
||||
restProps.onFocusChange?.(isFocused);
|
||||
|
||||
@@ -40,6 +40,5 @@ export const SearchFieldDefinition = defineComponent<SearchFieldOwnProps>()({
|
||||
label: {},
|
||||
description: {},
|
||||
secondaryLabel: {},
|
||||
isRequired: {},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -47,13 +47,9 @@ export type SearchFieldOwnProps = {
|
||||
label?: FieldLabelProps['label'];
|
||||
description?: FieldLabelProps['description'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
isRequired?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface SearchFieldProps
|
||||
extends Omit<
|
||||
AriaSearchFieldProps,
|
||||
'className' | 'isRequired' | 'description'
|
||||
>,
|
||||
extends Omit<AriaSearchFieldProps, 'className' | 'description'>,
|
||||
SearchFieldOwnProps {}
|
||||
|
||||
@@ -29,15 +29,8 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
||||
TextFieldDefinition,
|
||||
props,
|
||||
);
|
||||
const {
|
||||
classes,
|
||||
label,
|
||||
icon,
|
||||
isRequired,
|
||||
secondaryLabel,
|
||||
placeholder,
|
||||
description,
|
||||
} = ownProps;
|
||||
const { classes, label, icon, secondaryLabel, placeholder, description } =
|
||||
ownProps;
|
||||
|
||||
useEffect(() => {
|
||||
if (!label && !restProps['aria-label'] && !restProps['aria-labelledby']) {
|
||||
@@ -49,7 +42,7 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
||||
|
||||
// If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required.
|
||||
const secondaryLabelText =
|
||||
secondaryLabel || (isRequired ? 'Required' : null);
|
||||
secondaryLabel || (restProps.isRequired ? 'Required' : null);
|
||||
|
||||
return (
|
||||
<AriaTextField
|
||||
|
||||
@@ -39,6 +39,5 @@ export const TextFieldDefinition = defineComponent<TextFieldOwnProps>()({
|
||||
label: {},
|
||||
description: {},
|
||||
secondaryLabel: {},
|
||||
isRequired: {},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -42,12 +42,11 @@ export type TextFieldOwnProps = {
|
||||
label?: FieldLabelProps['label'];
|
||||
description?: FieldLabelProps['description'];
|
||||
secondaryLabel?: FieldLabelProps['secondaryLabel'];
|
||||
isRequired?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface TextFieldProps
|
||||
extends Omit<AriaTextFieldProps, 'className' | 'isRequired' | 'description'>,
|
||||
extends Omit<AriaTextFieldProps, 'className' | 'description'>,
|
||||
TextFieldOwnProps {
|
||||
/**
|
||||
* The HTML input type for the text field
|
||||
|
||||
Reference in New Issue
Block a user