Skip to content

fix(shell): update shell to correctly position calcite shell panel at shell's bottom #9748

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

Merged
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const CSS = {
main: "main",
content: "content",
contentBehind: "content--behind",
contentBottom: "content-bottom",
contentNonInteractive: "content--non-interactive",
footer: "footer",
positionedSlotWrapper: "positioned-slot-wrapper",
Expand Down
18 changes: 18 additions & 0 deletions packages/calcite-components/src/components/shell/shell.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,22 @@ describe("calcite-shell", () => {
const panelTop = await contentNode.find(`slot[name="${SLOTS.panelTop}"]`);
expect(panelTop).toBeNull();
});

it("should position panel-bottom slot at content's bottom when no other panels exist", async () => {
const page = await newE2EPage();

await page.setContent(
html`<calcite-shell>
<calcite-shell-panel slot="${SLOTS.panelBottom}" display-mode="float" layout="horizontal">
<p>Primary Content</p>
</calcite-shell-panel>
</calcite-shell>`,
);

await page.waitForChanges();

const contentBottom = await page.find(`calcite-shell >>> .${CSS.contentBottom}`);

expect(contentBottom).not.toBeNull();
});
});
5 changes: 5 additions & 0 deletions packages/calcite-components/src/components/shell/shell.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

.content {
@apply overflow-auto;
justify-content: space-between;
}

.content ::slotted(calcite-shell-center-row),
Expand Down Expand Up @@ -97,6 +98,10 @@ slot[name="panel-bottom"]::slotted(calcite-shell-center-row:not([detached])) {
min-inline-size: 0;
}

.content-bottom {
justify-content: flex-end;
}

::slotted(calcite-shell-center-row) {
flex: none;
align-self: stretch;
Expand Down
30 changes: 30 additions & 0 deletions packages/calcite-components/src/components/shell/shell.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ const centerRowHTML = html`
</calcite-panel>
`;

const bottomPanelHTML = html`
<calcite-panel heading="Panel bottom content">
<div>Content</div>
</calcite-panel>
`;

const centerRowWithActionBarHTML = html`
<calcite-action-bar slot="action-bar">
<calcite-action-group>
Expand Down Expand Up @@ -842,6 +848,30 @@ export const slottedPanelTop_TestOnly = (): string =>
</calcite-shell>
`);

export const contentBehindPanelBottom = (): string =>
html(`
<calcite-shell
content-behind
style="
width:700px;
height:700px;
position:relative;
"
>
<div
style="
width:100%;
height:100%;
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
linear-gradient(-45deg, #ccc 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #ccc 75%),
linear-gradient(-45deg, transparent 75%, #ccc 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;"></div>
<calcite-shell-panel slot="panel-bottom" display-mode="float" layout="horizontal">${bottomPanelHTML}</calcite-shell-panel>
</calcite-shell>
`);

export const slottedPanelBottom_TestOnly = (): string =>
html(`
<calcite-shell
Expand Down
46 changes: 41 additions & 5 deletions packages/calcite-components/src/components/shell/shell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Element, Fragment, h, Listen, Prop, State, VNode } from "@stencil/core";
import { Component, Element, Fragment, h, Listen, Prop, State, VNode, Watch } from "@stencil/core";
import {
ConditionalSlotComponent,
connectConditionalSlotComponent,
Expand Down Expand Up @@ -74,8 +74,20 @@ export class Shell implements ConditionalSlotComponent {

@State() hasSheets = false;

@State() hasPanelTop = false;

@State() hasPanelBottom = false;

@State() hasOnlyPanelBottom = false;

@State() panelIsResizing = false;

@Watch("hasPanelTop")
@Watch("hasPanelBottom")
updateHasOnlyPanelBottom(): void {
this.hasOnlyPanelBottom = !this.hasPanelTop && this.hasPanelBottom;
}

// --------------------------------------------------------------------------
//
// Lifecycle
Expand Down Expand Up @@ -131,6 +143,14 @@ export class Shell implements ConditionalSlotComponent {
});
};

handlePanelTopChange = (event: Event): void => {
this.hasPanelTop = slotChangeHasAssignedElement(event);
};

handlePanelBottomChange = (event: Event): void => {
this.hasPanelBottom = slotChangeHasAssignedElement(event);
};

// --------------------------------------------------------------------------
//
// Render Methods
Expand Down Expand Up @@ -188,8 +208,16 @@ export class Shell implements ConditionalSlotComponent {
const deprecatedCenterRowSlotNode: VNode = (
<slot key="center-row-slot" name={SLOTS.centerRow} />
);
const panelBottomSlotNode: VNode = <slot key="panel-bottom-slot" name={SLOTS.panelBottom} />;
const panelTopSlotNode: VNode = <slot key="panel-top-slot" name={SLOTS.panelTop} />;
const panelBottomSlotNode: VNode = (
<slot
key="panel-bottom-slot"
name={SLOTS.panelBottom}
onSlotchange={this.handlePanelBottomChange}
/>
);
const panelTopSlotNode: VNode = (
<slot key="panel-top-slot" name={SLOTS.panelTop} onSlotchange={this.handlePanelTopChange} />
);

const contentContainerKey = "content-container";

Expand All @@ -204,14 +232,22 @@ export class Shell implements ConditionalSlotComponent {
>
{defaultSlotContainerNode}
</div>,
<div class={CSS.contentBehindCenterContent}>
<div
class={{
[CSS.contentBehindCenterContent]: true,
[CSS.contentBottom]: this.hasOnlyPanelBottom,
}}
>
{panelTopSlotNode}
{panelBottomSlotNode}
{deprecatedCenterRowSlotNode}
</div>,
]
: [
<div class={CSS.content} key={contentContainerKey}>
<div
class={{ [CSS.content]: true, [CSS.contentBottom]: this.hasOnlyPanelBottom }}
key={contentContainerKey}
>
{panelTopSlotNode}
{defaultSlotContainerNode}
{panelBottomSlotNode}
Expand Down
Loading