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;
/**
* Defines the available placements that can be used when a flip occurs.
*/
"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;
/**
* Defines the available placements that can be used when a flip occurs.
*/
"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;
/**
* Defines the available placements that can be used when a flip occurs.
*/
"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;
/**
* Defines the available placements that can be used when a flip occurs.
*/
"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;
/**
* Defines the available placements that can be used when a flip occurs.
*/
"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;
/**
* Defines the available placements that can be used when a flip occurs.
*/
"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,15 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, defaults, focusable, hidden, renders, slots, t9n, themed } from "../../tests/commonTests";
import {
accessible,
defaults,
focusable,
hidden,
reflects,
renders,
slots,
t9n,
themed,
} from "../../tests/commonTests";
import { html } from "../../../support/formatting";
import { CSS, SLOTS } from "./resources";

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

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

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

it("sets placement and flipPlacements on internal calcite-action-menu", async () => {
const page = await newE2EPage({
html: html`
<calcite-action-group scale="l" overlay-positioning="fixed" placement="top">
<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>
`,
});
await page.waitForChanges();

const flipPlacements = ["top", "bottom"];

const actionGroup = await page.find("calcite-action-group");
actionGroup.setProperty("flipPlacements", flipPlacements);
await page.waitForChanges();

const actionMenu = await page.find("calcite-action-group >>> calcite-action-menu");

expect(await actionMenu.getProperty("placement")).toBe("top");
expect(await actionMenu.getProperty("flipPlacements")).toEqual(flipPlacements);
});

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 @@ -59,6 +59,11 @@ export class ActionGroup
this.menuOpen = false;
}

/**
* Defines the available placements that can be used when a flip occurs.
*/
@Prop() flipPlacements: FlipPlacement[];

/**
* Accessible name for the component.
*/
Expand Down Expand Up @@ -90,6 +95,11 @@ export class ActionGroup
*/
@Prop({ reflect: true }) overlayPositioning: OverlayPositioning = "absolute";

/**
* Determines where the action menu will be positioned.
*/
@Prop({ reflect: true }) placement: LogicalPlacement;

/**
* Specifies the size of the `calcite-action-menu`.
*/
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,
flipPlacements,
placement,
} = this;

return (
<calcite-action-menu
expanded={expanded}
flipPlacements={["left", "right"]}
flipPlacements={
flipPlacements ?? (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={placement ?? (layout === "horizontal" ? "bottom-start" : "leading-start")}
scale={scale}
>
<calcite-action
Expand Down
37 changes: 36 additions & 1 deletion packages/calcite-components/src/components/block/block.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { html } from "../../../support/formatting";
import { openClose } from "../../tests/commonTests";
import { skipAnimations } from "../../tests/utils";
import { CSS, SLOTS } from "./resources";
import { CSS, defaultMenuPlacement, SLOTS } from "./resources";

describe("calcite-block", () => {
describe("renders", () => {
Expand Down Expand Up @@ -43,6 +43,14 @@ describe("calcite-block", () => {
propertyName: "overlayPositioning",
defaultValue: "absolute",
},
{
propertyName: "placement",
defaultValue: defaultMenuPlacement,
},
{
propertyName: "flipPlacements",
defaultValue: undefined,
},
]);
});

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

Expand Down Expand Up @@ -161,6 +173,29 @@ describe("calcite-block", () => {
expect(await page.find("calcite-block >>> calcite-scrim")).toBeTruthy();
});

it("sets placement and flipPlacements on internal calcite-action-menu", async () => {
const page = await newE2EPage({
html: html`
driskull marked this conversation as resolved.
Show resolved Hide resolved
<calcite-block heading="heading" description="description" placement="top">
<calcite-action text="test" icon="banana" slot="${SLOTS.headerMenuActions}"></calcite-action>
<div class="content">content</div>
</calcite-block>
`,
});
await page.waitForChanges();

const flipPlacements = ["top", "bottom"];

const block = await page.find("calcite-block");
block.setProperty("flipPlacements", flipPlacements);
await page.waitForChanges();

const actionMenu = await page.find("calcite-block >>> calcite-action-menu");

expect(await actionMenu.getProperty("placement")).toBe("top");
expect(await actionMenu.getProperty("flipPlacements")).toEqual(flipPlacements);
});

it("can display/hide content", async () => {
const page = await newE2EPage();
await page.setContent("<calcite-block><div>some content</div></calcite-block>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ import { boolean } from "../../../.storybook/utils";
import { placeholderImage } from "../../../.storybook/placeholder-image";
import { html } from "../../../support/formatting";
import { ATTRIBUTES } from "../../../.storybook/resources";
import { placements } from "../../utils/floating-ui";
import { Block } from "./block";
import { defaultMenuPlacement } from "./resources";
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" | "placement"
>,
Pick<BlockSection, "toggleDisplay"> {
text: string;
sectionOpen: BlockSection["open"];
Expand All @@ -16,6 +21,7 @@ interface BlockStoryArgs
export default {
title: "Components/Block",
args: {
placement: defaultMenuPlacement,
heading: "Heading",
description: "description",
open: true,
Expand All @@ -28,6 +34,10 @@ export default {
toggleDisplay: toggleDisplay.defaultValue,
},
argTypes: {
placement: {
options: placements,
control: { type: "select" },
},
headingLevel: {
control: { type: "number", min: 1, max: 6, step: 1 },
},
Expand All @@ -42,6 +52,7 @@ export const simple = (args: BlockStoryArgs): string => html`
<calcite-block
heading="${args.heading}"
description="${args.description}"
placement="${args.placement}"
${boolean("open", args.open)}
${boolean("collapsible", args.collapsible)}
${boolean("loading", args.loading)}
Expand Down
Loading
Loading