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
8 changes: 8 additions & 0 deletions .changeset/context-menu-checked-item.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@gouvfr-lasuite/ui-components": minor
---

✨(front) support checked items in ContextMenu

Move `isChecked` to the shared `MenuItemAction` type so a ContextMenu item can
display the trailing checkmark already available in DropdownMenu.
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export const ContextMenuProvider = ({ children }: PropsWithChildren) => {
icon={item.icon}
label={item.label}
subText={item.subText}
isChecked={item.isChecked}
/>
</AriaMenuItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import { MenuItem } from "./types";
* | `callback` | `() => void` | Callback on click |
* | `isDisabled` | `boolean` | Disables the item |
* | `isHidden` | `boolean` | Hides the item |
* | `isChecked` | `boolean` | Shows a trailing checkmark (selected/active item) |
* | `variant` | `"default" \| "danger"` | Item style |
* | `keepOpen` | `boolean` | Prevents menu from closing on click |
*
Expand Down Expand Up @@ -529,6 +530,72 @@ export const FocusHighlighting: Story = {
),
};

const SelectedOptionContent = () => {
const [sortBy, setSortBy] = useState("name");

const sortItems: MenuItem[] = [
{ label: "Sort by", isDisabled: true },
{
label: "Name",
icon: <span className="material-icons">sort_by_alpha</span>,
isChecked: sortBy === "name",
callback: () => setSortBy("name"),
},
{
label: "Date modified",
icon: <span className="material-icons">schedule</span>,
isChecked: sortBy === "date",
callback: () => setSortBy("date"),
},
{
label: "Size",
icon: <span className="material-icons">data_usage</span>,
isChecked: sortBy === "size",
callback: () => setSortBy("size"),
},
{ type: "separator" },
{
label: "Refresh",
icon: <span className="material-icons">refresh</span>,
callback: () => console.log("Refresh"),
},
];

return (
<ContextMenu options={sortItems}>
<div
style={{
padding: "40px",
border: "2px dashed #ccc",
borderRadius: "8px",
textAlign: "center",
backgroundColor: "#f9f9f9",
}}
>
<p>Right-click here to pick a sort order</p>
<p style={{ fontSize: "14px", color: "#666", marginTop: "8px" }}>
Current sort: {sortBy}
</p>
</div>
</ContextMenu>
);
};

/**
* Items with `isChecked: true` display a trailing checkmark, which marks the
* currently selected/active option (same rendering as `DropdownMenu`).
*
* Combine it with `keepOpen: true` if the user should be able to change the
* selection several times without reopening the menu.
*/
export const SelectedOption: Story = {
args: {
options: [],
children: null,
},
render: () => <SelectedOptionContent />,
};

const WithModalContent = () => {
const modal = useModal();

Expand Down
3 changes: 1 addition & 2 deletions packages/ui-components/src/components/dropdown-menu/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { MenuItemAction, MenuItemSeparator } from "../menu/types";

/**
* DropdownMenu option extending the shared MenuItemAction.
* Adds selection-specific props: isChecked, value.
* Adds the selection value used with `selectedValues` (`isChecked` is shared).
*/
export type DropdownMenuOption = MenuItemAction & {
isChecked?: boolean;
value?: string;
/** @deprecated Use MenuItem with { type: "separator" } instead */
showSeparator?: boolean;
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-components/src/components/menu/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export type MenuItemAction = {
callback?: () => void | Promise<unknown>;
isDisabled?: boolean;
isHidden?: boolean;
/** Renders a trailing checkmark to mark the item as selected/active. */
isChecked?: boolean;
variant?: "default" | "danger";
keepOpen?: boolean;
testId?: string;
Expand Down
Loading