[EDIT: updated to focus the issue on the overlaying problem; also edits have been made to the CodePens] [EDIT2: updated to include details about the method suggested by Mohamed Mansour]
I'm having difficulties designing the layout for an Office UI Fabric React UI. This is the layout I currently have:
The goal is to keep the top CommandBar and headers (the text labels and search box) anchored to the top, with the DetailsList and filing button anchored to the bottom and taking up as much space as possible after the header area.
I've successfully used ScrollablePane and two Sticky components to anchor the CommandBar and button in the desired places. However, the DetailsList scrolls through the top Sticky containing the CommandBar and header, and the button is slightly overlayed on top of the DetailsList grid:
I've tried using the Fabric Grid component to layout all sections but it doesn't make a difference (if it would help at all). The best layout has the projects section in between two Sticky elements; the layout is horrible if the project section is added to either of the Header or Footer Sticky elements. How can I ensure that the DetailsList doesn't scroll under the top and bottom components?
Here are two code pens that illustrate the issue:
Without Grid: https://codepen.io/elegault/pen/aQayvY?editors=0010 (the fileHeader uses a grid for its elements, but all other elements are not in a grid)
const columns = [{
key: 'projectNameColumn',
name: 'Project',
fieldName: 'name',
minWidth: 100,
maxWidth: 200,
isResizable: true,
ariaLabel: 'Operations for Project'
}];
const items = [{
id: '0',
name: 'ABC Construction'
},
{
id: '1',
name: 'Air Bee and Bee'
},
{
id: '2',
name: 'Architectural Salvage'
},
{
id: '3',
name: 'Arkham Airport'
},
{
id: '4',
name: 'Arkham Assembly Hall'
},
{
id: '5',
name: 'Arkham Library'
},
{
id: '6',
name: 'zArkham Renovation'
},
{
id: '7',
name: 'Foo'
},
{
id: '8',
name: 'Foo1'
},
{
id: '9',
name: 'Foo2'
},
{
id: '10',
name: 'Foo3'
},
{
id: '11',
name: 'Foo4'
},
{
id: '12',
name: 'Foo5'
},
{
id: '13',
name: 'Foo6'
},
{
id: '14',
name: 'Foo7'
},
{
id: '15',
name: 'Foo8'
},
{
id: '16',
name: 'Foo9'
},
{
id: '17',
name: 'Foo10'
},
];
class Content extends React.Component {
public render() {
const fileHeader = <
div className = 'ms-Grid' >
<
div className = 'ms-Grid-row' >
<
div className = 'ms-Grid-col ms-sm2 ms-md2 ms-lg2' >
<
img width = '32'
height = '32'
alt = 'logo'
title = 'logo' / >
<
/div> <
div className = 'ms-Grid-col ms-sm10 ms-md10 ms-lg10' >
<
Fabric.Label className = 'ms-font-l ms-fontWeight-bold ms-fontColor-blue' > [TITLE] < /Fabric.Label> <
/div> <
/div> <
div className = 'ms-Grid-row' >
<
div className = 'ms-Grid-col ms-sm2 ms-md2 ms-lg2' >
<
/div> <
div className = 'ms-Grid-col ms-sm10 ms-md10 ms-lg10' >
<
Fabric.Label
className = 'ms-font-m ms-fontWeight-bold ms-fontColor-neutralPrimary' > SELECTED PROJECT: < /Fabric.Label> <
/div> <
/div> <
/div>;
const commandBar = < div >
<
Fabric.CommandBar
isSearchBoxVisible = {
false
}
items = {
[{
key: 'openWebApp',
name: 'Open Web App',
icon: 'OpenInNewWindow', // Or Link
['data-automation-id']: 'openWebAppButton',
title: 'Open web app',
href: 'http://www.codepen.io',
target: '_blank',
}]
}
farItems = {
[{
key: 'menuOptions',
name: 'Options',
icon: 'Settings',
iconOnly: 'true',
['data-automation-id']: 'settingsButton',
subMenuProps: {
items: [{
key: 'privacyPolicy',
name: 'Privacy Policy',
icon: 'PageLock',
href: 'http://www.codepen.io',
target: '_blank',
},
{
key: 'termsOfUse',
name: 'Terms & Conditions',
icon: 'TextDocument',
href: 'http://www.codepen.io',
target: '_blank'
}
]
}
}]
}
/> <
/div>;
const projects = < Fabric.MarqueeSelection selection = {
null
}
data - is - scrollable = {
false
} >
<
Fabric.DetailsList
items = {
items
}
columns = {
columns
}
/> <
/Fabric.MarqueeSelection>;
const selection = < div > [project name] < /div>;
const search = < Fabric.TextField label = 'Search projects:' / > ;
const fileButton = <
div >
<
Fabric.DefaultButton primary = {
true
} > File To Project < /Fabric.DefaultButton> <
/div>;
return ( <
Fabric.Fabric >
<
div style = {
{
height: '500px',
position: 'relative',
maxHeight: 'inherit'
}
} >
<
Fabric.ScrollablePane scrollbarVisibility = {
Fabric.ScrollbarVisibility.auto
} >
<
Fabric.Sticky stickyPosition = {
Fabric.StickyPositionType.Header
} > {
commandBar
} {
fileHeader
} {
selection
} {
search
} <
/Fabric.Sticky> {
projects
} <
Fabric.Sticky stickyPosition = {
Fabric.StickyPositionType.Footer
} > {
fileButton
} <
/Fabric.Sticky> <
/Fabric.ScrollablePane> <
/div> <
/Fabric.Fabric>
);
}
}
ReactDOM.render( <
Content / > ,
document.getElementById('content')
);
With full Grid: https://codepen.io/elegault/pen/wQxoRR
const columns = [
{
key: 'projectNameColumn',
name: 'Project',
fieldName: 'name',
minWidth: 100,
maxWidth: 200,
isResizable: true,
ariaLabel: 'Operations for Project'
}
];
const items = [
{id: '0', name: 'ABC Construction'},
{id: '1', name: 'Air Bee and Bee'},
{id: '2', name: 'Architectural Salvage'},
{id: '3', name: 'Arkham Airport'},
{id: '4', name: 'Arkham Assembly Hall'},
{id: '5', name: 'Arkham Library'},
{id: '6', name: 'zArkham Renovation'},
{id: '7', name: 'Foo'},
{id: '8', name: 'Foo1'},
{id: '9', name: 'Foo2'},
{id: '10', name: 'Foo3'},
{id: '11', name: 'Foo4'},
{id: '12', name: 'Foo5'},
{id: '13', name: 'Foo6'},
{id: '14', name: 'Foo7'},
{id: '15', name: 'Foo8'},
{id: '16', name: 'Foo9'},
{id: '17', name: 'Foo10'},
];
class Content extends React.Component {
public render() {
const commandBar = <div>
<Fabric.CommandBar
isSearchBoxVisible={false}
items={[
{
key: 'openWebApp',
name: 'Open Web App',
icon: 'OpenInNewWindow', // Or Link
title: 'Open web app',
href: 'http://www.codepen.io',
target: '_blank',
}
]}
farItems={[
{
key: 'menuOptions',
name: 'Options',
icon: 'Settings',
iconOnly: 'true',
['data-automation-id']: 'settingsButton',
subMenuProps: {
items: [
{
key: 'privacyPolicy',
name: 'Privacy Policy',
icon: 'PageLock',
href: 'http://www.codepen.io',
target: '_blank',
},
{
key: 'termsOfUse',
name: 'Terms & Conditions',
icon: 'TextDocument',
href: 'http://www.codepen.io',
target: '_blank'
}
]
}
}
]}
/>
</div>;
const projects = <Fabric.MarqueeSelection selection={null} data-is-scrollable={false}>
<Fabric.DetailsList
items={items}
columns={columns}
/>
</Fabric.MarqueeSelection>;
const selection = <div>[project name]</div>;
const search = <Fabric.TextField label='Search projects:'/>;
const fileButton =
<div>
<Fabric.DefaultButton primary={true}>File To Project</Fabric.DefaultButton>
</div>;
const fileHeader =
<div className='ms-Grid'>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm2 ms-md2 ms-lg2'>
<img width='32' height='32' alt='logo' title='logo'/>
</div>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
<Fabric.Label className='ms-font-l ms-fontWeight-bold ms-fontColor-blue'>[TITLE]</Fabric.Label>
</div>
</div>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm2 ms-md2 ms-lg2'>
</div>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
<Fabric.Label
className='ms-font-m ms-fontWeight-bold ms-fontColor-neutralPrimary'>SELECTED PROJECT:</Fabric.Label>
</div>
</div>
</div>;
return (
<Fabric.Fabric>
<div style={{height:'500px', position:'relative', maxHeight:'inherit'}}>
<Fabric.ScrollablePane scrollbarVisibility={Fabric.ScrollbarVisibility.auto}>
<div className='ms-Grid'>
<Fabric.Sticky stickyPosition={Fabric.StickyPositionType.Header}>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
{commandBar}
</div>
</div>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
{fileHeader}
</div>
</div>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
{selection}
</div>
</div>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
{search}
</div>
</div>
</Fabric.Sticky>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
{projects}
</div>
</div>
<Fabric.Sticky stickyPosition={Fabric.StickyPositionType.Footer}>
<div className='ms-Grid-row'>
<div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
{fileButton}
</div>
</div>
</Fabric.Sticky>
</div>
</Fabric.ScrollablePane>
</div>
</Fabric.Fabric>
);
}
}
ReactDOM.render(
<Content />,
document.getElementById('content')
);
Also, when attempting to use Flex classes or defined heights in parent divs (as per Mohamed Mansour's suggestion) in a project outside of the codepens that uses nested custom React components, the entire DetailsList fails to render. For example, this DOM explorer shows how even setting a defined height and using flex in multiple 'main' divs still fails to render DetailsList: