Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply proper styling for 'Who is my admin?' button and popover content #3389

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions frontend/src/components/WhosMyAdministrator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import { Button, Popover } from '@patternfly/react-core';
import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
import PopoverListContent from '~/components/PopoverListContent';
import { FindAdministratorOptions } from '~/pages/projects/screens/projects/const';

type Props = {
buttonLabel?: string;
headerContent?: string;
leadText?: string;
isInline?: boolean;
contentTestId?: string;
linkTestId?: string;
};

const WhosMyAdministrator: React.FC<Props> = ({
buttonLabel = "Who's my administrator?",
headerContent,
leadText,
isInline,
contentTestId,
linkTestId,
}) => (
<Popover
showClose
position="bottom"
headerContent={headerContent || 'Your administrator might be:'}
hasAutoWidth
maxWidth="370px"
bodyContent={
<PopoverListContent
data-testid={contentTestId}
leadText={leadText}
listHeading={headerContent ? 'Your administrator might be:' : undefined}
listItems={FindAdministratorOptions}
/>
}
>
<Button
isInline={isInline}
variant="link"
icon={<OutlinedQuestionCircleIcon />}
data-testid={linkTestId}
>
{buttonLabel}
</Button>
Comment on lines +39 to +46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requires an aria-label if there's no button label.

Likely always need a label prop and add a separate prop to hide the label.

</Popover>
);

export default WhosMyAdministrator;
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import {
EmptyStateBody,
EmptyStateHeader,
EmptyStateIcon,
EmptyStateFooter,
} from '@patternfly/react-core';
import { WrenchIcon } from '@patternfly/react-icons';
import MetricsPageToolbar from '~/concepts/metrics/MetricsPageToolbar';
import { DistributedWorkloadsContext } from '~/concepts/distributedWorkloads/DistributedWorkloadsContext';
import EmptyStateErrorMessage from '~/components/EmptyStateErrorMessage';
import { LoadingState } from '~/pages/distributedWorkloads/components/LoadingState';
import WhosMyAdministrator from '~/components/WhosMyAdministrator';
import {
DistributedWorkloadsTabId,
useDistributedWorkloadsTabs,
Expand Down Expand Up @@ -52,8 +54,9 @@ const GlobalDistributedWorkloadsTabs: React.FC<GlobalDistributedWorkloadsTabsPro
}

if (!clusterQueue.data || localQueues.data.length === 0) {
const nonAdmin = !clusterQueue.data;
const title = `Configure the ${!clusterQueue.data ? 'cluster queue' : 'project queue'}`;
const message = !clusterQueue.data
const message = nonAdmin
? 'Ask your cluster admin to configure the cluster queue.'
: 'Configure the queue for this project, or select a different project.';

Expand All @@ -65,6 +68,11 @@ const GlobalDistributedWorkloadsTabs: React.FC<GlobalDistributedWorkloadsTabsPro
icon={<EmptyStateIcon icon={WrenchIcon} />}
/>
<EmptyStateBody>{message}</EmptyStateBody>
{nonAdmin ? (
<EmptyStateFooter>
<WhosMyAdministrator />
</EmptyStateFooter>
) : null}
</EmptyState>
);
}
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/pages/home/projects/EmptyProjectsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@patternfly/react-core';
import { ArrowRightIcon } from '@patternfly/react-icons';
import getStartedImage from '~/images/AI_ML-illustration-Blog-thumbnail.svg';
import WhosMyAdministrator from '~/components/WhosMyAdministrator';

type EmptyProjectsCardProps = {
allowCreate: boolean;
Expand Down Expand Up @@ -53,7 +54,11 @@ const EmptyProjectsCard: React.FC<EmptyProjectsCardProps> = ({ allowCreate, onCr
Create a project
</Button>
</StackItem>
) : null}
) : (
<StackItem>
<WhosMyAdministrator isInline />
</StackItem>
)}
</Stack>
</FlexItem>
</Flex>
Expand Down
28 changes: 3 additions & 25 deletions frontend/src/pages/modelRegistry/ModelRegistryCoreLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import * as React from 'react';
import { Navigate, Outlet, useParams } from 'react-router';

import { Bullseye, Alert, Popover, List, ListItem, Button } from '@patternfly/react-core';
import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';

import { Bullseye, Alert } from '@patternfly/react-core';
import { conditionalArea, SupportedArea } from '~/concepts/areas';
import { ModelRegistryContextProvider } from '~/concepts/modelRegistry/context/ModelRegistryContext';
import ApplicationsPage from '~/pages/ApplicationsPage';
import TitleWithIcon from '~/concepts/design/TitleWithIcon';
import { ProjectObjectType, typedEmptyImage } from '~/concepts/design/utils';
import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext';
import WhosMyAdministrator from '~/components/WhosMyAdministrator';
import InvalidModelRegistry from './screens/InvalidModelRegistry';
import EmptyModelRegistryState from './screens/components/EmptyModelRegistryState';
import ModelRegistrySelectorNavigator from './screens/ModelRegistrySelectorNavigator';
Expand Down Expand Up @@ -82,27 +80,7 @@ const ModelRegistryCoreLoader: React.FC<ModelRegistryCoreLoaderProps> =
headerIcon={() => (
<img src={typedEmptyImage(ProjectObjectType.registeredModels)} alt="" />
)}
customAction={
<Popover
showClose
position="bottom"
headerContent="Your administrator might be:"
bodyContent={
<List>
<ListItem>
The person who gave you your username, or who helped you to log in for the
first time
</ListItem>
<ListItem>Someone in your IT department or help desk</ListItem>
<ListItem>A project manager or developer</ListItem>
</List>
}
>
<Button variant="link" icon={<OutlinedQuestionCircleIcon />}>
Who&apos;s my administrator?
</Button>
</Popover>
}
customAction={<WhosMyAdministrator />}
/>
),
headerContent: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import * as React from 'react';
import {
EmptyState,
EmptyStateBody,
EmptyStateFooter,
EmptyStateHeader,
EmptyStateIcon,
} from '@patternfly/react-core';
import EmptyDetailsList from '~/pages/projects/screens/detail/EmptyDetailsList';
import gearsImg from '~/images/gears.svg';
import WhosMyAdministrator from '~/components/WhosMyAdministrator';

const EmptyModelServingPlatform: React.FC = () => (
<EmptyState variant="xs">
Expand All @@ -20,13 +21,10 @@ const EmptyModelServingPlatform: React.FC = () => (
To enable model serving, an administrator must first select a model serving platform in the
cluster settings.
</EmptyStateBody>
<EmptyStateFooter>
<WhosMyAdministrator />
</EmptyStateFooter>
</EmptyState>
);

<EmptyDetailsList
title="No model serving platform selected"
description="To enable model serving, an administrator must first select a model serving platform in the cluster settings."
icon={() => <img src={gearsImg} alt="settings" />}
/>;

export default EmptyModelServingPlatform;
34 changes: 5 additions & 29 deletions frontend/src/pages/projects/screens/projects/EmptyProjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ import {
EmptyStateIcon,
EmptyStateHeader,
EmptyStateFooter,
Popover,
Button,
Icon,
} from '@patternfly/react-core';
import { useNavigate } from 'react-router-dom';
import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
import PopoverListContent from '~/components/PopoverListContent';
import projectsEmptyStateImg from '~/images/empty-state-projects-color.svg';
import { FindAdministratorOptions } from '~/pages/projects/screens/projects/const';
import WhosMyAdministrator from '~/components/WhosMyAdministrator';
import NewProjectButton from './NewProjectButton';

type EmptyProjectsProps = {
Expand Down Expand Up @@ -49,29 +44,10 @@ const EmptyProjects: React.FC<EmptyProjectsProps> = ({ allowCreate }) => {
/>
</>
) : (
<Popover
minWidth="400px"
headerContent="Your administrator might be:"
bodyContent={
<PopoverListContent
data-testid="projects-empty-admin-help-content"
listItems={FindAdministratorOptions}
/>
}
>
<Button
data-testid="projects-empty-admin-help"
isInline
variant="link"
icon={
<Icon isInline aria-label="More info">
<OutlinedQuestionCircleIcon />
</Icon>
}
>
{`Who's my administrator?`}
</Button>
</Popover>
<WhosMyAdministrator
contentTestId="projects-empty-admin-help-content"
linkTestId="projects-empty-admin-help"
/>
)}
</EmptyStateFooter>
</EmptyState>
Expand Down
36 changes: 7 additions & 29 deletions frontend/src/pages/projects/screens/projects/ProjectsToolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import * as React from 'react';
import {
Button,
Icon,
Popover,
SearchInput,
ToolbarGroup,
ToolbarItem,
} from '@patternfly/react-core';
import { SearchInput, ToolbarGroup, ToolbarItem } from '@patternfly/react-core';
import { useNavigate } from 'react-router-dom';
import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
import PopoverListContent from '~/components/PopoverListContent';
import FilterToolbar from '~/components/FilterToolbar';
import {
FindAdministratorOptions,
ProjectsFilterDataType,
projectsFilterOptions,
ProjectsFilterOptions,
} from '~/pages/projects/screens/projects/const';
import WhosMyAdministrator from '~/components/WhosMyAdministrator';
import NewProjectButton from './NewProjectButton';

type ProjectsToolbarProps = {
Expand Down Expand Up @@ -64,25 +55,12 @@ const ProjectsToolbar: React.FC<ProjectsToolbarProps> = ({
onProjectCreated={(projectName) => navigate(`/projects/${projectName}`)}
/>
) : (
<Popover
minWidth="400px"
<WhosMyAdministrator
buttonLabel="Need another project?"
headerContent="Need another project?"
bodyContent={
<PopoverListContent
data-testid="projects-admin-help-content"
leadText="To request a new project, contact your administrator."
listHeading="Your administrator might be:"
listItems={FindAdministratorOptions}
/>
}
>
<Button data-testid="projects-empty-admin-help" variant="link">
<Icon isInline aria-label="More info">
<OutlinedQuestionCircleIcon />
</Icon>
<span className="pf-v5-u-ml-xs">Need another project?</span>
</Button>
</Popover>
leadText="To request a new project, contact your administrator."
contentTestId="projects-admin-help-content"
/>
)}
</ToolbarItem>
</ToolbarGroup>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/pages/projects/screens/projects/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const initialProjectsFilterData: ProjectsFilterDataType = {
};

export const FindAdministratorOptions = [
'The person who gave you your username',
'Someone in your IT department or Help desk (at a company or school)',
'The person who manages your email service or web site (in a small business or club)',
'The person who gave you your username, or who helped you to log in for the first time',
'Someone in your IT department or help desk',
'A project manager or developer',
];
7 changes: 5 additions & 2 deletions frontend/src/pages/storageClasses/StorageClassesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';

import {
EmptyState,
EmptyStateVariant,
Expand All @@ -8,11 +7,12 @@ import {
Title,
Alert,
AlertActionCloseButton,
EmptyStateFooter,
} from '@patternfly/react-core';

import { ProjectObjectType, typedEmptyImage } from '~/concepts/design/utils';
import ApplicationsPage from '~/pages/ApplicationsPage';
import useStorageClasses from '~/concepts/k8s/useStorageClasses';
import WhosMyAdministrator from '~/components/WhosMyAdministrator';
import { StorageClassesTable } from './StorageClassesTable';
import { StorageClassContextProvider, useStorageClassContext } from './StorageClassesContext';

Expand Down Expand Up @@ -55,6 +55,9 @@ const StorageClassesPageInternal: React.FC<StorageClassesPageInternalProps> = ({
At least one OpenShift storage class is required to use OpenShift AI. Configure a
storage class in OpenShift, or request that your admin configure one.
</EmptyStateBody>
<EmptyStateFooter>
<WhosMyAdministrator />
</EmptyStateFooter>
</EmptyState>
</PageSection>
}
Expand Down