Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(action-group, block, panel): add menuPlacement and menuFlipPlacements properties #10249

Merged
merged 12 commits into from
Sep 25, 2024
48 changes: 48 additions & 0 deletions packages/calcite-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,10 @@ export namespace Components {
* When `true`, the component is expanded.
*/
"expanded": boolean;
/**
* Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available.
*/
"flipPlacements": FlipPlacement[];
/**
* Accessible name for the component.
*/
Expand All @@ -420,6 +424,10 @@ export namespace Components {
* Determines the type of positioning to use for the overlaid content. Using `"absolute"` will work for most cases. The component will be positioned inside of overflowing parent containers and will affect the container's layout. `"fixed"` should be used to escape an overflowing parent container, or when the reference element's `position` CSS property is `"fixed"`.
*/
"overlayPositioning": OverlayPositioning;
/**
* Determines where the action menu will be positioned.
*/
"placement": LogicalPlacement;
/**
* Specifies the size of the `calcite-action-menu`.
*/
Expand Down Expand Up @@ -626,6 +634,10 @@ export namespace Components {
* When `true`, displays a drag handle in the header.
*/
"dragHandle": boolean;
/**
* Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available.
*/
"flipPlacements": FlipPlacement[];
/**
* The component header text.
*/
Expand Down Expand Up @@ -666,6 +678,10 @@ export namespace Components {
* Determines the type of positioning to use for the overlaid content. Using `"absolute"` will work for most cases. The component will be positioned inside of overflowing parent containers and will affect the container's layout. `"fixed"` should be used to escape an overflowing parent container, or when the reference element's `position` CSS property is `"fixed"`.
*/
"overlayPositioning": OverlayPositioning;
/**
* Determines where the action menu will be positioned.
*/
"placement": LogicalPlacement;
/**
* Sets focus on the component's first tabbable element.
*/
Expand Down Expand Up @@ -3858,6 +3874,10 @@ export namespace Components {
* When `true`, interaction is prevented and the component is displayed with lower opacity.
*/
"disabled": boolean;
/**
* Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available.
*/
"flipPlacements": FlipPlacement[];
/**
* The component header text.
*/
Expand Down Expand Up @@ -3886,6 +3906,10 @@ export namespace Components {
* Determines the type of positioning to use for the overlaid content. Using `"absolute"` will work for most cases. The component will be positioned inside of overflowing parent containers and will affect the container's layout. `"fixed"` should be used to escape an overflowing parent container, or when the reference element's `position` CSS property is `"fixed"`.
*/
"overlayPositioning": OverlayPositioning;
/**
* Determines where the action menu will be positioned.
*/
"placement": LogicalPlacement;
/**
* Specifies the size of the component.
*/
Expand Down Expand Up @@ -8321,6 +8345,10 @@ declare namespace LocalJSX {
* When `true`, the component is expanded.
*/
"expanded"?: boolean;
/**
* Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available.
*/
"flipPlacements"?: FlipPlacement[];
/**
* Accessible name for the component.
*/
Expand All @@ -8346,6 +8374,10 @@ declare namespace LocalJSX {
* Determines the type of positioning to use for the overlaid content. Using `"absolute"` will work for most cases. The component will be positioned inside of overflowing parent containers and will affect the container's layout. `"fixed"` should be used to escape an overflowing parent container, or when the reference element's `position` CSS property is `"fixed"`.
*/
"overlayPositioning"?: OverlayPositioning;
/**
* Determines where the action menu will be positioned.
*/
"placement"?: LogicalPlacement;
/**
* Specifies the size of the `calcite-action-menu`.
*/
Expand Down Expand Up @@ -8559,6 +8591,10 @@ declare namespace LocalJSX {
* When `true`, displays a drag handle in the header.
*/
"dragHandle"?: boolean;
/**
* Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available.
*/
"flipPlacements"?: FlipPlacement[];
/**
* The component header text.
*/
Expand Down Expand Up @@ -8620,6 +8656,10 @@ declare namespace LocalJSX {
* Determines the type of positioning to use for the overlaid content. Using `"absolute"` will work for most cases. The component will be positioned inside of overflowing parent containers and will affect the container's layout. `"fixed"` should be used to escape an overflowing parent container, or when the reference element's `position` CSS property is `"fixed"`.
*/
"overlayPositioning"?: OverlayPositioning;
/**
* Determines where the action menu will be positioned.
*/
"placement"?: LogicalPlacement;
/**
* Displays a status-related indicator icon.
* @deprecated Use `icon-start` instead.
Expand Down Expand Up @@ -11980,6 +12020,10 @@ declare namespace LocalJSX {
* When `true`, interaction is prevented and the component is displayed with lower opacity.
*/
"disabled"?: boolean;
/**
* Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available.
*/
"flipPlacements"?: FlipPlacement[];
/**
* The component header text.
*/
Expand Down Expand Up @@ -12020,6 +12064,10 @@ declare namespace LocalJSX {
* Determines the type of positioning to use for the overlaid content. Using `"absolute"` will work for most cases. The component will be positioned inside of overflowing parent containers and will affect the container's layout. `"fixed"` should be used to escape an overflowing parent container, or when the reference element's `position` CSS property is `"fixed"`.
*/
"overlayPositioning"?: OverlayPositioning;
/**
* Determines where the action menu will be positioned.
*/
"placement"?: LogicalPlacement;
/**
* Specifies the size of the component.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, defaults, focusable, hidden, renders, slots, t9n, themed } from "../../tests/commonTests";
import {
accessible,
defaults,
focusable,
handlesActionMenuPlacements,
hidden,
reflects,
renders,
slots,
t9n,
themed,
} from "../../tests/commonTests";
import { html } from "../../../support/formatting";
import { CSS, SLOTS } from "./resources";

Expand All @@ -19,6 +30,23 @@ describe("calcite-action-group", () => {
propertyName: "overlayPositioning",
defaultValue: "absolute",
},
{
propertyName: "menuPlacement",
defaultValue: undefined,
},
{
propertyName: "menuFlipPlacements",
defaultValue: undefined,
},
]);
});

describe("reflects", () => {
reflects("calcite-action-group", [
{
propertyName: "menuPlacement",
value: "bottom",
},
]);
});

Expand All @@ -42,6 +70,15 @@ describe("calcite-action-group", () => {
slots("calcite-action-group", SLOTS);
});

describe("handles action-menu placement and flipPlacements", () => {
handlesActionMenuPlacements(html`
<calcite-action-group scale="l" overlay-positioning="fixed">
<calcite-action id="plus" slot="${SLOTS.menuActions}" text="Add" icon="plus"></calcite-action>
<calcite-action id="banana" slot="${SLOTS.menuActions}" text="Banana" icon="banana"></calcite-action>
</calcite-action-group>
`);
});

it("should honor scale of expand icon", async () => {
const page = await newE2EPage({ html: actionGroupHTML });
const menu = await page.find(`calcite-action-group >>> calcite-action-menu`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from "../../utils/t9n";
import { SLOTS as ACTION_MENU_SLOTS } from "../action-menu/resources";
import { Layout, Scale } from "../interfaces";
import { OverlayPositioning } from "../../utils/floating-ui";
import { FlipPlacement, LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui";
import { slotChangeHasAssignedElement } from "../../utils/dom";
import { Columns } from "./interfaces";
import { ActionGroupMessages } from "./assets/action-group/t9n";
Expand Down Expand Up @@ -95,6 +95,16 @@ export class ActionGroup
*/
@Prop({ reflect: true }) scale: Scale;

/**
* Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available.
*/
@Prop() menuFlipPlacements: FlipPlacement[];

/**
* Determines where the action menu will be positioned.
*/
@Prop({ reflect: true }) menuPlacement: LogicalPlacement;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we prefer actionMenuPlacement or is menuPlacement sufficient?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the current name is clear and concise.


/**
* Made into a prop for testing purposes only
*
Expand Down Expand Up @@ -178,19 +188,30 @@ export class ActionGroup
// --------------------------------------------------------------------------

renderMenu(): VNode {
const { expanded, menuOpen, scale, layout, messages, overlayPositioning, hasMenuActions } =
this;
const {
expanded,
menuOpen,
scale,
layout,
messages,
overlayPositioning,
hasMenuActions,
menuFlipPlacements,
menuPlacement,
} = this;

return (
<calcite-action-menu
expanded={expanded}
flipPlacements={["left", "right"]}
flipPlacements={
menuFlipPlacements ?? (layout === "horizontal" ? ["top", "bottom"] : ["left", "right"])
}
hidden={!hasMenuActions}
label={messages.more}
onCalciteActionMenuOpen={this.setMenuOpen}
open={menuOpen}
overlayPositioning={overlayPositioning}
placement={layout === "horizontal" ? "bottom-start" : "leading-start"}
placement={menuPlacement ?? (layout === "horizontal" ? "bottom-start" : "leading-start")}
scale={scale}
>
<calcite-action
Expand Down
23 changes: 23 additions & 0 deletions packages/calcite-components/src/components/block/block.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
delegatesToFloatingUiOwningComponent,
disabled,
focusable,
handlesActionMenuPlacements,
hidden,
reflects,
renders,
Expand All @@ -14,6 +15,7 @@ import {
import { html } from "../../../support/formatting";
import { openClose } from "../../tests/commonTests";
import { skipAnimations } from "../../tests/utils";
import { defaultEndMenuPlacement } from "../../utils/floating-ui";
import { CSS, SLOTS } from "./resources";

describe("calcite-block", () => {
Expand Down Expand Up @@ -43,6 +45,14 @@ describe("calcite-block", () => {
propertyName: "overlayPositioning",
defaultValue: "absolute",
},
{
propertyName: "menuPlacement",
defaultValue: defaultEndMenuPlacement,
},
{
propertyName: "menuFlipPlacements",
defaultValue: undefined,
},
]);
});

Expand All @@ -64,6 +74,10 @@ describe("calcite-block", () => {
propertyName: "overlayPositioning",
value: "fixed",
},
{
propertyName: "menuPlacement",
value: "bottom",
},
]);
});

Expand Down Expand Up @@ -133,6 +147,15 @@ describe("calcite-block", () => {
);
});

describe("handles action-menu placement and flipPlacements", () => {
handlesActionMenuPlacements(html`
<calcite-block heading="heading" description="description">
<calcite-action text="test" icon="banana" slot="${SLOTS.headerMenuActions}"></calcite-action>
<div class="content">content</div>
</calcite-block>
`);
});

it("has a loading state", async () => {
const page = await newE2EPage({
html: `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { boolean } from "../../../.storybook/utils";
import { placeholderImage } from "../../../.storybook/placeholder-image";
import { html } from "../../../support/formatting";
import { ATTRIBUTES } from "../../../.storybook/resources";
import { defaultEndMenuPlacement, placements } from "../../utils/floating-ui";
import { Block } from "./block";
const { toggleDisplay } = ATTRIBUTES;

interface BlockStoryArgs
extends Pick<Block, "heading" | "description" | "open" | "collapsible" | "loading" | "disabled" | "headingLevel">,
extends Pick<
Block,
"heading" | "description" | "open" | "collapsible" | "loading" | "disabled" | "headingLevel" | "menuPlacement"
>,
Pick<BlockSection, "toggleDisplay"> {
text: string;
sectionOpen: BlockSection["open"];
Expand All @@ -16,6 +20,7 @@ interface BlockStoryArgs
export default {
title: "Components/Block",
args: {
menuPlacement: defaultEndMenuPlacement,
heading: "Heading",
description: "description",
open: true,
Expand All @@ -28,6 +33,10 @@ export default {
toggleDisplay: toggleDisplay.defaultValue,
},
argTypes: {
menuPlacement: {
options: placements,
control: { type: "select" },
},
headingLevel: {
control: { type: "number", min: 1, max: 6, step: 1 },
},
Expand All @@ -42,6 +51,7 @@ export const simple = (args: BlockStoryArgs): string => html`
<calcite-block
heading="${args.heading}"
description="${args.description}"
menu-placement="${args.menuPlacement}"
${boolean("open", args.open)}
${boolean("collapsible", args.collapsible)}
${boolean("loading", args.loading)}
Expand Down
Loading
Loading