Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ComboBox: Option to use ariaLabel prop as preview text #4540

Merged
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "Handling the scenario where embedded text is passed in as the option's text. In This case, the ariaLabel prop will be displayed in the input and used for autocomplete matching. The value rendered in the menu will not change.",
"type": "patch"
}
],
"packageName": "office-ui-fabric-react",
"email": "kysedate@microsoft.com"
}
Original file line number Diff line number Diff line change
Expand Up @@ -583,18 +583,25 @@ export class ComboBox extends BaseComponent<IComboBoxProps, IComboBoxState> {
if (this.props.autoComplete === 'on') {

// If autoComplete is on, attempt to find a match where the text of an option starts with the updated value
const items = currentOptions.map((item, index) => { return { ...item, index }; }).filter((option) => option.itemType !== SelectableOptionMenuItemType.Header && option.itemType !== SelectableOptionMenuItemType.Divider).filter((option) => option.text.toLocaleLowerCase().indexOf(updatedValue) === 0);
const items = currentOptions.map((item, index) => { return { ...item, index }; })
.filter((option) => option.itemType !== SelectableOptionMenuItemType.Header && option.itemType !== SelectableOptionMenuItemType.Divider)
.filter((option) => this._getPreviewText(option).toLocaleLowerCase().indexOf(updatedValue) === 0);
if (items.length > 0) {
// use ariaLabel as the value when the option is set
const text: string = this._getPreviewText(items[0]);

// If the user typed out the complete option text, we don't need any suggested display text anymore
newSuggestedDisplayValue = items[0].text.toLocaleLowerCase() !== updatedValue ? items[0].text : '';
newSuggestedDisplayValue = text.toLocaleLowerCase() !== updatedValue ? text : '';

// remember the index of the match we found
newCurrentPendingValueValidIndex = items[0].index;
}
} else {

// If autoComplete is off, attempt to find a match only when the value is exactly equal to the text of an option
const items = currentOptions.map((item, index) => { return { ...item, index }; }).filter((option) => option.itemType !== SelectableOptionMenuItemType.Header && option.itemType !== SelectableOptionMenuItemType.Divider).filter((option) => option.text.toLocaleLowerCase() === updatedValue);
const items = currentOptions.map((item, index) => { return { ...item, index }; })
.filter((option) => option.itemType !== SelectableOptionMenuItemType.Header && option.itemType !== SelectableOptionMenuItemType.Divider)
.filter((option) => this._getPreviewText(option).toLocaleLowerCase() === updatedValue);

// if we fould a match remember the index
if (items.length === 1) {
Expand Down Expand Up @@ -642,11 +649,13 @@ export class ComboBox extends BaseComponent<IComboBoxProps, IComboBoxState> {
updatedValue = updatedValue.toLocaleLowerCase();

// If autoComplete is on, attempt to find a match where the text of an option starts with the updated value
const items = currentOptions.map((item, i) => { return { ...item, index: i }; }).filter((option) => option.itemType !== SelectableOptionMenuItemType.Header && option.itemType !== SelectableOptionMenuItemType.Divider).filter((option) => option.text.toLocaleLowerCase().indexOf(updatedValue) === 0);
const items = currentOptions.map((item, i) => { return { ...item, index: i }; })
.filter((option) => option.itemType !== SelectableOptionMenuItemType.Header && option.itemType !== SelectableOptionMenuItemType.Divider)
.filter((option) => option.text.toLocaleLowerCase().indexOf(updatedValue) === 0);

// If we found a match, udpdate the state
if (items.length > 0) {
this._setPendingInfo(originalUpdatedValue, items[0].index, items[0].text);
this._setPendingInfo(originalUpdatedValue, items[0].index, this._getPreviewText(items[0]));
}

// Schedule a timeout to clear the pending value after the timeout span
Expand Down Expand Up @@ -1040,7 +1049,7 @@ export class ComboBox extends BaseComponent<IComboBoxProps, IComboBoxState> {
onMouseLeave={ this._onOptionMouseLeave }
role='option'
aria-selected={ isSelected ? 'true' : 'false' }
ariaLabel={ item.text }
ariaLabel={ this._getPreviewText(item) }
Copy link
Contributor

Choose a reason for hiding this comment

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

What about the multiSelect case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what you mean..

Copy link
Contributor

Choose a reason for hiding this comment

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

the checkbox below seems to be missing the ariaLabel... we should fix that

disabled={ item.disabled }
> { <span ref={ isSelected ? this._selectedElement : undefined }>
{ onRenderOption(item, this._onRenderOptionContent) }
Expand All @@ -1051,6 +1060,7 @@ export class ComboBox extends BaseComponent<IComboBoxProps, IComboBoxState> {
<Checkbox
id={ id + '-list' + item.index }
ref={ 'option' + item.index }
ariaLabel={ this._getPreviewText(item) }
key={ item.key }
data-index={ item.index }
styles={ optionStyles }
Expand Down Expand Up @@ -1307,7 +1317,7 @@ export class ComboBox extends BaseComponent<IComboBoxProps, IComboBoxState> {

if (index >= 0 && index < currentOptions.length) {
const option = currentOptions[index];
this._setPendingInfo(option.text, index, option.text);
this._setPendingInfo(this._getPreviewText(option), index, this._getPreviewText(option));
} else {
this._clearPendingInfo();
}
Expand Down Expand Up @@ -1764,4 +1774,11 @@ export class ComboBox extends BaseComponent<IComboBoxProps, IComboBoxState> {

return retKeys;
}

// For scenarios where the option's text prop contains embedded styles, we use the option's
// ariaLabel value as the text in the input and for autocomplete matching. We know to use this
// when the useAriaLabelAsText prop is set to true
private _getPreviewText(item: IComboBoxOption): string {
return item.useAriaLabelAsText && item.ariaLabel ? item.ariaLabel : item.text;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ export interface IComboBoxOption extends ISelectableOption {
* the prop comboBoxOptionStyles
*/
styles?: Partial<IComboBoxOptionStyles>;

/**
* In scenarios where embedded data is used at the text prop, we will use the ariaLabel prop
* to set the aria-label and preview text. Default to false
* @default false;
*/
useAriaLabelAsText?: boolean;
}

export interface IComboBoxProps extends ISelectableDroppableTextProps<IComboBox> {
Expand Down