feat(HMS-3686): Update position of blueprint buttons

This commit is contained in:
Anna Vítová 2024-03-04 14:57:39 +01:00 committed by Lucas Garfield
parent 54bfe6dd92
commit 5813fdaf10
7 changed files with 159 additions and 111 deletions

View file

@ -0,0 +1,57 @@
import React, { useState } from 'react';
import {
Dropdown,
DropdownItem,
DropdownList,
MenuToggle,
MenuToggleElement,
} from '@patternfly/react-core';
import { EllipsisVIcon } from '@patternfly/react-icons';
interface BlueprintActionsMenuProps {
selectedBlueprint: string | undefined;
setShowDeleteModal: React.Dispatch<React.SetStateAction<boolean>>;
}
export const BlueprintActionsMenu: React.FunctionComponent<
BlueprintActionsMenuProps
> = ({ selectedBlueprint, setShowDeleteModal }: BlueprintActionsMenuProps) => {
const [showBlueprintActionsMenu, setShowBlueprintActionsMenu] =
useState(false);
const onSelect = () => {
setShowBlueprintActionsMenu(!showBlueprintActionsMenu);
};
return (
<Dropdown
ouiaId={`blueprints-dropdown`}
isOpen={showBlueprintActionsMenu}
onSelect={onSelect}
onOpenChange={(showBlueprintActionsMenu: boolean) =>
setShowBlueprintActionsMenu(showBlueprintActionsMenu)
}
shouldFocusToggleOnSelect
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
isExpanded={showBlueprintActionsMenu}
onClick={() => setShowBlueprintActionsMenu(!showBlueprintActionsMenu)}
variant="plain"
aria-label="blueprint menu toggle"
isDisabled={selectedBlueprint === undefined}
>
<EllipsisVIcon aria-hidden="true" />
</MenuToggle>
)}
>
<DropdownList>
<DropdownItem>Edit details</DropdownItem>
<DropdownItem>Download blueprint (.json)</DropdownItem>
<DropdownItem onClick={() => setShowDeleteModal(true)}>
Delete blueprint
</DropdownItem>
</DropdownList>
</Dropdown>
);
};

View file

@ -0,0 +1,29 @@
import React from 'react';
import { Button } from '@patternfly/react-core';
import { useComposeBlueprintMutation } from '../../store/imageBuilderApi';
interface BuildImagesButtonProps {
selectedBlueprint?: string | undefined;
}
export const BuildImagesButton: React.FunctionComponent<
BuildImagesButtonProps
> = ({ selectedBlueprint }: BuildImagesButtonProps) => {
const [buildBlueprint, { isLoading: imageBuildLoading }] =
useComposeBlueprintMutation();
const onBuildHandler = async () => {
selectedBlueprint && (await buildBlueprint({ id: selectedBlueprint }));
};
return (
<Button
ouiaId="build-images-button"
onClick={onBuildHandler}
isDisabled={!selectedBlueprint}
isLoading={imageBuildLoading}
>
Build images
</Button>
);
};

View file

@ -7,22 +7,27 @@ import {
ModalVariant,
} from '@patternfly/react-core';
import { useGetBlueprintsQuery } from '../../store/imageBuilderApi';
import {
useDeleteBlueprintMutation,
useGetBlueprintsQuery,
} from '../../store/imageBuilderApi';
interface DeleteBlueprintModalProps {
onDelete: () => Promise<void>;
selectedBlueprint: string | undefined;
setSelectedBlueprint: React.Dispatch<
React.SetStateAction<string | undefined>
>;
setShowDeleteModal: React.Dispatch<React.SetStateAction<boolean>>;
isOpen: boolean;
onClose: () => void;
}
export const DeleteBlueprintModal: React.FunctionComponent<
DeleteBlueprintModalProps
> = ({
onDelete,
selectedBlueprint,
setSelectedBlueprint,
setShowDeleteModal,
isOpen,
onClose,
}: DeleteBlueprintModalProps) => {
const { blueprintName } = useGetBlueprintsQuery(
{ search: undefined },
@ -35,20 +40,33 @@ export const DeleteBlueprintModal: React.FunctionComponent<
}),
}
);
const [deleteBlueprint] = useDeleteBlueprintMutation({
fixedCacheKey: 'delete-blueprint',
});
const handleDelete = async () => {
if (selectedBlueprint) {
setShowDeleteModal(false);
await deleteBlueprint({ id: selectedBlueprint });
setSelectedBlueprint(undefined);
}
};
const onDeleteClose = () => {
setShowDeleteModal(false);
};
return (
<Modal
variant={ModalVariant.small}
titleIconVariant="warning"
isOpen={isOpen}
onClose={onClose}
onClose={onDeleteClose}
title={`Permanently delete ${blueprintName}?`}
description={'All versions will be lost.'}
>
<ActionGroup>
<Button variant="danger" type="button" onClick={onDelete}>
<Button variant="danger" type="button" onClick={handleDelete}>
Delete
</Button>
<Button variant="link" type="button" onClick={onClose}>
<Button variant="link" type="button" onClick={onDeleteClose}>
Cancel
</Button>
</ActionGroup>

View file

@ -58,12 +58,21 @@ import {
computeHoursToExpiration,
timestampToDisplayString,
} from '../../Utilities/time';
import { BlueprintActionsMenu } from '../Blueprints/BlueprintActionsMenu';
import { BuildImagesButton } from '../Blueprints/BuildImagesButton';
import { DeleteBlueprintModal } from '../Blueprints/DeleteBlueprintModal';
type ImageTableProps = {
selectedBlueprint?: string | undefined;
setSelectedBlueprint: React.Dispatch<
React.SetStateAction<string | undefined>
>;
};
const ImagesTable = ({ selectedBlueprint }: ImageTableProps) => {
const ImagesTable = ({
selectedBlueprint,
setSelectedBlueprint,
}: ImageTableProps) => {
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
const { selectedBlueprintVersion } = useGetBlueprintsQuery(
@ -85,6 +94,7 @@ const ImagesTable = ({ selectedBlueprint }: ImageTableProps) => {
setPerPage(perPage);
};
const [showDeleteModal, setShowDeleteModal] = useState(false);
const {
data: blueprintsComposes,
isSuccess: isBlueprintsSuccess,
@ -163,6 +173,12 @@ const ImagesTable = ({ selectedBlueprint }: ImageTableProps) => {
return (
<>
<>
<DeleteBlueprintModal
selectedBlueprint={selectedBlueprint}
setSelectedBlueprint={setSelectedBlueprint}
setShowDeleteModal={setShowDeleteModal}
isOpen={showDeleteModal}
/>
<Toolbar>
<ToolbarContent>
<ToolbarItem>
@ -174,6 +190,19 @@ const ImagesTable = ({ selectedBlueprint }: ImageTableProps) => {
Create image
</Link>
</ToolbarItem>
{experimentalFlag && (
<>
<ToolbarItem>
<BuildImagesButton selectedBlueprint={selectedBlueprint} />
</ToolbarItem>
<ToolbarItem>
<BlueprintActionsMenu
selectedBlueprint={selectedBlueprint}
setShowDeleteModal={setShowDeleteModal}
/>
</ToolbarItem>
</>
)}
<ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
<Pagination
itemCount={itemCount}

View file

@ -64,7 +64,7 @@ export const LandingPage = () => {
<Quickstarts />
</PageSection>
<PageSection>
<ImagesTable />
<ImagesTable setSelectedBlueprint={setSelectedBlueprint} />
</PageSection>
</>
);
@ -83,7 +83,10 @@ export const LandingPage = () => {
/>
</SidebarPanel>
<SidebarContent>
<ImagesTable selectedBlueprint={selectedBlueprint} />
<ImagesTable
selectedBlueprint={selectedBlueprint}
setSelectedBlueprint={setSelectedBlueprint}
/>
</SidebarContent>
</Sidebar>
</PageSection>
@ -96,10 +99,7 @@ export const LandingPage = () => {
return (
<>
<ImageBuilderHeader
experimentalFlag={experimentalFlag}
selectedBlueprint={selectedBlueprint}
/>
<ImageBuilderHeader experimentalFlag={experimentalFlag} />
{edgeParityFlag ? (
<Tabs
className="pf-c-tabs pf-c-page-header pf-c-table"

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import {
Button,
@ -7,74 +7,33 @@ import {
TextContent,
Flex,
FlexItem,
Dropdown,
DropdownList,
MenuToggle,
MenuToggleElement,
DropdownItem,
} from '@patternfly/react-core';
import { ExternalLinkAltIcon, HelpIcon } from '@patternfly/react-icons';
import {
ExternalLinkAltIcon,
HelpIcon,
ImportIcon,
} from '@patternfly/react-icons';
// eslint-disable-next-line rulesdir/disallow-fec-relative-imports
import {
OpenSourceBadge,
PageHeader,
PageHeaderTitle,
} from '@redhat-cloud-services/frontend-components';
import { Link, useNavigate } from 'react-router-dom';
import { Link } from 'react-router-dom';
import {
useComposeBlueprintMutation,
useDeleteBlueprintMutation,
} from '../../store/imageBuilderApi';
import { resolveRelPath } from '../../Utilities/path';
import './ImageBuilderHeader.scss';
import { DeleteBlueprintModal } from '../Blueprints/DeleteBlueprintModal';
type ImageBuilderHeaderPropTypes = {
experimentalFlag?: string | true | undefined;
selectedBlueprint?: string | undefined;
};
export const ImageBuilderHeader = ({
experimentalFlag,
selectedBlueprint,
}: ImageBuilderHeaderPropTypes) => {
const [buildBlueprint, { isLoading: imageBuildLoading }] =
useComposeBlueprintMutation();
const navigate = useNavigate();
const onBuildHandler = async () => {
selectedBlueprint && (await buildBlueprint({ id: selectedBlueprint }));
};
const [isOpen, setIsOpen] = useState<boolean>(false);
const onSelect = () => {
setIsOpen(!isOpen);
};
const [showDeleteModal, setShowDeleteModal] = React.useState(false);
const [deleteBlueprint] = useDeleteBlueprintMutation({
fixedCacheKey: 'delete-blueprint',
});
const handleDelete = async () => {
if (selectedBlueprint) {
setShowDeleteModal(false);
await deleteBlueprint({ id: selectedBlueprint });
}
};
const onDeleteClose = () => {
setShowDeleteModal(false);
};
return (
<>
{/*@ts-ignore*/}
<DeleteBlueprintModal
onDelete={handleDelete}
selectedBlueprint={selectedBlueprint}
isOpen={showDeleteModal}
onClose={onDeleteClose}
/>
<PageHeader>
<PageHeader data-testid="image-builder-header">
<Flex>
<FlexItem>
<PageHeaderTitle className="title" title="Images" />
@ -139,58 +98,17 @@ export const ImageBuilderHeader = ({
<FlexItem align={{ default: 'alignRight' }}>
<Link
to={resolveRelPath('imagewizard')}
className="pf-c-button pf-m-primary"
className="pf-c-button pf-m-tertiary"
data-testid="create-image-action"
>
Create
</Link>
</FlexItem>
<FlexItem>
<Button
ouiaId="build-images-button"
onClick={onBuildHandler}
isDisabled={!selectedBlueprint}
isLoading={imageBuildLoading}
>
Build images
<Button variant="link" icon={<ImportIcon />} iconPosition="end">
Import{' '}
</Button>
</FlexItem>
<FlexItem>
<Dropdown
ouiaId={`blueprints-dropdown`}
isOpen={isOpen}
onSelect={onSelect}
onOpenChange={(isOpen: boolean) => setIsOpen(isOpen)}
shouldFocusToggleOnSelect
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
isExpanded={isOpen}
onClick={() => setIsOpen(!isOpen)}
variant="secondary"
aria-label={`blueprint ${selectedBlueprint} menu toggle`}
isDisabled={selectedBlueprint === undefined}
>
Blueprint actions
</MenuToggle>
)}
>
<DropdownList>
<DropdownItem
onClick={() =>
navigate(
resolveRelPath(`imagewizard/${selectedBlueprint}`)
)
}
>
Edit details
</DropdownItem>
<DropdownItem onClick={() => setShowDeleteModal(true)}>
Delete blueprint
</DropdownItem>
</DropdownList>
</Dropdown>
</FlexItem>{' '}
</>
)}
</Flex>

View file

@ -241,9 +241,6 @@ describe('Images Table Toolbar', () => {
await renderWithReduxRouter('', {});
await screen.findByTestId('images-table');
// check create image button
await screen.findByTestId('create-image-action');
// check pagination renders
await screen.findByTestId('images-pagination-top');
await screen.findByTestId('images-pagination-bottom');