Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: add isSelectedOnFocus prop to DetailsList",
"packageName": "@fluentui/react",
"email": "seanmonahan@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: add isSelectedOnFocus prop to DetailsList (#23467)",
"packageName": "office-ui-fabric-react",
"email": "seanmonahan@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3820,6 +3820,7 @@ export interface IDetailsListProps extends IBaseProps<IDetailsList>, IWithViewpo
initialFocusedIndex?: number;
isHeaderVisible?: boolean;
isPlaceholderData?: boolean;
isSelectedOnFocus?: boolean;
items: any[];
layoutMode?: DetailsListLayoutMode;
listProps?: IListProps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const DetailsListInner: React.ComponentType<IDetailsListInnerProps> = (
onRowDidMount,
onRowWillUnmount,
disableSelectionZone,
isSelectedOnFocus = true,
onColumnResized,
onColumnAutoResized,
onToggleCollapse,
Expand Down Expand Up @@ -594,7 +595,7 @@ const DetailsListInner: React.ComponentType<IDetailsListInnerProps> = (
if (focusZoneRef.current && focusZoneRef.current.focus()) {
// select the first item in list after down arrow key event
// only if nothing was selected; otherwise start with the already-selected item
if (selection.getSelectedIndices().length === 0) {
if (isSelectedOnFocus && selection.getSelectedIndices().length === 0) {
selection.setIndexSelected(0, true, false);
}

Expand All @@ -603,7 +604,7 @@ const DetailsListInner: React.ComponentType<IDetailsListInnerProps> = (
}
}
},
[selection, focusZoneRef],
[selection, focusZoneRef, isSelectedOnFocus],
);

const onContentKeyDown = React.useCallback(
Expand Down Expand Up @@ -680,6 +681,7 @@ const DetailsListInner: React.ComponentType<IDetailsListInnerProps> = (
selection={selection}
selectionPreservedOnEmptyClick={selectionPreservedOnEmptyClick}
selectionMode={selectionMode}
isSelectedOnFocus={isSelectedOnFocus}
onItemInvoked={onItemInvoked}
onItemContextMenu={onItemContextMenu}
enterModalOnTouch={enterModalSelectionOnTouch}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,63 @@ describe('DetailsList', () => {
);
});

it('focuses row by arrow key', () => {
jest.useFakeTimers();

let component: IDetailsList | null;
const onSelectionChanged = jest.fn();
const selection = new Selection({
onSelectionChanged,
});
safeMount(
<DetailsList
componentRef={ref => (component = ref)}
items={mockData(5)}
selection={selection}
skipViewportMeasures={true}
onShouldVirtualize={() => false}
/>,
wrapper => {
expect(component).toBeTruthy();
component!.focusIndex(0);
jest.runAllTimers();

onSelectionChanged.mockClear();
wrapper.find('.ms-DetailsList-headerWrapper').simulate('keyDown', { which: KeyCodes.down });
expect(onSelectionChanged).toHaveBeenCalledTimes(1);
},
);
});

it('does not focus by arrow key when isSelectedOnFocus is `false`', () => {
jest.useFakeTimers();

let component: IDetailsList | null;
const onSelectionChanged = jest.fn();
const selection = new Selection({
onSelectionChanged,
});
safeMount(
<DetailsList
componentRef={ref => (component = ref)}
items={mockData(5)}
selection={selection}
skipViewportMeasures={true}
onShouldVirtualize={() => false}
isSelectedOnFocus={false}
/>,
wrapper => {
expect(component).toBeTruthy();
component!.focusIndex(0);
jest.runAllTimers();

onSelectionChanged.mockClear();
wrapper.find('.ms-DetailsList-headerWrapper').simulate('keyDown', { which: KeyCodes.down });
expect(onSelectionChanged).toHaveBeenCalledTimes(0);
},
);
});

it('invokes optional onRenderMissingItem prop once per missing item rendered', () => {
const onRenderMissingItem = jest.fn();
const items = [...mockData(5), null, null];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,13 @@ export interface IDetailsListProps extends IBaseProps<IDetailsList>, IWithViewpo
/** Whether to disable the built-in SelectionZone, so the host component can provide its own. */
disableSelectionZone?: boolean;

/**
* Determines if an item is selected on focus.
*
* @defaultvalue true
*/
isSelectedOnFocus?: boolean;

/** Whether to animate updates */
enableUpdateAnimations?: boolean;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ exports[`DetailsList handles paged updates to items within groups 1`] = `
min-width: 100%;
}
data-automationid="GroupedList"
data-focuszone-id="FocusZone247"
data-focuszone-id="FocusZone276"
data-is-scrollable="false"
onBlur={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -139,7 +139,7 @@ exports[`DetailsList handles paged updates to items within groups 1`] = `
<div
aria-label="two 1"
className="ms-List"
id="GroupedListSection248"
id="GroupedListSection277"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -210,7 +210,7 @@ exports[`DetailsList handles paged updates to items within groups 1`] = `
<div
aria-label="two 2"
className="ms-List"
id="GroupedListSection249"
id="GroupedListSection278"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -364,7 +364,7 @@ exports[`DetailsList handles paged updates to items within groups 2`] = `
min-width: 100%;
}
data-automationid="GroupedList"
data-focuszone-id="FocusZone247"
data-focuszone-id="FocusZone276"
data-is-scrollable="false"
onBlur={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -405,7 +405,7 @@ exports[`DetailsList handles paged updates to items within groups 2`] = `
<div
aria-label="two 1"
className="ms-List"
id="GroupedListSection248"
id="GroupedListSection277"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -484,7 +484,7 @@ exports[`DetailsList handles paged updates to items within groups 2`] = `
<div
aria-label="two 2"
className="ms-List"
id="GroupedListSection249"
id="GroupedListSection278"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -646,7 +646,7 @@ exports[`DetailsList handles updates to items and groups 1`] = `
min-width: 100%;
}
data-automationid="GroupedList"
data-focuszone-id="FocusZone236"
data-focuszone-id="FocusZone265"
data-is-scrollable="false"
onBlur={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -687,7 +687,7 @@ exports[`DetailsList handles updates to items and groups 1`] = `
<div
aria-label="one 1"
className="ms-List"
id="GroupedListSection237"
id="GroupedListSection266"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -748,7 +748,7 @@ exports[`DetailsList handles updates to items and groups 1`] = `
<div
aria-label="one 2"
className="ms-List"
id="GroupedListSection238"
id="GroupedListSection267"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -809,7 +809,7 @@ exports[`DetailsList handles updates to items and groups 1`] = `
<div
aria-label="one 3"
className="ms-List"
id="GroupedListSection239"
id="GroupedListSection268"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -870,7 +870,7 @@ exports[`DetailsList handles updates to items and groups 1`] = `
<div
aria-label="one 4"
className="ms-List"
id="GroupedListSection240"
id="GroupedListSection269"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1014,7 +1014,7 @@ exports[`DetailsList handles updates to items and groups 2`] = `
min-width: 100%;
}
data-automationid="GroupedList"
data-focuszone-id="FocusZone236"
data-focuszone-id="FocusZone265"
data-is-scrollable="false"
onBlur={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -1055,7 +1055,7 @@ exports[`DetailsList handles updates to items and groups 2`] = `
<div
aria-label="one 1"
className="ms-List"
id="GroupedListSection237"
id="GroupedListSection266"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1116,7 +1116,7 @@ exports[`DetailsList handles updates to items and groups 2`] = `
<div
aria-label="one 2"
className="ms-List"
id="GroupedListSection238"
id="GroupedListSection267"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1177,7 +1177,7 @@ exports[`DetailsList handles updates to items and groups 2`] = `
<div
aria-label="one 3"
className="ms-List"
id="GroupedListSection239"
id="GroupedListSection268"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1238,7 +1238,7 @@ exports[`DetailsList handles updates to items and groups 2`] = `
<div
aria-label="one 4"
className="ms-List"
id="GroupedListSection240"
id="GroupedListSection269"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1382,7 +1382,7 @@ exports[`DetailsList handles updates to items and groups 3`] = `
min-width: 100%;
}
data-automationid="GroupedList"
data-focuszone-id="FocusZone236"
data-focuszone-id="FocusZone265"
data-is-scrollable="false"
onBlur={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -1423,7 +1423,7 @@ exports[`DetailsList handles updates to items and groups 3`] = `
<div
aria-label="two 1"
className="ms-List"
id="GroupedListSection243"
id="GroupedListSection272"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1502,7 +1502,7 @@ exports[`DetailsList handles updates to items and groups 3`] = `
<div
aria-label="two 2"
className="ms-List"
id="GroupedListSection244"
id="GroupedListSection273"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1664,7 +1664,7 @@ exports[`DetailsList handles updates to items and groups 4`] = `
min-width: 100%;
}
data-automationid="GroupedList"
data-focuszone-id="FocusZone236"
data-focuszone-id="FocusZone265"
data-is-scrollable="false"
onBlur={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -1705,7 +1705,7 @@ exports[`DetailsList handles updates to items and groups 4`] = `
<div
aria-label="two 1"
className="ms-List"
id="GroupedListSection243"
id="GroupedListSection272"
role="rowgroup"
>
<div
Expand Down Expand Up @@ -1784,7 +1784,7 @@ exports[`DetailsList handles updates to items and groups 4`] = `
<div
aria-label="two 2"
className="ms-List"
id="GroupedListSection244"
id="GroupedListSection273"
role="rowgroup"
>
<div
Expand Down