enterprise
enterprise copied to clipboard
PagePattern: List detail with dashboard view container enhancements
Is your feature request related to a problem or use case? Please describe. Attached is an example of a somewhat common pattern in Landmark. List with a detail section displayed within the horizontal tabs component. We are running into some issues with extra scrollbars when using a dashboard view "Homepage" layout inside of a tab panel. (see attached screenshots)
Describe the solution you'd like Some "out of the box" CSS support to handle this kind of pattern. We also set our own margin on the top/bottom containers. You can see this in the embedded HTML where I am using inline styles. I wasn't able to find an existing IDS container for this pattern. I am thinking this pattern in general could use some extra support.
Describe alternatives you've considered Currently we are working around some of these issues with internal CSS overrides. However this becomes problematic when CSS changes via IDS upgrades sometimes cause unexpected breaking changes.
Additional context
<a href="#maincontent" class="skip-link" data-translate="text">SkipToMain</a>
{{> includes/svg-inline-refs}}
{{> includes/applicationmenu}}
<section class="module-nav-container mode-collapsed">
<aside id="nav" class="module-nav" data-options="{ 'filterable': true }">
<div class="module-nav-bar">
<!-- Module Nav's top-level navigation items are inside the accordion -->
<div class="module-nav-accordion accordion panel" data-options="{'allowOnePane': false }">
<!-- Module switcher -->
<div class="module-nav-header accordion-section">
<div class="module-nav-switcher">
<div class="module-nav-section role-dropdown">
<label for="module-nav-role-switcher" class="label audible">Roles</label>
<select id="module-nav-role-switcher" name="module-nav-role-switcher" class="dropdown" data-options="{attributes: [{ name: 'id', value: 'switcher-id-1' }, { name: 'data-automation-id', value: 'switcher-automation-id' }]}" data-automation-id="custom-automation-dropdown-id">
<option value="admin" data-icon="{icon: 'app-ac'}">Admin Console</option>
<option value="job-console" data-icon="{icon: 'app-jo'}">Job Console</option>
<option value="landing-page-designer" data-icon="{icon: 'app-lmd'}">Landing Page Designer</option>
<option value="process-server-admin" data-icon="{icon: 'app-psa'}">Process Server Administrator</option>
<option value="proxy-management" data-icon="{icon: 'app-pm'}">Proxy Management</option>
<option value="security-system-management" data-icon="{icon: 'app-ssm'}">Security System Management</option>
<option value="user-management" data-icon="{icon: 'app-um'}">User Management</option>
</select>
</div>
</div>
</div>
<div class="module-nav-search-container accordion-section">
<label class="audible" for="module-nav-searchfield">Search</label>
<input id="module-nav-searchfield" data-init="false" class="searchfield module-nav-search" placeholder="Search" />
</div>
<!-- Most accordion items should be placed in the "main" section -->
<div class="module-nav-main accordion-section">
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-male"></use>
</svg>
<a href="#" id="item-config" data-automation-id="module-nav-item-config"><span>Configuration and Personalization</span></a>
</div>
<div class="accordion-pane">
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
</div>
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-database"></use>
</svg>
<a href="#" id="item-database" data-automation-id="module-nav-item-databasde"><span>Database</span></a>
</div>
<div class="accordion-pane">
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
</div>
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-translate"></use>
</svg>
<a href="#" id="item-translation" data-automation-id="module-nav-item-translation"><span>Data Translation</span></a>
</div>
<div class="accordion-pane">
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
</div>
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-security-on"></use>
</svg>
<a href="#" id="item-security" data-automation-id="module-nav-item-security"><span>Security</span></a>
</div>
<div class="accordion-pane">
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
</div>
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-line-bar-chart"></use>
</svg>
<a href="#" id="item-analytics" data-automation-id="module-nav-item-analytics"><span>Analytics</span></a>
</div>
<div class="accordion-pane">
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
<div class="accordion-header">
<a href="#"><span>Label</span></a>
</div>
</div>
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-import-spreadsheet"></use>
</svg>
<a href="#" id="item-audit" data-automation-id="module-nav-item-audit"><span>Audit and Monitoring</span></a>
</div>
</div>
<!-- Footer items are separate and "optionally" pinnable -->
<div class="module-nav-footer accordion-section">
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-document"></use>
</svg>
<a href="#" id="module-nav-item-documents" data-automation-id="module-nav-documents"><span>Documents</span></a>
</div>
<div class="accordion-header is-selected">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-report"></use>
</svg>
<a href="#" id="module-nav-item-reports" data-automation-id="module-nav-reports"><span>Reports</span></a>
</div>
<div class="accordion-header">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-notification"></use>
</svg>
<a href="#" id="module-nav-item-notifications"
data-automation-id="module-nav-notifications"><span>Notification</span></a>
</div>
</div>
<!-- Settings area is also separate and permanently "pinned" to the bottom -->
<div class="module-nav-settings accordion-section">
<div
class="module-nav-settings-btn accordion-header"
data-automation-id="module-nav-settings"
id="module-nav-settings-btn">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-settings"></use>
</svg>
<a href="#"><span>Settings</span></a>
</div>
<ul class="popupmenu module-nav-settings-menu">
<li>
<a href="#">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-observation-precautions"></use>
</svg>
<span>Jobs</span>
</a>
</li>
<li>
<a href="#">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-report"></use>
</svg>
<span>Reports</span>
</a>
</li>
<li>
<a href="#">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-isolation"></use>
</svg>
<span>Actions</span>
</a>
</li>
<li>
<a href="#">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-edit"></use>
</svg>
<span>Personalization</span>
</a>
</li>
<li class="separator"></li>
<li>
<a href="#">
<span>Create Report</span>
</a>
</li>
<li>
<a href="#">
<span>Proxy</span>
</a>
</li>
<li>
<a href="#">
<span>Set "As Of Date"</span>
</a>
</li>
<li>
<a href="#">
<span>User Context</span>
</a>
</li>
<li class="separator"></li>
<li>
<a href="#">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-settings"></use>
</svg>
<span>Settings</span>
</a>
</li>
<li>
<a href="#">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-info"></use>
</svg>
<span>About</span>
</a>
</li>
<li>
<a href="#">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-help"></use>
</svg>
<span>Help</span>
</a>
</li>
</ul>
</div>
</div>
</div>
<!-- Detail area is optional and can have additional context -->
<div class="module-nav-detail"> </div>
</aside>
<!-- Page Container -->
<div class="page-container scrollable">
<div style="margin: 5px 20px 10px 20px">
<div class="toolbar" role="toolbar">
<div class="title">
Compressors
<span class="datagrid-result-count">(N Results)</span>
</div>
<div class="buttonset">
<label class="audible" for="common-toolbar-searchfield">Keyword Search</label>
<input id="common-toolbar-searchfield" name="common-toolbar-searchfield" class="searchfield" />
<button class="btn btn-toggle no-ripple hide-focus is-pressed" type="button" aria-pressed="true" id="toggle-compact">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-change-row-line-height"></use>
</svg>
<span>Compact Mode</span>
</button>
</div>
<div class="more">
<button class="btn-actions" type="button">
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-more"></use>
</svg>
<span class="audible">More Actions</span>
</button>
<ul class="popupmenu">
<li><a data-option="personalize-columns" href="#" data-translate="text">PersonalizeColumns</a></li>
<li><a data-option="reset-layout" href="#" data-translate="text">ResetDefault</a></li>
<li class="separator"></li>
<li class="heading">Filter</li>
<li class="show-filter is-toggleable is-checked"><a data-option="show-filter-row" data-translate="text">ShowFilterRow</a></li>
<li class="is-indented"><a data-option="run-filter" data-translate="text">RunFilter</a></li>
<li class="is-indented"><a data-option="clear-filter" data-translate="text">ClearFilter</a></li>
<li class="separator single-selectable-section"></li>
<li class="heading">Row Height</li>
<li class="is-selectable is-checked"><a data-option="row-extra-small" href="#" data-translate="text">ExtraSmall</a></li>
<li class="is-selectable"><a data-option="row-small" href="#" data-translate="text">Small</a></li>
<li class="is-selectable"><a data-option="row-medium" href="#" data-translate="text">Medium</a></li>
<li class="is-selectable"><a data-option="row-large" href="#" data-translate="text">Large</a></li>
</ul>
</div>
</div>
<div id="datagrid">
</div>
</div>
<div style="margin: 0 20px">
<div id="header-tabs" class="tab-container" data-options='{ "containerElement": "#tab-panel-container" }'>
<ul class="tab-list">
<li class="tab"><a id="tabs-1" data-automation-id="tabs-1-a" href="#header-tabs-1">Tab 1</a></li>
<li class="tab"><a id="tabs-2" data-automation-id="tabs-2-a" href="#header-tabs-2">Tab 2</a></li>
</ul>
</div>
<div id="tab-panel-container">
<div id="header-tabs-1" data-automation-id="tabs-1-panel" class="tab-panel">
<div class="homepage" data-columns="3">
<div class="content">
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Widget</h2>
</div>
</div>
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Widget</h2>
</div>
</div>
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Widget</h2>
</div>
</div>
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Widget</h2>
</div>
</div>
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Widget</h2>
</div>
</div>
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Widget</h2>
</div>
</div>
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Widget</h2>
</div>
</div>
</div>
</div>
</div>
<div id="header-tabs-2" data-automation-id="tabs-2-panel" class="tab-panel">
<div class="homepage" data-columns="3">
<div class="content">
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Business Insight</h2>
<button class="btn-actions" type="button">
<span class="audible">Actions</span>
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-more"></use>
</svg>
</button>
<ul class="popupmenu actions top">
<li><a href="#">Action One</a></li>
<li><a href="#">Action Two</a></li>
</ul>
</div>
<div class="widget-content">
<div id="datagrid1">
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<h2 class="widget-title">Calendar (Feb 2021)</h2>
<button class="btn-actions" type="button">
<span class="audible">Actions</span>
<svg class="icon" focusable="false" aria-hidden="true" role="presentation">
<use href="#icon-more"></use>
</svg>
</button>
<ul class="popupmenu actions top">
<li><a href="#">Action One</a></li>
<li><a href="#">Action Two</a></li>
</ul>
</div>
<div class="widget-content">
<div class="calendar" data-init="false" id="calendar-one">
<div class="calendar-monthview">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script>
$('body').one('initialized', function () {
$('#calendar-one').calendar({
month: 1,
day: 2,
year: 2022,
showToday: false,
showViewChanger: false,
});
var grid,
columns = [],
data = [];
//Define Columns for the Grid.
columns.push({ id: 'selectionCheckbox', sortable: false, resizable: false, formatter: Soho.Formatters.SelectionCheckbox, align: 'center' }),
columns.push({ id: 'id', name: 'Id', field: 'id', formatter: Soho.Formatters.Readonly, filterType: 'text'});
columns.push({ id: 'productId', name: 'Product Id', field: 'productId', formatter: Soho.Formatters.Readonly, filterType: 'text'});
columns.push({ id: 'productName', name: 'Product Name', sortable: false, field: 'productName', filterType: 'text', formatter: Soho.Formatters.Hyperlink, editor: Soho.Editors.Input});
columns.push({ id: 'activity', hidden: true, name: 'Activity', field: 'activity', filterType: 'integer', editor: Soho.Editors.Input});
columns.push({ id: 'quantity', name: 'Quantity', field: 'quantity', filterType: 'contents', options: [{id: '1', value: '1', label: '1'}, {id: '2', value: '2', label: '2'}]});
columns.push({ id: 'price', name: 'Price', field: 'price', width: 125, filterType: 'decimal', formatter: Soho.Formatters.Decimal});
columns.push({ id: 'orderDate', name: 'Order Date', field: 'orderDate', filterType: 'date', formatter: Soho.Formatters.Date, dateFormat: 'M/d/yyyy'});
//Init and get the api for the grid
grid = $('#datagrid').datagrid({
allowSelectAcrossPages: true,
columns: columns,
selectable: 'multiple',
paging: true,
editable: true,
filterable: true,
pagesize: 10,
rowHeight: 'extra-small',
resultsText: function (grid, count, filterCount) {
console.log('ResultsText:', grid, count, filterCount);
if (filterCount > 0) {
return '(' + filterCount + ' of '+ count + ')';
}
else {
return '(' + 'all '+ count + ')';
}
},
source: function(req, response) {
var url = '{{basepath}}api/compressors?pageNum='+ req.activePage +'&pageSize='+ req.pagesize;
if (req.sortField) {
url += '&sortField=' + req.sortField + '&sortAsc=' + req.sortAsc;
}
if (req.filterExpr && req.filterExpr[0]) {
url += '&filterValue=' + req.filterExpr[0].value;
url += '&filterOp=' + req.filterExpr[0].operator;
url += '&filterColumn=' + req.filterExpr[0].columnId;
}
//Get Page Based on info in Req, return results into response;
$.getJSON(url, function(res) {
// This is the total going into the grid so the pager works (filtered total or total)
req.total = res.total;
req.preserveSelected = true;
if ((req.filterExpr && req.filterExpr[0])) {
req.total = res.total;
req.grandTotal = res.grandTotal; // This is the total amount on the server
}
response(res.data, req);
});
},
toolbar: {title: 'Data Grid Header Title', filterRow: true, personalize: true, results: true, keywordFilter: false, actions: true, rowHeight: true, filterRow: true}
});
$('#toggle-compact').on('click', function () {
Soho.utils.toggleCompactMode(document.querySelector('form'));
});
$('#header-tabs').on('focusin', 'a', function() {
if (console && typeof console.log === 'function') {
console.log('Tab Content: "'+ $(this).text().trim() +'"');
}
});
var grid1,
columns1 = [];
columns1.push({ id: 'productId', name: 'Product Id', field: 'productId', percent: .30});
columns1.push({ id: 'productName', name: 'Product Name', sortable: false, field: 'productName', percent: .40, formatter: Soho.Formatters.Hyperlink, editor: Soho.Editors.Input});
columns1.push({ id: 'quantity', name: 'Quantity', field: 'quantity', percent: .30 });
//Init and get the api for the grid
grid1 = $('#datagrid1').datagrid({
columns: columns1,
selectable: 'multiple',
editable: true,
rowHeight: 'medium',
isList: true,
indeterminate: true,
showPageSizeSelector: false,
paging: true,
pagesize: 10,
source: function(req, response) {
var url = '{{basepath}}api/compressors?pageNum='+ req.activePage +'&pageSize='+ req.pagesize;
if (req.sortField) {
url += '&sortField=' + req.sortField + '&sortAsc=' + req.sortAsc;
}
if (req.filterExpr && req.filterExpr[0]) {
url += '&filterValue=' + req.filterExpr[0].value;
url += '&filterOp=' + req.filterExpr[0].operator;
url += '&filterColumn=' + req.filterExpr[0].columnId;
}
//Get Page Based on info in Req, return results into response;
$.getJSON(url, function(res) {
req.total = res.total;
response(res.data, req);
});
},
}).on('selected', function (e, args) {
console.log(args.row, args.item);
});
$('html').personalize({ colors: '#fff' });
$('[data-theme-name="theme-classic"]').parent().addClass('is-disabled');
const navEl = $('#nav');
const navAPI = navEl.data('modulenav');
const containerEl = $('.module-nav-container');
const displayCheckEl = $('#display-detail-check');
const hideCompletelyEl = $('#hide-completely');
const hamburgerBtnEl = $('#header-hamburger');
const pinSectionsCheckEl = $('#pin-sections');
const disableSwitcher = $('#disable-switcher');
displayCheckEl.on('change.test', (e) => {
navAPI.updated({ showDetailView: displayCheckEl[0].checked });
});
pinSectionsCheckEl.on('change.test', (e) => {
navAPI.updated({ pinSections: pinSectionsCheckEl[0].checked });
});
disableSwitcher.on('change.test', (e, mode) => {
navAPI.updated({ disableSwitcher: disableSwitcher[0].checked });
})
if (hamburgerBtnEl.length) {
hamburgerBtnEl.on('click.test', (e) => {
navAPI.handleHamburgerClick();
});
}
navEl.on('displaymodechange.test', (e, mode) => {
console.log('Display mode changed to:', mode);
});
// Connect component event handling
const roleSwitcherEl = $('.role-dropdown');
const roleSwitcherAPI = roleSwitcherEl.data('modulenavswitcher');
const roleDropdownEl = $('#module-nav-role-switcher');
const roleButtonEl = $('.module-btn button');
roleDropdownEl.on('change.test', (e) => {
console.info('Module Nav Role Change:', e.target.value);
});
roleButtonEl.on('click.test', (e) => {
console.info('Module Button Click');
})
hideCompletelyEl.on('change.test', (e) => {
console.log(hideCompletelyEl[0].checked)
if (hideCompletelyEl[0].checked) {
navAPI.updated({ displayMode: false });
hamburgerBtnEl.attr('disabled', 'true');
}
else {
navAPI.updated({ displayMode: navAPI.isLargerThanBreakpoint() ? 'collapsed' : 'expanded' });
hamburgerBtnEl.removeAttr('disabled');
}
});
// Customizations to the component after init
navAPI.updated({ displayMode: 'collapsed' });
$('.module-nav-switcher').on('listcontextmenu', function (e, delegate) {
console.log('Context Menu Event Fired')
const url = $(delegate.target).text().replace(/^\s+|\s+$/g, '').replace(' ', '').toLocaleLowerCase();
$(delegate.target)
.attr('href', `https://example.com/${url}`);
});
});
</script>