Skip to content

Commit 1167a93

Browse files
fix(segment): scroll to active segment-button on first load (#28276)
Issue number: resolves #28096 --------- <!-- 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. --> When a segment button is clicked, the segment will auto-scroll to position the newly active button fully in view. However, this behavior does not occur on first load. This means that when a segment is initialized with a `value` corresponding to an off-screen button, the button will remain off-screen. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> The same auto-scroll behavior from button click now also occurs on component load. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
1 parent dc75392 commit 1167a93

File tree

2 files changed

+84
-26
lines changed

2 files changed

+84
-26
lines changed

Diff for: core/src/components/segment/segment.tsx

+39-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ComponentInterface, EventEmitter } from '@stencil/core';
22
import { Component, Element, Event, Host, Listen, Prop, State, Watch, h, writeTask } from '@stencil/core';
33
import type { Gesture, GestureDetail } from '@utils/gesture';
4+
import { raf } from '@utils/helpers';
45
import { isRTL } from '@utils/rtl';
56
import { createColorClasses, hostContext } from '@utils/theme';
67

@@ -83,31 +84,7 @@ export class Segment implements ComponentInterface {
8384
* Used by `ion-segment-button` to determine if the button should be checked.
8485
*/
8586
this.ionSelect.emit({ value });
86-
87-
if (this.scrollable) {
88-
const buttons = this.getButtons();
89-
const activeButton = buttons.find((button) => button.value === value);
90-
if (activeButton !== undefined) {
91-
/**
92-
* Scrollable segment buttons should be
93-
* centered within the view including
94-
* buttons that are partially offscreen.
95-
*/
96-
activeButton.scrollIntoView({
97-
behavior: 'smooth',
98-
inline: 'center',
99-
100-
/**
101-
* Segment should scroll on the
102-
* horizontal axis. `block: 'nearest'`
103-
* ensures that the vertical axis
104-
* does not scroll if the segment
105-
* as a whole is already in view.
106-
*/
107-
block: 'nearest',
108-
});
109-
}
110-
}
87+
this.scrollActiveButtonIntoView();
11188
}
11289

11390
/**
@@ -163,6 +140,14 @@ export class Segment implements ComponentInterface {
163140
async componentDidLoad() {
164141
this.setCheckedClasses();
165142

143+
/**
144+
* We need to wait for the buttons to all be rendered
145+
* before we can scroll.
146+
*/
147+
raf(() => {
148+
this.scrollActiveButtonIntoView();
149+
});
150+
166151
this.gesture = (await import('../../utils/gesture')).createGesture({
167152
el: this.el,
168153
gestureName: 'segment',
@@ -320,6 +305,35 @@ export class Segment implements ComponentInterface {
320305
}
321306
}
322307

308+
private scrollActiveButtonIntoView() {
309+
const { scrollable, value } = this;
310+
311+
if (scrollable) {
312+
const buttons = this.getButtons();
313+
const activeButton = buttons.find((button) => button.value === value);
314+
if (activeButton !== undefined) {
315+
/**
316+
* Scrollable segment buttons should be
317+
* centered within the view including
318+
* buttons that are partially offscreen.
319+
*/
320+
activeButton.scrollIntoView({
321+
behavior: 'smooth',
322+
inline: 'center',
323+
324+
/**
325+
* Segment should scroll on the
326+
* horizontal axis. `block: 'nearest'`
327+
* ensures that the vertical axis
328+
* does not scroll if the segment
329+
* as a whole is already in view.
330+
*/
331+
block: 'nearest',
332+
});
333+
}
334+
}
335+
}
336+
323337
private setNextIndex(detail: GestureDetail, isEnd = false) {
324338
const rtl = isRTL(this.el);
325339
const activated = this.activated;

Diff for: core/src/components/segment/test/scrollable/segment.e2e.ts

+45-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { expect } from '@playwright/test';
22
import { configs, test } from '@utils/test/playwright';
33

44
configs().forEach(({ title, screenshot, config }) => {
5-
test.describe(title('segment: scrollable'), () => {
5+
test.describe(title('segment: scrollable (rendering)'), () => {
66
test('should not have visual regressions', async ({ page }) => {
77
await page.setContent(
88
`
@@ -45,3 +45,47 @@ configs().forEach(({ title, screenshot, config }) => {
4545
});
4646
});
4747
});
48+
49+
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
50+
test.describe(title('segment: scrollable (functionality)'), () => {
51+
test('should scroll active button into view when value is already set', async ({ page }) => {
52+
await page.setContent(
53+
`
54+
<ion-segment scrollable="true" value="8">
55+
<ion-segment-button value="1">
56+
<ion-label>First</ion-label>
57+
</ion-segment-button>
58+
<ion-segment-button value="2">
59+
<ion-label>Second</ion-label>
60+
</ion-segment-button>
61+
<ion-segment-button value="3">
62+
<ion-label>Third</ion-label>
63+
</ion-segment-button>
64+
<ion-segment-button value="4">
65+
<ion-label>Fourth</ion-label>
66+
</ion-segment-button>
67+
<ion-segment-button value="5">
68+
<ion-label>Fifth</ion-label>
69+
</ion-segment-button>
70+
<ion-segment-button value="6">
71+
<ion-label>Sixth</ion-label>
72+
</ion-segment-button>
73+
<ion-segment-button value="7">
74+
<ion-label>Seventh</ion-label>
75+
</ion-segment-button>
76+
<ion-segment-button id="activeButton" value="8">
77+
<ion-label>Eighth</ion-label>
78+
</ion-segment-button>
79+
<ion-segment-button value="9">
80+
<ion-label>Ninth</ion-label>
81+
</ion-segment-button>
82+
</ion-segment>
83+
`,
84+
config
85+
);
86+
87+
const activeButton = page.locator('#activeButton');
88+
await expect(activeButton).toBeInViewport();
89+
});
90+
});
91+
});

0 commit comments

Comments
 (0)