feat(avatar): add styles for default (round) shape in ionic theme (#29550)

Issue number: internal

---------

## What is the current behavior?
Avatar does not have styles for the `"round"` shape in the ionic theme.

## What is the new behavior?
- Adds the styles for the default (round) shape (border radius)
- Adds e2e test for the round shape
- Updates the screenshots for the avatar `size` due to the new default
shape

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

## Other information

[Preview](https://ionic-framework-git-rou-10738-ionic1.vercel.app/src/components/avatar/test/shape?ionic:theme=ionic)
This commit is contained in:
Brandy Carney
2024-05-28 13:25:54 -04:00
committed by GitHub
parent 01c1b4fef2
commit d4971581cc
63 changed files with 159 additions and 6 deletions

View File

@ -184,6 +184,7 @@ ion-app,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-avatar,shadow
ion-avatar,prop,mode,"ios" | "md",undefined,false,false
ion-avatar,prop,shape,"round" | undefined,undefined,false,false
ion-avatar,prop,size,"large" | "medium" | "small" | "xlarge" | "xsmall" | undefined,undefined,false,false
ion-avatar,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-avatar,css-prop,--border-radius,ionic

View File

@ -335,6 +335,10 @@ export namespace Components {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Set to `"round"` for an avatar with fully rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
*/
"shape"?: 'round';
/**
* Set to `"xsmall"` for the smallest size, `"small"` for a compact size, `"medium"` for the default height and width, `"large"` for a larger size, or `"xlarge"` for the largest dimensions. Defaults to `"medium"` for the `ionic` theme, undefined for all other themes.
*/
@ -5567,6 +5571,10 @@ declare namespace LocalJSX {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Set to `"round"` for an avatar with fully rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
*/
"shape"?: 'round';
/**
* Set to `"xsmall"` for the smallest size, `"small"` for a compact size, `"medium"` for the default height and width, `"large"` for a larger size, or `"xlarge"` for the largest dimensions. Defaults to `"medium"` for the `ionic` theme, undefined for all other themes.
*/

View File

@ -22,6 +22,8 @@
}
:host(:not(.avatar-image)) {
@include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start));
border: globals.$ionic-border-size-025 solid globals.$ionic-color-neutral-800;
}
@ -79,7 +81,14 @@
width: globals.$ionic-scale-1400;
height: globals.$ionic-scale-1400;
font-size: globals.$ionic-font-size-550;
font-size: globals.$ionic-font-size-500;
line-height: globals.$ionic-line-height-700;
}
// Avatar Shapes
// --------------------------------------------------
:host(.avatar-round) {
--border-radius: #{globals.$ionic-border-radius-full};
}

View File

@ -28,6 +28,13 @@ export class Avatar implements ComponentInterface {
*/
@Prop() size?: `xsmall` | 'small' | 'medium' | 'large' | 'xlarge';
/**
* Set to `"round"` for an avatar with fully rounded corners.
*
* Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
*/
@Prop() shape?: 'round';
get hasImage() {
return !!this.el.querySelector('ion-img') || !!this.el.querySelector('img');
}
@ -48,15 +55,33 @@ export class Avatar implements ComponentInterface {
return size;
}
private getShape(): string | undefined {
const theme = getIonTheme(this);
const { shape } = this;
// TODO(ROU-10755): Remove theme check when shapes are defined for all themes.
if (theme !== 'ionic') {
return undefined;
}
if (shape === undefined) {
return 'round';
}
return shape;
}
render() {
const theme = getIonTheme(this);
const size = this.getSize();
const shape = this.getShape();
return (
<Host
class={{
[theme]: true,
[`avatar-${size}`]: size !== undefined,
[`avatar-${shape}`]: shape !== undefined,
[`avatar-image`]: this.hasImage,
}}
>

View File

@ -0,0 +1,54 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* This behavior does not vary across directions.
*/
configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screenshot, title }) => {
test.describe(title('avatar: shape'), () => {
test.describe('round', () => {
test('should not have visual regressions when containing text', async ({ page }) => {
await page.setContent(
`
<ion-avatar shape="round">AB</ion-avatar>
`,
config
);
const avatar = page.locator('ion-avatar');
await expect(avatar).toHaveScreenshot(screenshot(`avatar-shape-round-text`));
});
test('should not have visual regressions when containing an icon', async ({ page }) => {
await page.setContent(
`
<ion-avatar shape="round">
<ion-icon name="person-outline"></ion-icon>
</ion-avatar>
`,
config
);
const avatar = page.locator('ion-avatar');
await expect(avatar).toHaveScreenshot(screenshot(`avatar-shape-round-icon`));
});
test('should not have visual regressions when containing an image', async ({ page }) => {
await page.setContent(
`
<ion-avatar shape="round">
<img src="/src/components/avatar/test/avatar.svg"/>
</ion-avatar>
`,
config
);
const avatar = page.locator('ion-avatar');
await expect(avatar).toHaveScreenshot(screenshot(`avatar-shape-round-image`));
});
});
});
});

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Avatar - Shape</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.container {
display: flex;
gap: 10px;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Avatar - Shape</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding" id="content" no-bounce>
<h2>Default</h2>
<div class="container">
<ion-avatar>AB</ion-avatar>
<ion-avatar>
<ion-icon name="person-outline"></ion-icon>
</ion-avatar>
<ion-avatar>
<img src="/src/components/avatar/test/avatar.svg" />
</ion-avatar>
</div>
<h2>Round</h2>
<div class="container">
<ion-avatar shape="round" size="xsmall">AB</ion-avatar>
<ion-avatar shape="round" size="small">AB</ion-avatar>
<ion-avatar shape="round" size="medium">AB</ion-avatar>
<ion-avatar shape="round" size="large">AB</ion-avatar>
<ion-avatar shape="round" size="xlarge">AB</ion-avatar>
</div>
</ion-content>
</ion-app>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 740 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 688 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 819 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 695 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 B

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 771 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 849 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 957 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 871 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 B

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 968 B

View File

@ -210,14 +210,14 @@ export declare interface IonApp extends Components.IonApp {}
@ProxyCmp({
inputs: ['mode', 'size', 'theme']
inputs: ['mode', 'shape', 'size', 'theme']
})
@Component({
selector: 'ion-avatar',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['mode', 'size', 'theme'],
inputs: ['mode', 'shape', 'size', 'theme'],
})
export class IonAvatar {
protected el: HTMLElement;

View File

@ -290,14 +290,14 @@ export declare interface IonApp extends Components.IonApp {}
@ProxyCmp({
defineCustomElementFn: defineIonAvatar,
inputs: ['mode', 'size', 'theme']
inputs: ['mode', 'shape', 'size', 'theme']
})
@Component({
selector: 'ion-avatar',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['mode', 'size', 'theme'],
inputs: ['mode', 'shape', 'size', 'theme'],
standalone: true
})
export class IonAvatar {

View File

@ -103,7 +103,8 @@ export const IonAccordionGroup = /*@__PURE__*/ defineContainer<JSX.IonAccordionG
export const IonAvatar = /*@__PURE__*/ defineContainer<JSX.IonAvatar>('ion-avatar', defineIonAvatar, [
'size'
'size',
'shape'
]);