feat(avatar): add styles for default (medium) size in ionic theme (#29538)

Issue number: internal

---------

## What is the current behavior?
Avatar does not have any styles in the ionic theme.

## What is the new behavior?
- Adds background, border and font styles for avatar
- Adds the styles for the medium size and defaults the size to medium
- Adds e2e test for avatar sizes

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

## Other information

[Preview](https://ionic-framework-git-rou-10735-ionic1.vercel.app/src/components/avatar/test/size?ionic:theme=ionic)
This commit is contained in:
Brandy Carney
2024-05-23 12:12:31 -04:00
committed by GitHub
parent b21f95cced
commit b9af47ae0d
18 changed files with 204 additions and 7 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,size,"medium" | undefined,undefined,false,false
ion-avatar,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-avatar,css-prop,--border-radius,ionic
ion-avatar,css-prop,--border-radius,ios

View File

@ -335,6 +335,10 @@ export namespace Components {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Set to `"medium"` for the default height and width. Defaults to `"medium"` for the `ionic` theme, undefined for all other themes.
*/
"size"?: 'medium';
/**
* The theme determines the visual appearance of the component.
*/
@ -5563,6 +5567,10 @@ declare namespace LocalJSX {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Set to `"medium"` for the default height and width. Defaults to `"medium"` for the `ionic` theme, undefined for all other themes.
*/
"size"?: 'medium';
/**
* The theme determines the visual appearance of the component.
*/

View File

@ -0,0 +1,38 @@
@use "../../themes/ionic/ionic.globals.scss" as globals;
@import "./avatar";
// Ionic Avatar
// --------------------------------------------------
:host {
display: flex;
align-items: center;
justify-content: center;
background: globals.$ionic-color-neutral-100;
color: globals.$ionic-color-neutral-800;
font-weight: globals.$ionic-font-weight-regular;
line-height: globals.$ionic-line-height-600;
}
:host(:not(.avatar-image)) {
border: globals.$ionic-border-size-025 solid globals.$ionic-color-neutral-800;
}
// Avatar Sizes
// --------------------------------------------------
:host(.avatar-medium) {
--padding-top: #{globals.$ionic-space-0};
--padding-end: #{globals.$ionic-space-200};
--padding-bottom: #{globals.$ionic-space-0};
--padding-start: #{globals.$ionic-space-200};
width: globals.$ionic-scale-1000;
height: globals.$ionic-scale-1000;
font-size: globals.$ionic-font-size-400;
}

View File

@ -1,5 +1,5 @@
import type { ComponentInterface } from '@stencil/core';
import { Component, Host, h } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core';
import { getIonTheme } from '../../global/ionic-global';
@ -12,17 +12,50 @@ import { getIonTheme } from '../../global/ionic-global';
styleUrls: {
ios: 'avatar.ios.scss',
md: 'avatar.md.scss',
ionic: 'avatar.md.scss',
ionic: 'avatar.ionic.scss',
},
shadow: true,
})
export class Avatar implements ComponentInterface {
@Element() el!: HTMLElement;
/**
* Set to `"medium"` for the default height and width.
*
* Defaults to `"medium"` for the `ionic` theme, undefined for all other themes.
*/
@Prop() size?: 'medium';
get hasImage() {
return !!this.el.querySelector('ion-img') || !!this.el.querySelector('img');
}
private getSize(): string | undefined {
const theme = getIonTheme(this);
const { size } = this;
// TODO(ROU-10752): Remove theme check when sizes are defined for all themes.
if (theme !== 'ionic') {
return undefined;
}
if (size === undefined) {
return 'medium';
}
return size;
}
render() {
const theme = getIonTheme(this);
const size = this.getSize();
return (
<Host
class={{
[theme]: true,
[`avatar-${size}`]: size !== undefined,
[`avatar-image`]: this.hasImage,
}}
>
<slot></slot>

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: size'), () => {
test.describe('medium', () => {
test('should not have visual regressions when containing text', async ({ page }) => {
await page.setContent(
`
<ion-avatar size="medium">AB</ion-avatar>
`,
config
);
const avatar = page.locator('ion-avatar');
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-medium-text`));
});
test('should not have visual regressions when containing an icon', async ({ page }) => {
await page.setContent(
`
<ion-avatar size="medium">
<ion-icon name="person-outline"></ion-icon>
</ion-avatar>
`,
config
);
const avatar = page.locator('ion-avatar');
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-medium-icon`));
});
test('should not have visual regressions when containing an image', async ({ page }) => {
await page.setContent(
`
<ion-avatar size="medium">
<img src="/src/components/avatar/test/avatar.svg"/>
</ion-avatar>
`,
config
);
const avatar = page.locator('ion-avatar');
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-medium-image`));
});
});
});
});

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Avatar - Size</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 - Size</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding" id="content" no-bounce>
<h2>Text</h2>
<div class="container">
<ion-avatar>AB</ion-avatar>
<ion-avatar size="medium">AB</ion-avatar>
</div>
<h2>Icons</h2>
<div class="container">
<ion-avatar>
<ion-icon name="person-outline"></ion-icon>
</ion-avatar>
<ion-avatar size="medium">
<ion-icon name="person-outline"></ion-icon>
</ion-avatar>
</div>
<h2>Images</h2>
<div class="container">
<ion-avatar>
<img src="/src/components/avatar/test/avatar.svg" />
</ion-avatar>
<ion-avatar size="medium">
<img src="/src/components/avatar/test/avatar.svg" />
</ion-avatar>
</div>
</ion-content>
</ion-app>
</body>
</html>