fix(ui/datatables): make empty table label consistent [EE-6499] (#11612)

pull/11905/head
Chaim Lev-Ari 2024-06-02 12:29:20 +03:00 committed by GitHub
parent 02fbdfec36
commit b7cde35c3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 45 additions and 93 deletions

View File

@ -60,7 +60,6 @@ const ngModule = angular
.component(
'edgeGroupAssociationTable',
r2a(withReactQuery(EdgeGroupAssociationTable), [
'emptyContentLabel',
'onClickRow',
'query',
'title',

View File

@ -84,7 +84,6 @@ export const ngModule = angular
'initialIngressControllers',
'allowNoneIngressClass',
'isLoading',
'noIngressControllerLabel',
'view',
])
)

View File

@ -17,8 +17,9 @@
</por-select>
</div>
</div>
<effective-access-viewer-datatable dataset="$ctrl.userRoles"> </effective-access-viewer-datatable>
<div ng-if="$ctrl.selectedUserId">
<effective-access-viewer-datatable ng-if="$ctrl.selectedUserId" dataset="$ctrl.userRoles"> </effective-access-viewer-datatable>
</div>
</form>
</rd-widget-body>
</rd-widget>

View File

@ -236,7 +236,6 @@ export const ngModule = angular
.component(
'groupAssociationTable',
r2a(withReactQuery(GroupAssociationTable), [
'emptyContentLabel',
'onClickRow',
'query',
'title',

View File

@ -30,7 +30,6 @@ export function ContainersDatatable({ dataset, onRemoveClick }: Props) {
title="Containers"
titleIcon={Box}
getRowId={(container) => container.id}
emptyContentLabel="No container available."
data-cy="containers-datatable"
renderTableActions={(selectedRows) => (
<div className="flex gap-2">

View File

@ -87,7 +87,6 @@ export function StackContainersDatatable({ environment, stackName }: Props) {
)}
dataset={containersQuery.data || []}
isLoading={!containersQuery.data}
emptyContentLabel="No containers found"
/>
</TableSettingsProvider>
</RowProvider>

View File

@ -13,7 +13,7 @@ interface Props<T extends DefaultType = DefaultType> {
export function TableContent<T extends DefaultType = DefaultType>({
isLoading = false,
rows,
emptyContent = 'No items available',
emptyContent = 'No items available.',
renderRow,
}: Props<T>) {
if (isLoading) {

View File

@ -40,7 +40,6 @@ export function ConfigsDatatable({ dataset, onRefresh, onRemoveClick }: Props) {
settingsManager={tableState}
title="Configs"
titleIcon={Clipboard}
emptyContentLabel="No config available."
renderTableSettings={() => (
<TableSettingsMenu>
<TableSettingsMenuAutoRefresh

View File

@ -1,30 +1,30 @@
import { Box } from 'lucide-react';
import { Environment } from '@/react/portainer/environments/types';
import type { DockerContainer } from '@/react/docker/containers/types';
import { useShowGPUsColumn } from '@/react/docker/containers/utils';
import { Environment } from '@/react/portainer/environments/types';
import { Table, Datatable } from '@@/datatables';
import {
buildAction,
QuickActionsSettings,
} from '@@/datatables/QuickActionsSettings';
import { Datatable, Table } from '@@/datatables';
import {
ColumnVisibilityMenu,
getColumnVisibilityState,
} from '@@/datatables/ColumnVisibilityMenu';
import { TableSettingsProvider } from '@@/datatables/useTableSettings';
import { useTableState } from '@@/datatables/useTableState';
import {
QuickActionsSettings,
buildAction,
} from '@@/datatables/QuickActionsSettings';
import { mergeOptions } from '@@/datatables/extend-options/mergeOptions';
import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters';
import { TableSettingsProvider } from '@@/datatables/useTableSettings';
import { useTableState } from '@@/datatables/useTableState';
import { useContainers } from '../../queries/containers';
import { createStore } from './datatable-store';
import { ContainersDatatableSettings } from './ContainersDatatableSettings';
import { useColumns } from './columns';
import { ContainersDatatableActions } from './ContainersDatatableActions';
import { ContainersDatatableSettings } from './ContainersDatatableSettings';
import { RowProvider } from './RowContext';
import { useColumns } from './columns';
import { createStore } from './datatable-store';
const storageKey = 'containers';
const settingsStore = createStore(storageKey);
@ -93,7 +93,6 @@ export function ContainersDatatable({
</>
)}
dataset={containersQuery.data || []}
emptyContentLabel="No containers found"
extendTableOptions={mergeOptions(
withColumnFilters(
tableState.columnFilters,

View File

@ -52,7 +52,6 @@ export function ProcessesDatatable({
settingsManager={tableState}
disableSelect
isLoading={!dataset}
emptyContentLabel="No processes found."
data-cy="docker-container-stats-processes-datatable"
/>
);

View File

@ -48,7 +48,6 @@ export function EventsDatatable({ dataset }: { dataset: Array<DockerEvent> }) {
title="Events"
titleIcon={Clock}
disableSelect
emptyContentLabel="No event available."
data-cy="docker-events-datatable"
/>
);

View File

@ -98,7 +98,6 @@ export function ImagesDatatable({
isLoading={imagesQuery.isLoading}
settingsManager={tableState}
columns={columns}
emptyContentLabel="No images found"
renderTableSettings={() => (
<TableSettingsMenu>
<TableSettingsMenuAutoRefresh

View File

@ -37,7 +37,6 @@ export function MacvlanNodesSelector({
columns={columns}
dataset={dataset || []}
isLoading={!dataset}
emptyContentLabel="No node available"
settingsManager={tableState}
data-cy="macvlan-nodes-selector-datatable"
extendTableOptions={mergeOptions(

View File

@ -74,7 +74,6 @@ export function NetworksDatatable({ dataset, onRemove, onRefresh }: Props) {
)}
</>
)}
emptyContentLabel="No networks available."
renderTableActions={(selectedRows) => (
<div className="flex gap-3">
<Authorized

View File

@ -68,7 +68,6 @@ export function SecretsDatatable({
isLoading={!dataset}
disableSelect={!hasWriteAccessQuery.authorized}
settingsManager={tableState}
emptyContentLabel="No secret available."
data-cy="docker-secrets-datatable"
renderTableActions={(selectedItems) =>
hasWriteAccessQuery.authorized && (

View File

@ -1,15 +1,15 @@
import { List } from 'lucide-react';
import { Datatable } from '@@/datatables';
import {
BasicTableSettings,
type FilteredColumnsTableSettings,
filteredColumnsSettings,
} from '@@/datatables/types';
import { useTableStateWithStorage } from '@@/datatables/useTableState';
import { withMeta } from '@@/datatables/extend-options/withMeta';
import { mergeOptions } from '@@/datatables/extend-options/mergeOptions';
import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters';
import { withMeta } from '@@/datatables/extend-options/withMeta';
import {
BasicTableSettings,
filteredColumnsSettings,
type FilteredColumnsTableSettings,
} from '@@/datatables/types';
import { useTableStateWithStorage } from '@@/datatables/useTableState';
import { useColumns } from './columns';
import { DecoratedTask } from './types';
@ -45,7 +45,6 @@ export function TasksDatatable({
settingsManager={tableState}
columns={columns}
dataset={dataset}
emptyContentLabel="No task available."
extendTableOptions={mergeOptions(
withMeta({ table: 'tasks', serviceName }),
withColumnFilters(tableState.columnFilters, tableState.setColumnFilters)

View File

@ -23,7 +23,6 @@ export function TasksDatatable({
columns={columns}
dataset={dataset}
search={search}
emptyContentLabel="No task matching filter."
aria-label="Tasks table"
data-cy="docker-service-tasks-nested-datatable"
/>

View File

@ -51,7 +51,6 @@ export function NodesDatatable({
columns={columns}
dataset={dataset || []}
isLoading={!dataset}
emptyContentLabel="No node available"
settingsManager={tableState}
extendTableOptions={withMeta({
table: 'nodes',

View File

@ -58,7 +58,6 @@ export function VolumesDatatable({
dataset={dataset || []}
isLoading={!dataset}
settingsManager={tableState}
emptyContentLabel="No volume available."
renderTableActions={(selectedItems) => (
<TableActions selectedItems={selectedItems} onRemove={onRemove} />
)}

View File

@ -25,7 +25,6 @@ export function AssociatedEdgeEnvironmentsSelector({
<div className="w-1/2">
<EdgeGroupAssociationTable
title="Available environments"
emptyContentLabel="No environment available"
query={{
types: EdgeTypes,
excludeIds: value,
@ -41,7 +40,6 @@ export function AssociatedEdgeEnvironmentsSelector({
<div className="w-1/2">
<EdgeGroupAssociationTable
title="Associated environments"
emptyContentLabel="No associated environment'"
query={{
types: EdgeTypes,
endpointIds: value,

View File

@ -41,13 +41,11 @@ const columns = [
export function EdgeGroupAssociationTable({
title,
query,
emptyContentLabel,
onClickRow,
'data-cy': dataCy,
}: {
title: string;
query: EnvironmentsQueryParams;
emptyContentLabel: string;
onClickRow: (env: Environment) => void;
} & AutomationTestingProps) {
const tableState = useTableStateWithoutStorage('Name');
@ -97,7 +95,6 @@ export function EdgeGroupAssociationTable({
onClick={() => onClickRow(row.original)}
/>
)}
emptyContentLabel={emptyContentLabel}
data-cy={dataCy}
disableSelect
/>

View File

@ -36,7 +36,6 @@ export function Datatable() {
dataset={environments}
title="Edge Devices Waiting Room"
titleIcon={Box}
emptyContentLabel="No Edge Devices found"
renderTableActions={(selectedRows) => (
<TableActions selectedRows={selectedRows} />
)}

View File

@ -24,7 +24,6 @@ export function EdgeGroupsDatatable() {
columns={columns}
dataset={edgeGroupsQuery.data || []}
settingsManager={tableState}
emptyContentLabel="No Edge group available."
isLoading={edgeGroupsQuery.isLoading}
renderTableActions={(selectedItems) => (
<TableActions selectedItems={selectedItems} />

View File

@ -23,7 +23,6 @@ export function EdgeJobsDatatable() {
isLoading={jobsQuery.isLoading}
dataset={jobsQuery.data || []}
settingsManager={tableState}
emptyContentLabel="No Edge jobs available."
title="Edge Jobs"
titleIcon={Clock}
renderTableActions={(selectedItems) => (

View File

@ -111,7 +111,6 @@ export function EnvironmentsDatatable() {
page={page}
onPageChange={setPage}
totalCount={environmentsQuery.totalCount}
emptyContentLabel="No environment available."
disableSelect
description={
<div className="w-1/4">

View File

@ -36,7 +36,6 @@ export function EdgeStacksDatatable() {
dataset={edgeStacksQuery.data || []}
initialTableState={getColumnVisibilityState(tableState.hiddenColumns)}
settingsManager={tableState}
emptyContentLabel="No stack available."
isLoading={edgeStacksQuery.isLoading}
renderTableSettings={(tableInstance) => (
<TableSettingsMenus

View File

@ -55,7 +55,6 @@ export function ApplicationContainersDatatable() {
podsQuery.isLoading ||
useServerMetricsQuery.isLoading
}
emptyContentLabel="No containers found"
title="Application containers"
titleIcon={Server}
getRowId={(row) => row.podName} // use pod name because it's unique (name is not unique)

View File

@ -64,7 +64,6 @@ export function PlacementsDatatable({
/>
</TableSettingsMenu>
)}
emptyContentLabel="No node available."
renderSubRow={(row) => (
<SubRow node={row.original} cellCount={row.getVisibleCells().length} />
)}

View File

@ -70,7 +70,6 @@ export function ApplicationsStacksDatatable({
<SubRows stack={row.original} span={row.getVisibleCells().length} />
)}
noWidget
emptyContentLabel="No stack available."
description={
<div className="w-full">
<div className="float-right mr-2 min-w-[140px]">

View File

@ -180,8 +180,8 @@ function InnerForm({
values={values.ingressClasses}
initialValues={initialValues.ingressClasses}
isLoading={isIngressClassesLoading}
noIngressControllerLabel="No supported ingress controllers found."
view="cluster"
noIngressControllerLabel="No supported ingress controllers found."
/>
<div className="form-group">
<div className="col-sm-12">

View File

@ -53,7 +53,6 @@ export function NodesDatatable() {
kubernetesEndpointsQuery.isLoading ||
environmentQuery.isLoading
}
emptyContentLabel="No Nodes found"
title="Nodes"
titleIcon={HardDrive}
getRowId={(row) => row.metadata?.uid ?? ''}

View File

@ -35,7 +35,6 @@ export function AccessTable({
titleIcon={UserX}
dataset={dataset}
columns={columns}
emptyContentLabel="No namespace has been authorized yet."
settingsManager={tableState}
renderTableActions={(selectedItems) => (
<DeleteButton

View File

@ -23,7 +23,7 @@ interface Props {
initialValues: IngressControllerClassMap[] | undefined;
isLoading: boolean;
noIngressControllerLabel: string;
view: string;
view: 'namespace' | 'cluster';
}
export function IngressClassDatatable({
@ -44,7 +44,6 @@ export function IngressClassDatatable({
dataset={values || []}
columns={columns}
isLoading={isLoading}
emptyContentLabel={noIngressControllerLabel}
title="Ingress Controllers"
titleIcon={Route}
getRowId={(row) => `${row.Name}-${row.ClassName}-${row.Type}`}
@ -94,12 +93,19 @@ export function IngressClassDatatable({
function renderIngressClassDescription() {
return (
<div className="flex flex-col gap-3">
{!isLoading && values && values.length === 0 && (
<TextTip>{noIngressControllerLabel}</TextTip>
)}
<div className="text-muted flex w-full flex-col !text-xs">
<div className="mt-1">{description}</div>
{initialValues && values && isUnsavedChanges(initialValues, values) && (
{initialValues &&
values &&
isUnsavedChanges(initialValues, values) && (
<TextTip>Unsaved changes.</TextTip>
)}
</div>
</div>
);
}

View File

@ -27,7 +27,6 @@ interface Props {
initialIngressControllers: IngressControllerClassMap[] | undefined;
allowNoneIngressClass: boolean;
isLoading: boolean;
noIngressControllerLabel: string;
view: string;
}
@ -40,7 +39,6 @@ export function IngressClassDatatableAngular({
ingressControllers,
allowNoneIngressClass,
isLoading,
noIngressControllerLabel,
view,
}: Props) {
const tableState = useTableState(settingsStore, storageKey);
@ -99,7 +97,6 @@ export function IngressClassDatatableAngular({
dataset={ingControllerFormValues || []}
columns={columns}
isLoading={isLoading}
emptyContentLabel={noIngressControllerLabel}
title="Ingress Controllers"
titleIcon={Route}
getRowId={(row) => `${row.Name}-${row.ClassName}-${row.Type}`}

View File

@ -31,7 +31,6 @@ export function EventsDatatable({
columns={columns}
settingsManager={tableState}
isLoading={isLoading}
emptyContentLabel="No event available."
title="Events"
titleIcon={History}
getRowId={(row) => row.metadata?.uid || ''}

View File

@ -104,7 +104,6 @@ export function ConfigMapsDatatable() {
columns={columns}
settingsManager={tableState}
isLoading={configMapsQuery.isLoading || namespacesQuery.isLoading}
emptyContentLabel="No ConfigMaps found"
title="ConfigMaps"
titleIcon={FileCode}
getRowId={(row) => row.metadata?.uid ?? ''}

View File

@ -104,7 +104,6 @@ export function SecretsDatatable() {
columns={columns}
settingsManager={tableState}
isLoading={secretsQuery.isLoading || namespacesQuery.isLoading}
emptyContentLabel="No secrets found"
title="Secrets"
titleIcon={Lock}
getRowId={(row) => row.metadata?.uid ?? ''}

View File

@ -89,7 +89,6 @@ export function IngressDatatable() {
dataset={ingressesWithIsSystem}
columns={columns}
isLoading={ingressesQuery.isLoading || namespacesQuery.isLoading}
emptyContentLabel="No supported ingresses found"
title="Ingresses"
titleIcon={Route}
getRowId={(row) => row.Name + row.Type + row.Namespace}

View File

@ -83,10 +83,10 @@ export function NamespaceInnerForm({
onChange={(classes) => setFieldValue('ingressClasses', classes)}
values={values.ingressClasses}
description="Enable the ingress controllers that users can select when publishing applications in this namespace."
noIngressControllerLabel="No ingress controllers available in the cluster. Go to the cluster setup view to configure and allow the use of ingress controllers in the cluster."
view="namespace"
isLoading={ingressClassesQuery.isLoading}
initialValues={initialValues.ingressClasses}
noIngressControllerLabel="No ingress controllers available in the cluster. Go to the cluster setup view to configure and allow the use of ingress controllers in the cluster."
/>
</FormSection>
)}

View File

@ -84,7 +84,6 @@ export function ServicesDatatable() {
columns={columns}
settingsManager={tableState}
isLoading={servicesQuery.isLoading || namespacesQuery.isLoading}
emptyContentLabel="No services found"
title="Services"
titleIcon={Shuffle}
getRowId={(row) => row.UID}

View File

@ -60,7 +60,6 @@ export function AccessDatatable({
roles: rolesState,
})}
isRowSelectable={({ original: item }) => !inheritFrom || !item.Inherited}
emptyContentLabel="No authorized users or teams."
renderTableActions={(selectedItems) => (
<>
<RemoveAccessButton items={selectedItems} onClick={onRemove} />

View File

@ -70,7 +70,6 @@ export function HelmRepositoryDatatable() {
renderTableActions={(selectedRows) => (
<HelmRepositoryDatatableActions selectedItems={selectedRows} />
)}
emptyContentLabel="No Helm repository found"
isLoading={helmReposQuery.isLoading}
isRowSelectable={(row) => !row.original.Global}
data-cy="helm-repositories-datatable"

View File

@ -36,7 +36,6 @@ export function TagsDatatable({
dataset={dataset || []}
columns={columns}
isLoading={!dataset}
emptyContentLabel="No tag available."
settingsManager={tableState}
renderTableActions={(selectedItems) => (
<DeleteButton

View File

@ -25,7 +25,6 @@ export function AssociatedEnvironmentsSelector({
<div className="w-1/2">
<GroupAssociationTable
title="Available environments"
emptyContentLabel="No environment available"
query={{
groupIds: [1],
excludeIds: value,
@ -41,7 +40,6 @@ export function AssociatedEnvironmentsSelector({
<div className="w-1/2">
<GroupAssociationTable
title="Associated environments"
emptyContentLabel="No associated environment'"
query={{
endpointIds: value,
}}

View File

@ -23,13 +23,11 @@ const columns = [
export function GroupAssociationTable({
title,
query,
emptyContentLabel,
onClickRow,
'data-cy': dataCy,
}: {
title: string;
query: EnvironmentsQueryParams;
emptyContentLabel: string;
onClickRow?: (env: Environment) => void;
} & AutomationTestingProps) {
const tableState = useTableStateWithoutStorage('Name');
@ -61,7 +59,6 @@ export function GroupAssociationTable({
onClick={onClickRow ? () => onClickRow(row.original) : undefined}
/>
)}
emptyContentLabel={emptyContentLabel}
data-cy={dataCy}
disableSelect
/>

View File

@ -70,7 +70,6 @@ export function ListView() {
settingsManager={tableState}
title="Update & rollback"
titleIcon={Clock}
emptyContentLabel="No schedules found"
isLoading={listQuery.isLoading}
renderTableActions={(selectedRows) => (
<TableActions selectedRows={selectedRows} />

View File

@ -47,7 +47,6 @@ export function NotificationsView() {
titleIcon={Bell}
dataset={userNotifications}
settingsManager={tableState}
emptyContentLabel="No notifications found"
renderTableActions={(selectedRows) => (
<TableActions selectedRows={selectedRows} />
)}

View File

@ -38,7 +38,6 @@ export function GitlabProjectTable({
columns={columns}
dataset={dataset}
settingsManager={tableState}
emptyContentLabel="No projects available."
title="Gitlab projects"
titleIcon={ListIcon}
extendTableOptions={withControlledSelected(

View File

@ -35,7 +35,6 @@ export function TagsDatatable({
dataset={dataset || []}
isLoading={!dataset}
settingsManager={tableState}
emptyContentLabel="No tags available."
renderTableActions={(selectedItems) =>
advancedFeaturesAvailable && (
<DeleteButton

View File

@ -16,7 +16,6 @@ export function RepositoriesDatatable({ dataset }: { dataset?: Repository[] }) {
dataset={dataset || []}
isLoading={!dataset}
settingsManager={tableState}
emptyContentLabel="No repository available."
disableSelect
data-cy="registry-repositories-datatable"
/>

View File

@ -22,7 +22,6 @@ export function LDAPGroupsTable({ dataset }: { dataset?: Value[] }) {
title="Groups"
titleIcon={Users}
settingsManager={tableState}
emptyContentLabel="No groups found."
disableSelect
data-cy="ldap-groups-datatable"
/>

View File

@ -21,7 +21,6 @@ export function LDAPUsersTable({ dataset }: { dataset?: string[] }) {
title="Users"
titleIcon={Users}
settingsManager={tableState}
emptyContentLabel="No users found."
disableSelect
data-cy="ldap-users-datatable"
/>

View File

@ -31,7 +31,6 @@ export function FDOProfilesDatatable({
title="Device Profiles"
titleIcon={List}
disableSelect={!isFDOEnabled}
emptyContentLabel="No profiles found"
getRowId={(row) => row.id.toString()}
isLoading={isLoading}
renderTableActions={(selectedItems) => (

View File

@ -12,6 +12,14 @@ export function EffectiveAccessViewerDatatable({
}) {
const tableState = useTableStateWithStorage('access-viewer', 'Environment');
if (dataset?.length === 0) {
return (
<TextTip color="blue">
The selected user does not have access to any environments.
</TextTip>
);
}
return (
<Datatable
dataset={dataset || []}
@ -22,14 +30,9 @@ export function EffectiveAccessViewerDatatable({
description={
<TextTip color="blue">
Effective role for each environment will be displayed for the selected
user
user.
</TextTip>
}
emptyContentLabel={
dataset
? 'The selected user does not have access to any environment(s)'
: 'Select a user to show associated access and role'
}
disableSelect
data-cy="effective-access-viewer-datatable"
/>

View File

@ -34,7 +34,6 @@ export function RbacRolesDatatable({
dataset={dataset || []}
columns={columns}
isLoading={!dataset}
emptyContentLabel="No role available."
settingsManager={tableState}
disableSelect
data-cy="rbac-roles-datatable"

View File

@ -47,7 +47,6 @@ export function TeamsDatatable({ teams, isAdmin }: Props) {
/>
)
}
emptyContentLabel="No teams found"
data-cy="teams-datatable"
/>
);