diff --git a/packages/calcite-components/src/components.d.ts b/packages/calcite-components/src/components.d.ts index 718aac02da7..a6ee8543333 100644 --- a/packages/calcite-components/src/components.d.ts +++ b/packages/calcite-components/src/components.d.ts @@ -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. */ @@ -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`. */ @@ -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. */ @@ -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. */ @@ -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. */ @@ -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. */ @@ -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. */ @@ -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`. */ @@ -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. */ @@ -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. @@ -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. */ @@ -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. */ diff --git a/packages/calcite-components/src/components/action-group/action-group.e2e.ts b/packages/calcite-components/src/components/action-group/action-group.e2e.ts index 288761da844..c2ba175fd86 100755 --- a/packages/calcite-components/src/components/action-group/action-group.e2e.ts +++ b/packages/calcite-components/src/components/action-group/action-group.e2e.ts @@ -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"; @@ -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", + }, ]); }); @@ -42,6 +70,15 @@ describe("calcite-action-group", () => { slots("calcite-action-group", SLOTS); }); + describe("handles action-menu placement and flipPlacements", () => { + handlesActionMenuPlacements(html` + + + + + `); + }); + 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`); diff --git a/packages/calcite-components/src/components/action-group/action-group.tsx b/packages/calcite-components/src/components/action-group/action-group.tsx index 4d9f309e002..df224f634e9 100755 --- a/packages/calcite-components/src/components/action-group/action-group.tsx +++ b/packages/calcite-components/src/components/action-group/action-group.tsx @@ -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"; @@ -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; + /** * Made into a prop for testing purposes only * @@ -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 ( diff --git a/packages/calcite-components/src/components/panel/panel.e2e.ts b/packages/calcite-components/src/components/panel/panel.e2e.ts index e13772267a4..7900f2fb432 100644 --- a/packages/calcite-components/src/components/panel/panel.e2e.ts +++ b/packages/calcite-components/src/components/panel/panel.e2e.ts @@ -12,8 +12,10 @@ import { slots, t9n, themed, + handlesActionMenuPlacements, } from "../../tests/commonTests"; import { GlobalTestProps } from "../../tests/utils"; +import { defaultEndMenuPlacement } from "../../utils/floating-ui"; import { CSS, IDS, SLOTS } from "./resources"; type TestWindow = GlobalTestProps<{ @@ -88,6 +90,14 @@ describe("calcite-panel", () => { hidden("calcite-panel"); }); + describe("handles action-menu placement and flipPlacements", () => { + handlesActionMenuPlacements(html` + + + + `); + }); + describe("defaults", () => { defaults("calcite-panel", [ { @@ -122,6 +132,14 @@ describe("calcite-panel", () => { propertyName: "scale", defaultValue: "m", }, + { + propertyName: "menuPlacement", + defaultValue: defaultEndMenuPlacement, + }, + { + propertyName: "menuFlipPlacements", + defaultValue: undefined, + }, ]); }); @@ -143,6 +161,10 @@ describe("calcite-panel", () => { propertyName: "overlayPositioning", value: "fixed", }, + { + propertyName: "menuPlacement", + value: "bottom", + }, ]); }); diff --git a/packages/calcite-components/src/components/panel/panel.stories.ts b/packages/calcite-components/src/components/panel/panel.stories.ts index d6a48a5ecc3..9f318bbcd71 100644 --- a/packages/calcite-components/src/components/panel/panel.stories.ts +++ b/packages/calcite-components/src/components/panel/panel.stories.ts @@ -1,6 +1,7 @@ import { boolean, modesDarkDefault } from "../../../.storybook/utils"; import { html } from "../../../support/formatting"; import { ATTRIBUTES } from "../../../.storybook/resources"; +import { defaultEndMenuPlacement, placements } from "../../utils/floating-ui"; import { Panel } from "./panel"; import { SLOTS } from "./resources"; const { collapseDirection, scale } = ATTRIBUTES; @@ -8,7 +9,15 @@ const { collapseDirection, scale } = ATTRIBUTES; interface PanelStoryArgs extends Pick< Panel, - "closed" | "disabled" | "closable" | "collapsed" | "collapsible" | "collapseDirection" | "loading" | "scale" + | "closed" + | "disabled" + | "closable" + | "collapsed" + | "collapsible" + | "collapseDirection" + | "loading" + | "scale" + | "menuPlacement" > { heightScale: string; } @@ -16,6 +25,7 @@ interface PanelStoryArgs export default { title: "Components/Panel", args: { + menuPlacement: defaultEndMenuPlacement, closed: false, disabled: false, closable: false, @@ -27,6 +37,10 @@ export default { loading: false, }, argTypes: { + menuPlacement: { + options: placements, + control: { type: "select" }, + }, collapseDirection: { options: collapseDirection.values, control: { type: "select" }, @@ -91,6 +105,7 @@ export const simple = (args: PanelStoryArgs): string => html` heightScale="${args.heightScale}" scale="${args.scale}" ${boolean("loading", args.loading)} + menu-placement="${args.menuPlacement}" heading="Heading" description="A great panel description" > diff --git a/packages/calcite-components/src/components/panel/panel.tsx b/packages/calcite-components/src/components/panel/panel.tsx index 47245776ec7..5219f432203 100644 --- a/packages/calcite-components/src/components/panel/panel.tsx +++ b/packages/calcite-components/src/components/panel/panel.tsx @@ -39,7 +39,12 @@ import { T9nComponent, updateMessages, } from "../../utils/t9n"; -import { OverlayPositioning } from "../../utils/floating-ui"; +import { + defaultEndMenuPlacement, + FlipPlacement, + LogicalPlacement, + OverlayPositioning, +} from "../../utils/floating-ui"; import { CollapseDirection } from "../interfaces"; import { Scale } from "../interfaces"; import { PanelMessages } from "./assets/panel/t9n"; @@ -130,11 +135,21 @@ export class Panel /** A description for the component. */ @Prop() description: string; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + @Prop() menuFlipPlacements: FlipPlacement[]; + /** * When `true`, the action menu items in the `header-menu-actions` slot are open. */ @Prop({ reflect: true }) menuOpen = false; + /** + * Determines where the action menu will be positioned. + */ + @Prop({ reflect: true }) menuPlacement: LogicalPlacement = defaultEndMenuPlacement; + /** * Use this property to override individual strings used by the component. */ @@ -548,17 +563,17 @@ export class Panel } renderMenu(): VNode { - const { hasMenuItems, messages, menuOpen } = this; + const { hasMenuItems, messages, menuOpen, menuFlipPlacements, menuPlacement } = this; return (