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,11 @@
{
"changes": [
{
"packageName": "@uifabric/utilities",
"comment": "EventGroup.raise: event args are now correctly mixed into the event object.",
"type": "patch"
}
],
"packageName": "@uifabric/utilities",
"email": "dzearing@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "DetailsList: headers now resize correctly and respect maxWidth.",
"type": "patch"
}
],
"packageName": "office-ui-fabric-react",
"email": "dzearing@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as React from 'react';
import { DetailsHeader } from './DetailsHeader';
import { DetailsListLayoutMode, IColumn } from './DetailsList.types';
import { Selection, SelectionMode } from '../../utilities/selection/index';
import { EventGroup, createRef } from '../../Utilities';
import { mount } from 'enzyme';
import * as renderer from 'react-test-renderer';

const _items: {}[] = [];
const _selection = new Selection();
const _columns: IColumn[] = [
{ key: 'a', name: 'a', fieldName: 'a', minWidth: 200, maxWidth: 400, calculatedWidth: 200, isResizable: true },
{ key: 'b', name: 'b', fieldName: 'a', minWidth: 200, maxWidth: 400, calculatedWidth: 200, isResizable: true }
];

_selection.setItems(_items);

describe('DetailsHeader', () => {

it('can render', () => {
const component = renderer.create(
<DetailsHeader
selection={ _selection }
selectionMode={ SelectionMode.multiple }
layoutMode={ DetailsListLayoutMode.fixedColumns }
columns={ _columns }
/>
);
expect(component.toJSON()).toMatchSnapshot();
});

it('can resize columns', () => {
let lastResize = { size: -1, index: -1 };

const onColumnResized = (
column: IColumn,
size: number,
index: number
): { size: number; index: number; } => lastResize = { size, index };
const headerRef = createRef<any>();

const columns = [];
const wrapper = mount(
<DetailsHeader
componentRef={ headerRef }
selection={ _selection }
selectionMode={ SelectionMode.multiple }
layoutMode={ DetailsListLayoutMode.fixedColumns }
columns={ _columns }
onColumnResized={ onColumnResized }
/>
);

const rootElement = wrapper.getDOMNode();
const sizerElement = wrapper.find('[data-sizer-index=0]').getDOMNode();
const header: any = headerRef.value;

// Trigger a mousedown, which validates that the ref to focuszone is hooking up events.
EventGroup.raise(
sizerElement,
'mousedown',
{
clientX: 0,
button: 0
},
true
);

// Validate we go into resize mode.
expect(sizerElement.classList.contains('is-resizing')).toBe(true);
expect(!!wrapper.state().isSizing).toBe(false);

// Mouse move 1 pixel to the right to get into sizing mode.
wrapper.simulate('mousemove', { clientX: 1 });
expect(!!wrapper.state().isSizing).toBe(true);

// The header is 200; move mouse 100 to the right, the header should be 300.
header._onSizerMouseMove({ clientX: 100 });
expect(lastResize).toEqual({ index: 0, size: 300 });

// Mouse move 300 pixels to the right (should be capped at 400px width
header._onSizerMouseMove({ clientX: 300 });
expect(lastResize).toEqual({ index: 0, size: 400 });

// Complete sizing.
header._onSizerMouseUp();
expect(!!wrapper.state().isSizing).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { findDOMNode } from 'react-dom';
import {
BaseComponent,
css,
Expand All @@ -9,7 +10,7 @@ import {
createRef
} from '../../Utilities';
import { IColumn, DetailsListLayoutMode, ColumnActionsMode } from './DetailsList.types';
import { FocusZone, FocusZoneDirection } from '../../FocusZone';
import { IFocusZone, FocusZone, FocusZoneDirection } from '../../FocusZone';
import { Icon } from '../../Icon';
import { Layer } from '../../Layer';
import { GroupSpacer } from '../GroupedList/GroupSpacer';
Expand All @@ -28,7 +29,7 @@ const INNER_PADDING = 16;
const ISPADDED_WIDTH = 24;

export interface IDetailsHeader {
focus(): boolean;
focus: () => boolean;
}

export interface IDetailsHeaderProps extends React.Props<DetailsHeader> {
Expand Down Expand Up @@ -81,7 +82,7 @@ export class DetailsHeader extends BaseComponent<IDetailsHeaderProps, IDetailsHe
collapseAllVisibility: CollapseAllVisibility.visible
};

private _root = createRef<FocusZone>();
private _root = createRef<IFocusZone>();

private _id: string;

Expand All @@ -101,11 +102,8 @@ export class DetailsHeader extends BaseComponent<IDetailsHeaderProps, IDetailsHe

public componentDidMount() {
const { selection } = this.props;
const rootElement = this._root.value;

if (!rootElement) {
return;
}
const focusZone = this._root.value;
const rootElement = findDOMNode(focusZone as any);

this._events.on(selection, SELECTION_CHANGE, this._onSelectionChanged);

Expand Down Expand Up @@ -516,9 +514,12 @@ export class DetailsHeader extends BaseComponent<IDetailsHeaderProps, IDetailsHe
movement = -movement;
}

const column = columns[columnResizeDetails!.columnIndex];
const requestedSize = columnResizeDetails!.columnMinWidth + movement;

onColumnResized(
columns[columnResizeDetails!.columnIndex],
columnResizeDetails!.columnMinWidth + movement,
column,
!!column.maxWidth ? Math.min(column.maxWidth, requestedSize) : requestedSize,
columnResizeDetails!.columnIndex
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DetailsHeader can render 1`] = `
<div
aria-describedby={undefined}
aria-label={undefined}
aria-labelledby={undefined}
className="ms-FocusZone ms-DetailsHeader"
data-automationid="DetailsHeader"
data-focuszone-id="FocusZone1"
onFocus={[Function]}
onKeyDown={[Function]}
onMouseDownCapture={[Function]}
onMouseMove={[Function]}
role="row"
>
<div
aria-colindex={0}
aria-labelledby="header0-check"
className="ms-DetailsHeader-cell ms-DetailsHeader-cellIsCheck"
onClick={[Function]}
role="columnheader"
>
<span
className=""
>
<div
aria-checked={undefined}
aria-describedby="header0-checkTooltip"
aria-label={undefined}
className="ms-DetailsRow-check ms-DetailsRow-check--isHeader undefined"
data-automationid="DetailsRowCheck"
data-is-focusable={true}
data-selection-toggle={true}
id="header0-check"
role="checkbox"
>
<div
className=
ms-Check
{
height: 18px;
line-height: 1;
position: relative;
user-select: none;
vertical-align: top;
width: 18px;
}
&:before {
background: #ffffff;
border-radius: 50%;
bottom: 1px;
content: "";
left: 1px;
opacity: 1;
position: absolute;
right: 1px;
top: 1px;
}
.checkHost:hover &, .checkHost:focus &, &:hover, &:focus {
opacity: 1;
}
>
<i
aria-hidden={true}
className=
ms-Check-circle
{
display: inline-block;
}
{
color: #c8c8c8;
font-size: 18px;
height: 18px;
left: 0px;
position: absolute;
text-align: center;
top: 0px;
vertical-align: middle;
width: 18px;
}
@media screen and (-ms-high-contrast: active){& {
color: WindowText;
}
data-icon-name="CircleRing"
role="presentation"
/>
<i
aria-hidden={true}
className=
ms-Check-check
{
display: inline-block;
}
{
color: #c8c8c8;
font-size: 16px;
height: 18px;
left: .5px;
opacity: 0;
position: absolute;
text-align: center;
top: 0px;
vertical-align: middle;
width: 18px;
}
&:hover {
opacity: 1;
}
@media screen and (-ms-high-contrast: active){& {
-ms-high-contrast-adjust: none;
}
data-icon-name="StatusCircleCheckmark"
role="presentation"
/>
</div>
</div>
</span>
</div>
<div
aria-colindex={1}
aria-disabled={false}
aria-sort="none"
className="ms-DetailsHeader-cell is-actionable undefined"
data-automationid="ColumnsHeaderColumn"
data-item-key="a"
role="columnheader"
style={
Object {
"width": 216,
}
}
>
<span
className=""
>
<span
aria-describedby="header0-a-tooltip"
aria-haspopup={false}
aria-label={undefined}
aria-labelledby="header0-a-name "
className="ms-DetailsHeader-cellTitle"
data-is-focusable={true}
id="header0-a"
onClick={[Function]}
onContextMenu={[Function]}
role="button"
>
<span
className="ms-DetailsHeader-cellName"
id="header0-a-name"
>
a
</span>
</span>
</span>
</div>
<div
aria-hidden={true}
className="ms-DetailsHeader-cellSizer"
data-is-focusable={false}
data-sizer-index={0}
onBlur={[Function]}
onClick={[Function]}
onDoubleClick={[Function]}
role="button"
/>
<div
aria-colindex={2}
aria-disabled={false}
aria-sort="none"
className="ms-DetailsHeader-cell is-actionable undefined"
data-automationid="ColumnsHeaderColumn"
data-item-key="b"
role="columnheader"
style={
Object {
"width": 216,
}
}
>
<span
className=""
>
<span
aria-describedby="header0-b-tooltip"
aria-haspopup={false}
aria-label={undefined}
aria-labelledby="header0-b-name "
className="ms-DetailsHeader-cellTitle"
data-is-focusable={true}
id="header0-b"
onClick={[Function]}
onContextMenu={[Function]}
role="button"
>
<span
className="ms-DetailsHeader-cellName"
id="header0-b-name"
>
b
</span>
</span>
</span>
</div>
<div
aria-hidden={true}
className="ms-DetailsHeader-cellSizer"
data-is-focusable={false}
data-sizer-index={1}
onBlur={[Function]}
onClick={[Function]}
onDoubleClick={[Function]}
role="button"
/>
</div>
`;
Loading