Skip to content

Commit

Permalink
feat(editable): introduce new nested card variant (#1478)
Browse files Browse the repository at this point in the history
* feat(icons): introduce new vertical draggable icon

* feat(editable): introduce nested version of accordion

* fix(contactus): update location card to use nested

* fix: border radius of error divider
  • Loading branch information
dcshzj authored Sep 6, 2023
1 parent 59a5f3f commit 0385f1b
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 45 deletions.
21 changes: 21 additions & 0 deletions src/assets/icons/BxDraggableVertical.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const BxDraggableVertical = (
props: React.SVGProps<SVGSVGElement>
): JSX.Element => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<circle cx="9" cy="18" r="2" fill="#BABECB" />
<circle cx="9" cy="12" r="2" fill="#BABECB" />
<circle cx="9" cy="6" r="2" fill="#BABECB" />
<circle cx="15" cy="18" r="2" fill="#BABECB" />
<circle cx="15" cy="12" r="2" fill="#BABECB" />
<circle cx="15" cy="6" r="2" fill="#BABECB" />
</svg>
)
}
1 change: 1 addition & 0 deletions src/assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from "./BxsClearRocket"
export * from "./BxLifeBuoy"
export * from "./BxCopy"
export * from "./BxDraggable"
export * from "./BxDraggableVertical"
4 changes: 2 additions & 2 deletions src/layouts/components/ContactUs/LocationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export const LocationCard = ({
<Editable.Accordion>
{/* Note: contentEditable is required to stop drag and drop
from hitting the first level drag and drop */}
<VStack p={0} spacing="1.125rem" contentEditable>
<VStack p={0} spacing="0.75rem" contentEditable>
{frontMatter.operating_hours.map(
(operatingHour, operatingHourIndex) => (
<Editable.DraggableAccordionItem
Expand All @@ -145,9 +145,9 @@ export const LocationCard = ({
isInvalid={_.some(
errors.operating_hours[operatingHourIndex]
)}
isNested
>
<Editable.Section mt="-0.5rem">
{/* Operating hours days */}
<FormControl
isRequired
isInvalid={
Expand Down
196 changes: 153 additions & 43 deletions src/layouts/components/Editable/Editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,54 @@ import { IconButton } from "@opengovsg/design-system-react"
import { PropsWithChildren } from "react"
import { v4 as uuid } from "uuid"

import { BxDraggable } from "assets"
import { BxDraggable, BxDraggableVertical } from "assets"

type EditableAccordionItemStyleTypes =
| "card-borderColor"
| "hover-bgColor"
| "tag-pb"
| "title-color"
| "title-color-hover"
type EditableAccordionItemStyleProps = {
item: EditableAccordionItemStyleTypes
isExpanded?: boolean
isNested?: boolean
isInvalid?: boolean
isDragging?: boolean
}
const getDraggableAccordionItemStyle = ({
item,
isExpanded,
isNested,
isInvalid,
isDragging,
}: EditableAccordionItemStyleProps): string => {
switch (item) {
case "card-borderColor":
return (isExpanded && isNested) || isDragging
? "base.divider.brand"
: "base.divider.medium"
case "hover-bgColor":
if (isExpanded) return "none"
return isNested
? "interaction.muted.sub.hover"
: "interaction.muted.main.hover"
case "tag-pb":
return isNested ? "0.875rem" : "1.37rem"
case "title-color":
if (!isNested) return "base.content.default"
return isExpanded || isDragging
? "base.content.strong"
: "base.content.medium"
case "title-color-hover":
if (!isNested) return "inherit"
return isInvalid && !isExpanded
? "base.content.medium"
: "base.content.strong"
default:
return ""
}
}

interface SidebarHeaderProps {
title: string
Expand Down Expand Up @@ -177,16 +224,7 @@ export const EditableDroppable = ({
}

const BaseAccordionItem = forwardRef((props: AccordionItemProps, ref) => {
return (
<AccordionItem
border="1px solid"
borderColor="base.divider.medium"
w="100%"
bg="base.canvas.default"
ref={ref}
{...props}
/>
)
return <AccordionItem border="none" w="100%" ref={ref} {...props} />
})

interface EditableCardProps {
Expand All @@ -209,7 +247,11 @@ const EditableAccordionItem = ({
return (
<BaseAccordionItem pos="relative">
{({ isExpanded }) => (
<>
<Box
bgColor="base.canvas.default"
border="1px solid"
borderColor="base.divider.medium"
>
{!isExpanded && isInvalid && (
<Divider
border="4px solid"
Expand All @@ -232,7 +274,7 @@ const EditableAccordionItem = ({
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb="2.25rem">{children}</AccordionPanel>
</>
</Box>
)}
</BaseAccordionItem>
)
Expand All @@ -246,73 +288,121 @@ interface DraggableAccordionItemProps {
index: number
draggableId: string
isInvalid?: boolean
isNested?: boolean
}
// NOTE: Separating editable/draggable
// due to semantics on `Draggables`
/**
* This component displays an accordion item that can be expanded to show its children.
* This component is draggable and **needs** to be part of a `Droppable` component.
*
* Take note that this component is draggable on the entire card when collapsed
* and that only the chevron can be used to expand this component.
* Note:
* - This component is draggable on the entire card when collapsed
* - Only the chevron can be used to expand this component
* - Tag is not supported when isNested is true
*
* @param isInvalid this prop determines whether the error border will be present on collapse.
* @param isNested this prop determines the appropriate styling to use (main vs nested accordion item).
*/
const DraggableAccordionItem = ({
tag,
title,
children,
index,
isInvalid,
isNested,
}: PropsWithChildren<DraggableAccordionItemProps>) => {
return (
<Draggable draggableId={uuid()} index={index}>
{(draggableProvided) => (
{(draggableProvided, snapshot) => (
<BaseAccordionItem
borderRadius="0.5rem"
borderRadius={isNested ? "0.375rem" : "0.5rem"}
{...draggableProvided.draggableProps}
ref={draggableProvided.innerRef}
boxShadow="sm"
{...draggableProvided.dragHandleProps}
position="relative"
>
{({ isExpanded }) => (
<>
<IconButton
pointerEvents="none"
position="absolute"
top="0.5rem"
left="0"
right="0"
variant="clear"
cursor="grab"
aria-label="drag item"
margin="0 auto"
w="fit-content"
icon={<BxDraggable />}
/>
<Box
borderRadius={isNested ? "0.375rem" : "0.5rem"}
boxShadow={isNested ? "" : "sm"}
bgColor={
isExpanded && isNested
? "base.canvas.brand-subtle"
: "base.canvas.default"
}
border="1px solid"
borderColor={getDraggableAccordionItemStyle({
item: "card-borderColor",
isExpanded,
isNested,
isDragging: snapshot.isDragging,
})}
>
{!isNested && (
<IconButton
pointerEvents="none"
position="absolute"
top="0.5rem"
left="0"
right="0"
variant="clear"
cursor="grab"
aria-label="drag item"
margin="0 auto"
w="fit-content"
icon={<BxDraggable />}
/>
)}
{!isExpanded && isInvalid && (
<Divider
border="4px solid"
borderWidth={isNested ? "2px" : "4px"}
borderStyle="solid"
borderColor="utility.feedback.critical"
orientation="vertical"
left={0}
position="absolute"
borderLeftRadius="0.5rem"
borderLeftRadius={isNested ? "0.375rem" : "0.5rem"}
h="-webkit-fill-available"
/>
)}
<Flex
flexDir="row"
pt="1.88rem"
pb={isExpanded ? "0rem" : "0.88rem"}
pt={isNested ? "0.375rem" : "1.88rem"}
pb={!isExpanded && !isNested ? "0.88rem" : "0rem"}
role="group"
_hover={{
bgColor: isExpanded ? "none" : "interaction.muted.main.hover",
borderRadius: "0.5rem",
bgColor: getDraggableAccordionItemStyle({
item: "hover-bgColor",
isExpanded,
isNested,
}),
borderRadius: isNested ? "0.375rem" : "0.5rem",
}}
bgColor="none"
>
{isNested && (
<IconButton
pointerEvents="none"
position="absolute"
left="0rem"
variant="clear"
cursor="grab"
aria-label="drag item"
margin="0 auto"
w="fit-content"
icon={<BxDraggableVertical />}
/>
)}
<Flex
pl="1.5rem"
pb={tag ? "1rem" : "1.37rem"}
pb={
tag
? "1rem"
: getDraggableAccordionItemStyle({
item: "tag-pb",
isNested,
})
}
pt={tag ? "0" : "0.37rem"}
flex="1"
flexDir="column"
Expand All @@ -323,21 +413,41 @@ const DraggableAccordionItem = ({
>
{tag}
<Text
textStyle="h6"
textStyle={isNested ? "subhead-1" : "h6"}
color={getDraggableAccordionItemStyle({
item: "title-color",
isExpanded,
isNested,
isDragging: snapshot.isDragging,
})}
textAlign="left"
mt="0.25rem"
ml={isNested ? "1rem" : "0"}
noOfLines={1}
maxW="100%"
_groupHover={{
color: getDraggableAccordionItemStyle({
item: "title-color-hover",
isExpanded,
isNested,
isInvalid,
}),
}}
>
{title}
</Text>
</Flex>
<AccordionButton w="auto" h="fit-content" py="1rem" mr="0.5rem">
<AccordionButton
w="auto"
h="fit-content"
py={isNested ? "0.75rem" : "1rem"}
mr="0.5rem"
>
<AccordionIcon />
</AccordionButton>
</Flex>
<AccordionPanel pb={4}>{children}</AccordionPanel>
</>
</Box>
)}
</BaseAccordionItem>
)}
Expand Down

0 comments on commit 0385f1b

Please sign in to comment.