feat(HMS-3686): Update position of blueprint buttons
This commit is contained in:
parent
54bfe6dd92
commit
5813fdaf10
7 changed files with 159 additions and 111 deletions
57
src/Components/Blueprints/BlueprintActionsMenu.tsx
Normal file
57
src/Components/Blueprints/BlueprintActionsMenu.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
29
src/Components/Blueprints/BuildImagesButton.tsx
Normal file
29
src/Components/Blueprints/BuildImagesButton.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue