Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
106bcc4
Update data-testid attributes to use dynamic props for better flexibi…
arome Feb 13, 2026
3403a38
run prettier fix
arome Feb 13, 2026
62659ec
Update data-testid attribute in EditorContainer for consistency
arome Feb 13, 2026
57617e1
Add support for customizable data-testids in PropertyEditor components
arome Feb 13, 2026
4ea504a
Merge remote-tracking branch 'origin/master' into omar/fix-data-testid
arome Mar 6, 2026
107ab08
fix(a11y): forward id to interactive elements in new property editors
arome Apr 24, 2026
52453ee
test: add coverage for id/label a11y fix in new property editors
arome Apr 24, 2026
8f6a99f
docs: add Storybook stories for new property editor label/a11y fix
arome Apr 24, 2026
13ef9cf
docs: add legacy editor targeting strategies story
arome Apr 24, 2026
f320609
docs: fix number editor targeting annotation in legacy story
arome Apr 24, 2026
18154f6
fix: add id={property.name} to legacy enum, boolean, and toggle editors
arome Apr 24, 2026
14c2ee2
docs: update legacy targeting story to reflect id fix on all editor t…
arome Apr 24, 2026
3acc81e
fix: add id={property.name} to legacy ImageCheckBoxEditor
arome Apr 24, 2026
1e5ce0b
fix: add id={property.name} to remaining legacy editors
arome Apr 24, 2026
f49abf8
fix: add id prop to ImageCheckBox and wire it in ImageCheckBoxEditor
arome Apr 24, 2026
ed3e17d
fix: propagate id prop through all new-editor old-editor interop wrap…
arome Apr 24, 2026
2cd20f3
fix: propagate id through DateTimeEditor, DateEditor, and DateInput
arome Apr 24, 2026
e8e45c5
fix: add id={property.name} to legacy NumericInputEditor
arome Apr 24, 2026
cfa944a
Merge branch 'master' into omar/fix-data-testid
arome Apr 24, 2026
f468041
chore: remove NewPropertyEditors stories file to streamline documenta…
arome Apr 24, 2026
d3e72a3
chore: update core-react api.md and add beforeunload to cspell words
arome Apr 24, 2026
7e2871b
chore: fix prettier formatting and update components-react api.md
arome Apr 24, 2026
1771bec
PR Comment: revert the data-testid changes
arome Apr 27, 2026
4978487
fix: update runtimeArgs from "start" to "dev" in launch configuration…
arome Apr 27, 2026
8af3bf0
fix: standardize data-testid attributes in ColorEditor and WeightEdit…
arome Apr 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sweet-islands-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@itwin/components-react": minor
---

Provide option to define data-testids for PropertyEditor components
1 change: 1 addition & 0 deletions .vscode/cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"backstageevent",
"badeditor",
"badtype",
"beforeunload",
"betools",
"borderless",
"boxshadow",
Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "start"],
"runtimeArgs": ["run", "dev"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"serverReadyAction": {
Expand Down
1 change: 1 addition & 0 deletions common/api/components-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ export interface EditorProps<TMetadata = ValueMetadata, TValue = Value> {
commit?: () => void;
// (undocumented)
disabled?: boolean;
id?: string;
// (undocumented)
metadata: TMetadata;
// (undocumented)
Expand Down
1 change: 1 addition & 0 deletions common/api/core-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ export interface ImageCheckBoxProps extends CommonProps {
border?: boolean;
checked?: boolean;
disabled?: boolean;
id?: string;
imageOff: string | React_2.ReactNode;
imageOn: string | React_2.ReactNode;
inputClassName?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export class BooleanEditor
this.props.propertyRecord?.isReadonly
}
data-testid="components-checkbox-editor"
id={this.props.propertyRecord?.property.name}
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.

The id properties must be unique in a document.
Looks like the same property record (with the same property name, which results to duplicate ids) might be rendered multiple times e.g. in a property grid. @saskliutas

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Double checked PropertyRecords we created for iModel element property grid and it looks like we should create unique names inside that single property grid. But there might be custom property grids or I guess multiple iModel property grids present that might result in duplicating ids.

So I think we should think about how to ensure unique ids in document scope.

Copy link
Copy Markdown
Contributor Author

@arome arome Apr 28, 2026

Choose a reason for hiding this comment

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

Any idea how we can tackle this? The only solution i can think of is allowing the user to pass in the unique id, but this will take us back to the initial problem I was trying to solve in this PR. It doesnt have to be data-testid but we should have a way to pass some unique id from these generic property builder.

></Checkbox>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,11 @@ export class DateTimeEditor
);

return (
<div className={className} ref={this._divElement}>
<div
className={className}
ref={this._divElement}
id={this.props.propertyRecord?.property.name}
>
<PopupButton
label={this.state.displayValue}
onClose={this._handleClose}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export class EnumButtonGroupEditor
)}
style={this.props.style}
ref={this._divElement}
id={this.props.propertyRecord?.property.name}
>
{this.state.choices &&
this.state.enumIcons.length &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export class EnumEditor
value={selectValue}
onChange={this._updateSelectValue}
data-testid="components-select-editor"
id={this.props.propertyRecord?.property.name}
options={this.state.options}
triggerProps={{
ref: (el) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export class IconEditor
readonly={this.props.propertyRecord?.isReadonly}
onIconChange={this._onIconChange}
data-testid="components-icon-editor"
id={this.props.propertyRecord?.property.name}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export class ImageCheckBoxEditor
disabled={isDisabled}
onClick={this._handleClick}
data-testid="components-imagecheckbox-editor"
id={this.props.propertyRecord?.property.name}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ export class NumericInputEditor
return (
<NumericEditor
ref={this._inputElement}
id={this.props.propertyRecord?.property.name}
className={className}
style={style}
value={this.state.value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,11 @@ export class SliderEditor
);

return (
<div className={className} ref={this._divElement}>
<div
className={className}
ref={this._divElement}
id={this.props.propertyRecord?.property.name}
>
<PopupButton
label={this.state.value}
onClose={this._handleClose}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,11 @@ export class TextareaEditor
textareaProps["aria-label"] = UiComponents.translate("editor.textarea");

return (
<div className={className} ref={this._divElement}>
<div
className={className}
ref={this._divElement}
id={this.props.propertyRecord?.property.name}
>
<PopupButton
label={this.state.inputValue}
closeOnEnter={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export class ToggleEditor
disabled={this.props.propertyRecord?.isDisabled}
onChange={this._updateToggleValue}
data-testid="components-toggle-editor"
id={this.props.propertyRecord?.property.name}
autoFocus={this.props.setFocus} // eslint-disable-line jsx-a11y/no-autofocus
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ export interface EditorProps<TMetadata = ValueMetadata, TValue = Value> {
cancel?: () => void;
disabled?: boolean;
size?: "small" | "large";
/**
* HTML `id` attribute forwarded to the interactive element. Used to associate
* the editor with a `<label htmlFor>` so that `getByLabelText` queries work and
* the UI meets accessibility requirements.
*/
id?: string;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function BooleanEditor({
onChange,
commit,
disabled,
id,
}: EditorProps<ValueMetadata, BooleanValue>) {
const currentValue = value?.value ?? false;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -30,6 +31,7 @@ export function BooleanEditor({

return (
<Checkbox
id={id}
checked={currentValue}
onChange={handleChange}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function DateEditor({
commit,
size,
disabled,
id,
}: EditorProps<ValueMetadata, DateValue>) {
return (
<DateInput
Expand All @@ -31,6 +32,7 @@ export function DateEditor({
onClose={commit}
size={size}
disabled={disabled}
id={id}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface DateInputProps {
size?: "small" | "large";
disabled?: boolean;
showTimePicker?: boolean;
id?: string;
}

/**
Expand All @@ -27,6 +28,7 @@ export function DateInput({
size,
showTimePicker,
disabled,
id,
}: DateInputProps) {
const currentValue = value ?? new Date();
const dateStr = showTimePicker
Expand All @@ -50,7 +52,7 @@ export function DateInput({
}
}}
>
<Button style={{ width: "100%" }} size={size} disabled={disabled}>
<Button id={id} style={{ width: "100%" }} size={size} disabled={disabled}>
{dateStr}
</Button>
</Popover>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function DateTimeEditor({
commit,
size,
disabled,
id,
}: EditorProps<ValueMetadata, DateValue>) {
return (
<DateInput
Expand All @@ -32,6 +33,7 @@ export function DateTimeEditor({
size={size}
disabled={disabled}
showTimePicker={true}
id={id}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function EnumEditor({
commit,
size,
disabled,
id,
}: EditorProps<EnumValueMetadata, EnumValue>) {
const choices = metadata.choices;
const currentValue = getEnumValue(value, choices);
Expand All @@ -35,6 +36,7 @@ export function EnumEditor({

return (
<Select
id={id}
size={size}
value={currentValue.choice}
onChange={handleChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {
* Fallback editor that renders readonly value if no editor is found.
* @internal
*/
export function FallbackEditor({ value, size }: EditorProps) {
return <Input readOnly value={getTextValue(value)} size={size} />;
export function FallbackEditor({ value, size, id }: EditorProps) {
return <Input id={id} readOnly value={getTextValue(value)} size={size} />;
}

function getTextValue(value?: Value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ export function NumericEditor({
onChange,
size,
disabled,
id,
}: EditorProps<ValueMetadata, NumericValue>) {
const currentValue = getNumericValue(value);
return (
<Input
id={id}
value={currentValue.displayValue}
onChange={(e) => {
const parsedValue = parseFloat(e.target.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ export function TextEditor({
onChange,
size,
disabled,
id,
}: EditorProps<ValueMetadata, TextValue>) {
const currentValue = value ? value : { value: "" };

return (
<Input
id={id}
value={currentValue.value}
onChange={(e) => onChange({ value: e.target.value })}
size={size}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function ToggleEditor({
commit,
disabled,
size,
id,
}: EditorProps<ValueMetadata, BooleanValue>) {
const currentValue = value ?? { value: false };
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -31,6 +32,7 @@ export function ToggleEditor({

return (
<ToggleSwitch
id={id}
checked={currentValue.value}
onChange={handleChange}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export function PropertyRecordEditor({
onClick={onClick}
disabled={propertyRecord.isDisabled || propertyRecord.isReadonly}
size={size}
id={propertyRecord.property.name}
/>
);
}
Expand All @@ -86,6 +87,7 @@ function CommittingEditor({
onClick,
disabled,
size,
id,
}: {
metadata: ValueMetadata;
initialValue?: Value;
Expand All @@ -94,6 +96,7 @@ function CommittingEditor({
onClick?: () => void;
disabled?: boolean;
size?: "small" | "large";
id?: string;
}) {
const { value, onChange, onKeydown, commit, cancel } = useCommittableValue({
initialValue,
Expand All @@ -116,6 +119,7 @@ function CommittingEditor({
cancel={cancel}
disabled={disabled}
size={size}
id={id}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ function ColorEditor({
onChange,
commit,
size,
id,
}: EditorProps<OldEditorMetadata, NumericValue>) {
const colorParams = useColorEditorParams(metadata);
const colors = colorParams?.colorValues ?? [];
Expand Down Expand Up @@ -81,7 +82,7 @@ function ColorEditor({
}
}}
>
<IconButton size={size}>
<IconButton id={id} size={size}>
<ColorSwatch color={activeColor} />
</IconButton>
</Popover>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export function CustomNumberEditor({
size,
disabled,
decoration,
id,
}: CustomNumberEditorProps) {
const formatParams = useCustomFormattedNumberParams(metadata);
const sizeParams = useInputEditorSizeParams(metadata);
Expand Down Expand Up @@ -101,6 +102,7 @@ export function CustomNumberEditor({
<InputWithDecorations.Icon>{icon}</InputWithDecorations.Icon>
) : null}
<InputWithDecorations.Input
id={id}
ref={inputRef}
style={style}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function EnumButtonGroupEditor({
size,
disabled,
metadata,
id,
}: EditorProps<OldEditorMetadata, EnumValue>) {
const enumMetadata = useEnumMetadata(metadata);
const buttonGroupParams = useButtonGroupEditorParams(metadata);
Expand All @@ -53,7 +54,7 @@ function EnumButtonGroupEditor({
const currentValue = value ? value : { choice: firstChoice?.value ?? "" };

return (
<ButtonGroup orientation="horizontal">
<ButtonGroup id={id} orientation="horizontal">
{enumMetadata.choices.map((choice) => {
const icon = findIcon(enumIcons?.get(choice.value));
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function NumericInputEditor({
onChange,
size,
disabled,
id,
}: EditorProps<OldEditorMetadata, NumericValue>) {
const sizeParams = useInputEditorSizeParams(metadata);
const rangeParams = useRangeEditorParams(metadata);
Expand Down Expand Up @@ -67,7 +68,13 @@ function NumericInputEditor({
});

return (
<Input style={style} size={size} disabled={disabled} {...inputProps} />
<Input
id={id}
style={style}
size={size}
disabled={disabled}
{...inputProps}
/>
);
}

Expand Down
Loading
Loading