mirror of
https://github.com/outline/outline.git
synced 2026-08-03 13:27:25 +03:00
fix: Button elements in DocumentMeta incorrectly rendered as links (#13187)
* fix: Button elements in DocumentMeta rendered as links * fix: Expose comments sidebar state on toggle button Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -11,20 +11,32 @@ import type Revision from "~/models/Revision";
|
||||
import DocumentBreadcrumb from "~/components/DocumentBreadcrumb";
|
||||
import DocumentTasks from "~/components/DocumentTasks";
|
||||
import Flex from "~/components/Flex";
|
||||
import NudeButton from "~/components/NudeButton";
|
||||
import Time from "~/components/Time";
|
||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||
import useStores from "~/hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
/** Additional content appended to the end of the meta. */
|
||||
children?: React.ReactNode;
|
||||
/** Show the collection that the document belongs to. */
|
||||
showCollection?: boolean;
|
||||
/** Show the published time, even when the document has since been updated. */
|
||||
showPublished?: boolean;
|
||||
/** Show when the current user last viewed the document. */
|
||||
showLastViewed?: boolean;
|
||||
/** Show the number of documents nested under this one. */
|
||||
showParentDocuments?: boolean;
|
||||
/** The document to display meta information for. */
|
||||
document: Document;
|
||||
/** A revision of the document, when displaying meta for a point in history. */
|
||||
revision?: Revision;
|
||||
/** Replace the current history entry instead of pushing a new one when `to` is set. */
|
||||
replace?: boolean;
|
||||
/** Destination to link the meta content to. */
|
||||
to?: LocationDescriptor;
|
||||
/** Called when the meta content is clicked, renders it as a button. Takes precedence over `to`. */
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const DocumentMeta: React.FC<Props> = ({
|
||||
@@ -37,6 +49,7 @@ const DocumentMeta: React.FC<Props> = ({
|
||||
children,
|
||||
replace,
|
||||
to,
|
||||
onClick,
|
||||
...rest
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -176,7 +189,9 @@ const DocumentMeta: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Container align="center" $rtl={document.dir === "rtl"} {...rest} dir="ltr">
|
||||
{to ? (
|
||||
{onClick ? (
|
||||
<MetaButton onClick={onClick}>{content}</MetaButton>
|
||||
) : to ? (
|
||||
<Link to={to} replace={replace}>
|
||||
{content}
|
||||
</Link>
|
||||
@@ -212,6 +227,23 @@ const DocumentMeta: React.FC<Props> = ({
|
||||
);
|
||||
};
|
||||
|
||||
/** A button that visually matches the surrounding meta text. */
|
||||
export const MetaButton = styled(NudeButton)`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: 0;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
text-align: inherit;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Separator = styled.span`
|
||||
padding: 0 0.4em;
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type { LocationDescriptor } from "history";
|
||||
import { observer, useObserver } from "mobx-react";
|
||||
import { CommentIcon } from "outline-icons";
|
||||
import { useRef, Fragment } from "react";
|
||||
import { useRef, Fragment, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import type Document from "~/models/Document";
|
||||
import type Revision from "~/models/Revision";
|
||||
import type Template from "~/models/Template";
|
||||
import { openDocumentInsights } from "~/actions/definitions/documents";
|
||||
import DocumentMeta, { Separator } from "~/components/DocumentMeta";
|
||||
import DocumentMeta, { MetaButton, Separator } from "~/components/DocumentMeta";
|
||||
import Fade from "~/components/Fade";
|
||||
import { useSplitView } from "~/components/SplitView/context";
|
||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||
@@ -18,7 +18,6 @@ import usePolicy from "~/hooks/usePolicy";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import { documentPath } from "~/utils/routeHelpers";
|
||||
import NudeButton from "~/components/NudeButton";
|
||||
|
||||
type Props = {
|
||||
/* The document to display meta data for */
|
||||
@@ -31,6 +30,7 @@ type Props = {
|
||||
function TitleDocumentMeta({ to, document, revision, rtl, ...rest }: Props) {
|
||||
const { views, comments, ui } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const { pane } = useSplitView();
|
||||
const sidebarContext = useLocationSidebarContext();
|
||||
const team = useCurrentTeam();
|
||||
@@ -44,77 +44,58 @@ function TitleDocumentMeta({ to, document, revision, rtl, ...rest }: Props) {
|
||||
|
||||
const commentsCount = comments.unresolvedCommentsInDocumentCount(document.id);
|
||||
const commentingEnabled = team.commentingEnabled;
|
||||
const commentsOpen = ui.getRightSidebar(pane) === "comments";
|
||||
|
||||
const handleClickMeta = useCallback(() => {
|
||||
if (to) {
|
||||
history.replace(to);
|
||||
}
|
||||
}, [history, to]);
|
||||
|
||||
const handleClickComment = useCallback(() => {
|
||||
history.push({
|
||||
pathname: documentPath(document as Document),
|
||||
state: { sidebarContext },
|
||||
});
|
||||
ui.setRightSidebar(commentsOpen ? null : "comments", pane);
|
||||
}, [history, document, sidebarContext, ui, pane, commentsOpen]);
|
||||
|
||||
return (
|
||||
<Meta
|
||||
document={document as Document}
|
||||
revision={revision}
|
||||
to={to}
|
||||
replace
|
||||
onClick={to ? handleClickMeta : undefined}
|
||||
$rtl={rtl}
|
||||
{...rest}
|
||||
>
|
||||
{commentingEnabled && can.comment && (
|
||||
<>
|
||||
<Separator />
|
||||
<CommentLink
|
||||
to={{
|
||||
pathname: documentPath(document as Document),
|
||||
state: { sidebarContext },
|
||||
}}
|
||||
onClick={() =>
|
||||
ui.setRightSidebar(
|
||||
ui.getRightSidebar(pane) === "comments" ? null : "comments",
|
||||
pane
|
||||
)
|
||||
}
|
||||
>
|
||||
<MetaButton onClick={handleClickComment} aria-expanded={commentsOpen}>
|
||||
<CommentIcon size={18} />
|
||||
{commentsCount
|
||||
? t("{{ count }} comment", { count: commentsCount })
|
||||
: t("Comment")}
|
||||
</CommentLink>
|
||||
</MetaButton>
|
||||
</>
|
||||
)}
|
||||
{totalViewers && can.listViews && !(document as Document).isDraft ? (
|
||||
<Wrapper>
|
||||
<Separator />
|
||||
<InsightsButton action={openDocumentInsights}>
|
||||
<MetaButton action={openDocumentInsights}>
|
||||
{t("Viewed by")}{" "}
|
||||
{onlyYou
|
||||
? t("only you")
|
||||
: `${totalViewers} ${
|
||||
totalViewers === 1 ? t("person") : t("people")
|
||||
}`}
|
||||
</InsightsButton>
|
||||
</MetaButton>
|
||||
</Wrapper>
|
||||
) : null}
|
||||
</Meta>
|
||||
);
|
||||
}
|
||||
|
||||
const CommentLink = styled(Link)`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
`;
|
||||
|
||||
const InsightsButton = styled(NudeButton)`
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
text-decoration: none;
|
||||
cursor: var(--pointer);
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Meta = styled(DocumentMeta)<{ $rtl?: boolean }>`
|
||||
justify-content: ${(props) => (props.$rtl ? "flex-end" : "flex-start")};
|
||||
margin: -12px 0 2em 0;
|
||||
|
||||
Reference in New Issue
Block a user