Skip to content

Commit f42e39e

Browse files
fix(toolbar): allow slot to size to content to account for images (#30508)
Sets the `flex-basis` variable to `auto` if the slot contains an img to allow it to calculate the width using the image's intrinsic width. --------- Co-authored-by: Brandy Smith <[email protected]>
1 parent 33dcf98 commit f42e39e

File tree

31 files changed

+87
-3
lines changed

31 files changed

+87
-3
lines changed

core/src/components/toolbar/test/basic/toolbar.e2e.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,67 @@ configs({ modes: ['ios', 'md', 'ionic-md'], palettes: ['light', 'dark'] }).forEa
180180
});
181181
});
182182
});
183+
184+
configs({ modes: ['ios', 'md', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
185+
test.describe(title('toolbar: basic'), () => {
186+
test.describe(title('slot content'), () => {
187+
test('should not have visual regressions with slotted svgs', async ({ page }) => {
188+
await page.setContent(
189+
`
190+
<ion-header>
191+
<ion-toolbar>
192+
<img src="/src/components/toolbar/test/image.svg" slot="start"/>
193+
<ion-title>Toolbar</ion-title>
194+
<ion-img src="/src/components/toolbar/test/image.svg" slot="end"/>
195+
</ion-toolbar>
196+
</ion-header>
197+
`,
198+
config
199+
);
200+
201+
const header = page.locator('ion-header');
202+
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic-slotted-svgs`));
203+
});
204+
205+
test('should not have visual regressions with slotted images', async ({ page }) => {
206+
await page.setContent(
207+
`
208+
<ion-header>
209+
<ion-toolbar>
210+
<img src="https://picsum.photos/id/237/50/50" slot="start" />
211+
<ion-title>Toolbar</ion-title>
212+
<ion-img src="https://picsum.photos/id/237/50/50" slot="end"></ion-img>
213+
</ion-toolbar>
214+
</ion-header>
215+
`,
216+
config
217+
);
218+
219+
const header = page.locator('ion-header');
220+
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic-slotted-images`));
221+
});
222+
223+
test('should not have visual regressions with nested slotted images', async ({ page }) => {
224+
await page.setContent(
225+
`
226+
<ion-header>
227+
<ion-toolbar>
228+
<div slot="start">
229+
<img src="https://picsum.photos/id/237/50/50" />
230+
</div>
231+
<ion-title>Toolbar</ion-title>
232+
<div slot="end">
233+
<ion-img src="https://picsum.photos/id/237/50/50"></ion-img>
234+
</div>
235+
</ion-toolbar>
236+
</ion-header>
237+
`,
238+
config
239+
);
240+
241+
const header = page.locator('ion-header');
242+
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic-nested-slotted-images`));
243+
});
244+
});
245+
});
246+
});
Lines changed: 1 addition & 0 deletions
Loading

core/src/components/toolbar/toolbar.ionic.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@
7070
// Ionic Toolbar Slot Placement
7171
// --------------------------------------------------
7272
// We're using the slots to force the main toolbar content to be
73-
// cenetered in the toolbar. This is a bit of a hack but it works.
73+
// centered in the toolbar. This is a bit of a hack but it works.
7474
// The main content is placed in the center and then JavaScript evaluates
7575
// the sizes of the different slots and sets what size the pairs should be
7676
// based on the larger one. We then use `flex-basis` to set the expected
7777
// size of the slots and disable `flex-shrink` so that the smaller slot cannot
78-
// shrink and throw off the center, we also diable flex-grow so that slots do
78+
// shrink and throw off the center, we also disable flex-grow so that slots do
7979
// not grow more than they need. The slots are paired up so the mirroring slots
8080
// always have the same size. That is, start and end are paired and primary
8181
// and secondary are paired. All of this works together to force the main

core/src/components/toolbar/toolbar.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,28 @@ export class Toolbar implements ComponentInterface {
110110
const slots = ['start', 'end', 'primary', 'secondary'];
111111
slots.forEach((slot) => {
112112
if (this.el.classList.contains(`has-${slot}-content`)) {
113-
const slotElement = this.el.shadowRoot?.querySelector(`slot[name="${slot}"]`) as HTMLElement | null;
113+
const slotElement = this.el.shadowRoot?.querySelector(`slot[name="${slot}"]`) as HTMLSlotElement | null;
114114
if (slotElement) {
115+
// Check if the slot contains an img or ion-img
116+
const assignedElements = slotElement.assignedElements({ flatten: true });
117+
const hasImg = assignedElements.some((el) => {
118+
if (el.tagName === 'IMG' || el.tagName === 'ION-IMG') {
119+
return true;
120+
}
121+
// Check for nested images
122+
return el.querySelector('img, ion-img');
123+
});
124+
125+
// Temporarily allow slot to size to content by setting flex-basis
126+
// to 'auto'. This ensures that slotted images can render at their
127+
// intrinsic width for measurement.
128+
if (hasImg) {
129+
const { name } = slotPairs.find((pair) => pair.slots.includes(slot))!;
130+
this.el.style.setProperty(`--${name}-size`, 'auto');
131+
}
132+
115133
const width = slotElement.offsetWidth;
134+
116135
if (width > 0) {
117136
slotWidths.set(slot, width);
118137
} else {

0 commit comments

Comments
 (0)