Skip to content

Commit

Permalink
feat(action-group, block, panel): add 'placement' and 'flipPlacements…
Browse files Browse the repository at this point in the history
…' properties. #7516
  • Loading branch information
driskull committed Sep 9, 2024
1 parent ebdf6ff commit e31a3ce
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 12 deletions.
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
35 changes: 35 additions & 0 deletions packages/calcite-components/src/components/block/block.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ describe("calcite-block", () => {
propertyName: "overlayPositioning",
defaultValue: "absolute",
},
{
propertyName: "placement",
defaultValue: "bottom-end",
},
{
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`
<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
26 changes: 24 additions & 2 deletions packages/calcite-components/src/components/block/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
setUpLoadableComponent,
} from "../../utils/loadable";
import { onToggleOpenCloseComponent, OpenCloseComponent } from "../../utils/openCloseComponent";
import { OverlayPositioning } from "../../utils/floating-ui";
import { FlipPlacement, LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui";
import { FlipContext } from "../interfaces";
import { IconNameOrString } from "../icon/interfaces";
import { CSS, ICONS, IDS, SLOTS } from "./resources";
Expand Down Expand Up @@ -94,6 +94,11 @@ export class Block
*/
@Prop({ reflect: true }) dragHandle = false;

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

/**
* The component header text.
*/
Expand Down Expand Up @@ -128,6 +133,11 @@ export class Block
onToggleOpenCloseComponent(this);
}

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

/**
* Displays a status-related indicator icon.
*
Expand Down Expand Up @@ -405,7 +415,17 @@ export class Block
}

render(): VNode {
const { collapsible, el, loading, open, heading, messages, description } = this;
const {
collapsible,
el,
loading,
open,
heading,
messages,
description,
flipPlacements,
placement,
} = this;

const toggleLabel = open ? messages.collapse : messages.expand;

Expand Down Expand Up @@ -459,8 +479,10 @@ export class Block
) : null}
{hasMenuActions ? (
<calcite-action-menu
flipPlacements={flipPlacements ?? ["top", "bottom"]}
label={messages.options}
overlayPositioning={this.overlayPositioning}
placement={placement}
>
<slot name={SLOTS.headerMenuActions} />
</calcite-action-menu>
Expand Down
Loading

0 comments on commit e31a3ce

Please sign in to comment.