From 49e95d21e6f90c560886f8c236917dcf79892064 Mon Sep 17 00:00:00 2001 From: manaswinidas Date: Fri, 12 Apr 2024 12:52:02 +0530 Subject: [PATCH] Empty state registered models table --- frontend/src/__mocks__/mockModelRegistries.ts | 213 ++++++++++++++++++ .../src/__mocks__/mockRegisteredModelsList.ts | 6 +- .../src/__mocks__/mockSingleModelRegistry.ts | 143 ++++++++++++ .../__mocks__/mockSingleModelRegistryRoute.ts | 90 ++++++++ .../e2e/modelRegistry/ModelRegistry.cy.ts | 32 +++ .../cypress/cypress/pages/modelRegistry.ts | 15 +- .../screens/EmptyRegisteredModels.tsx | 28 +++ .../modelRegistry/screens/ModelRegistry.tsx | 20 +- .../screens/RegisteredModelListView.tsx | 137 +---------- .../screens/RegisteredModelsTableToolbar.tsx | 148 ++++++++++++ 10 files changed, 690 insertions(+), 142 deletions(-) create mode 100644 frontend/src/__mocks__/mockModelRegistries.ts create mode 100644 frontend/src/__mocks__/mockSingleModelRegistry.ts create mode 100644 frontend/src/__mocks__/mockSingleModelRegistryRoute.ts create mode 100644 frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx create mode 100644 frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx diff --git a/frontend/src/__mocks__/mockModelRegistries.ts b/frontend/src/__mocks__/mockModelRegistries.ts new file mode 100644 index 0000000000..89ff2c60ce --- /dev/null +++ b/frontend/src/__mocks__/mockModelRegistries.ts @@ -0,0 +1,213 @@ +export const mockModelRegistries = { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + items: [ + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + kind: 'ModelRegistry', + metadata: { + creationTimestamp: '2024-03-19T08:16:56Z', + finalizers: ['modelregistry.opendatahub.io/finalizer'], + generation: 1, + managedFields: [ + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:spec': { + '.': {}, + 'f:grpc': { + '.': {}, + 'f:port': {}, + }, + 'f:rest': { + '.': {}, + 'f:port': {}, + 'f:serviceRoute': {}, + }, + }, + }, + manager: 'Mozilla', + operation: 'Update', + time: '2024-03-19T08:16:56Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:finalizers': { + '.': {}, + 'v:"modelregistry.opendatahub.io/finalizer"': {}, + }, + }, + }, + manager: 'manager', + operation: 'Update', + time: '2024-03-19T08:16:56Z', + }, + ], + name: 'example', + namespace: 'shared', + resourceVersion: '39722859', + uid: 'f054dd1a-53e2-4b45-bdb1-dc1a3fcf5815', + }, + spec: { + grpc: { + port: 1111, + }, + rest: { + port: 1111, + serviceRoute: 'disabled', + }, + }, + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + kind: 'ModelRegistry', + metadata: { + annotations: { + 'kubectl.kubernetes.io/last-applied-configuration': + '{"apiVersion":"modelregistry.opendatahub.io/v1alpha1","kind":"ModelRegistry","metadata":{"annotations":{},"labels":{"app.kubernetes.io/created-by":"model-registry-operator","app.kubernetes.io/instance":"modelregistry-sample","app.kubernetes.io/managed-by":"kustomize","app.kubernetes.io/name":"modelregistry","app.kubernetes.io/part-of":"model-registry-operator"},"name":"modelregistry-sample","namespace":"shared"},"spec":{"grpc":{"port":9090},"postgres":{"database":"model-registry","host":"model-registry-db","passwordSecret":{"key":"database-password","name":"model-registry-db"},"username":"mlmduser"},"rest":{"port":8080,"serviceRoute":"disabled"}}}\n', + }, + creationTimestamp: '2024-03-14T08:01:42Z', + finalizers: ['modelregistry.opendatahub.io/finalizer'], + generation: 1, + labels: { + 'app.kubernetes.io/created-by': 'model-registry-operator', + 'app.kubernetes.io/instance': 'modelregistry-sample', + 'app.kubernetes.io/managed-by': 'kustomize', + 'app.kubernetes.io/name': 'modelregistry', + 'app.kubernetes.io/part-of': 'model-registry-operator', + }, + managedFields: [ + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:annotations': { + '.': {}, + 'f:kubectl.kubernetes.io/last-applied-configuration': {}, + }, + 'f:labels': { + '.': {}, + 'f:app.kubernetes.io/created-by': {}, + 'f:app.kubernetes.io/instance': {}, + 'f:app.kubernetes.io/managed-by': {}, + 'f:app.kubernetes.io/name': {}, + 'f:app.kubernetes.io/part-of': {}, + }, + }, + 'f:spec': { + '.': {}, + 'f:grpc': { + '.': {}, + 'f:port': {}, + }, + 'f:postgres': { + '.': {}, + 'f:database': {}, + 'f:host': {}, + 'f:passwordSecret': { + '.': {}, + 'f:key': {}, + 'f:name': {}, + }, + 'f:port': {}, + 'f:skipDBCreation': {}, + 'f:sslMode': {}, + 'f:username': {}, + }, + 'f:rest': { + '.': {}, + 'f:port': {}, + 'f:serviceRoute': {}, + }, + }, + }, + manager: 'kubectl-client-side-apply', + operation: 'Update', + time: '2024-03-14T08:01:42Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:finalizers': { + '.': {}, + 'v:"modelregistry.opendatahub.io/finalizer"': {}, + }, + }, + }, + manager: 'manager', + operation: 'Update', + time: '2024-03-14T08:11:17Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:status': { + '.': {}, + 'f:conditions': {}, + }, + }, + manager: 'manager', + operation: 'Update', + subresource: 'status', + time: '2024-03-22T09:30:02Z', + }, + ], + name: 'modelregistry-sample', + namespace: 'shared', + resourceVersion: '41871020', + uid: '6687fd4e-c417-43c1-92f4-7b6908541c83', + }, + spec: { + grpc: { + port: 9090, + }, + postgres: { + database: 'model-registry', + host: 'model-registry-db', + passwordSecret: { + key: 'database-password', + name: 'model-registry-db', + }, + port: 5432, + skipDBCreation: false, + sslMode: 'disable', + username: 'mlmduser', + }, + rest: { + port: 8080, + serviceRoute: 'disabled', + }, + }, + status: { + conditions: [ + { + lastTransitionTime: '2024-03-22T09:30:02Z', + message: 'Deployment for custom resource modelregistry-sample was successfully created', + reason: 'CreatedDeployment', + status: 'True', + type: 'Progressing', + }, + { + lastTransitionTime: '2024-03-14T08:11:26Z', + message: 'Deployment for custom resource modelregistry-sample is available', + reason: 'DeploymentAvailable', + status: 'True', + type: 'Available', + }, + ], + }, + }, + ], + kind: 'ModelRegistryList', + metadata: { + continue: '', + resourceVersion: '55673902', + }, +}; diff --git a/frontend/src/__mocks__/mockRegisteredModelsList.ts b/frontend/src/__mocks__/mockRegisteredModelsList.ts index cac4e1a1ed..e040273bce 100644 --- a/frontend/src/__mocks__/mockRegisteredModelsList.ts +++ b/frontend/src/__mocks__/mockRegisteredModelsList.ts @@ -1,9 +1,11 @@ import { RegisteredModelList } from '~/concepts/modelRegistry/types'; import { mockRegisteredModel } from './mockRegisteredModel'; -export const mockRegisteredModelList = (): RegisteredModelList => ({ +export const mockRegisteredModelList = ({ + size = 2, +}: Partial): RegisteredModelList => ({ items: [mockRegisteredModel({ name: 'test-1' }), mockRegisteredModel({ name: 'test-2' })], nextPageToken: '', pageSize: 0, - size: 4, + size, }); diff --git a/frontend/src/__mocks__/mockSingleModelRegistry.ts b/frontend/src/__mocks__/mockSingleModelRegistry.ts new file mode 100644 index 0000000000..5000948b24 --- /dev/null +++ b/frontend/src/__mocks__/mockSingleModelRegistry.ts @@ -0,0 +1,143 @@ +export const mockSingleModelRegistry = { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + kind: 'ModelRegistry', + metadata: { + annotations: { + 'kubectl.kubernetes.io/last-applied-configuration': + '{"apiVersion":"modelregistry.opendatahub.io/v1alpha1","kind":"ModelRegistry","metadata":{"annotations":{},"labels":{"app.kubernetes.io/created-by":"model-registry-operator","app.kubernetes.io/instance":"modelregistry-sample","app.kubernetes.io/managed-by":"kustomize","app.kubernetes.io/name":"modelregistry","app.kubernetes.io/part-of":"model-registry-operator"},"name":"modelregistry-sample","namespace":"shared"},"spec":{"grpc":{"port":9090},"postgres":{"database":"model-registry","host":"model-registry-db","passwordSecret":{"key":"database-password","name":"model-registry-db"},"username":"mlmduser"},"rest":{"port":8080,"serviceRoute":"disabled"}}}\n', + }, + creationTimestamp: '2024-03-14T08:01:42Z', + finalizers: ['modelregistry.opendatahub.io/finalizer'], + generation: 1, + labels: { + 'app.kubernetes.io/created-by': 'model-registry-operator', + 'app.kubernetes.io/instance': 'modelregistry-sample', + 'app.kubernetes.io/managed-by': 'kustomize', + 'app.kubernetes.io/name': 'modelregistry', + 'app.kubernetes.io/part-of': 'model-registry-operator', + }, + managedFields: [ + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:annotations': { + '.': {}, + 'f:kubectl.kubernetes.io/last-applied-configuration': {}, + }, + 'f:labels': { + '.': {}, + 'f:app.kubernetes.io/created-by': {}, + 'f:app.kubernetes.io/instance': {}, + 'f:app.kubernetes.io/managed-by': {}, + 'f:app.kubernetes.io/name': {}, + 'f:app.kubernetes.io/part-of': {}, + }, + }, + 'f:spec': { + '.': {}, + 'f:grpc': { + '.': {}, + 'f:port': {}, + }, + 'f:postgres': { + '.': {}, + 'f:database': {}, + 'f:host': {}, + 'f:passwordSecret': { + '.': {}, + 'f:key': {}, + 'f:name': {}, + }, + 'f:port': {}, + 'f:skipDBCreation': {}, + 'f:sslMode': {}, + 'f:username': {}, + }, + 'f:rest': { + '.': {}, + 'f:port': {}, + 'f:serviceRoute': {}, + }, + }, + }, + manager: 'kubectl-client-side-apply', + operation: 'Update', + time: '2024-03-14T08:01:42Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:finalizers': { + '.': {}, + 'v:"modelregistry.opendatahub.io/finalizer"': {}, + }, + }, + }, + manager: 'manager', + operation: 'Update', + time: '2024-03-14T08:11:17Z', + }, + { + apiVersion: 'modelregistry.opendatahub.io/v1alpha1', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:status': { + '.': {}, + 'f:conditions': {}, + }, + }, + manager: 'manager', + operation: 'Update', + subresource: 'status', + time: '2024-03-22T09:30:02Z', + }, + ], + name: 'modelregistry-sample', + namespace: 'shared', + resourceVersion: '41871020', + uid: '6687fd4e-c417-43c1-92f4-7b6908541c83', + }, + spec: { + grpc: { + port: 9090, + }, + postgres: { + database: 'model-registry', + host: 'model-registry-db', + passwordSecret: { + key: 'database-password', + name: 'model-registry-db', + }, + port: 5432, + skipDBCreation: false, + sslMode: 'disable', + username: 'mlmduser', + }, + rest: { + port: 8080, + serviceRoute: 'disabled', + }, + }, + status: { + conditions: [ + { + lastTransitionTime: '2024-03-22T09:30:02Z', + message: 'Deployment for custom resource modelregistry-sample was successfully created', + reason: 'CreatedDeployment', + status: 'True', + type: 'Progressing', + }, + { + lastTransitionTime: '2024-03-14T08:11:26Z', + message: 'Deployment for custom resource modelregistry-sample is available', + reason: 'DeploymentAvailable', + status: 'True', + type: 'Available', + }, + ], + }, +}; diff --git a/frontend/src/__mocks__/mockSingleModelRegistryRoute.ts b/frontend/src/__mocks__/mockSingleModelRegistryRoute.ts new file mode 100644 index 0000000000..a87f118f81 --- /dev/null +++ b/frontend/src/__mocks__/mockSingleModelRegistryRoute.ts @@ -0,0 +1,90 @@ +export const mockSingleModelRegistryRoute = { + kind: 'Route', + apiVersion: 'route.openshift.io/v1', + metadata: { + name: 'modelregistry-sample', + namespace: 'shared', + uid: '6fda5a9e-e75d-4d63-a1ac-15c172d4c833', + resourceVersion: '41961020', + creationTimestamp: '2024-03-22T11:35:18Z', + labels: { + app: 'modelregistry-sample', + component: 'model-registry', + }, + annotations: { + 'openshift.io/host.generated': 'true', + }, + managedFields: [ + { + manager: 'Mozilla', + operation: 'Update', + apiVersion: 'route.openshift.io/v1', + time: '2024-03-22T11:35:18Z', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:metadata': { + 'f:labels': { + '.': {}, + 'f:app': {}, + 'f:component': {}, + }, + }, + 'f:spec': { + 'f:port': { + '.': {}, + 'f:targetPort': {}, + }, + 'f:to': { + 'f:kind': {}, + 'f:name': {}, + 'f:weight': {}, + }, + 'f:wildcardPolicy': {}, + }, + }, + }, + { + manager: 'openshift-router', + operation: 'Update', + apiVersion: 'route.openshift.io/v1', + time: '2024-03-22T11:35:18Z', + fieldsType: 'FieldsV1', + fieldsV1: { + 'f:status': { + 'f:ingress': {}, + }, + }, + subresource: 'status', + }, + ], + }, + spec: { + host: 'modelregistry-sample-shared.apps.modelserving-ui.dev.datahub.redhat.com', + to: { + kind: 'Service', + name: 'modelregistry-sample', + weight: 100, + }, + port: { + targetPort: 'http-api', + }, + wildcardPolicy: 'None', + }, + status: { + ingress: [ + { + host: 'modelregistry-sample-shared.apps.modelserving-ui.dev.datahub.redhat.com', + routerName: 'default', + conditions: [ + { + type: 'Admitted', + status: 'True', + lastTransitionTime: '2024-03-22T11:35:18Z', + }, + ], + wildcardPolicy: 'None', + routerCanonicalHostname: 'router-default.apps.modelserving-ui.dev.datahub.redhat.com', + }, + ], + }, +}; diff --git a/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts b/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts index 74f67223f5..66d3f7b85c 100644 --- a/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/e2e/modelRegistry/ModelRegistry.cy.ts @@ -1,5 +1,9 @@ import { mockComponents } from '~/__mocks__/mockComponents'; import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig'; +import { mockModelRegistries } from '~/__mocks__/mockModelRegistries'; +import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList'; +import { mockSingleModelRegistry } from '~/__mocks__/mockSingleModelRegistry'; +import { mockSingleModelRegistryRoute } from '~/__mocks__/mockSingleModelRegistryRoute'; import { modelRegistry } from '~/__tests__/cypress/cypress/pages/modelRegistry'; type HandlersProps = { @@ -35,4 +39,32 @@ describe('Model Registry Global', () => { modelRegistry.tabEnabled(); }); + it('No registered models in the selected Model Registry', () => { + initIntercepts({ + disableModelRegistryFeature: false, + }); + + cy.intercept( + '/api/k8s/apis/modelregistry.opendatahub.io/v1alpha1/modelregistries', + mockModelRegistries, + ); + cy.intercept( + '/api/k8s/apis/modelregistry.opendatahub.io/v1alpha1/namespaces/shared/modelregistries/modelregistry-sample', + mockSingleModelRegistry, + ); + cy.intercept( + '/api/k8s/apis/route.openshift.io/v1/namespaces/shared/routes/modelregistry-sample', + mockSingleModelRegistryRoute, + ); + + cy.intercept( + { method: 'POST', pathname: '/api/proxy/api/model_registry/v1alpha2/registered_models' }, + mockRegisteredModelList({ size: 0 }), + ); + + modelRegistry.visitModelRegistry(); + modelRegistry.navigate(); + modelRegistry.tableToolbarExists(); + modelRegistry.registeredModelsEmpty(); + }); }); diff --git a/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts b/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts index 77d21c6cc2..1eed86bd9f 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts @@ -6,6 +6,11 @@ class ModelRegistry { this.waitLanding(); } + visitModelRegistry() { + cy.visitWithLogin(`/modelRegistry`); + this.wait(); + } + visit(modelRegistry?: string) { cy.visit(`/modelRegistry${modelRegistry}`); this.wait(); @@ -17,7 +22,7 @@ class ModelRegistry { } private wait() { - cy.findByTestId('app-page-title').contains('Model Registry'); + cy.findByTestId('app-page-title').should('exist'); cy.testA11y(); } @@ -30,6 +35,14 @@ class ModelRegistry { return this; } + registeredModelsEmpty() { + cy.findByTestId('no-registered-models').should('exist'); + } + + tableToolbarExists() { + cy.findByTestId('registered-models-table-toolbar').should('exist'); + } + tabEnabled() { appChrome.findNavItem('Model Registry').should('exist'); return this; diff --git a/frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx b/frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx new file mode 100644 index 0000000000..89a9c814cd --- /dev/null +++ b/frontend/src/pages/modelRegistry/screens/EmptyRegisteredModels.tsx @@ -0,0 +1,28 @@ +import { + EmptyState, + EmptyStateBody, + EmptyStateIcon, + EmptyStateVariant, + Title, +} from '@patternfly/react-core'; +import { PlusCircleIcon } from '@patternfly/react-icons'; +import * as React from 'react'; + +type EmptyRegisteredModelsType = { + preferredModelRegistry?: string; +}; +const EmptyRegisteredModels: React.FC = ({ preferredModelRegistry }) => ( + + + + No models in model registry + + + {preferredModelRegistry} has no models registered to it. Register a model to this +
+ registry or select a different one. +
+
+); + +export default EmptyRegisteredModels; diff --git a/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx b/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx index 2a8495409c..ce5196351b 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx @@ -2,21 +2,31 @@ import React from 'react'; import ApplicationsPage from '~/pages/ApplicationsPage'; import { ProjectObjectType } from '~/concepts/design/utils'; import TitleWithIcon from '~/concepts/design/TitleWithIcon'; -import ModelRegistryEmpty from '~/pages/modelRegistry/ModelRegistryEmpty'; import { ModelRegistryContext } from '~/concepts/modelRegistry/context/ModelRegistryContext'; import useRegisteredModels from '~/concepts/modelRegistry/apiHooks/useRegisteredModels'; import RegisteredModelListView from './RegisteredModelListView'; +import EmptyRegisteredModels from './EmptyRegisteredModels'; +import RegisteredModelTableToolbar from './RegisteredModelsTableToolbar'; const ModelRegistry: React.FC = () => { - const { modelRegistries } = React.useContext(ModelRegistryContext); + const { preferredModelRegistry } = React.useContext(ModelRegistryContext); const [registeredModels, loaded, loadError] = useRegisteredModels(); return ( } + empty={registeredModels.size === 0} + emptyStatePage={ + <> + + + + } title={ - + } description="View and manage your registered models." loadError={loadError} diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx index 0f629f6850..bab4eded64 100644 --- a/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModelListView.tsx @@ -1,23 +1,8 @@ import * as React from 'react'; -import { - Dropdown, - DropdownItem, - DropdownList, - MenuToggle, - MenuToggleAction, - MenuToggleElement, - SearchInput, - ToolbarFilter, - ToolbarGroup, - ToolbarItem, - ToolbarToggleGroup, -} from '@patternfly/react-core'; -import { EllipsisVIcon, FilterIcon } from '@patternfly/react-icons'; import { SearchType } from '~/concepts/dashboard/DashboardSearchField'; import { RegisteredModel } from '~/concepts/modelRegistry/types'; -import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; import RegisteredModelTable from './RegisteredModelTable'; -import ModelRegistrySelector from './ModelRegistrySelector'; +import RegisteredModelTableToolbar from './RegisteredModelsTableToolbar'; type RegisteredModelListViewProps = { registeredModels: RegisteredModel[]; @@ -26,12 +11,9 @@ type RegisteredModelListViewProps = { const RegisteredModelListView: React.FC = ({ registeredModels: unfilteredRegisteredModels, }) => { - const [searchType, setSearchType] = React.useState(SearchType.KEYWORD); + const [searchType] = React.useState(SearchType.KEYWORD); const [search, setSearch] = React.useState(''); - const [isRegisterNewVersionOpen, setIsRegisterNewVersionOpen] = React.useState(false); - const [isArchivedModelKebabOpen, setIsArchivedModelKebabOpen] = React.useState(false); - const filteredRegisteredModels = unfilteredRegisteredModels.filter((rm) => { if (!search) { return true; @@ -57,124 +39,11 @@ const RegisteredModelListView: React.FC = ({ setSearch(''); }; - const searchTypes = React.useMemo(() => [SearchType.KEYWORD], []); // TODO Add owner once RHOAIENG-5066 is completed. - - const tooltipRef = React.useRef(null); - - const toggleGroupItems = ( - - setSearch('')} - deleteChipGroup={() => setSearch('')} - categoryName="Keyword" - > - ({ - key, - label: key, - }))} - value={searchType} - onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); - }} - icon={} - /> - - - { - setSearch(searchValue); - }} - onClear={() => setSearch('')} - style={{ minWidth: '200px' }} - /> - - - ); - return ( - - - - } breakpoint="xl"> - {toggleGroupItems} - - - setIsRegisterNewVersionOpen(false)} - onOpenChange={(isOpen) => setIsRegisterNewVersionOpen(isOpen)} - toggle={(toggleRef) => ( - setIsRegisterNewVersionOpen(!isRegisterNewVersionOpen)} - isExpanded={isRegisterNewVersionOpen} - splitButtonOptions={{ - variant: 'action', - items: [ - undefined} - > - Register model - , - ], - }} - aria-label="Register model toggle" - data-testid="register-model-split-button" - /> - )} - > - - undefined} - ref={tooltipRef} - isDisabled // This feature is currently disabled but will be enabled in a future PR post-summit release. - > - Register new version - - - - - - setIsArchivedModelKebabOpen(false)} - onOpenChange={(isOpen: boolean) => setIsArchivedModelKebabOpen(isOpen)} - toggle={(tr: React.Ref) => ( - setIsArchivedModelKebabOpen(!isArchivedModelKebabOpen)} - isExpanded={isArchivedModelKebabOpen} - > - - - )} - shouldFocusToggleOnSelect - > - - View archived models - - - - - } + toolbarContent={} /> ); }; diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx new file mode 100644 index 0000000000..faeb5d3453 --- /dev/null +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModelsTableToolbar.tsx @@ -0,0 +1,148 @@ +import * as React from 'react'; +import { + Dropdown, + DropdownItem, + DropdownList, + MenuToggle, + MenuToggleAction, + MenuToggleElement, + SearchInput, + Toolbar, + ToolbarContent, + ToolbarFilter, + ToolbarGroup, + ToolbarItem, + ToolbarToggleGroup, +} from '@patternfly/react-core'; +import { EllipsisVIcon, FilterIcon } from '@patternfly/react-icons'; +import { SearchType } from '~/concepts/dashboard/DashboardSearchField'; +import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; +import ModelRegistrySelector from './ModelRegistrySelector'; + +const RegisteredModelTableToolbar: React.FC = () => { + const [searchType, setSearchType] = React.useState(SearchType.KEYWORD); + const [search, setSearch] = React.useState(''); + + const [isRegisterNewVersionOpen, setIsRegisterNewVersionOpen] = React.useState(false); + const [isArchivedModelKebabOpen, setIsArchivedModelKebabOpen] = React.useState(false); + + const searchTypes = React.useMemo(() => [SearchType.KEYWORD], []); // TODO Add owner once RHOAIENG-5066 is completed. + + const tooltipRef = React.useRef(null); + + const toggleGroupItems = ( + + setSearch('')} + deleteChipGroup={() => setSearch('')} + categoryName="Keyword" + > + ({ + key, + label: key, + }))} + value={searchType} + onChange={(newSearchType) => { + setSearchType(newSearchType as SearchType); + }} + icon={} + /> + + + { + setSearch(searchValue); + }} + onClear={() => setSearch('')} + style={{ minWidth: '200px' }} + /> + + + ); + + return ( + + + + + + } breakpoint="xl"> + {toggleGroupItems} + + + setIsRegisterNewVersionOpen(false)} + onOpenChange={(isOpen) => setIsRegisterNewVersionOpen(isOpen)} + toggle={(toggleRef) => ( + setIsRegisterNewVersionOpen(!isRegisterNewVersionOpen)} + isExpanded={isRegisterNewVersionOpen} + splitButtonOptions={{ + variant: 'action', + items: [ + undefined} + > + Register model + , + ], + }} + aria-label="Register model toggle" + data-testid="register-model-split-button" + /> + )} + > + + undefined} + ref={tooltipRef} + isDisabled // This feature is currently disabled but will be enabled in a future PR post-summit release. + > + Register new version + + + + + + setIsArchivedModelKebabOpen(false)} + onOpenChange={(isOpen: boolean) => setIsArchivedModelKebabOpen(isOpen)} + toggle={(tr: React.Ref) => ( + setIsArchivedModelKebabOpen(!isArchivedModelKebabOpen)} + isExpanded={isArchivedModelKebabOpen} + aria-label="View archived models" + > + + + )} + shouldFocusToggleOnSelect + > + + View archived models + + + + + + ); +}; + +export default RegisteredModelTableToolbar;