feat(content): add fixedSlotPlacement prop (#29233)

Issue number: Internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->
Content in the `fixed` slot is always placed after the main content in
the DOM.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- A new `fixedSlotPlacement` prop on Content allows developers to place
fixed content either before or after the main content in the DOM

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer
for more information.
-->


## Other information

Dev build: `8.0.0-dev.11712072527.1dd97c66`

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

⚠️This feature will not be part of the v8.0 release. As a result, do not
merge this into `feature-8.0`. However, I am putting this PR up based
off `feature-8.0` so it can get reviewed by the team.

---------

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
This commit is contained in:
Shawn Taylor
2024-04-02 11:52:02 -04:00
committed by Liam DeBeasi
parent 0f5d1c02d2
commit 90a7e70a1c
7 changed files with 59 additions and 6 deletions

View File

@ -75,6 +75,15 @@ export class Content implements ComponentInterface {
*/
@Prop() fullscreen = false;
/**
* Controls where the fixed content is placed relative to the main content
* in the DOM. This can be used to control the order in which fixed elements
* receive keyboard focus.
* For example, if a FAB in the fixed slot should receive keyboard focus before
* the main page content, set this property to `'before'`.
*/
@Prop() fixedSlotPlacement: 'after' | 'before' = 'after';
/**
* If `true` and the content does not cause an overflow scroll, the scroll interaction will cause a bounce.
* If the content exceeds the bounds of ionContent, nothing will change.
@ -423,7 +432,7 @@ export class Content implements ComponentInterface {
}
render() {
const { isMainContent, scrollX, scrollY, el } = this;
const { fixedSlotPlacement, isMainContent, scrollX, scrollY, el } = this;
const rtl = isRTL(el) ? 'rtl' : 'ltr';
const mode = getIonMode(this);
const forceOverscroll = this.shouldForceOverscroll();
@ -446,6 +455,9 @@ export class Content implements ComponentInterface {
}}
>
<div ref={(el) => (this.backgroundContentEl = el)} id="background-content" part="background"></div>
{fixedSlotPlacement === 'before' ? <slot name="fixed"></slot> : null}
<div
class={{
'inner-scroll': true,
@ -467,7 +479,7 @@ export class Content implements ComponentInterface {
</div>
) : null}
<slot name="fixed"></slot>
{fixedSlotPlacement === 'after' ? <slot name="fixed"></slot> : null}
</Host>
);
}

View File

@ -0,0 +1,31 @@
import { newSpecPage } from '@stencil/core/testing';
import { Content } from '../content';
describe('content: fixed slot placement', () => {
it('should should fixed slot after content', async () => {
const page = await newSpecPage({
components: [Content],
html: '<ion-content></ion-content>',
});
const content = page.body.querySelector('ion-content')!;
const fixedSlot = content.shadowRoot!.querySelector('slot[name="fixed"]')!;
const scrollEl = content.shadowRoot!.querySelector('[part="scroll"]')!;
expect(fixedSlot.nextElementSibling).not.toBe(scrollEl);
});
it('should should fixed slot before content', async () => {
const page = await newSpecPage({
components: [Content],
html: `<ion-content fixed-slot-placement="before"></ion-content>`,
});
const content = page.body.querySelector('ion-content')!;
const fixedSlot = content.shadowRoot!.querySelector('slot[name="fixed"]')!;
const scrollEl = content.shadowRoot!.querySelector('[part="scroll"]')!;
expect(fixedSlot.nextElementSibling).toBe(scrollEl);
});
});