1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-23 15:29:42 +02:00

refactor(containers): migrate view to react [EE-2212] (#6577)

Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
This commit is contained in:
Chaim Lev-Ari 2022-08-11 07:33:29 +03:00 committed by GitHub
parent 5ee570e075
commit bed4257194
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 1616 additions and 875 deletions

View file

@ -1,256 +1,105 @@
import { useEffect } from 'react';
import {
useTable,
useSortBy,
useFilters,
useGlobalFilter,
usePagination,
Row,
} from 'react-table';
import { useRowSelectColumn } from '@lineup-lite/hooks';
import _ from 'lodash';
import { useDebounce } from '@/portainer/hooks/useDebounce';
import type {
ContainersTableSettings,
DockerContainer,
} from '@/react/docker/containers/types';
import { useEnvironment } from '@/portainer/environments/useEnvironment';
import { Environment } from '@/portainer/environments/types';
import type { DockerContainer } from '@/react/docker/containers/types';
import { PaginationControls } from '@@/PaginationControls';
import { TableSettingsMenu, Datatable } from '@@/datatables';
import {
QuickActionsSettings,
buildAction,
QuickActionsSettings,
} from '@@/datatables/QuickActionsSettings';
import {
Table,
TableActions,
TableContainer,
TableHeaderRow,
TableRow,
TableSettingsMenu,
TableTitle,
TableTitleActions,
} from '@@/datatables';
import { multiple } from '@@/datatables/filter-types';
import { useTableSettings } from '@@/datatables/useTableSettings';
import { ColumnVisibilityMenu } from '@@/datatables/ColumnVisibilityMenu';
import { useRepeater } from '@@/datatables/useRepeater';
import { SearchBar, useSearchBarState } from '@@/datatables/SearchBar';
import { useRowSelect } from '@@/datatables/useRowSelect';
import { Checkbox } from '@@/form-components/Checkbox';
import { TableFooter } from '@@/datatables/TableFooter';
import { SelectedRowsCount } from '@@/datatables/SelectedRowsCount';
import { ContainersDatatableActions } from './ContainersDatatableActions';
import { useContainers } from '../../queries/containers';
import { createStore } from './datatable-store';
import { ContainersDatatableSettings } from './ContainersDatatableSettings';
import { useColumns } from './columns';
import { ContainersDatatableActions } from './ContainersDatatableActions';
import { RowProvider } from './RowContext';
export interface ContainerTableProps {
isAddActionVisible: boolean;
dataset: DockerContainer[];
onRefresh?(): Promise<void>;
const storageKey = 'containers';
const useStore = createStore(storageKey);
const actions = [
buildAction('logs', 'Logs'),
buildAction('inspect', 'Inspect'),
buildAction('stats', 'Stats'),
buildAction('exec', 'Console'),
buildAction('attach', 'Attach'),
];
export interface Props {
isHostColumnVisible: boolean;
tableKey?: string;
environment: Environment;
}
export function ContainersDatatable({
isAddActionVisible,
dataset,
onRefresh,
isHostColumnVisible,
}: ContainerTableProps) {
const { settings, setTableSettings } =
useTableSettings<ContainersTableSettings>();
const [searchBarValue, setSearchBarValue] = useSearchBarState('containers');
const columns = useColumns();
const endpoint = useEnvironment();
useRepeater(settings.autoRefreshRate, onRefresh);
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
prepareRow,
selectedFlatRows,
allColumns,
gotoPage,
setPageSize,
setHiddenColumns,
toggleHideColumn,
setGlobalFilter,
state: { pageIndex, pageSize },
} = useTable<DockerContainer>(
{
defaultCanFilter: false,
columns,
data: dataset,
filterTypes: { multiple },
initialState: {
pageSize: settings.pageSize || 10,
hiddenColumns: settings.hiddenColumns,
sortBy: [settings.sortBy],
globalFilter: searchBarValue,
},
isRowSelectable(row: Row<DockerContainer>) {
return !row.original.IsPortainer;
},
autoResetSelectedRows: false,
getRowId(originalRow: DockerContainer) {
return originalRow.Id;
},
selectCheckboxComponent: Checkbox,
},
useFilters,
useGlobalFilter,
useSortBy,
usePagination,
useRowSelect,
useRowSelectColumn
environment,
}: Props) {
const settings = useStore();
const columns = useColumns(isHostColumnVisible);
const hidableColumns = _.compact(
columns.filter((col) => col.canHide).map((col) => col.id)
);
const debouncedSearchValue = useDebounce(searchBarValue);
useEffect(() => {
setGlobalFilter(debouncedSearchValue);
}, [debouncedSearchValue, setGlobalFilter]);
useEffect(() => {
toggleHideColumn('host', !isHostColumnVisible);
}, [toggleHideColumn, isHostColumnVisible]);
const columnsToHide = allColumns.filter((colInstance) => {
const columnDef = columns.find((c) => c.id === colInstance.id);
return columnDef?.canHide;
});
const actions = [
buildAction('logs', 'Logs'),
buildAction('inspect', 'Inspect'),
buildAction('stats', 'Stats'),
buildAction('exec', 'Console'),
buildAction('attach', 'Attach'),
];
const tableProps = getTableProps();
const tbodyProps = getTableBodyProps();
const containersQuery = useContainers(
environment.Id,
true,
undefined,
settings.autoRefreshRate * 1000
);
return (
<TableContainer>
<TableTitle icon="box" featherIcon label="Containers">
<SearchBar
value={searchBarValue}
onChange={handleSearchBarChange}
placeholder="Search for a container..."
/>
<TableActions>
<RowProvider context={{ environment }}>
<Datatable
titleOptions={{
icon: 'fa-cubes',
title: 'Containers',
}}
settingsStore={settings}
columns={columns}
renderTableActions={(selectedRows) => (
<ContainersDatatableActions
selectedItems={selectedFlatRows.map((row) => row.original)}
isAddActionVisible={isAddActionVisible}
endpointId={endpoint.Id}
/>
</TableActions>
<TableTitleActions>
<ColumnVisibilityMenu<DockerContainer>
columns={columnsToHide}
onChange={handleChangeColumnsVisibility}
value={settings.hiddenColumns}
selectedItems={selectedRows}
isAddActionVisible
endpointId={environment.Id}
/>
)}
isLoading={containersQuery.isLoading}
isRowSelectable={(row) => !row.original.IsPortainer}
initialTableState={{ hiddenColumns: settings.hiddenColumns }}
renderTableSettings={(tableInstance) => {
const columnsToHide = tableInstance.allColumns.filter((colInstance) =>
hidableColumns?.includes(colInstance.id)
);
<TableSettingsMenu
quickActions={<QuickActionsSettings actions={actions} />}
>
<ContainersDatatableSettings isRefreshVisible={!!onRefresh} />
</TableSettingsMenu>
</TableTitleActions>
</TableTitle>
<Table
className={tableProps.className}
role={tableProps.role}
style={tableProps.style}
>
<thead>
{headerGroups.map((headerGroup) => {
const { key, className, role, style } =
headerGroup.getHeaderGroupProps();
return (
<TableHeaderRow<DockerContainer>
key={key}
className={className}
role={role}
style={style}
headers={headerGroup.headers}
onSortChange={handleSortChange}
return (
<>
<ColumnVisibilityMenu<DockerContainer>
columns={columnsToHide}
onChange={(hiddenColumns) => {
settings.setHiddenColumns(hiddenColumns);
tableInstance.setHiddenColumns(hiddenColumns);
}}
value={settings.hiddenColumns}
/>
);
})}
</thead>
<tbody
className={tbodyProps.className}
role={tbodyProps.role}
style={tbodyProps.style}
>
{page.length > 0 ? (
page.map((row) => {
prepareRow(row);
const { key, className, role, style } = row.getRowProps();
return (
<TableRow<DockerContainer>
cells={row.cells}
key={key}
className={className}
role={role}
style={style}
<TableSettingsMenu
quickActions={<QuickActionsSettings actions={actions} />}
>
<ContainersDatatableSettings
isRefreshVisible
settings={settings}
/>
);
})
) : (
<tr>
<td colSpan={columns.length} className="text-center text-muted">
No container available.
</td>
</tr>
)}
</tbody>
</Table>
<TableFooter>
<SelectedRowsCount value={selectedFlatRows.length} />
<PaginationControls
showAll
pageLimit={pageSize}
page={pageIndex + 1}
onPageChange={(p) => gotoPage(p - 1)}
totalCount={dataset.length}
onPageLimitChange={handlePageSizeChange}
/>
</TableFooter>
</TableContainer>
</TableSettingsMenu>
</>
);
}}
storageKey={storageKey}
dataset={containersQuery.data || []}
emptyContentLabel="No containers found"
/>
</RowProvider>
);
function handlePageSizeChange(pageSize: number) {
setPageSize(pageSize);
setTableSettings((settings) => ({ ...settings, pageSize }));
}
function handleChangeColumnsVisibility(hiddenColumns: string[]) {
setHiddenColumns(hiddenColumns);
setTableSettings((settings) => ({ ...settings, hiddenColumns }));
}
function handleSearchBarChange(value: string) {
setSearchBarValue(value);
}
function handleSortChange(id: string, desc: boolean) {
setTableSettings((settings) => ({
...settings,
sortBy: { id, desc },
}));
}
}

View file

@ -4,8 +4,9 @@ import * as notifications from '@/portainer/services/notifications';
import { useAuthorizations, Authorized } from '@/portainer/hooks/useUser';
import { confirmContainerDeletion } from '@/portainer/services/modal.service/prompt';
import { setPortainerAgentTargetHeader } from '@/portainer/services/http-request.helper';
import type {
import {
ContainerId,
ContainerStatus,
DockerContainer,
} from '@/react/docker/containers/types';
import {
@ -40,13 +41,22 @@ export function ContainersDatatableActions({
}: Props) {
const selectedItemCount = selectedItems.length;
const hasPausedItemsSelected = selectedItems.some(
(item) => item.Status === 'paused'
(item) => item.State === ContainerStatus.Paused
);
const hasStoppedItemsSelected = selectedItems.some((item) =>
['stopped', 'created'].includes(item.Status)
[
ContainerStatus.Stopped,
ContainerStatus.Created,
ContainerStatus.Exited,
].includes(item.Status)
);
const hasRunningItemsSelected = selectedItems.some((item) =>
['running', 'healthy', 'unhealthy', 'starting'].includes(item.Status)
[
ContainerStatus.Running,
ContainerStatus.Healthy,
ContainerStatus.Unhealthy,
ContainerStatus.Starting,
].includes(item.Status)
);
const isAuthorized = useAuthorizations([
@ -95,7 +105,7 @@ export function ContainersDatatableActions({
<Button
color="light"
onClick={() => onKillClick(selectedItems)}
disabled={selectedItemCount === 0}
disabled={selectedItemCount === 0 || hasStoppedItemsSelected}
>
<i className="fa fa-bomb space-right" aria-hidden="true" />
Kill
@ -228,7 +238,7 @@ export function ContainersDatatableActions({
function onRemoveClick(selectedItems: DockerContainer[]) {
const isOneContainerRunning = selectedItems.some(
(container) => container.Status === 'running'
(container) => container.State === 'running'
);
const runningTitle = isOneContainerRunning ? 'running' : '';

View file

@ -1,37 +0,0 @@
import { EnvironmentProvider } from '@/portainer/environments/useEnvironment';
import type { Environment } from '@/portainer/environments/types';
import { TableSettingsProvider } from '@@/datatables/useTableSettings';
import {
ContainersDatatable,
ContainerTableProps,
} from './ContainersDatatable';
interface Props extends ContainerTableProps {
endpoint: Environment;
}
export function ContainersDatatableContainer({
endpoint,
tableKey = 'containers',
...props
}: Props) {
const defaultSettings = {
autoRefreshRate: 0,
truncateContainerName: 32,
hiddenQuickActions: [],
hiddenColumns: [],
pageSize: 10,
sortBy: { id: 'state', desc: false },
};
return (
<EnvironmentProvider environment={endpoint}>
<TableSettingsProvider defaults={defaultSettings} storageKey={tableKey}>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<ContainersDatatable {...props} />
</TableSettingsProvider>
</EnvironmentProvider>
);
}

View file

@ -1,17 +1,18 @@
import type { ContainersTableSettings } from '@/react/docker/containers/types';
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
import { useTableSettings } from '@@/datatables/useTableSettings';
import { Checkbox } from '@@/form-components/Checkbox';
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
import { TableSettings } from './types';
import { TRUNCATE_LENGTH } from './datatable-store';
interface Props {
isRefreshVisible: boolean;
isRefreshVisible?: boolean;
settings: TableSettings;
}
export function ContainersDatatableSettings({ isRefreshVisible }: Props) {
const { settings, setTableSettings } =
useTableSettings<ContainersTableSettings>();
export function ContainersDatatableSettings({
isRefreshVisible,
settings,
}: Props) {
return (
<>
<Checkbox
@ -19,23 +20,18 @@ export function ContainersDatatableSettings({ isRefreshVisible }: Props) {
label="Truncate container name"
checked={settings.truncateContainerName > 0}
onChange={() =>
setTableSettings((settings) => ({
...settings,
truncateContainerName: settings.truncateContainerName > 0 ? 0 : 32,
}))
settings.setTruncateContainerName(
settings.truncateContainerName > 0 ? 0 : TRUNCATE_LENGTH
)
}
/>
{isRefreshVisible && (
<TableSettingsMenuAutoRefresh
value={settings.autoRefreshRate}
onChange={handleRefreshRateChange}
onChange={(value) => settings.setAutoRefreshRate(value)}
/>
)}
</>
);
function handleRefreshRateChange(autoRefreshRate: number) {
setTableSettings({ autoRefreshRate });
}
}

View file

@ -0,0 +1,11 @@
import { Environment } from '@/portainer/environments/types';
import { createRowContext } from '@@/datatables/RowContext';
interface RowContextState {
environment: Environment;
}
const { RowProvider, useRowContext } = createRowContext<RowContextState>();
export { RowProvider, useRowContext };

View file

@ -1,12 +1,28 @@
import { Column } from 'react-table';
import { CellProps, Column } from 'react-table';
import type { DockerContainer } from '@/react/docker/containers/types';
import { useEnvironmentId } from '@/portainer/hooks/useEnvironmentId';
import { useContainerGpus } from '@/react/docker/containers/queries/gpus';
export const gpus: Column<DockerContainer> = {
Header: 'GPUs',
accessor: 'Gpus',
id: 'gpus',
disableFilters: true,
canHide: true,
Filter: () => null,
Cell: GpusCell,
};
function GpusCell({
row: { original: container },
}: CellProps<DockerContainer>) {
const containerId = container.Id;
const environmentId = useEnvironmentId();
const gpusQuery = useContainerGpus(environmentId, containerId);
if (!gpusQuery.data) {
return null;
}
return <>{gpusQuery.data}</>;
}

View file

@ -1,9 +1,9 @@
import { Column } from 'react-table';
import { useSref } from '@uirouter/react';
import { useEnvironment } from '@/portainer/environments/useEnvironment';
import { EnvironmentStatus } from '@/portainer/environments/types';
import type { DockerContainer } from '@/react/docker/containers/types';
import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper';
import { useCurrentEnvironment } from '@/portainer/hooks/useCurrentEnvironment';
export const image: Column<DockerContainer> = {
Header: 'Image',
@ -21,14 +21,15 @@ interface Props {
}
function ImageCell({ value: imageName }: Props) {
const endpoint = useEnvironment();
const offlineMode = endpoint.Status !== EnvironmentStatus.Up;
const linkProps = useSref('docker.images.image', { id: imageName });
const shortImageName = trimSHASum(imageName);
const linkProps = useSref('docker.images.image', { id: imageName });
if (offlineMode) {
return shortImageName;
const environmentQuery = useCurrentEnvironment();
const environment = environmentQuery.data;
if (!environment || isOfflineEndpoint(environment)) {
return <span>{shortImageName}</span>;
}
return (

View file

@ -1,3 +1,4 @@
import _ from 'lodash';
import { useMemo } from 'react';
import { created } from './created';
@ -12,21 +13,22 @@ import { stack } from './stack';
import { state } from './state';
import { gpus } from './gpus';
export function useColumns() {
export function useColumns(isHostColumnVisible: boolean) {
return useMemo(
() => [
name,
state,
quickActions,
stack,
image,
created,
ip,
host,
gpus,
ports,
ownership,
],
[]
() =>
_.compact([
name,
state,
quickActions,
stack,
image,
created,
ip,
isHostColumnVisible && host,
gpus,
ports,
ownership,
]),
[isHostColumnVisible]
);
}

View file

@ -1,14 +1,14 @@
import { CellProps, Column, TableInstance } from 'react-table';
import { CellProps, Column } from 'react-table';
import _ from 'lodash';
import { useSref } from '@uirouter/react';
import { useEnvironment } from '@/portainer/environments/useEnvironment';
import type {
ContainersTableSettings,
DockerContainer,
} from '@/react/docker/containers/types';
import type { DockerContainer } from '@/react/docker/containers/types';
import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper';
import { useCurrentEnvironment } from '@/portainer/hooks/useCurrentEnvironment';
import { useTableSettings } from '@@/datatables/useTableSettings';
import { useTableSettings } from '@@/datatables/useZustandTableSettings';
import { TableSettings } from '../types';
export const name: Column<DockerContainer> = {
Header: 'Name',
@ -27,23 +27,24 @@ export const name: Column<DockerContainer> = {
export function NameCell({
value: name,
row: { original: container },
}: CellProps<TableInstance>) {
const { settings } = useTableSettings<ContainersTableSettings>();
const truncate = settings.truncateContainerName;
const endpoint = useEnvironment();
const offlineMode = endpoint.Status !== 1;
const linkProps = useSref('docker.containers.container', {
}: CellProps<DockerContainer>) {
const linkProps = useSref('.container', {
id: container.Id,
nodeName: container.NodeName,
});
const { settings } = useTableSettings<TableSettings>();
const truncate = settings.truncateContainerName;
const environmentQuery = useCurrentEnvironment();
const environment = environmentQuery.data;
let shortName = name;
if (truncate > 0) {
shortName = _.truncate(name, { length: truncate });
}
if (offlineMode) {
if (!environment || isOfflineEndpoint(environment)) {
return <span>{shortName}</span>;
}

View file

@ -1,8 +1,8 @@
import { Column } from 'react-table';
import _ from 'lodash';
import { useEnvironment } from '@/portainer/environments/useEnvironment';
import type { DockerContainer, Port } from '@/react/docker/containers/types';
import { useCurrentEnvironment } from '@/portainer/hooks/useCurrentEnvironment';
export const ports: Column<DockerContainer> = {
Header: 'Published Ports',
@ -20,12 +20,15 @@ interface Props {
}
function PortsCell({ value: ports }: Props) {
const { PublicURL: publicUrl } = useEnvironment();
const environmentQuery = useCurrentEnvironment();
if (ports.length === 0) {
const environment = environmentQuery.data;
if (!environment || ports.length === 0) {
return '-';
}
const { PublicURL: publicUrl } = environment;
return _.uniqBy(ports, 'public').map((port) => (
<a
key={`${port.host}:${port.public}`}

View file

@ -1,15 +1,14 @@
import { CellProps, Column } from 'react-table';
import { useEnvironment } from '@/portainer/environments/useEnvironment';
import { useAuthorizations } from '@/portainer/hooks/useUser';
import { ContainerQuickActions } from '@/react/docker/containers/components/ContainerQuickActions/ContainerQuickActions';
import type {
ContainersTableSettings,
DockerContainer,
} from '@/react/docker/containers/types';
import { EnvironmentStatus } from '@/portainer/environments/types';
import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper';
import { useCurrentEnvironment } from '@/portainer/hooks/useCurrentEnvironment';
import { ContainerQuickActions } from '@/react/docker/containers/components/ContainerQuickActions';
import { DockerContainer } from '@/react/docker/containers/types';
import { useTableSettings } from '@@/datatables/useTableSettings';
import { useTableSettings } from '@@/datatables/useZustandTableSettings';
import { TableSettings } from '../types';
export const quickActions: Column<DockerContainer> = {
Header: 'Quick Actions',
@ -25,10 +24,12 @@ export const quickActions: Column<DockerContainer> = {
function QuickActionsCell({
row: { original: container },
}: CellProps<DockerContainer>) {
const endpoint = useEnvironment();
const offlineMode = endpoint.Status !== EnvironmentStatus.Up;
const environmentQuery = useCurrentEnvironment();
const { settings } = useTableSettings<ContainersTableSettings>();
const environment = environmentQuery.data;
const offlineMode = !environment || isOfflineEndpoint(environment);
const { settings } = useTableSettings<TableSettings>();
const { hiddenQuickActions = [] } = settings;

View file

@ -1,10 +1,9 @@
import { Column } from 'react-table';
import { CellProps, Column } from 'react-table';
import clsx from 'clsx';
import _ from 'lodash';
import type {
DockerContainer,
DockerContainerStatus,
import {
type DockerContainer,
ContainerStatus,
} from '@/react/docker/containers/types';
import { DefaultFilter } from '@@/datatables/Filter';
@ -20,11 +19,14 @@ export const state: Column<DockerContainer> = {
canHide: true,
};
function StatusCell({ value: status }: { value: DockerContainerStatus }) {
const statusNormalized = _.toLower(status);
const hasHealthCheck = ['starting', 'healthy', 'unhealthy'].includes(
statusNormalized
);
function StatusCell({
value: status,
}: CellProps<DockerContainer, ContainerStatus>) {
const hasHealthCheck = [
ContainerStatus.Starting,
ContainerStatus.Healthy,
ContainerStatus.Unhealthy,
].includes(status);
const statusClassName = getClassName();
@ -40,22 +42,21 @@ function StatusCell({ value: status }: { value: DockerContainerStatus }) {
);
function getClassName() {
if (includeString(['paused', 'starting', 'unhealthy'])) {
return 'warning';
}
if (includeString(['created'])) {
return 'info';
}
if (includeString(['stopped', 'dead', 'exited'])) {
return 'danger';
}
return 'success';
function includeString(values: DockerContainerStatus[]) {
return values.some((val) => statusNormalized.includes(val));
switch (status) {
case ContainerStatus.Paused:
case ContainerStatus.Starting:
case ContainerStatus.Unhealthy:
return 'warning';
case ContainerStatus.Created:
return 'info';
case ContainerStatus.Stopped:
case ContainerStatus.Dead:
case ContainerStatus.Exited:
return 'danger';
case ContainerStatus.Healthy:
case ContainerStatus.Running:
default:
return 'success';
}
}
}

View file

@ -0,0 +1,40 @@
import create from 'zustand';
import { persist } from 'zustand/middleware';
import { keyBuilder } from '@/portainer/hooks/useLocalStorage';
import {
paginationSettings,
sortableSettings,
refreshableSettings,
hiddenColumnsSettings,
} from '@/react/components/datatables/types';
import { QuickAction, TableSettings } from './types';
export const TRUNCATE_LENGTH = 32;
export function createStore(storageKey: string) {
return create<TableSettings>()(
persist(
(set) => ({
...sortableSettings(set),
...paginationSettings(set),
...hiddenColumnsSettings(set),
...refreshableSettings(set),
truncateContainerName: TRUNCATE_LENGTH,
setTruncateContainerName(truncateContainerName: number) {
set({
truncateContainerName,
});
},
hiddenQuickActions: [] as QuickAction[],
setHiddenQuickActions: (hiddenQuickActions: QuickAction[]) =>
set({ hiddenQuickActions }),
}),
{
name: keyBuilder(storageKey),
}
)
);
}

View file

@ -0,0 +1 @@
export { ContainersDatatable } from './ContainersDatatable';

View file

@ -0,0 +1,23 @@
import {
PaginationTableSettings,
RefreshableTableSettings,
SettableColumnsTableSettings,
SortableTableSettings,
} from '@/react/components/datatables/types';
export type QuickAction = 'attach' | 'exec' | 'inspect' | 'logs' | 'stats';
export interface SettableQuickActionsTableSettings<TAction> {
hiddenQuickActions: TAction[];
setHiddenQuickActions: (hiddenQuickActions: TAction[]) => void;
}
export interface TableSettings
extends SortableTableSettings,
PaginationTableSettings,
SettableColumnsTableSettings,
SettableQuickActionsTableSettings<QuickAction>,
RefreshableTableSettings {
truncateContainerName: number;
setTruncateContainerName: (value: number) => void;
}