mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Migrate remaining popovers to Radix (#9555)
* reaction picker * api key expiry date picker * find and replace popover * slack list item
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
import { ReactionIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PopoverDisclosure, usePopoverState } from "reakit";
|
||||
import EventBoundary from "@shared/components/EventBoundary";
|
||||
import Flex from "~/components/Flex";
|
||||
import { createLazyComponent } from "~/components/LazyLoad";
|
||||
import NudeButton from "~/components/NudeButton";
|
||||
import PlaceholderText from "~/components/PlaceholderText";
|
||||
import Popover from "~/components/Popover";
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
} from "~/components/primitives/Popover";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
import useOnClickOutside from "~/hooks/useOnClickOutside";
|
||||
import useWindowSize from "~/hooks/useWindowSize";
|
||||
import Tooltip from "../Tooltip";
|
||||
|
||||
@@ -20,109 +22,59 @@ const EmojiPanel = createLazyComponent(
|
||||
type Props = {
|
||||
/** Callback when an emoji is selected by the user. */
|
||||
onSelect: (emoji: string) => Promise<void>;
|
||||
/** Callback when the picker is opened. */
|
||||
onOpen?: () => void;
|
||||
/** Callback when the picker is closed. */
|
||||
onClose?: () => void;
|
||||
/** Optional classname. */
|
||||
className?: string;
|
||||
size?: number;
|
||||
};
|
||||
|
||||
const ReactionPicker: React.FC<Props> = ({
|
||||
onSelect,
|
||||
onOpen,
|
||||
onClose,
|
||||
className,
|
||||
size,
|
||||
}) => {
|
||||
const ReactionPicker: React.FC<Props> = ({ onSelect, className, size }) => {
|
||||
const { t } = useTranslation();
|
||||
const popover = usePopoverState({
|
||||
modal: true,
|
||||
unstable_offset: [0, 0],
|
||||
placement: "bottom-end",
|
||||
});
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const { width: windowWidth } = useWindowSize();
|
||||
const isMobile = useMobile();
|
||||
|
||||
const [query, setQuery] = React.useState("");
|
||||
const contentRef = React.useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const popoverWidth = isMobile ? windowWidth : 300;
|
||||
// In mobile, popover is absolutely positioned to leave 8px on both sides.
|
||||
const panelWidth = isMobile ? windowWidth - 16 : popoverWidth;
|
||||
const { toggle, hide } = popover;
|
||||
const handlePopoverButtonClick = React.useCallback(
|
||||
(ev: React.MouseEvent) => {
|
||||
ev.stopPropagation();
|
||||
toggle();
|
||||
},
|
||||
[toggle]
|
||||
);
|
||||
|
||||
const handleEmojiSelect = React.useCallback(
|
||||
(emoji: string) => {
|
||||
hide();
|
||||
setOpen(false);
|
||||
void onSelect(emoji);
|
||||
},
|
||||
[hide, onSelect]
|
||||
);
|
||||
|
||||
// Popover open effect
|
||||
React.useEffect(() => {
|
||||
if (popover.visible) {
|
||||
onOpen?.();
|
||||
} else {
|
||||
onClose?.();
|
||||
}
|
||||
}, [popover.visible, onOpen, onClose]);
|
||||
|
||||
// Custom click outside handling rather than using `hideOnClickOutside` from reakit so that we can
|
||||
// prevent event bubbling.
|
||||
useOnClickOutside(
|
||||
contentRef,
|
||||
(event) => {
|
||||
if (
|
||||
popover.visible &&
|
||||
!popover.unstable_disclosureRef.current?.contains(event.target as Node)
|
||||
) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
popover.hide();
|
||||
}
|
||||
},
|
||||
{ capture: true }
|
||||
[onSelect]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PopoverDisclosure {...popover}>
|
||||
{(props) => (
|
||||
<Tooltip content={t("Add reaction")} placement="top">
|
||||
<NudeButton
|
||||
{...props}
|
||||
aria-label={t("Reaction picker")}
|
||||
className={className}
|
||||
onClick={handlePopoverButtonClick}
|
||||
onMouseEnter={() => EmojiPanel.preload()}
|
||||
size={size}
|
||||
>
|
||||
<ReactionIcon size={22} />
|
||||
</NudeButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</PopoverDisclosure>
|
||||
<Popover
|
||||
{...popover}
|
||||
ref={contentRef}
|
||||
width={popoverWidth}
|
||||
shrink
|
||||
<Popover open={open} onOpenChange={setOpen} modal={true}>
|
||||
<Tooltip content={t("Add reaction")} placement="top">
|
||||
<PopoverTrigger>
|
||||
<NudeButton
|
||||
aria-label={t("Reaction picker")}
|
||||
className={className}
|
||||
onMouseEnter={() => EmojiPanel.preload()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
size={size}
|
||||
>
|
||||
<ReactionIcon size={22} />
|
||||
</NudeButton>
|
||||
</PopoverTrigger>
|
||||
</Tooltip>
|
||||
<PopoverContent
|
||||
aria-label={t("Reaction picker")}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
hideOnClickOutside={false}
|
||||
width={popoverWidth}
|
||||
side="bottom"
|
||||
align="end"
|
||||
shrink
|
||||
onCloseAutoFocus={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{popover.visible && (
|
||||
{open && (
|
||||
<React.Suspense fallback={<Placeholder />}>
|
||||
<EventBoundary>
|
||||
<EmojiPanel.Component
|
||||
@@ -136,8 +88,8 @@ const ReactionPicker: React.FC<Props> = ({
|
||||
</EventBoundary>
|
||||
</React.Suspense>
|
||||
)}
|
||||
</Popover>
|
||||
</>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
} from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { usePopoverState } from "reakit/Popover";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import { depths, s } from "@shared/styles";
|
||||
import { altDisplay, isModKey, metaDisplay } from "@shared/utils/keyboard";
|
||||
@@ -15,24 +14,26 @@ import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
import Input from "~/components/Input";
|
||||
import NudeButton from "~/components/NudeButton";
|
||||
import Popover from "~/components/Popover";
|
||||
import { Portal } from "~/components/Portal";
|
||||
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
|
||||
import Tooltip from "~/components/Tooltip";
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
} from "~/components/primitives/Popover";
|
||||
import useKeyDown from "~/hooks/useKeyDown";
|
||||
import useOnClickOutside from "~/hooks/useOnClickOutside";
|
||||
import Desktop from "~/utils/Desktop";
|
||||
import { useEditor } from "./EditorContext";
|
||||
|
||||
type KeyboardShortcutsProps = {
|
||||
popover: ReturnType<typeof usePopoverState>;
|
||||
open: boolean;
|
||||
handleOpen: ({ withReplace }: { withReplace: boolean }) => void;
|
||||
handleCaseSensitive: () => void;
|
||||
handleRegex: () => void;
|
||||
};
|
||||
|
||||
function useKeyboardShortcuts({
|
||||
popover,
|
||||
open,
|
||||
handleOpen,
|
||||
handleCaseSensitive,
|
||||
handleRegex,
|
||||
@@ -41,7 +42,7 @@ function useKeyboardShortcuts({
|
||||
useKeyDown(
|
||||
(ev) =>
|
||||
isModKey(ev) &&
|
||||
!popover.visible &&
|
||||
!open &&
|
||||
ev.code === "KeyF" &&
|
||||
// Keyboard handler is through the AppMenu on Desktop v1.2.0+
|
||||
!(Desktop.bridge && "onFindInPage" in Desktop.bridge),
|
||||
@@ -54,7 +55,7 @@ function useKeyboardShortcuts({
|
||||
|
||||
// Enable/disable case sensitive search
|
||||
useKeyDown(
|
||||
(ev) => isModKey(ev) && ev.altKey && ev.code === "KeyC" && popover.visible,
|
||||
(ev) => isModKey(ev) && ev.altKey && ev.code === "KeyC" && open,
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
handleCaseSensitive();
|
||||
@@ -64,7 +65,7 @@ function useKeyboardShortcuts({
|
||||
|
||||
// Enable/disable regex search
|
||||
useKeyDown(
|
||||
(ev) => isModKey(ev) && ev.altKey && ev.code === "KeyR" && popover.visible,
|
||||
(ev) => isModKey(ev) && ev.altKey && ev.code === "KeyR" && open,
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
handleRegex();
|
||||
@@ -97,9 +98,7 @@ export default function FindAndReplace({
|
||||
totalResults,
|
||||
}: Props) {
|
||||
const editor = useEditor();
|
||||
const finalFocusRef = React.useRef<HTMLElement>(
|
||||
editor.view.dom.parentElement
|
||||
);
|
||||
const [localOpen, setLocalOpen] = React.useState(open);
|
||||
const selectionRef = React.useRef<string | undefined>();
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
const inputReplaceRef = React.useRef<HTMLInputElement>(null);
|
||||
@@ -110,12 +109,10 @@ export default function FindAndReplace({
|
||||
const [regexEnabled, setRegex] = React.useState(false);
|
||||
const [searchTerm, setSearchTerm] = React.useState("");
|
||||
const [replaceTerm, setReplaceTerm] = React.useState("");
|
||||
const popover = usePopoverState();
|
||||
const { show } = popover;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
show();
|
||||
setLocalOpen(true);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
@@ -127,16 +124,16 @@ export default function FindAndReplace({
|
||||
if ("onFindInPage" in Desktop.bridge) {
|
||||
Desktop.bridge.onFindInPage(() => {
|
||||
selectionRef.current = window.getSelection()?.toString();
|
||||
show();
|
||||
setLocalOpen(true);
|
||||
});
|
||||
}
|
||||
if ("onReplaceInPage" in Desktop.bridge) {
|
||||
Desktop.bridge.onReplaceInPage(() => {
|
||||
setShowReplace(true);
|
||||
show();
|
||||
setLocalOpen(true);
|
||||
});
|
||||
}
|
||||
}, [show]);
|
||||
}, []);
|
||||
|
||||
// Callbacks
|
||||
const selectInputText = React.useCallback(() => {
|
||||
@@ -159,7 +156,7 @@ export default function FindAndReplace({
|
||||
const shouldShowReplace = !readOnly && withReplace;
|
||||
|
||||
// If already open, switch focus to corresponding input text.
|
||||
if (popover.visible) {
|
||||
if (localOpen) {
|
||||
if (shouldShowReplace) {
|
||||
setShowReplace(true);
|
||||
selectInputReplaceText();
|
||||
@@ -171,13 +168,13 @@ export default function FindAndReplace({
|
||||
}
|
||||
|
||||
selectionRef.current = window.getSelection()?.toString();
|
||||
popover.show();
|
||||
setLocalOpen(true);
|
||||
|
||||
if (shouldShowReplace) {
|
||||
setShowReplace(true);
|
||||
}
|
||||
},
|
||||
[popover, readOnly, selectInputText, selectInputReplaceText]
|
||||
[localOpen, readOnly, selectInputText, selectInputReplaceText]
|
||||
);
|
||||
|
||||
const handleMore = React.useCallback(() => {
|
||||
@@ -295,10 +292,8 @@ export default function FindAndReplace({
|
||||
[handleReplace]
|
||||
);
|
||||
|
||||
useOnClickOutside(popover.unstable_referenceRef, popover.hide);
|
||||
|
||||
useKeyboardShortcuts({
|
||||
popover,
|
||||
open: localOpen,
|
||||
handleOpen,
|
||||
handleCaseSensitive,
|
||||
handleRegex,
|
||||
@@ -316,7 +311,7 @@ export default function FindAndReplace({
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (popover.visible) {
|
||||
if (localOpen) {
|
||||
onOpen();
|
||||
const startSearchText = selectionRef.current || searchTerm;
|
||||
|
||||
@@ -339,7 +334,7 @@ export default function FindAndReplace({
|
||||
editor.commands.clearSearch();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [popover.visible]);
|
||||
}, [localOpen]);
|
||||
|
||||
const disabled = totalResults === 0;
|
||||
const navigation = (
|
||||
@@ -368,15 +363,16 @@ export default function FindAndReplace({
|
||||
);
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Popover
|
||||
{...popover}
|
||||
unstable_finalFocusRef={finalFocusRef}
|
||||
style={style}
|
||||
<Popover open={localOpen} onOpenChange={setLocalOpen}>
|
||||
<PopoverTrigger>
|
||||
<span style={style} />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
aria-label={t("Find and replace")}
|
||||
scrollable={false}
|
||||
minWidth={420}
|
||||
width={0}
|
||||
minWidth={420}
|
||||
scrollable={false}
|
||||
onPointerDownOutside={() => setLocalOpen(false)}
|
||||
>
|
||||
<Content column>
|
||||
<Flex gap={4}>
|
||||
@@ -467,8 +463,8 @@ export default function FindAndReplace({
|
||||
)}
|
||||
</ResizingHeightContainer>
|
||||
</Content>
|
||||
</Popover>
|
||||
</Portal>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,14 @@ import { CalendarIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { DayPicker } from "react-day-picker";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { usePopoverState, PopoverDisclosure } from "reakit";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import { dateLocale } from "@shared/utils/date";
|
||||
import Button from "~/components/Button";
|
||||
import Popover from "~/components/Popover";
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
} from "~/components/primitives/Popover";
|
||||
import useUserLocale from "~/hooks/useUserLocale";
|
||||
|
||||
type Props = {
|
||||
@@ -17,14 +20,12 @@ type Props = {
|
||||
|
||||
const ExpiryDatePicker = ({ selectedDate, onSelect }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
const userLocale = useUserLocale();
|
||||
const locale = dateLocale(userLocale);
|
||||
|
||||
const popover = usePopoverState({ gutter: 0, placement: "right" });
|
||||
const popoverContentRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const styles = React.useMemo(
|
||||
() =>
|
||||
({
|
||||
@@ -41,29 +42,26 @@ const ExpiryDatePicker = ({ selectedDate, onSelect }: Props) => {
|
||||
|
||||
const handleSelect = React.useCallback(
|
||||
(date: Date) => {
|
||||
popover.hide();
|
||||
setOpen(false);
|
||||
onSelect(date);
|
||||
},
|
||||
[popover, onSelect]
|
||||
[onSelect]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PopoverDisclosure {...popover}>
|
||||
{(props) => (
|
||||
<StyledPopoverButton {...props} icon={<Icon />} neutral>
|
||||
{selectedDate
|
||||
? formatDate(selectedDate, "MMM dd, yyyy", { locale })
|
||||
: t("Choose a date")}
|
||||
</StyledPopoverButton>
|
||||
)}
|
||||
</PopoverDisclosure>
|
||||
<Popover
|
||||
{...popover}
|
||||
ref={popoverContentRef}
|
||||
width={280}
|
||||
shrink
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger>
|
||||
<StyledPopoverButton icon={<Icon />} neutral>
|
||||
{selectedDate
|
||||
? formatDate(selectedDate, "MMM dd, yyyy", { locale })
|
||||
: t("Choose a date")}
|
||||
</StyledPopoverButton>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
aria-label={t("Choose a date")}
|
||||
width={280}
|
||||
side="right"
|
||||
shrink
|
||||
>
|
||||
<DayPicker
|
||||
required
|
||||
@@ -73,8 +71,8 @@ const ExpiryDatePicker = ({ selectedDate, onSelect }: Props) => {
|
||||
style={styles}
|
||||
disabled={{ before: new Date() }}
|
||||
/>
|
||||
</Popover>
|
||||
</>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -35,10 +35,6 @@ type Props = {
|
||||
focused: boolean;
|
||||
/** Whether the thread is displayed in a recessed/backgrounded state */
|
||||
recessed: boolean;
|
||||
/** Enable scroll for the comments container */
|
||||
enableScroll: () => void;
|
||||
/** Disable scroll for the comments container */
|
||||
disableScroll: () => void;
|
||||
/** Number of replies before collapsing */
|
||||
collapseThreshold?: number;
|
||||
/** Number of replies to display when collapsed */
|
||||
@@ -50,8 +46,6 @@ function CommentThread({
|
||||
document,
|
||||
recessed,
|
||||
focused,
|
||||
enableScroll,
|
||||
disableScroll,
|
||||
collapseThreshold = 5,
|
||||
collapseNumDisplayed = 3,
|
||||
}: Props) {
|
||||
@@ -248,8 +242,6 @@ function CommentThread({
|
||||
lastOfAuthor={lastOfAuthor}
|
||||
previousCommentCreatedAt={commentsInThread[index - 1]?.createdAt}
|
||||
dir={document.dir}
|
||||
enableScroll={enableScroll}
|
||||
disableScroll={disableScroll}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -88,10 +88,6 @@ type Props = {
|
||||
onUpdate?: (id: string, attrs: { resolved: boolean }) => void;
|
||||
/** Text to highlight at the top of the comment */
|
||||
highlightedText?: string;
|
||||
/** Enable scroll for the comments container */
|
||||
enableScroll: () => void;
|
||||
/** Disable scroll for the comments container */
|
||||
disableScroll: () => void;
|
||||
};
|
||||
|
||||
function CommentThreadItem({
|
||||
@@ -105,8 +101,6 @@ function CommentThreadItem({
|
||||
onDelete,
|
||||
onUpdate,
|
||||
highlightedText,
|
||||
enableScroll,
|
||||
disableScroll,
|
||||
}: Props) {
|
||||
const { t } = useTranslation();
|
||||
const user = useCurrentUser();
|
||||
@@ -240,8 +234,6 @@ function CommentThreadItem({
|
||||
<Action
|
||||
as={ReactionPicker}
|
||||
onSelect={handleAddReaction}
|
||||
onOpen={disableScroll}
|
||||
onClose={enableScroll}
|
||||
size={28}
|
||||
$rounded
|
||||
/>
|
||||
@@ -262,8 +254,6 @@ function CommentThreadItem({
|
||||
<Action
|
||||
as={ReactionPicker}
|
||||
onSelect={handleAddReaction}
|
||||
onOpen={disableScroll}
|
||||
onClose={enableScroll}
|
||||
$rounded
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -12,7 +12,6 @@ import Empty from "~/components/Empty";
|
||||
import Fade from "~/components/Fade";
|
||||
import Flex from "~/components/Flex";
|
||||
import Scrollable from "~/components/Scrollable";
|
||||
import useBoolean from "~/hooks/useBoolean";
|
||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||
import useFocusedComment from "~/hooks/useFocusedComment";
|
||||
import useKeyDown from "~/hooks/useKeyDown";
|
||||
@@ -33,8 +32,6 @@ function Comments() {
|
||||
const { t } = useTranslation();
|
||||
const match = useRouteMatch<{ documentSlug: string }>();
|
||||
const params = useQuery();
|
||||
// We need to control scroll behaviour when reaction picker is opened / closed.
|
||||
const [scrollable, enableScroll, disableScroll] = useBoolean(true);
|
||||
const document = documents.getByUrl(match.params.documentSlug);
|
||||
const focusedComment = useFocusedComment();
|
||||
const can = usePolicy(document);
|
||||
@@ -138,8 +135,6 @@ function Comments() {
|
||||
bottomShadow={!focusedComment}
|
||||
hiddenScrollbars
|
||||
topShadow
|
||||
overflow={scrollable ? "auto" : "hidden"}
|
||||
style={{ overflowX: "hidden" }}
|
||||
ref={scrollableRef}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
@@ -152,8 +147,6 @@ function Comments() {
|
||||
document={document}
|
||||
recessed={!!focusedComment && focusedComment.id !== thread.id}
|
||||
focused={focusedComment?.id === thread.id}
|
||||
enableScroll={enableScroll}
|
||||
disableScroll={disableScroll}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
||||
@@ -2,7 +2,6 @@ import uniq from "lodash/uniq";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { usePopoverState, PopoverDisclosure } from "reakit/Popover";
|
||||
import { toast } from "sonner";
|
||||
import styled from "styled-components";
|
||||
import { s } from "@shared/styles";
|
||||
@@ -14,9 +13,13 @@ import ButtonLink from "~/components/ButtonLink";
|
||||
import Flex from "~/components/Flex";
|
||||
import CollectionIcon from "~/components/Icons/CollectionIcon";
|
||||
import ListItem from "~/components/List/Item";
|
||||
import Popover from "~/components/Popover";
|
||||
import Switch from "~/components/Switch";
|
||||
import Text from "~/components/Text";
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
} from "~/components/primitives/Popover";
|
||||
|
||||
type Props = {
|
||||
integration: Integration<IntegrationType.Post>;
|
||||
@@ -43,11 +46,6 @@ function SlackListItem({ integration, collection }: Props) {
|
||||
"documents.update": t("document updated"),
|
||||
};
|
||||
|
||||
const popover = usePopoverState({
|
||||
gutter: 0,
|
||||
placement: "bottom-start",
|
||||
});
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
key={integration.id}
|
||||
@@ -68,32 +66,32 @@ function SlackListItem({ integration, collection }: Props) {
|
||||
em: <strong />,
|
||||
}}
|
||||
/>{" "}
|
||||
<PopoverDisclosure {...popover}>
|
||||
{(props) => (
|
||||
<ButtonLink {...props}>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<ButtonLink>
|
||||
{integration.events.map((ev) => mapping[ev]).join(", ")}
|
||||
</ButtonLink>
|
||||
)}
|
||||
</PopoverDisclosure>
|
||||
<Popover {...popover} aria-label={t("Settings")}>
|
||||
<Events>
|
||||
<h3>{t("Notifications")}</h3>
|
||||
<Text as="p" type="secondary">
|
||||
{t("These events should be posted to Slack")}
|
||||
</Text>
|
||||
<Switch
|
||||
label={t("Document published")}
|
||||
name="documents.publish"
|
||||
checked={integration.events.includes("documents.publish")}
|
||||
onChange={handleChange("documents.publish")}
|
||||
/>
|
||||
<Switch
|
||||
label={t("Document updated")}
|
||||
name="documents.update"
|
||||
checked={integration.events.includes("documents.update")}
|
||||
onChange={handleChange("documents.update")}
|
||||
/>
|
||||
</Events>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="bottom" align="start">
|
||||
<Events>
|
||||
<h3>{t("Notifications")}</h3>
|
||||
<Text as="p" type="secondary">
|
||||
{t("These events should be posted to Slack")}
|
||||
</Text>
|
||||
<Switch
|
||||
label={t("Document published")}
|
||||
name="documents.publish"
|
||||
checked={integration.events.includes("documents.publish")}
|
||||
onChange={handleChange("documents.publish")}
|
||||
/>
|
||||
<Switch
|
||||
label={t("Document updated")}
|
||||
name="documents.update"
|
||||
checked={integration.events.includes("documents.update")}
|
||||
onChange={handleChange("documents.update")}
|
||||
/>
|
||||
</Events>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user