feat: add close button for popover element (#5205)

fixes #3988 

## Description

This PR adds `PopoverClose` button to `Popover` element. Just exported
and added the component from `@radix-ui/popover`. Which is where all the
rest of the elements are being used.

Existing project on staging -
https://p-33460026-be01-4d80-8f4f-1d6677c38fe0-dot-pop-close.staging.webstudio.is
Deployed project on staging - https://jk-popover-close-1ph18.wstd.work


![popover-close](https://github.com/user-attachments/assets/7670dcc7-2afb-4edf-a638-2a32cb48a9e6)

## Steps for reproduction
- You can go to staging, and create a project with popover.
- Deploy the project and checked the rendered popover

## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [x] made a self-review

## Before merging

- [x] tested locally and on preview environment (preview dev login:
0000)
This commit is contained in:
Jaya Krishna Namburu authored and GitHub committed 2025-05-14 16:44:11 +05:30
1 parent a44530b068
commit 24efd92764
8 files changed
+130 -4

No files matched your search

@@ -61,3 +61,4 @@ export const propsPopoverContent: Record<string, PropMeta> = {
options: ["always", "optimized"],
},
};
export const propsPopoverClose: Record<string, PropMeta> = {};
@@ -2,11 +2,13 @@ import {
Box as Box,
Button as Button,
Text as Text,
HtmlEmbed as HtmlEmbed,
} from "@webstudio-is/sdk-components-react";
import {
Popover as Popover,
PopoverTrigger as PopoverTrigger,
PopoverContent as PopoverContent,
PopoverClose as PopoverClose,
} from "../components";
const Component = () => {
@@ -18,6 +20,14 @@ const Component = () => {
</PopoverTrigger>
<PopoverContent className={"w-popover-content w-popover-content-1"}>
<Text className={"w-text"}>{"The text you can edit"}</Text>
<PopoverClose className={"w-close-button w-close-button-1"}>
<HtmlEmbed
code={
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="100%" height="100%" style="display: block;"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M12.5 3 3 12.5M3 3l9.5 9.5"/></svg>'
}
className={"w-html-embed"}
/>
</PopoverClose>
</PopoverContent>
</Popover>
</Box>
@@ -131,6 +141,11 @@ const Story = {
text-transform: none;
margin: 0
}
:where(div.w-html-embed) {
display: contents;
white-space: normal;
white-space-collapse: collapse
}
:where(div.w-text) {
box-sizing: border-box;
border-top-width: 1px;
@@ -140,6 +155,18 @@ const Story = {
outline-width: 1px;
min-height: 1em
}
:where(button.w-close-button) {
background-color: transparent;
background-image: none;
font-family: inherit;
font-size: 100%;
line-height: 1.15;
box-sizing: border-box;
text-transform: none;
border: 1px solid rgba(226, 232, 240, 1);
margin: 0;
padding: 0px
}
:where(div.w-popover-content) {
box-sizing: border-box;
border-top-width: 1px;
@@ -196,6 +223,30 @@ const Story = {
border: 1px solid rgba(226, 232, 240, 1);
padding: 1rem
}
.w-close-button-1 {
position: absolute;
right: 1rem;
top: 1rem;
border-top-left-radius: 0.125rem;
border-top-right-radius: 0.125rem;
border-bottom-right-radius: 0.125rem;
border-bottom-left-radius: 0.125rem;
opacity: 0.7;
display: flex;
align-items: center;
justify-content: center;
height: 1rem;
width: 1rem;
background-color: transparent;
outline: medium none currentcolor;
border: 0 none currentcolor
}
.w-close-button-1:focus-visible {
box-shadow: 0 0 0 2px rgba(255, 255, 255, 1), 0 0 0 calc(2px + 2px) rgba(148, 163, 184, 1)
}
.w-close-button-1:hover {
opacity: 1
}
}
`}
</style>
@@ -12,7 +12,12 @@ export {
DialogTitle,
DialogDescription,
} from "./dialog";
export { Popover, PopoverTrigger, PopoverContent } from "./popover";
export {
Popover,
PopoverTrigger,
PopoverContent,
PopoverClose,
} from "./popover";
export { Tooltip, TooltipTrigger, TooltipContent } from "./tooltip";
export { Tabs, TabsList, TabsTrigger, TabsContent } from "./tabs";
export { Label } from "./label";
@@ -16,6 +16,7 @@ export {
metaPopover as Popover,
metaPopoverTrigger as PopoverTrigger,
metaPopoverContent as PopoverContent,
metaPopoverClose as PopoverClose,
} from "./popover.ws";
export {
metaTooltip as Tooltip,
@@ -1,3 +1,4 @@
import { LargeXIcon } from "@webstudio-is/icons/svg";
import {
$,
css,
@@ -14,6 +15,8 @@ import {
spacing,
width,
zIndex,
height,
opacity,
} from "./shared/theme";
/**
@@ -52,6 +55,38 @@ export const meta: TemplateMeta = {
`}
>
<$.Text>{new PlaceholderValue("The text you can edit")}</$.Text>
<radix.PopoverClose
ws:label="Close Button"
/**
* absolute right-4 top-4
* rounded-sm opacity-70
* ring-offset-background
* hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2
* flex items-center justify-center h-4 w-4
**/
ws:style={css`
position: absolute;
right: ${spacing[4]};
top: ${spacing[4]};
border-radius: ${borderRadius.sm};
display: flex;
align-items: center;
justify-content: center;
height: ${height[4]};
width: ${height[4]};
border: 0;
background-color: transparent;
outline: none;
&:hover {
opacity: ${opacity[100]};
}
&:focus-visible {
box-shadow: ${boxShadow.ring};
}
`}
>
<$.HtmlEmbed ws:label="Close Icon" code={LargeXIcon} />
</radix.PopoverClose>
</radix.PopoverContent>
</radix.Popover>
),
@@ -54,6 +54,8 @@ export const PopoverContent = forwardRef<
)
);
export const PopoverClose = PopoverPrimitive.Close;
/* BUILDER HOOKS */
const namespace = "@webstudio-is/sdk-components-react-radix";
@@ -1,12 +1,23 @@
import { PopoverIcon, TriggerIcon, ContentIcon } from "@webstudio-is/icons/svg";
import type { WsComponentMeta, WsComponentPropsMeta } from "@webstudio-is/sdk";
import { div } from "@webstudio-is/sdk/normalize.css";
import {
PopoverIcon,
TriggerIcon,
ContentIcon,
ButtonElementIcon,
} from "@webstudio-is/icons/svg";
import {
defaultStates,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "@webstudio-is/sdk";
import { button, div } from "@webstudio-is/sdk/normalize.css";
import { radix } from "./shared/meta";
import {
propsPopover,
propsPopoverContent,
propsPopoverTrigger,
propsPopoverClose,
} from "./__generated__/popover.props";
import { buttonReset } from "./shared/preset-styles";
// @todo add [data-state] to button and link
export const metaPopoverTrigger: WsComponentMeta = {
@@ -22,6 +33,7 @@ export const metaPopoverContent: WsComponentMeta = {
contentModel: {
category: "none",
children: ["instance"],
descendants: [radix.PopoverClose],
},
presetStyle: {
div,
@@ -37,6 +49,19 @@ export const metaPopover: WsComponentMeta = {
},
};
export const metaPopoverClose: WsComponentMeta = {
icon: ButtonElementIcon,
label: "Close Button",
contentModel: {
category: "none",
children: ["instance", "rich-text"],
},
states: defaultStates,
presetStyle: {
button: [buttonReset, button].flat(),
},
};
export const propsMetaPopover: WsComponentPropsMeta = {
props: propsPopover,
initialProps: ["open"],
@@ -50,3 +75,8 @@ export const propsMetaPopoverContent: WsComponentPropsMeta = {
props: propsPopoverContent,
initialProps: ["side", "sideOffset", "align", "alignOffset"],
};
export const propsMetaPopoverClose: WsComponentPropsMeta = {
props: propsPopoverClose,
initialProps: [],
};
@@ -16,6 +16,7 @@ export {
propsMetaPopover as Popover,
propsMetaPopoverTrigger as PopoverTrigger,
propsMetaPopoverContent as PopoverContent,
propsMetaPopoverClose as PopoverClose,
} from "./popover.ws";
export {
propsMetaTooltip as Tooltip,