Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/components/HashMenu/HashWpMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
insertWpChip,
insertWpChipIntoBlock,
} from "./editorUtils";
import { useTranslation } from "react-i18next";

const Menu = styled.div.attrs({ className: "op-bn-hash-menu" })`
${defaultWpVariables}
Expand Down Expand Up @@ -51,6 +52,7 @@ export function createHashWpMenuComponent(
items,
selectedIndex,
}) => {
const { t } = useTranslation();
const searchQuery = items[0]?.title ?? "";
const visibleResults = (resultsRef.current ?? []).slice(0, MAX_RESULTS);

Expand Down Expand Up @@ -82,15 +84,15 @@ export function createHashWpMenuComponent(
if (!searchQuery) {
return (
<Menu>
<EmptyState>Type to search work packages…</EmptyState>
<EmptyState>{t("hashMenu.typeToSearch")}</EmptyState>
</Menu>
);
}

if (visibleResults.length === 0) {
return (
<Menu>
<EmptyState>No results for "{searchQuery}"</EmptyState>
<EmptyState>{t("hashMenu.noResults", { query: searchQuery })}</EmptyState>
</Menu>
);
}
Expand Down
4 changes: 3 additions & 1 deletion lib/components/InlineWorkPackage/InlineWorkPackageChip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getPendingCallbacks, clearInlineWpCallbacks } from "./callbacks";
import type { InlineWpSize } from "../WorkPackage/types";
import { wpBridge } from "../../services/wpBridge";
import { BlockCard } from "../BlockWorkPackage/BlockCard";
import { useTranslation } from "react-i18next";

interface InlineWorkPackageChipProps {
inlineContent: { props: { wpid: string; size: string; instanceId: string } };
Expand All @@ -36,6 +37,7 @@ const InlineChip = styled.span.attrs({
`;

export const InlineWorkPackageChip = ({ inlineContent, contentRef }: InlineWorkPackageChipProps) => {
const { t } = useTranslation();
const rawWpid = inlineContent.props.wpid;
const size = (inlineContent.props.size ?? "s") as InlineWpSize;
const instanceId = inlineContent.props.instanceId;
Expand Down Expand Up @@ -102,7 +104,7 @@ export const InlineWorkPackageChip = ({ inlineContent, contentRef }: InlineWorkP
return (
<InlineChip
role="button"
aria-label={`Work package #${wpid}`}
aria-label={t("options.chipAriaLabel", { id: wpid })}
ref={setRef}
selected={isSelected}
onClick={(e) => {
Expand Down
53 changes: 22 additions & 31 deletions lib/components/WorkPackage/OptionsPopover.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import type { WorkPackage } from "../../openProjectTypes";
import { linkToWorkPackage } from "../../services/openProjectApi";
import type { InlineWpSize, BlockWpSize, WpSize } from "./types";
import type { InlineWpSize, BlockWpSize } from "./types";
import styled from "styled-components";
import { defaultWpVariables } from "./atoms";
import {
Expand All @@ -24,15 +25,6 @@ export interface WpOptionsProps {
onResizeBlock?: (size: BlockWpSize) => void;
}

const SIZE_META: Record<WpSize, { label: string; desc: string }> = {
xxs: { label: "Tiny (inline)", desc: "Identifier" },
xs: { label: "Compact (inline)", desc: "Type, Identifier, Subject" },
s: { label: "Regular (inline)", desc: "Status, Type, Identifier, Subject" },
m: { label: "Compact card", desc: "Compact card - Status, Type, Identifier, Subject" },
l: { label: "Regular card", desc: "Regular card - Identifier, Subject, Type, Status, Parent, Project" },
xl: { label: "Full card", desc: "Full card - Identifier, Subject, Type, Status, Parent, Project, Description" },
};

const INLINE_SIZE_OPTIONS: InlineWpSize[] = ["xxs", "xs", "s"];
const BLOCK_SIZE_OPTIONS: BlockWpSize[] = ["m", "l", "xl"];

Expand All @@ -48,12 +40,13 @@ export const WpOptionsPopover = ({
onConvertToInline,
onResizeBlock,
}: WpOptionsProps) => {
const { t } = useTranslation();
const [showSizes, setShowSizes] = useState(false);

const isBlock = currentSize === undefined;
const displayedSize = isBlock
? SIZE_META[currentBlockSize ?? "m"].label
: SIZE_META[currentSize].label;

const displayedSizeKey = isBlock ? (currentBlockSize ?? "m") : currentSize;
const displayedSize = t(`sizes.${displayedSizeKey}.label`);

const closeMenu = () => {
setShowSizes(false);
Expand All @@ -64,22 +57,22 @@ export const WpOptionsPopover = ({
// Prevent editor/parent handlers from stealing focus or closing the popover
<Popover onMouseDown={(e) => e.stopPropagation()}>
<PopBtn
title="Open in new tab"
aria-label={`Open work package #${wp.id} in new tab`}
title={t("options.openInNewTab")}
aria-label={t("options.openAriaLabel", { id: wp.id })}
onClick={(e) => {
e.stopPropagation();
window.open(linkToWorkPackage(wp.id), "_blank", "noopener,noreferrer");
}}
>
<IcOpen /> Open
<IcOpen /> {t("options.open")}
</PopBtn>

<Divider />

<SizeButtonWrapper>
<PopBtn
title="Change size"
aria-label="Change size"
title={t("options.changeSize")}
aria-label={t("options.changeSize")}
onClick={(e) => {
e.stopPropagation();
setShowSizes((prev) => !prev);
Expand All @@ -92,13 +85,12 @@ export const WpOptionsPopover = ({

{showSizes && (
<SizeMenu onMouseDown={(e) => e.stopPropagation()}>
<SizeMenuLabel>Inline size</SizeMenuLabel>
<SizeMenuLabel>{t("options.inlineSizeLabel")}</SizeMenuLabel>
{INLINE_SIZE_OPTIONS.map((size) => {
const option = SIZE_META[size];
return (
<SizeBtn
key={size}
aria-label={option.label}
aria-label={t(`sizes.${size}.label`)}
$active={!isBlock && currentSize === size}
onMouseDown={(e) => {
e.preventDefault();
Expand All @@ -111,21 +103,20 @@ export const WpOptionsPopover = ({
closeMenu();
}}
>
<SizeBtnLabel>{option.label}</SizeBtnLabel>
<SizeBtnDesc>{option.desc}</SizeBtnDesc>
<SizeBtnLabel>{t(`sizes.${size}.label`)}</SizeBtnLabel>
<SizeBtnDesc>{t(`sizes.${size}.desc`)}</SizeBtnDesc>
</SizeBtn>
);
})}

<SizeMenuDivider />

<SizeMenuLabel>Block size</SizeMenuLabel>
<SizeMenuLabel>{t("options.blockSizeLabel")}</SizeMenuLabel>
{BLOCK_SIZE_OPTIONS.map((size) => {
const option = SIZE_META[size];
return (
<SizeBtn
key={size}
aria-label={option.label}
aria-label={t(`sizes.${size}.label`)}
$active={isBlock && currentBlockSize === size}
onMouseDown={(e) => {
e.preventDefault();
Expand All @@ -138,8 +129,8 @@ export const WpOptionsPopover = ({
closeMenu();
}}
>
<SizeBtnLabel>{option.label}</SizeBtnLabel>
<SizeBtnDesc>{option.desc}</SizeBtnDesc>
<SizeBtnLabel>{t(`sizes.${size}.label`)}</SizeBtnLabel>
<SizeBtnDesc>{t(`sizes.${size}.desc`)}</SizeBtnDesc>
</SizeBtn>
);
})}
Expand All @@ -151,16 +142,16 @@ export const WpOptionsPopover = ({

<PopBtn
$danger
title="Remove"
title={t("options.remove")}
data-testid="remove-btn"
aria-label="Remove work package"
aria-label={t("options.removeAriaLabel")}
onClick={(e) => {
e.stopPropagation();
onRemove?.();
onClose();
}}
>
<IcDelete /> Remove
<IcDelete /> {t("options.remove")}
</PopBtn>
</Popover>
);
Expand Down
25 changes: 24 additions & 1 deletion lib/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@ export const en = {
"header": "Error",
"message": "Could not load work package"
}
}
},
"options": {
"openInNewTab": "Open in new tab",
"open": "Open",
"changeSize": "Change size",
"remove": "Remove",
"removeAriaLabel": "Remove work package",
"openAriaLabel": "Open work package #{{id}} in new tab",
"inlineSizeLabel": "Inline size",
"blockSizeLabel": "Block size",
"chipAriaLabel": "Work package #{{id}}",
},
"sizes": {
"xxs": { "label": "Tiny (inline)", "desc": "Identifier" },
"xs": { "label": "Compact (inline)", "desc": "Type, Identifier, Subject" },
"s": { "label": "Regular (inline)", "desc": "Status, Type, Identifier, Subject" },
"m": { "label": "Compact card", "desc": "Compact card - Status, Type, Identifier, Subject" },
"l": { "label": "Regular card", "desc": "Regular card - Identifier, Subject, Type, Status, Parent, Project" },
"xl": { "label": "Full card", "desc": "Full card - Identifier, Subject, Type, Status, Parent, Project, Description" }
},
"hashMenu": {
"typeToSearch": "Type to search work packages…",
"noResults": "No results for \"{{query}}\""
},
}
};
Loading