-
Notifications
You must be signed in to change notification settings - Fork 73
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
PROD-2481 Update manage datasets pages #5191
Changes from 16 commits
d48482b
968258b
76b3fb8
ffa9ab6
2c9be2c
bd86904
d15b840
a2a9675
bc6530f
d55bb93
1a9890b
21a5b57
8538347
9f014ce
6cb04f4
c0df9d4
92832e8
d081341
32b67cf
0b21ec2
6e8301e
4937471
3e16c05
99bbd6c
54fb01f
de1e130
0551240
6c08541
752ed61
0ad2e9b
fbaa7ab
77e461b
88e1f39
48aea5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,26 @@ | ||
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from "fidesui"; | ||
import { | ||
Box, | ||
Breadcrumb, | ||
BreadcrumbItem, | ||
BreadcrumbLink, | ||
BreadcrumbProps as ChakraBreadcrumbProps, | ||
HTMLChakraProps, | ||
} from "fidesui"; | ||
import { Url } from "next/dist/shared/lib/router/router"; | ||
import NextLink from "next/link"; | ||
|
||
export interface BreadcrumbsProps { | ||
export interface BreadcrumbsProps extends ChakraBreadcrumbProps { | ||
breadcrumbs: { | ||
title: string; | ||
link?: string; | ||
link?: Url; // Next.js link url. It can be a string or an URL object (accepts query params) | ||
onClick?: () => void; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't know this existed, neat. |
||
isOpaque?: boolean; | ||
icon?: React.ReactNode; | ||
}[]; | ||
fontSize?: string; | ||
fontWeight?: string; | ||
separator?: string; | ||
lastItemStyles?: HTMLChakraProps<"li">; | ||
normalItemStyles?: HTMLChakraProps<"li">; | ||
} | ||
|
||
/** | ||
|
@@ -16,25 +29,39 @@ export interface BreadcrumbsProps { | |
* @param breadcrumbs - array of breadcrumbs | ||
* @param breadcrumbs.title - title of the breadcrumb | ||
* @param breadcrumbs.link - (optional) link to the page | ||
* @param breadcrumbs.onClick - (optional) function to call when the breadcrumb is clicked | ||
* @param breadcrumbs.isOpaque - (optional) if true, the breadcrumb will be black, otherwise gray | ||
* @param breadcrumbs.icon - (optional) icon to show before the title | ||
*/ | ||
const Breadcrumbs = ({ breadcrumbs }: BreadcrumbsProps) => ( | ||
const Breadcrumbs = ({ | ||
breadcrumbs, | ||
fontSize = "2xl", | ||
fontWeight = "semibold", | ||
separator = "->", | ||
lastItemStyles = { | ||
color: "black", | ||
}, | ||
normalItemStyles = { | ||
color: "gray.500", | ||
}, | ||
...otherChakraBreadcrumbProps | ||
}: BreadcrumbsProps) => ( | ||
<Breadcrumb | ||
separator="->" | ||
fontSize="2xl" | ||
fontWeight="semibold" | ||
separator={separator} | ||
fontSize={fontSize} | ||
fontWeight={fontWeight} | ||
data-testid="breadcrumbs" | ||
{...otherChakraBreadcrumbProps} | ||
> | ||
{breadcrumbs.map((breadcumbItem, index) => { | ||
const isLast = index + 1 === breadcrumbs.length; | ||
const hasLink = !!breadcumbItem.link || !!breadcumbItem.onClick; | ||
|
||
return ( | ||
<BreadcrumbItem | ||
color={isLast || breadcumbItem.isOpaque ? "black" : "gray.500"} | ||
{...normalItemStyles} | ||
{...(isLast ? lastItemStyles : {})} | ||
key={breadcumbItem.title} | ||
> | ||
{hasLink ? ( | ||
{breadcumbItem?.icon && <Box mr={2}>{breadcumbItem.icon}</Box>} | ||
{breadcumbItem.link ? ( | ||
<BreadcrumbLink | ||
as={NextLink} | ||
href={breadcumbItem.link} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated edit drawer component with some new styling. |
||
Box, | ||
Button, | ||
CloseSolidIcon, | ||
CloseIcon, | ||
Drawer, | ||
DrawerBody, | ||
DrawerContent, | ||
|
@@ -10,10 +10,11 @@ import { | |
DrawerOverlay, | ||
IconButton, | ||
Text, | ||
TrashCanSolidIcon, | ||
} from "fidesui"; | ||
import { ReactNode } from "react"; | ||
|
||
import { TrashCanOutlineIcon } from "~/features/common/Icon/TrashCanOutlineIcon"; | ||
|
||
interface Props { | ||
header?: ReactNode; | ||
description?: string; | ||
|
@@ -30,13 +31,16 @@ export const EditDrawerHeader = ({ | |
title: string; | ||
onDelete?: () => void; | ||
}) => ( | ||
<DrawerHeader py={2} display="flex" alignItems="center"> | ||
<Text mr="2">{title}</Text> | ||
<DrawerHeader py={0} display="flex" alignItems="flex-start"> | ||
<Text mr="2" color="gray.700" fontSize="lg" lineHeight={1.8}> | ||
{title} | ||
</Text> | ||
{onDelete ? ( | ||
<IconButton | ||
variant="outline" | ||
aria-label="delete" | ||
icon={<TrashCanSolidIcon />} | ||
size="xs" | ||
icon={<TrashCanOutlineIcon fontSize="small" />} | ||
size="sm" | ||
onClick={onDelete} | ||
data-testid="delete-btn" | ||
/> | ||
|
@@ -56,7 +60,7 @@ export const EditDrawerFooter = ({ | |
formId?: string; | ||
isSaving?: boolean; | ||
} & Pick<Props, "onClose">) => ( | ||
<DrawerFooter justifyContent="flex-start"> | ||
<DrawerFooter justifyContent="space-between"> | ||
<Button onClick={onClose} mr={2} size="sm" variant="outline"> | ||
Cancel | ||
</Button> | ||
|
@@ -81,22 +85,35 @@ const EditDrawer = ({ | |
children, | ||
footer, | ||
}: Props) => ( | ||
<Drawer placement="right" isOpen={isOpen} onClose={onClose} size="lg"> | ||
<Drawer placement="right" isOpen={isOpen} onClose={onClose} size="md"> | ||
<DrawerOverlay /> | ||
<DrawerContent data-testid="edit-drawer-content" py={2}> | ||
<Box display="flex" justifyContent="flex-end" mr={2}> | ||
<Button | ||
variant="ghost" | ||
onClick={onClose} | ||
data-testid="close-drawer-btn" | ||
> | ||
<CloseSolidIcon width="17px" /> | ||
</Button> | ||
<Box | ||
display="flex" | ||
justifyContent="space-between" | ||
alignItems="top" | ||
mr={2} | ||
py={2} | ||
gap={2} | ||
> | ||
<Box flex={1} minH={8}> | ||
{header} | ||
</Box> | ||
<Box display="flex" justifyContent="flex-end" mr={2}> | ||
<IconButton | ||
aria-label="Close editor" | ||
variant="outline" | ||
onClick={onClose} | ||
data-testid="close-drawer-btn" | ||
size="sm" | ||
icon={<CloseIcon fontSize="smaller" />} | ||
/> | ||
</Box> | ||
</Box> | ||
{header} | ||
<DrawerBody> | ||
|
||
<DrawerBody pt={1}> | ||
{description ? ( | ||
<Text fontSize="sm" mb={4}> | ||
<Text fontSize="sm" mb={4} color="gray.600"> | ||
{description} | ||
</Text> | ||
) : null} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { createIcon } from "fidesui"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copied the icons from the D&D branch but I placed them in a more generic folder so we can use them for both features. |
||
|
||
export const DatabaseIcon = createIcon({ | ||
displayName: "DatabaseIcon", | ||
viewBox: "0 0 12 12", | ||
path: ( | ||
<path | ||
fill="currentColor" | ||
d="M6 12C4.32222 12 2.90278 11.7417 1.74167 11.225C0.580556 10.7083 0 10.0778 0 9.33333V2.66667C0 1.93333 0.586111 1.30556 1.75833 0.783333C2.93056 0.261111 4.34444 0 6 0C7.65556 0 9.06944 0.261111 10.2417 0.783333C11.4139 1.30556 12 1.93333 12 2.66667V9.33333C12 10.0778 11.4194 10.7083 10.2583 11.225C9.09722 11.7417 7.67778 12 6 12ZM6 4.01667C6.98889 4.01667 7.98333 3.875 8.98333 3.59167C9.98333 3.30833 10.5444 3.00556 10.6667 2.68333C10.5444 2.36111 9.98611 2.05556 8.99167 1.76667C7.99722 1.47778 7 1.33333 6 1.33333C4.98889 1.33333 3.99722 1.475 3.025 1.75833C2.05278 2.04167 1.48889 2.35 1.33333 2.68333C1.48889 3.01667 2.05278 3.32222 3.025 3.6C3.99722 3.87778 4.98889 4.01667 6 4.01667ZM6 7.33333C6.46667 7.33333 6.91667 7.31111 7.35 7.26667C7.78333 7.22222 8.19722 7.15833 8.59167 7.075C8.98611 6.99167 9.35833 6.88889 9.70833 6.76667C10.0583 6.64444 10.3778 6.50556 10.6667 6.35V4.35C10.3778 4.50556 10.0583 4.64444 9.70833 4.76667C9.35833 4.88889 8.98611 4.99167 8.59167 5.075C8.19722 5.15833 7.78333 5.22222 7.35 5.26667C6.91667 5.31111 6.46667 5.33333 6 5.33333C5.53333 5.33333 5.07778 5.31111 4.63333 5.26667C4.18889 5.22222 3.76944 5.15833 3.375 5.075C2.98056 4.99167 2.61111 4.88889 2.26667 4.76667C1.92222 4.64444 1.61111 4.50556 1.33333 4.35V6.35C1.61111 6.50556 1.92222 6.64444 2.26667 6.76667C2.61111 6.88889 2.98056 6.99167 3.375 7.075C3.76944 7.15833 4.18889 7.22222 4.63333 7.26667C5.07778 7.31111 5.53333 7.33333 6 7.33333ZM6 10.6667C6.51111 10.6667 7.03056 10.6278 7.55833 10.55C8.08611 10.4722 8.57222 10.3694 9.01667 10.2417C9.46111 10.1139 9.83333 9.96945 10.1333 9.80833C10.4333 9.64722 10.6111 9.48333 10.6667 9.31667V7.68333C10.3778 7.83889 10.0583 7.97778 9.70833 8.1C9.35833 8.22222 8.98611 8.325 8.59167 8.40833C8.19722 8.49167 7.78333 8.55556 7.35 8.6C6.91667 8.64444 6.46667 8.66667 6 8.66667C5.53333 8.66667 5.07778 8.64444 4.63333 8.6C4.18889 8.55556 3.76944 8.49167 3.375 8.40833C2.98056 8.325 2.61111 8.22222 2.26667 8.1C1.92222 7.97778 1.61111 7.83889 1.33333 7.68333V9.33333C1.38889 9.5 1.56389 9.66111 1.85833 9.81667C2.15278 9.97222 2.52222 10.1139 2.96667 10.2417C3.41111 10.3694 3.9 10.4722 4.43333 10.55C4.96667 10.6278 5.48889 10.6667 6 10.6667Z" | ||
/> | ||
), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { createIcon } from "fidesui"; | ||
|
||
export const DatasetIcon = createIcon({ | ||
displayName: "DatasetIcon", | ||
viewBox: "0 0 16 16", | ||
path: ( | ||
<path | ||
fill="currentColor" | ||
d="M2 14V2H14V14H2ZM3.33333 12.6667H12.6667V3.33333H3.33333V12.6667ZM4.66667 7.33333H7.33333V4.66667H4.66667V7.33333ZM8.66667 7.33333H11.3333V4.66667H8.66667V7.33333ZM4.66667 11.3333H7.33333V8.66667H4.66667V11.3333ZM8.66667 11.3333H11.3333V8.66667H8.66667V11.3333Z" | ||
/> | ||
), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { createIcon } from "fidesui"; | ||
|
||
export const FieldIcon = createIcon({ | ||
displayName: "FieldIcon", | ||
viewBox: "0 0 16 16", | ||
path: ( | ||
<path | ||
d="M12.6667 12.6667V10.6667H3.33333V12.6667H12.6667ZM12.6667 9.33333V6.66667H3.33333V9.33333H12.6667ZM12.6667 5.33333V3.33333H3.33333V5.33333H12.6667ZM3.33333 14C2.96667 14 2.65278 13.8694 2.39167 13.6083C2.13056 13.3472 2 13.0333 2 12.6667V3.33333C2 2.96667 2.13056 2.65278 2.39167 2.39167C2.65278 2.13056 2.96667 2 3.33333 2H12.6667C13.0333 2 13.3472 2.13056 13.6083 2.39167C13.8694 2.65278 14 2.96667 14 3.33333V12.6667C14 13.0333 13.8694 13.3472 13.6083 13.6083C13.3472 13.8694 13.0333 14 12.6667 14H3.33333Z" | ||
fill="currentColor" | ||
/> | ||
), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { createIcon } from "fidesui"; | ||
|
||
export const TableIcon = createIcon({ | ||
displayName: "TableIcon", | ||
viewBox: "0 0 16 16", | ||
path: ( | ||
<path | ||
fill="currentColor" | ||
d="M2 14V2H14V14H2ZM3.33333 6H12.6667V3.33333H3.33333V6ZM6.88333 9.33333H9.11667V7.33333H6.88333V9.33333ZM6.88333 12.6667H9.11667V10.6667H6.88333V12.6667ZM3.33333 9.33333H5.55V7.33333H3.33333V9.33333ZM10.45 9.33333H12.6667V7.33333H10.45V9.33333ZM3.33333 12.6667H5.55V10.6667H3.33333V12.6667ZM10.45 12.6667H12.6667V10.6667H10.45V12.6667Z" | ||
/> | ||
), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { Box, BoxProps } from "fidesui"; | ||
import { Box, BoxProps, Flex } from "fidesui"; | ||
import { isArray } from "lodash"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added rightContent prop to PageHeader. Useful in new designs for buttons on the right of the page header. |
||
import { isValidElement, ReactElement } from "react"; | ||
|
||
|
@@ -7,6 +7,7 @@ import Breadcrumbs, { BreadcrumbsProps } from "~/features/common/Breadcrumbs"; | |
interface PageHeaderProps extends BoxProps { | ||
breadcrumbs: BreadcrumbsProps["breadcrumbs"] | ReactElement | false; | ||
isSticky?: boolean; | ||
rightContent?: ReactElement; | ||
} | ||
|
||
/** | ||
|
@@ -16,12 +17,14 @@ interface PageHeaderProps extends BoxProps { | |
* Can be an array of breadcrumb items (more information on Breadcrumbs component), a React element | ||
* if you want to render something else, or false to not show any breadcrumbs. | ||
* @param isSticky - Whether the page header should stick to the top of the page while scrolling. Defaults to true. | ||
* @param children - Additional content to display in the header. | ||
* @param children - Additional content to display in the header at the bottom. | ||
* @param rightContent - Additional content to display in the header on the right side. Usually for displaying buttons. | ||
*/ | ||
const PageHeader = ({ | ||
breadcrumbs, | ||
isSticky = true, | ||
children, | ||
rightContent, | ||
...otherProps | ||
}: PageHeaderProps): JSX.Element => ( | ||
<Box | ||
|
@@ -30,14 +33,19 @@ const PageHeader = ({ | |
{...(isSticky ? { position: "sticky", top: 0, left: 0, zIndex: 10 } : {})} | ||
{...otherProps} | ||
> | ||
{/* If breadcrumbs is an array, render the Breadcrumbs component. */} | ||
{isArray(breadcrumbs) && ( | ||
<Box marginBottom={children ? 4 : 0}> | ||
<Breadcrumbs breadcrumbs={breadcrumbs} /> | ||
<Flex alignItems="flex-start"> | ||
<Box flex={1}> | ||
{/* If breadcrumbs is an array, render the Breadcrumbs component. */} | ||
{isArray(breadcrumbs) && ( | ||
<Box marginBottom={children ? 4 : 0}> | ||
<Breadcrumbs breadcrumbs={breadcrumbs} /> | ||
</Box> | ||
)} | ||
{/* If breadcrumbs is a React element, render it. */} | ||
{isValidElement(breadcrumbs) && breadcrumbs} | ||
</Box> | ||
)} | ||
{/* If breadcrumbs is a React element, render it. */} | ||
{isValidElement(breadcrumbs) && breadcrumbs} | ||
{rightContent && <Box>{rightContent}</Box>} | ||
</Flex> | ||
|
||
{children} | ||
</Box> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made a more generic version of the TaxonomyDisplayAndEdit component so I could use it in the dataset page. The idea is that later we can re-use it for D&D or for the TaxonomyDisplayAndEdit component. |
||
Badge, | ||
Box, | ||
CloseIcon, | ||
EditIcon, | ||
IconButton, | ||
SmallAddIcon, | ||
Wrap, | ||
} from "fidesui"; | ||
import { useCallback, useState } from "react"; | ||
import TaxonomySelectDropdown from "~/features/common/dropdown/TaxonomySelectDropdown"; | ||
import { useOutsideClick } from "~/features/common/hooks"; | ||
|
||
import useTaxonomies from "./hooks/useTaxonomies"; | ||
|
||
interface TaxonomiesPickerProps { | ||
selectedTaxonomies: string[]; | ||
onAddTaxonomy: (taxonomy: string) => void; | ||
onRemoveTaxonomy: (taxonomy: string) => void; | ||
} | ||
|
||
const TaxonomiesPicker = ({ | ||
selectedTaxonomies, | ||
onAddTaxonomy, | ||
onRemoveTaxonomy, | ||
}: TaxonomiesPickerProps) => { | ||
const [isAdding, setIsAdding] = useState(false); | ||
const { getDataCategoryDisplayName } = useTaxonomies(); | ||
|
||
const handleClickOutside = useCallback(() => { | ||
setIsAdding(false); | ||
}, []); | ||
|
||
const { ref } = useOutsideClick(handleClickOutside); | ||
|
||
if (!selectedTaxonomies?.length) { | ||
return <Badge textTransform="none">None</Badge>; | ||
} | ||
|
||
return ( | ||
<Wrap | ||
py={2} | ||
alignItems="center" | ||
position="relative" | ||
width="100%" | ||
gap={2} | ||
overflowX="auto" | ||
ref={ref} | ||
> | ||
{selectedTaxonomies.map((category) => ( | ||
<Badge | ||
fontWeight="normal" | ||
textTransform="none" | ||
data-testid={`classification-${category}`} | ||
px={1.5} | ||
key={category} | ||
> | ||
{getDataCategoryDisplayName(category)} | ||
<IconButton | ||
onClick={() => onRemoveTaxonomy(category)} | ||
icon={<CloseIcon boxSize={2} />} | ||
size="2xs" | ||
mt={-0.5} | ||
ml={2} | ||
aria-label="Remove category" | ||
/> | ||
</Badge> | ||
))} | ||
<IconButton | ||
w="20px" | ||
h="20px" | ||
minW="20px" | ||
borderRadius="sm" | ||
icon={<SmallAddIcon />} | ||
onClick={() => setIsAdding(true)} | ||
data-testid="add-category-btn" | ||
aria-label="Add category" | ||
/> | ||
|
||
{isAdding && ( | ||
<Box | ||
className="select-wrapper" | ||
position="absolute" | ||
zIndex={10} | ||
top="0" | ||
left="0" | ||
width="100%" | ||
height="max" | ||
bgColor="#fff" | ||
> | ||
<TaxonomySelectDropdown | ||
onChange={(o) => { | ||
setIsAdding(false); | ||
onAddTaxonomy(o.value); | ||
}} | ||
menuIsOpen | ||
/> | ||
</Box> | ||
)} | ||
</Wrap> | ||
); | ||
}; | ||
export default TaxonomiesPicker; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the breadcrumbs component. Added support for icons, expanded Link to be able to receive query params, added prop optionally be able to override some styles. Remove isOpaque props since it was too specific and it won't be used anymore. Removed onClick since it wasn't working nor being used.