Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Radio Group](#version-9x-radio-group)
- [Spinner](#version-9x-spinner)
- [Textarea](#version-9x-textarea)
- [Thumbnail](#version-9x-thumbnail)

<h2 id="version-9x-global-styles">Global Styles</h2>

Expand Down Expand Up @@ -245,3 +246,28 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
Converted `ion-textarea` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).

If you were targeting the internals of `ion-textarea` in your CSS, you will need to target the `wrapper`, `container`, `label`, `native`, `supporting-text`, `helper-text`, `error-text`, `counter`, or `bottom` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.

<h4 id="version-9x-thumbnail">Thumbnail</h4>

The following breaking changes apply to `ion-thumbnail`:

1. `--size` has been split into separate `--ion-thumbnail-width` and `--ion-thumbnail-height` CSS variables.
2. `--border-radius` has been replaced.
3. Theme classes (`ion-thumbnail.md`, `ion-thumbnail.ios`) are no longer supported.

<h5>Removed CSS variables</h5>

`--size` and `--border-radius` have been removed. Use the new token structure for global styles, or the corresponding CSS variable for component-specific overrides:

| Old (8.x) | New token (global) | New CSS variable (component-specific) |
|---|---|---|
| `--size` | `IonThumbnail.width` | `--ion-thumbnail-width` |
| `--size` | `IonThumbnail.height` | `--ion-thumbnail-height` |
| `--border-radius` | `IonThumbnail.border.radius` | `--ion-thumbnail-border-radius` |

> [!NOTE]
> Code that previously set `--size: 48px` on `ion-thumbnail` must now set both `--ion-thumbnail-width: 48px` and `--ion-thumbnail-height: 48px`.

<h5>Theme classes</h5>

Remove any instances that target the theme classes: `ion-thumbnail.md`, `ion-thumbnail.ios`.
6 changes: 3 additions & 3 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2806,9 +2806,9 @@ ion-textarea,part,wrapper

ion-thumbnail,shadow
ion-thumbnail,prop,mode,"ios" | "md",undefined,false,false
ion-thumbnail,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-thumbnail,css-prop,--border-radius
ion-thumbnail,css-prop,--size
ion-thumbnail,css-prop,--ion-thumbnail-border-radius
ion-thumbnail,css-prop,--ion-thumbnail-height
ion-thumbnail,css-prop,--ion-thumbnail-width

ion-title,shadow
ion-title,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
Expand Down
1 change: 1 addition & 0 deletions core/scripts/vercel-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ echo "Copying core output..."
cp -r "${CORE_DIR}/src" "${OUTPUT_DIR}/src"
cp -r "${CORE_DIR}/dist" "${OUTPUT_DIR}/dist"
cp -r "${CORE_DIR}/css" "${OUTPUT_DIR}/css"
cp -r "${CORE_DIR}/themes" "${OUTPUT_DIR}/themes"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The themes/ directory (generated by npm run build.themes and dynamically imported by scripts.js at runtime as /themes/{theme}/default.tokens.js) wasn't being copied into the Vercel output, so the token modules 404'd in deployed previews while working fine locally where the dev server serves it from core/.

mkdir -p "${OUTPUT_DIR}/scripts"
cp -r "${CORE_DIR}/scripts/testing" "${OUTPUT_DIR}/scripts/testing"

Expand Down
8 changes: 0 additions & 8 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4239,10 +4239,6 @@ export namespace Components {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonTitle {
/**
Expand Down Expand Up @@ -10335,10 +10331,6 @@ declare namespace LocalJSX {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonTitle {
/**
Expand Down
6 changes: 3 additions & 3 deletions core/src/components/item-divider/item-divider.scss
Original file line number Diff line number Diff line change
Expand Up @@ -308,15 +308,15 @@

// Thumbnail
::slotted(ion-thumbnail) {
// TODO(FW-6862): separate width and height tokens for thumbnails
--size: var(--ion-item-divider-thumbnail-width);

@include mixins.margin(
var(--ion-item-divider-thumbnail-margin-top),
var(--ion-item-divider-thumbnail-margin-end),
var(--ion-item-divider-thumbnail-margin-bottom),
var(--ion-item-divider-thumbnail-margin-start)
);

width: var(--ion-item-divider-thumbnail-width, revert-layer);
height: var(--ion-item-divider-thumbnail-height, revert-layer);
Comment on lines +318 to +319
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these tokens are not set then default to the size that was set prior like the original --ion-thumbnail-width/--ion-thumbnail-height.

}

::slotted(ion-thumbnail[slot="start"]) {
Expand Down
4 changes: 3 additions & 1 deletion core/src/components/item/item.ios.scss
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@
// --------------------------------------------------

::slotted(ion-thumbnail) {
--size: #{$item-ios-thumbnail-size};
// TODO(FW-6847): replace $item-md-thumbnail-* to revert-layer pattern used in item-divider.scss. Assuming the --ion-item-thumbnail-width/height tokens will be used in the migration, else update them.
width: var(--ion-item-thumbnail-width, $item-ios-thumbnail-width);
height: var(--ion-item-thumbnail-height, $item-ios-thumbnail-height);
}

// iOS Item Avatar/Thumbnail
Expand Down
7 changes: 5 additions & 2 deletions core/src/components/item/item.ios.vars.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ $item-ios-avatar-width: 36px;
/// @prop - Height of the avatar in the item
$item-ios-avatar-height: $item-ios-avatar-width;

/// @prop - Size of the thumbnail in the item
$item-ios-thumbnail-size: 56px;
/// @prop - Width of the thumbnail in the item
$item-ios-thumbnail-width: 56px;

/// @prop - Height of the thumbnail in the item
$item-ios-thumbnail-height: $item-ios-thumbnail-width;

/// @prop - Padding top for the item content
$item-ios-padding-top: 10px;
Expand Down
4 changes: 3 additions & 1 deletion core/src/components/item/item.md.scss
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@
// --------------------------------------------------

::slotted(ion-thumbnail) {
--size: #{$item-md-thumbnail-size};
// TODO(FW-6847): replace $item-md-thumbnail-* to revert-layer pattern used in item-divider.scss. Assuming the --ion-item-thumbnail-width/height tokens will be used in the migration, else update them.
width: var(--ion-item-thumbnail-width, $item-md-thumbnail-width);
height: var(--ion-item-thumbnail-height, $item-md-thumbnail-height);
}

// Material Design Item Avatar/Thumbnail
Expand Down
7 changes: 5 additions & 2 deletions core/src/components/item/item.md.vars.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ $item-md-avatar-width: 40px;
/// @prop - Height of the avatar in the item
$item-md-avatar-height: $item-md-avatar-width;

/// @prop - Size of the thumbnail in the item
$item-md-thumbnail-size: 56px;
/// @prop - Width of the thumbnail in the item
$item-md-thumbnail-width: 56px;

/// @prop - Height of the thumbnail in the item
$item-md-thumbnail-height: $item-md-thumbnail-width;

/// @prop - Padding top for the item content
$item-md-padding-top: 10px;
Expand Down
7 changes: 7 additions & 0 deletions core/src/components/thumbnail/test/basic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
</ion-thumbnail>
<ion-label>Item Thumbnail</ion-label>
</ion-item>

<ion-item-divider id="ion-item-divider">
<ion-thumbnail slot="start">
<img src="/src/components/thumbnail/test/thumbnail.svg" />
</ion-thumbnail>
<ion-label>Item Divider Thumbnail</ion-label>
</ion-item-divider>
</ion-content>
</ion-app>
</body>
Expand Down
40 changes: 38 additions & 2 deletions core/src/components/thumbnail/test/basic/thumbnail.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

/**
* ion-thumbnail does not have mode/RTL-specific logic
* This behavior does not vary across modes/directions
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('thumbnail: rendering'), () => {
Expand All @@ -22,8 +22,15 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c

await page.setContent(
`
<style>
ion-thumbnail {
--ion-item-thumbnail-width: 20px;
--ion-item-thumbnail-height: 20px;
}
</style>

<ion-item>
<ion-thumbnail style="--size: 20px">
<ion-thumbnail>
<img src="/src/components/thumbnail/test/thumbnail.svg" />
</ion-thumbnail>
</ion-item>
Expand All @@ -34,6 +41,29 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c
const item = page.locator('ion-item');
await expect(item).toHaveScreenshot(screenshot(`thumbnail-ion-item-size-diff`));
});

test('size should be customizable in <ion-item-divider>', async ({ page }) => {
await page.setContent(
`
<style>
ion-item-divider {
--ion-item-divider-thumbnail-width: 20px;
--ion-item-divider-thumbnail-height: 20px;
}
</style>

<ion-item-divider>
<ion-thumbnail>
<img src="/src/components/thumbnail/test/thumbnail.svg" />
</ion-thumbnail>
</ion-item-divider>
`,
config
);

const itemDivider = page.locator('ion-item-divider');
await expect(itemDivider).toHaveScreenshot(screenshot(`thumbnail-ion-item-divider-size`));
});
});
});

Expand All @@ -53,5 +83,11 @@ configs().forEach(({ title, screenshot, config }) => {

await expect(referenceEl).toHaveScreenshot(screenshot(`thumbnail-ion-item-diff`));
});

test('should not have visual regressions when rendering inside of an <ion-item-divider>', async ({ page }) => {
const referenceEl = page.locator('#ion-item-divider');

await expect(referenceEl).toHaveScreenshot(screenshot('thumbnail-ion-item-divider'));
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions core/src/components/thumbnail/thumbnail.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type IonThumbnailRecipe = {
height?: string;
width?: string;

border?: {
radius?: string;
};
};
18 changes: 8 additions & 10 deletions core/src/components/thumbnail/thumbnail.scss
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
@import "../../themes/native/native.globals";
@use "../../themes/mixins" as mixins;

// Thumbnail
// --------------------------------------------------

:host {
/**
* @prop --border-radius: Border radius of the thumbnail
* @prop --size: Size of the thumbnail
* @prop --ion-thumbnail-width: Width of the thumbnail
* @prop --ion-thumbnail-height: Height of the thumbnail
* @prop --ion-thumbnail-border-radius: Border radius of the thumbnail and the slotted image
*/
--size: 48px; // TODO(FW-6862): separate width and height tokens for thumbnails
--border-radius: 0;

@include border-radius(var(--border-radius));
@include mixins.border-radius(var(--ion-thumbnail-border-radius));

display: block;

width: var(--size, 48px);
height: var(--size, 48px);
width: var(--ion-thumbnail-width);
height: var(--ion-thumbnail-height);
}

::slotted(ion-img),
::slotted(img) {
@include border-radius(var(--border-radius));
@include mixins.border-radius(var(--ion-thumbnail-border-radius));

width: 100%;
height: 100%;
Expand Down
10 changes: 1 addition & 9 deletions core/src/components/thumbnail/thumbnail.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import type { ComponentInterface } from '@stencil/core';
import { Component, Host, h } from '@stencil/core';

import { getIonTheme } from '../../global/ionic-global';

/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
* @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component.
*/
@Component({
tag: 'ion-thumbnail',
Expand All @@ -14,13 +11,8 @@ import { getIonTheme } from '../../global/ionic-global';
})
export class Thumbnail implements ComponentInterface {
render() {
const theme = getIonTheme(this);
return (
<Host
class={{
[theme]: true,
}}
>
<Host>
<slot></slot>
</Host>
);
Expand Down
9 changes: 9 additions & 0 deletions core/src/themes/ionic/default.tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -803,5 +803,14 @@ export const defaultTheme: DefaultTheme = {
},
},
},

IonThumbnail: {
height: 'var(--ion-scaling-xl)',
width: 'var(--ion-scaling-xl)',

border: {
radius: 'var(--ion-radii-xxxxs)',
},
},
},
};
9 changes: 9 additions & 0 deletions core/src/themes/ios/default.tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -831,5 +831,14 @@ export const defaultTheme: DefaultTheme = {
},
},
},

IonThumbnail: {
height: 'var(--ion-scaling-xxxl)',
width: 'var(--ion-scaling-xxxl)',

border: {
radius: 'var(--ion-radii-xxxxs)',
},
},
},
};
9 changes: 9 additions & 0 deletions core/src/themes/md/default.tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -956,5 +956,14 @@ export const defaultTheme: DefaultTheme = {
},
},
},

IonThumbnail: {
height: 'var(--ion-scaling-xxxl)',
width: 'var(--ion-scaling-xxxl)',

border: {
radius: 'var(--ion-radii-xxxxs)',
},
},
},
};
2 changes: 2 additions & 0 deletions core/src/themes/themes.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { IonChipConfig, IonChipRecipe } from '../components/chip/chip.inter
import type { IonItemDividerRecipe } from '../components/item-divider/item-divider.interfaces';
import type { IonProgressBarConfig, IonProgressBarRecipe } from '../components/progress-bar/progress-bar.interfaces';
import type { IonSpinnerConfig, IonSpinnerRecipe } from '../components/spinner/spinner.interfaces';
import type { IonThumbnailRecipe } from '../components/thumbnail/thumbnail.interfaces';
import type { IonicConfig as IonicGlobalConfig } from '../utils/config';

// Platform-specific theme
Expand Down Expand Up @@ -290,6 +291,7 @@ type Components = {
IonItemDivider?: IonItemDividerRecipe;
IonProgressBar?: IonProgressBarRecipe;
IonSpinner?: IonSpinnerRecipe;
IonThumbnail?: IonThumbnailRecipe;

IonCard?: any;
IonItem?: any;
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/src/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2489,14 +2489,14 @@ the user clears the textarea by performing a keydown event.


@ProxyCmp({
inputs: ['mode', 'theme']
inputs: ['mode']
})
@Component({
selector: 'ion-thumbnail',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['mode', 'theme'],
inputs: ['mode'],
})
export class IonThumbnail {
protected el: HTMLIonThumbnailElement;
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/standalone/src/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2185,14 +2185,14 @@ export declare interface IonText extends Components.IonText {}

@ProxyCmp({
defineCustomElementFn: defineIonThumbnail,
inputs: ['mode', 'theme']
inputs: ['mode']
})
@Component({
selector: 'ion-thumbnail',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['mode', 'theme'],
inputs: ['mode'],
standalone: true
})
export class IonThumbnail {
Expand Down
Loading