Skip to content

Commit f56c625

Browse files
authored
fix(shell): update shell to correctly position calcite shell panel at shell's bottom (#9748)
**Related Issue:** [#8269](#8269) ## Summary Update `calcite-shell` to correctly position `calcite-shell-panel` with `slot="panel-bottom"` at calcite-shell's bottom, when there is no `calcite-shell-panel` with `slot="panel-top"`.
1 parent e74edd5 commit f56c625

File tree

5 files changed

+95
-5
lines changed

5 files changed

+95
-5
lines changed

packages/calcite-components/src/components/shell/resources.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export const CSS = {
22
main: "main",
33
content: "content",
44
contentBehind: "content--behind",
5+
contentBottom: "content-bottom",
56
contentNonInteractive: "content--non-interactive",
67
footer: "footer",
78
positionedSlotWrapper: "positioned-slot-wrapper",

packages/calcite-components/src/components/shell/shell.e2e.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,22 @@ describe("calcite-shell", () => {
121121
const panelTop = await contentNode.find(`slot[name="${SLOTS.panelTop}"]`);
122122
expect(panelTop).toBeNull();
123123
});
124+
125+
it("should position panel-bottom slot at content's bottom when no other panels exist", async () => {
126+
const page = await newE2EPage();
127+
128+
await page.setContent(
129+
html`<calcite-shell>
130+
<calcite-shell-panel slot="${SLOTS.panelBottom}" display-mode="float" layout="horizontal">
131+
<p>Primary Content</p>
132+
</calcite-shell-panel>
133+
</calcite-shell>`,
134+
);
135+
136+
await page.waitForChanges();
137+
138+
const contentBottom = await page.find(`calcite-shell >>> .${CSS.contentBottom}`);
139+
140+
expect(contentBottom).not.toBeNull();
141+
});
124142
});

packages/calcite-components/src/components/shell/shell.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
.content {
4242
@apply overflow-auto;
43+
justify-content: space-between;
4344
}
4445

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

101+
.content-bottom {
102+
justify-content: flex-end;
103+
}
104+
100105
::slotted(calcite-shell-center-row) {
101106
flex: none;
102107
align-self: stretch;

packages/calcite-components/src/components/shell/shell.stories.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ const centerRowHTML = html`
9999
</calcite-panel>
100100
`;
101101

102+
const bottomPanelHTML = html`
103+
<calcite-panel heading="Panel bottom content">
104+
<div>Content</div>
105+
</calcite-panel>
106+
`;
107+
102108
const centerRowWithActionBarHTML = html`
103109
<calcite-action-bar slot="action-bar">
104110
<calcite-action-group>
@@ -842,6 +848,30 @@ export const slottedPanelTop_TestOnly = (): string =>
842848
</calcite-shell>
843849
`);
844850

851+
export const contentBehindPanelBottom = (): string =>
852+
html(`
853+
<calcite-shell
854+
content-behind
855+
style="
856+
width:700px;
857+
height:700px;
858+
position:relative;
859+
"
860+
>
861+
<div
862+
style="
863+
width:100%;
864+
height:100%;
865+
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
866+
linear-gradient(-45deg, #ccc 25%, transparent 25%),
867+
linear-gradient(45deg, transparent 75%, #ccc 75%),
868+
linear-gradient(-45deg, transparent 75%, #ccc 75%);
869+
background-size: 20px 20px;
870+
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;"></div>
871+
<calcite-shell-panel slot="panel-bottom" display-mode="float" layout="horizontal">${bottomPanelHTML}</calcite-shell-panel>
872+
</calcite-shell>
873+
`);
874+
845875
export const slottedPanelBottom_TestOnly = (): string =>
846876
html(`
847877
<calcite-shell

packages/calcite-components/src/components/shell/shell.tsx

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Element, Fragment, h, Listen, Prop, State, VNode } from "@stencil/core";
1+
import { Component, Element, Fragment, h, Listen, Prop, State, VNode, Watch } from "@stencil/core";
22
import {
33
ConditionalSlotComponent,
44
connectConditionalSlotComponent,
@@ -74,8 +74,20 @@ export class Shell implements ConditionalSlotComponent {
7474

7575
@State() hasSheets = false;
7676

77+
@State() hasPanelTop = false;
78+
79+
@State() hasPanelBottom = false;
80+
81+
@State() hasOnlyPanelBottom = false;
82+
7783
@State() panelIsResizing = false;
7884

85+
@Watch("hasPanelTop")
86+
@Watch("hasPanelBottom")
87+
updateHasOnlyPanelBottom(): void {
88+
this.hasOnlyPanelBottom = !this.hasPanelTop && this.hasPanelBottom;
89+
}
90+
7991
// --------------------------------------------------------------------------
8092
//
8193
// Lifecycle
@@ -131,6 +143,14 @@ export class Shell implements ConditionalSlotComponent {
131143
});
132144
};
133145

146+
handlePanelTopChange = (event: Event): void => {
147+
this.hasPanelTop = slotChangeHasAssignedElement(event);
148+
};
149+
150+
handlePanelBottomChange = (event: Event): void => {
151+
this.hasPanelBottom = slotChangeHasAssignedElement(event);
152+
};
153+
134154
// --------------------------------------------------------------------------
135155
//
136156
// Render Methods
@@ -188,8 +208,16 @@ export class Shell implements ConditionalSlotComponent {
188208
const deprecatedCenterRowSlotNode: VNode = (
189209
<slot key="center-row-slot" name={SLOTS.centerRow} />
190210
);
191-
const panelBottomSlotNode: VNode = <slot key="panel-bottom-slot" name={SLOTS.panelBottom} />;
192-
const panelTopSlotNode: VNode = <slot key="panel-top-slot" name={SLOTS.panelTop} />;
211+
const panelBottomSlotNode: VNode = (
212+
<slot
213+
key="panel-bottom-slot"
214+
name={SLOTS.panelBottom}
215+
onSlotchange={this.handlePanelBottomChange}
216+
/>
217+
);
218+
const panelTopSlotNode: VNode = (
219+
<slot key="panel-top-slot" name={SLOTS.panelTop} onSlotchange={this.handlePanelTopChange} />
220+
);
193221

194222
const contentContainerKey = "content-container";
195223

@@ -204,14 +232,22 @@ export class Shell implements ConditionalSlotComponent {
204232
>
205233
{defaultSlotContainerNode}
206234
</div>,
207-
<div class={CSS.contentBehindCenterContent}>
235+
<div
236+
class={{
237+
[CSS.contentBehindCenterContent]: true,
238+
[CSS.contentBottom]: this.hasOnlyPanelBottom,
239+
}}
240+
>
208241
{panelTopSlotNode}
209242
{panelBottomSlotNode}
210243
{deprecatedCenterRowSlotNode}
211244
</div>,
212245
]
213246
: [
214-
<div class={CSS.content} key={contentContainerKey}>
247+
<div
248+
class={{ [CSS.content]: true, [CSS.contentBottom]: this.hasOnlyPanelBottom }}
249+
key={contentContainerKey}
250+
>
215251
{panelTopSlotNode}
216252
{defaultSlotContainerNode}
217253
{panelBottomSlotNode}

0 commit comments

Comments
 (0)