Skip to content

Commit

Permalink
feat(content): add fixedSlotPlacement prop (#29233)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mapsandapps and liamdebeasi committed Apr 3, 2024
1 parent 0f5d1c0 commit 90a7e70
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 6 deletions.
1 change: 1 addition & 0 deletions core/api.txt
Expand Up @@ -361,6 +361,7 @@ ion-col,css-prop,--ion-grid-columns

ion-content,shadow
ion-content,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-content,prop,fixedSlotPlacement,"after" | "before",'after',false,false
ion-content,prop,forceOverscroll,boolean | undefined,undefined,false,false
ion-content,prop,fullscreen,boolean,false,false,false
ion-content,prop,scrollEvents,boolean,false,false,false
Expand Down
8 changes: 8 additions & 0 deletions core/src/components.d.ts
Expand Up @@ -762,6 +762,10 @@ export namespace Components {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* 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'`.
*/
"fixedSlotPlacement": 'after' | 'before';
/**
* 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. Note, this does not disable the system bounce on iOS. That is an OS level setting.
*/
Expand Down Expand Up @@ -5482,6 +5486,10 @@ declare namespace LocalJSX {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* 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'`.
*/
"fixedSlotPlacement"?: 'after' | 'before';
/**
* 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. Note, this does not disable the system bounce on iOS. That is an OS level setting.
*/
Expand Down
16 changes: 14 additions & 2 deletions core/src/components/content/content.tsx
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand All @@ -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,
Expand All @@ -467,7 +479,7 @@ export class Content implements ComponentInterface {
</div>
) : null}

<slot name="fixed"></slot>
{fixedSlotPlacement === 'after' ? <slot name="fixed"></slot> : null}
</Host>
);
}
Expand Down
31 changes: 31 additions & 0 deletions core/src/components/content/test/content.spec.ts
@@ -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);
});
});
4 changes: 2 additions & 2 deletions packages/angular/src/directives/proxies.ts
Expand Up @@ -592,15 +592,15 @@ export declare interface IonCol extends Components.IonCol {}


@ProxyCmp({
inputs: ['color', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
methods: ['getScrollElement', 'scrollToTop', 'scrollToBottom', 'scrollByPoint', 'scrollToPoint']
})
@Component({
selector: 'ion-content',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
})
export class IonContent {
protected el: HTMLElement;
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/standalone/src/directives/proxies.ts
Expand Up @@ -659,15 +659,15 @@ export declare interface IonCol extends Components.IonCol {}

@ProxyCmp({
defineCustomElementFn: defineIonContent,
inputs: ['color', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
methods: ['getScrollElement', 'scrollToTop', 'scrollToBottom', 'scrollByPoint', 'scrollToPoint']
})
@Component({
selector: 'ion-content',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'scrollEvents', 'scrollX', 'scrollY'],
standalone: true
})
export class IonContent {
Expand Down
1 change: 1 addition & 0 deletions packages/vue/src/proxies.ts
Expand Up @@ -262,6 +262,7 @@ export const IonCol = /*@__PURE__*/ defineContainer<JSX.IonCol>('ion-col', defin
export const IonContent = /*@__PURE__*/ defineContainer<JSX.IonContent>('ion-content', defineIonContent, [
'color',
'fullscreen',
'fixedSlotPlacement',
'forceOverscroll',
'scrollX',
'scrollY',
Expand Down

0 comments on commit 90a7e70

Please sign in to comment.