mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
Table performance paging (#7399)
* dereactifying the row before passing it to the commponent * debouncin * i mean... throttle * initial * UI functionality, switching between modes, prevention of export in performance mode, respect size option in swgs * added limit maintenance in table row collectins, autoscroll respecting sort order * updating the logic to work correctly :) * added handling for overflow rows, this way if an object is removed, we can go back to the most recent rows for all remaining items and repopulate the table if necessary * removing debug row numbers * Closes #7268 - Layout and style sanding and polishing. - Added title to button. - More direct button labeling. * Closes #7268 Partially closes #7147 - Removed footer hover behavior: table footer now always visible. - Tweaks to style, margin etc. to make footer more compact. * moved row limiting out of table row collections and into telemetry collections, table row collections will only limit what they return in getRows, handling sorting when in different modes * have swgs return enough data to fill the requested bounds * support minmax in swgs * using undefined for more clarity * clearing up boolean typo * Address lint fixes * removing autoscroll for descending, it is not necessary * update snapshots * lint --------- Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com> Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
parent
b985619d16
commit
b9df97e2bc
@ -492,7 +492,8 @@
|
||||
"gcov",
|
||||
"WCAG",
|
||||
"stackedplot",
|
||||
"Andale"
|
||||
"Andale",
|
||||
"checksnapshots"
|
||||
],
|
||||
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US"],
|
||||
"ignorePaths": [
|
||||
|
@ -109,7 +109,7 @@ For those interested in the mechanics of snapshot testing with Playwright, you c
|
||||
// from our package.json or circleCI configuration file
|
||||
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v{X.X.X}-focal /bin/bash
|
||||
npm install
|
||||
npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot
|
||||
npm run test:e2e:checksnapshots
|
||||
```
|
||||
|
||||
### Updating Snapshots
|
||||
@ -134,6 +134,12 @@ npm install
|
||||
npm run test:e2e:updatesnapshots
|
||||
```
|
||||
|
||||
Once that's done, you'll need to run the following to verify that the changes do not cause more problems:
|
||||
|
||||
```sh
|
||||
npm run test:e2e:checksnapshots
|
||||
```
|
||||
|
||||
## Automated Accessibility (a11y) Testing
|
||||
|
||||
Open MCT incorporates accessibility testing through two primary methods to ensure its compliance with accessibility standards:
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 39 KiB |
@ -92,6 +92,8 @@ GeneratorProvider.prototype.request = function (domainObject, request) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
||||
workerRequest.start = request.start;
|
||||
workerRequest.end = request.end;
|
||||
workerRequest.size = request.size;
|
||||
workerRequest.strategy = request.strategy;
|
||||
|
||||
return this.workerInterface.request(workerRequest);
|
||||
};
|
||||
|
@ -130,48 +130,37 @@
|
||||
var now = Date.now();
|
||||
var start = request.start;
|
||||
var end = request.end > now ? now : request.end;
|
||||
var amplitude = request.amplitude;
|
||||
var period = request.period;
|
||||
var offset = request.offset;
|
||||
var dataRateInHz = request.dataRateInHz;
|
||||
var phase = request.phase;
|
||||
var randomness = request.randomness;
|
||||
var loadDelay = Math.max(request.loadDelay, 0);
|
||||
var infinityValues = request.infinityValues;
|
||||
var exceedFloat32 = request.exceedFloat32;
|
||||
|
||||
var size = request.size;
|
||||
var duration = end - start;
|
||||
var step = 1000 / dataRateInHz;
|
||||
var maxPoints = Math.floor(duration / step);
|
||||
var nextStep = start - (start % step) + step;
|
||||
|
||||
var data = [];
|
||||
|
||||
for (; nextStep < end && data.length < 5000; nextStep += step) {
|
||||
data.push({
|
||||
utc: nextStep,
|
||||
yesterday: nextStep - 60 * 60 * 24 * 1000,
|
||||
sin: sin(
|
||||
nextStep,
|
||||
period,
|
||||
amplitude,
|
||||
offset,
|
||||
phase,
|
||||
randomness,
|
||||
infinityValues,
|
||||
exceedFloat32
|
||||
),
|
||||
wavelengths: wavelengths(),
|
||||
intensities: intensities(),
|
||||
cos: cos(
|
||||
nextStep,
|
||||
period,
|
||||
amplitude,
|
||||
offset,
|
||||
phase,
|
||||
randomness,
|
||||
infinityValues,
|
||||
exceedFloat32
|
||||
)
|
||||
});
|
||||
if (request.strategy === 'minmax' && size) {
|
||||
// Calculate the number of cycles to include based on size (2 points per cycle)
|
||||
var totalCycles = Math.min(Math.floor(size / 2), Math.floor(duration / period));
|
||||
|
||||
for (let cycle = 0; cycle < totalCycles; cycle++) {
|
||||
// Distribute cycles evenly across the time range
|
||||
let cycleStart = start + (duration / totalCycles) * cycle;
|
||||
let minPointTime = cycleStart; // Assuming min at the start of the cycle
|
||||
let maxPointTime = cycleStart + period / 2; // Assuming max at the halfway of the cycle
|
||||
|
||||
data.push(createDataPoint(minPointTime, request), createDataPoint(maxPointTime, request));
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < maxPoints && nextStep < end; i++, nextStep += step) {
|
||||
data.push(createDataPoint(nextStep, request));
|
||||
}
|
||||
}
|
||||
|
||||
if (request.strategy !== 'minmax' && size) {
|
||||
data = data.slice(-size);
|
||||
}
|
||||
|
||||
if (loadDelay === 0) {
|
||||
@ -181,6 +170,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
function createDataPoint(time, request) {
|
||||
return {
|
||||
utc: time,
|
||||
yesterday: time - 60 * 60 * 24 * 1000,
|
||||
sin: sin(
|
||||
time,
|
||||
request.period,
|
||||
request.amplitude,
|
||||
request.offset,
|
||||
request.phase,
|
||||
request.randomness,
|
||||
request.infinityValues,
|
||||
request.exceedFloat32
|
||||
),
|
||||
wavelengths: wavelengths(),
|
||||
intensities: intensities(),
|
||||
cos: cos(
|
||||
time,
|
||||
request.period,
|
||||
request.amplitude,
|
||||
request.offset,
|
||||
request.phase,
|
||||
request.randomness,
|
||||
request.infinityValues,
|
||||
request.exceedFloat32
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
function postOnRequest(message, request, data) {
|
||||
self.postMessage({
|
||||
id: message.id,
|
||||
|
@ -111,6 +111,7 @@
|
||||
"test:e2e:unstable": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @unstable",
|
||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
|
||||
"test:e2e:generatedata": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @generatedata",
|
||||
"test:e2e:checksnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --retries=0",
|
||||
"test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
|
||||
"test:e2e:visual:ci": "percy exec --config ./e2e/.percy.ci.yml --partial -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --project=chrome --grep-invert @unstable",
|
||||
"test:e2e:visual:full": "percy exec --config ./e2e/.percy.nightly.yml -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --grep-invert @unstable",
|
||||
|
@ -209,6 +209,8 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
let added = [];
|
||||
let addedIndices = [];
|
||||
let hasDataBeforeStartBound = false;
|
||||
let size = this.options.size;
|
||||
let enforceSize = size !== undefined && this.options.enforceSize;
|
||||
|
||||
// loop through, sort and dedupe
|
||||
for (let datum of data) {
|
||||
@ -271,6 +273,13 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
}
|
||||
} else {
|
||||
this.emit('add', added, addedIndices);
|
||||
|
||||
if (enforceSize && this.boundedTelemetry.length > size) {
|
||||
const removeCount = this.boundedTelemetry.length - size;
|
||||
const removed = this.boundedTelemetry.splice(0, removeCount);
|
||||
|
||||
this.emit('remove', removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,11 @@ export default class TelemetryTable extends EventEmitter {
|
||||
|
||||
this.domainObject = domainObject;
|
||||
this.openmct = openmct;
|
||||
this.rowCount = 100;
|
||||
this.tableComposition = undefined;
|
||||
this.datumCache = [];
|
||||
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
||||
this.telemetryMode = this.configuration.getTelemetryMode();
|
||||
this.rowLimit = this.configuration.getRowLimit();
|
||||
this.paused = false;
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
@ -101,18 +102,40 @@ export default class TelemetryTable extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
updateTelemetryMode(mode) {
|
||||
if (this.telemetryMode === mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetryMode = mode;
|
||||
|
||||
this.updateRowLimit();
|
||||
|
||||
this.clearAndResubscribe();
|
||||
}
|
||||
|
||||
updateRowLimit() {
|
||||
if (this.telemetryMode === 'performance') {
|
||||
this.tableRows.setLimit(this.rowLimit);
|
||||
} else {
|
||||
this.tableRows.removeLimit();
|
||||
}
|
||||
}
|
||||
|
||||
createTableRowCollections() {
|
||||
this.tableRows = new TableRowCollection();
|
||||
|
||||
//Fetch any persisted default sort
|
||||
let sortOptions = this.configuration.getConfiguration().sortOptions;
|
||||
|
||||
//If no persisted sort order, default to sorting by time system, ascending.
|
||||
//If no persisted sort order, default to sorting by time system, descending.
|
||||
sortOptions = sortOptions || {
|
||||
key: this.openmct.time.timeSystem().key,
|
||||
direction: 'asc'
|
||||
direction: 'desc'
|
||||
};
|
||||
|
||||
this.updateRowLimit();
|
||||
|
||||
this.tableRows.sortBy(sortOptions);
|
||||
this.tableRows.on('resetRowsFromAllData', this.resetRowsFromAllData);
|
||||
}
|
||||
@ -144,6 +167,11 @@ export default class TelemetryTable extends EventEmitter {
|
||||
|
||||
this.removeTelemetryCollection(keyString);
|
||||
|
||||
if (this.telemetryMode === 'performance') {
|
||||
requestOptions.size = this.rowLimit;
|
||||
requestOptions.enforceSize = true;
|
||||
}
|
||||
|
||||
this.telemetryCollections[keyString] = this.openmct.telemetry.requestCollection(
|
||||
telemetryObject,
|
||||
requestOptions
|
||||
|
@ -48,6 +48,10 @@ export default class TelemetryTableConfiguration extends EventEmitter {
|
||||
configuration.columnOrder = configuration.columnOrder || [];
|
||||
configuration.cellFormat = configuration.cellFormat || {};
|
||||
configuration.autosize = configuration.autosize === undefined ? true : configuration.autosize;
|
||||
// anything that doesn't have a telemetryMode existed before the change and should stay as it was for consistency
|
||||
configuration.telemetryMode = configuration.telemetryMode ?? 'unlimited';
|
||||
configuration.persistModeChange = configuration.persistModeChange ?? true;
|
||||
configuration.rowLimit = configuration.rowLimit ?? 50;
|
||||
|
||||
return configuration;
|
||||
}
|
||||
@ -137,6 +141,42 @@ export default class TelemetryTableConfiguration extends EventEmitter {
|
||||
}, {});
|
||||
}
|
||||
|
||||
getTelemetryMode() {
|
||||
let configuration = this.getConfiguration();
|
||||
|
||||
return configuration.telemetryMode;
|
||||
}
|
||||
|
||||
setTelemetryMode(mode) {
|
||||
let configuration = this.getConfiguration();
|
||||
configuration.telemetryMode = mode;
|
||||
this.updateConfiguration(configuration);
|
||||
}
|
||||
|
||||
getRowLimit() {
|
||||
let configuration = this.getConfiguration();
|
||||
|
||||
return configuration.rowLimit;
|
||||
}
|
||||
|
||||
setRowLimit(limit) {
|
||||
let configuration = this.getConfiguration();
|
||||
configuration.rowLimit = limit;
|
||||
this.updateConfiguration(configuration);
|
||||
}
|
||||
|
||||
getPersistModeChange() {
|
||||
let configuration = this.getConfiguration();
|
||||
|
||||
return configuration.persistModeChange;
|
||||
}
|
||||
|
||||
setPersistModeChange(value) {
|
||||
let configuration = this.getConfiguration();
|
||||
configuration.persistModeChange = value;
|
||||
this.updateConfiguration(configuration);
|
||||
}
|
||||
|
||||
getColumnWidths() {
|
||||
let configuration = this.getConfiguration();
|
||||
|
||||
|
@ -20,17 +20,57 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default {
|
||||
name: 'Telemetry Table',
|
||||
description:
|
||||
'Display values for one or more telemetry end points in a scrolling table. Each row is a time-stamped value.',
|
||||
creatable: true,
|
||||
cssClass: 'icon-tabular-scrolling',
|
||||
initialize(domainObject) {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {
|
||||
columnWidths: {},
|
||||
hiddenColumns: {}
|
||||
};
|
||||
}
|
||||
};
|
||||
export default function getTelemetryTableType(options = {}) {
|
||||
const { telemetryMode = 'performance', persistModeChanges = true, rowLimit = 50 } = options;
|
||||
|
||||
return {
|
||||
name: 'Telemetry Table',
|
||||
description:
|
||||
'Display values for one or more telemetry end points in a scrolling table. Each row is a time-stamped value.',
|
||||
creatable: true,
|
||||
cssClass: 'icon-tabular-scrolling',
|
||||
form: [
|
||||
{
|
||||
key: 'telemetryMode',
|
||||
name: 'Telemetry Mode',
|
||||
control: 'select',
|
||||
options: [
|
||||
{
|
||||
value: 'performance',
|
||||
name: 'Performance Mode'
|
||||
},
|
||||
{
|
||||
value: 'unlimited',
|
||||
name: 'Unlimited Mode'
|
||||
}
|
||||
],
|
||||
cssClass: 'l-inline',
|
||||
property: ['configuration', 'telemetryMode']
|
||||
},
|
||||
{
|
||||
name: 'Persist Telemetry Mode Changes',
|
||||
control: 'toggleSwitch',
|
||||
cssClass: 'l-input',
|
||||
key: 'persistModeChanges',
|
||||
property: ['configuration', 'persistModeChanges']
|
||||
},
|
||||
{
|
||||
name: 'Performance Mode Row Limit',
|
||||
control: 'toggleSwitch',
|
||||
cssClass: 'l-input',
|
||||
key: 'rowLimit',
|
||||
property: ['configuration', 'rowLimit']
|
||||
}
|
||||
],
|
||||
initialize(domainObject) {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {
|
||||
columnWidths: {},
|
||||
hiddenColumns: {},
|
||||
telemetryMode,
|
||||
persistModeChanges,
|
||||
rowLimit
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import TableComponent from './components/TableComponent.vue';
|
||||
import TelemetryTable from './TelemetryTable.js';
|
||||
|
||||
export default class TelemetryTableView {
|
||||
constructor(openmct, domainObject, objectPath) {
|
||||
constructor(openmct, domainObject, objectPath, options) {
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
this.objectPath = objectPath;
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
import TelemetryTableView from './TelemetryTableView.js';
|
||||
|
||||
export default function TelemetryTableViewProvider(openmct) {
|
||||
export default function TelemetryTableViewProvider(openmct, options) {
|
||||
function hasTelemetry(domainObject) {
|
||||
if (!Object.prototype.hasOwnProperty.call(domainObject, 'telemetry')) {
|
||||
return false;
|
||||
@ -44,7 +44,7 @@ export default function TelemetryTableViewProvider(openmct) {
|
||||
return domainObject.type === 'table';
|
||||
},
|
||||
view(domainObject, objectPath) {
|
||||
return new TelemetryTableView(openmct, domainObject, objectPath);
|
||||
return new TelemetryTableView(openmct, domainObject, objectPath, options);
|
||||
},
|
||||
priority() {
|
||||
return 1;
|
||||
|
@ -129,6 +129,15 @@ export default class TableRowCollection extends EventEmitter {
|
||||
this.rows[index] = foundRow;
|
||||
}
|
||||
|
||||
setLimit(rowLimit) {
|
||||
this.rowLimit = rowLimit;
|
||||
}
|
||||
|
||||
removeLimit() {
|
||||
this.rowLimit = null;
|
||||
delete this.rowLimit;
|
||||
}
|
||||
|
||||
sortCollection(rows) {
|
||||
const sortedRows = _.orderBy(
|
||||
rows,
|
||||
@ -363,10 +372,22 @@ export default class TableRowCollection extends EventEmitter {
|
||||
}
|
||||
|
||||
getRows() {
|
||||
if (this.rowLimit && this.rows.length > this.rowLimit) {
|
||||
if (this.sortOptions.direction === 'desc') {
|
||||
return this.rows.slice(0, this.rowLimit);
|
||||
} else {
|
||||
return this.rows.slice(-this.rowLimit);
|
||||
}
|
||||
}
|
||||
|
||||
return this.rows;
|
||||
}
|
||||
|
||||
getRowsLength() {
|
||||
if (this.rowLimit && this.rows.length > this.rowLimit) {
|
||||
return this.rowLimit;
|
||||
}
|
||||
|
||||
return this.rows.length;
|
||||
}
|
||||
|
||||
|
@ -277,6 +277,8 @@
|
||||
class="c-telemetry-table__footer"
|
||||
:marked-rows="markedRows.length"
|
||||
:total-rows="totalNumberOfRows"
|
||||
:telemetry-mode="telemetryMode"
|
||||
@telemetry-mode-change="updateTelemetryMode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -288,12 +290,12 @@ import _ from 'lodash';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import throttle from '../../../utils/throttle';
|
||||
|
||||
import CSVExporter from '../../../exporters/CSVExporter.js';
|
||||
import ProgressBar from '../../../ui/components/ProgressBar.vue';
|
||||
import Search from '../../../ui/components/SearchComponent.vue';
|
||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
||||
import throttle from '../../../utils/throttle';
|
||||
import SizingRow from './SizingRow.vue';
|
||||
import TableColumnHeader from './TableColumnHeader.vue';
|
||||
import TableFooterIndicator from './TableFooterIndicator.vue';
|
||||
@ -302,7 +304,7 @@ import TelemetryTableRow from './TableRow.vue';
|
||||
const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
const RESIZE_POLL_INTERVAL = 200;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 100;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = ROW_HEIGHT * 3;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -386,7 +388,9 @@ export default {
|
||||
enableRegexSearch: {},
|
||||
hideHeaders: configuration.hideHeaders,
|
||||
totalNumberOfRows: 0,
|
||||
rowContext: {}
|
||||
rowContext: {},
|
||||
telemetryMode: configuration.telemetryMode,
|
||||
persistModeChanges: configuration.persistModeChanges
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -439,6 +443,12 @@ export default {
|
||||
watch: {
|
||||
loading: {
|
||||
handler(isLoading) {
|
||||
if (isLoading) {
|
||||
this.setLoadingPromise();
|
||||
} else {
|
||||
this.loadFinishResolve();
|
||||
}
|
||||
|
||||
if (this.viewActionsCollection) {
|
||||
let action = isLoading ? 'disable' : 'enable';
|
||||
this.viewActionsCollection[action](['export-csv-all']);
|
||||
@ -559,6 +569,12 @@ export default {
|
||||
this.table.destroy();
|
||||
},
|
||||
methods: {
|
||||
setLoadingPromise() {
|
||||
this.loadFinishResolve = null;
|
||||
this.isFinishedLoading = new Promise((resolve, reject) => {
|
||||
this.loadFinishResolve = resolve;
|
||||
});
|
||||
},
|
||||
updateVisibleRows() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = this.renderWhenVisible(() => {
|
||||
@ -640,6 +656,17 @@ export default {
|
||||
return toRaw(this.visibleRows[rowIndex]);
|
||||
},
|
||||
sortBy(columnKey) {
|
||||
let timeSystemKey = this.openmct.time.getTimeSystem().key;
|
||||
|
||||
if (this.telemetryMode === 'performance' && columnKey !== timeSystemKey) {
|
||||
this.confirmUnlimitedMode('Switch to Unlimited Telemetry and Sort', () => {
|
||||
this.initiateSort(columnKey);
|
||||
});
|
||||
} else {
|
||||
this.initiateSort(columnKey);
|
||||
}
|
||||
},
|
||||
initiateSort(columnKey) {
|
||||
// If sorting by the same column, flip the sort direction.
|
||||
if (this.sortOptions.key === columnKey) {
|
||||
if (this.sortOptions.direction === 'asc') {
|
||||
@ -650,7 +677,7 @@ export default {
|
||||
} else {
|
||||
this.sortOptions = {
|
||||
key: columnKey,
|
||||
direction: 'asc'
|
||||
direction: 'desc'
|
||||
};
|
||||
}
|
||||
|
||||
@ -660,7 +687,7 @@ export default {
|
||||
this.updateVisibleRows();
|
||||
this.synchronizeScrollX();
|
||||
|
||||
if (this.shouldSnapToBottom()) {
|
||||
if (this.shouldAutoScroll()) {
|
||||
this.autoScroll = true;
|
||||
} else {
|
||||
// If user scrolls away from bottom, disable auto-scroll.
|
||||
@ -668,13 +695,17 @@ export default {
|
||||
this.autoScroll = false;
|
||||
}
|
||||
},
|
||||
shouldSnapToBottom() {
|
||||
shouldAutoScroll() {
|
||||
if (this.sortOptions.direction === 'desc') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
this.scrollable.scrollTop >=
|
||||
this.scrollable.scrollHeight - this.scrollable.offsetHeight - AUTO_SCROLL_TRIGGER_HEIGHT
|
||||
);
|
||||
},
|
||||
scrollToBottom() {
|
||||
initiateAutoScroll() {
|
||||
this.scrollable.scrollTop = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
synchronizeScrollX() {
|
||||
@ -723,7 +754,7 @@ export default {
|
||||
}
|
||||
|
||||
if (this.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
this.initiateAutoScroll();
|
||||
}
|
||||
|
||||
this.updateVisibleRows();
|
||||
@ -750,12 +781,25 @@ export default {
|
||||
headers: headerKeys
|
||||
});
|
||||
},
|
||||
exportAllDataAsCSV() {
|
||||
getTableRowData() {
|
||||
const justTheData = this.table.tableRows
|
||||
.getRows()
|
||||
.map((row) => row.getFormattedDatum(this.headers));
|
||||
|
||||
this.exportAsCSV(justTheData);
|
||||
return justTheData;
|
||||
},
|
||||
exportAllDataAsCSV() {
|
||||
if (this.telemetryMode === 'performance') {
|
||||
this.confirmUnlimitedMode('Switch to Unlimited Telemetry and Export', () => {
|
||||
const data = this.getTableRowData();
|
||||
|
||||
this.exportAsCSV(data);
|
||||
});
|
||||
} else {
|
||||
const data = this.getTableRowData();
|
||||
|
||||
this.exportAsCSV(data);
|
||||
}
|
||||
},
|
||||
exportMarkedDataAsCSV() {
|
||||
const data = this.table.tableRows
|
||||
@ -849,7 +893,7 @@ export default {
|
||||
// On some resize events scrollTop is reset to 0. Possibly due to a transition we're using?
|
||||
// Need to preserve scroll position in this case.
|
||||
if (this.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
this.initiateAutoScroll();
|
||||
} else {
|
||||
this.scrollable.scrollTop = scrollTop;
|
||||
}
|
||||
@ -1106,6 +1150,54 @@ export default {
|
||||
this.viewActionsCollection.hide(['expand-columns']);
|
||||
}
|
||||
},
|
||||
confirmUnlimitedMode(
|
||||
label,
|
||||
callback,
|
||||
message = 'A new data request for all telemetry values for all endpoints will be made which will take some time. Do you want to continue?'
|
||||
) {
|
||||
const dialog = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message,
|
||||
buttons: [
|
||||
{
|
||||
label,
|
||||
emphasis: true,
|
||||
callback: async () => {
|
||||
this.updateTelemetryMode();
|
||||
await this.isFinishedLoading;
|
||||
|
||||
callback();
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
updateTelemetryMode() {
|
||||
this.telemetryMode = this.telemetryMode === 'unlimited' ? 'performance' : 'unlimited';
|
||||
|
||||
if (this.persistModeChanges) {
|
||||
this.table.configuration.setTelemetryMode(this.telemetryMode);
|
||||
}
|
||||
|
||||
this.table.updateTelemetryMode(this.telemetryMode);
|
||||
|
||||
const timeSystemKey = this.openmct.time.getTimeSystem().key;
|
||||
|
||||
if (this.telemetryMode === 'performance' && this.sortOptions.key !== timeSystemKey) {
|
||||
this.openmct.notifications.info(
|
||||
'Switched to Performance Mode: Table now sorted by time for optimized efficiency.'
|
||||
);
|
||||
this.initiateSort(timeSystemKey);
|
||||
}
|
||||
},
|
||||
setRowHeight(height) {
|
||||
this.rowHeight = height;
|
||||
this.setHeight();
|
||||
|
@ -36,11 +36,11 @@
|
||||
|
||||
<div class="c-table-indicator__counts">
|
||||
<span
|
||||
:aria-label="totalRows + ' rows visible after any filtering'"
|
||||
:title="totalRows + ' rows visible after any filtering'"
|
||||
:aria-label="rowCountTitle"
|
||||
:title="rowCountTitle"
|
||||
class="c-table-indicator__elem c-table-indicator__row-count"
|
||||
>
|
||||
{{ totalRows }} Rows
|
||||
{{ rowCount }} Rows
|
||||
</span>
|
||||
|
||||
<span
|
||||
@ -51,6 +51,10 @@
|
||||
>
|
||||
{{ markedRows }} Marked
|
||||
</span>
|
||||
|
||||
<button :title="telemetryModeButtonTitle" class="c-button" @click="toggleTelemetryMode">
|
||||
{{ telemetryModeButtonLabel }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -74,8 +78,13 @@ export default {
|
||||
totalRows: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
telemetryMode: {
|
||||
type: String,
|
||||
default: 'performance'
|
||||
}
|
||||
},
|
||||
emits: ['telemetry-mode-change'],
|
||||
data() {
|
||||
return {
|
||||
filterNames: [],
|
||||
@ -93,6 +102,9 @@ export default {
|
||||
return !_.isEqual(filtersToCompare, _.omit(filters, [USE_GLOBAL]));
|
||||
});
|
||||
},
|
||||
isUnlimitedMode() {
|
||||
return this.telemetryMode === 'unlimited';
|
||||
},
|
||||
label() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_LABEL_MIXED;
|
||||
@ -100,6 +112,22 @@ export default {
|
||||
return FILTER_INDICATOR_LABEL;
|
||||
}
|
||||
},
|
||||
rowCount() {
|
||||
return this.isUnlimitedMode ? this.totalRows : 'LATEST 50';
|
||||
},
|
||||
rowCountTitle() {
|
||||
return this.isUnlimitedMode
|
||||
? this.totalRows + ' rows visible after any filtering'
|
||||
: 'performance mode limited to 50 rows';
|
||||
},
|
||||
telemetryModeButtonLabel() {
|
||||
return this.isUnlimitedMode ? 'SHOW LATEST 50' : 'SHOW ALL';
|
||||
},
|
||||
telemetryModeButtonTitle() {
|
||||
return this.isUnlimitedMode
|
||||
? 'Change to Performance mode (latest 50 values)'
|
||||
: 'Change to show all values';
|
||||
},
|
||||
title() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_TITLE_MIXED;
|
||||
@ -117,6 +145,9 @@ export default {
|
||||
this.table.configuration.off('change', this.handleConfigurationChanges);
|
||||
},
|
||||
methods: {
|
||||
toggleTelemetryMode() {
|
||||
this.$emit('telemetry-mode-change');
|
||||
},
|
||||
setFilterNames() {
|
||||
let names = [];
|
||||
let composition = this.openmct.composition.get(this.table.configuration.domainObject);
|
||||
|
@ -18,6 +18,7 @@
|
||||
&__counts {
|
||||
//background: rgba(deeppink, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1 1 auto;
|
||||
justify-content: flex-end;
|
||||
overflow: hidden;
|
||||
|
@ -169,30 +169,13 @@
|
||||
}
|
||||
|
||||
&__footer {
|
||||
$pt: 2px;
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
margin-top: $interiorMargin;
|
||||
padding: $pt 0;
|
||||
margin-bottom: $interiorMarginSm;
|
||||
overflow: hidden;
|
||||
transition: all 250ms;
|
||||
|
||||
&:not(.is-filtering) {
|
||||
.c-frame & {
|
||||
height: 0;
|
||||
padding: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-frame & {
|
||||
// target .c-frame .c-telemetry-table {}
|
||||
$pt: 2px;
|
||||
&:hover {
|
||||
.c-telemetry-table__footer:not(.is-filtering) {
|
||||
height: $pt + 16px;
|
||||
padding: initial;
|
||||
visibility: visible;
|
||||
.c-frame & {
|
||||
.c-button {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,16 +19,17 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import TableConfigurationViewProvider from './TableConfigurationViewProvider.js';
|
||||
import TelemetryTableType from './TelemetryTableType.js';
|
||||
import getTelemetryTableType from './TelemetryTableType.js';
|
||||
import TelemetryTableViewProvider from './TelemetryTableViewProvider.js';
|
||||
import TelemetryTableViewActions from './ViewActions.js';
|
||||
|
||||
export default function plugin() {
|
||||
export default function plugin(options) {
|
||||
return function install(openmct) {
|
||||
openmct.objectViews.addProvider(new TelemetryTableViewProvider(openmct));
|
||||
openmct.objectViews.addProvider(new TelemetryTableViewProvider(openmct, options));
|
||||
openmct.inspectorViews.addProvider(new TableConfigurationViewProvider(openmct));
|
||||
openmct.types.addType('table', TelemetryTableType);
|
||||
openmct.types.addType('table', getTelemetryTableType(options));
|
||||
openmct.composition.addPolicy((parent, child) => {
|
||||
if (parent.type === 'table') {
|
||||
return Object.prototype.hasOwnProperty.call(child, 'telemetry');
|
||||
|
Loading…
Reference in New Issue
Block a user