Merge branch 'next' into chore-sync-next-ROU-4848
@ -15,6 +15,7 @@ ignoreFiles:
|
|||||||
- src/themes/functions.string.scss
|
- src/themes/functions.string.scss
|
||||||
- src/themes/native.theme.default.scss
|
- src/themes/native.theme.default.scss
|
||||||
- src/css/themes/*.scss
|
- src/css/themes/*.scss
|
||||||
|
- scripts/tokens/*.css
|
||||||
|
|
||||||
indentation: 2
|
indentation: 2
|
||||||
|
|
||||||
|
@ -184,6 +184,8 @@ ion-app,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
|||||||
|
|
||||||
ion-avatar,shadow
|
ion-avatar,shadow
|
||||||
ion-avatar,prop,mode,"ios" | "md",undefined,false,false
|
ion-avatar,prop,mode,"ios" | "md",undefined,false,false
|
||||||
|
ion-avatar,prop,shape,"rectangular" | "round" | "soft" | 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,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||||
ion-avatar,css-prop,--border-radius,ionic
|
ion-avatar,css-prop,--border-radius,ionic
|
||||||
ion-avatar,css-prop,--border-radius,ios
|
ion-avatar,css-prop,--border-radius,ios
|
||||||
@ -310,6 +312,7 @@ ion-backdrop,event,ionBackdropTap,void,true
|
|||||||
ion-badge,shadow
|
ion-badge,shadow
|
||||||
ion-badge,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
|
ion-badge,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
|
||||||
ion-badge,prop,mode,"ios" | "md",undefined,false,false
|
ion-badge,prop,mode,"ios" | "md",undefined,false,false
|
||||||
|
ion-badge,prop,size,"large" | "medium" | "small" | "xlarge" | undefined,undefined,false,false
|
||||||
ion-badge,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
ion-badge,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||||
ion-badge,css-prop,--background,ionic
|
ion-badge,css-prop,--background,ionic
|
||||||
ion-badge,css-prop,--background,ios
|
ion-badge,css-prop,--background,ios
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
"build.css": "npm run css.sass && npm run css.minify",
|
"build.css": "npm run css.sass && npm run css.minify",
|
||||||
"build.debug": "npm run clean && stencil build --debug",
|
"build.debug": "npm run clean && stencil build --debug",
|
||||||
"build.docs.json": "stencil build --docs-json dist/docs.json",
|
"build.docs.json": "stencil build --docs-json dist/docs.json",
|
||||||
"build.tokens": "node ./scripts/tokens/index.js && npm run lint.sass.fix && npm run prettier.tokens",
|
"build.tokens": "node ./scripts/tokens/index.js && npm run lint.fix && npm run prettier.tokens",
|
||||||
"clean": "node scripts/clean.js",
|
"clean": "node scripts/clean.js",
|
||||||
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
|
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
|
||||||
"css.sass": "sass --embed-sources --style compressed src/css:./css",
|
"css.sass": "sass --embed-sources --style compressed src/css:./css",
|
||||||
@ -90,7 +90,7 @@
|
|||||||
"lint.ts.fix": "npm run eslint -- --fix",
|
"lint.ts.fix": "npm run eslint -- --fix",
|
||||||
"prerender.e2e": "node scripts/testing/prerender.js",
|
"prerender.e2e": "node scripts/testing/prerender.js",
|
||||||
"prettier": "prettier \"./src/**/*.{html,ts,tsx,js,jsx,scss}\"",
|
"prettier": "prettier \"./src/**/*.{html,ts,tsx,js,jsx,scss}\"",
|
||||||
"prettier.tokens": "prettier \"./src/foundations/*.scss\" --write --cache",
|
"prettier.tokens": "prettier \"./src/foundations/*.{scss, html}\" --write --cache",
|
||||||
"start": "npm run build.css && stencil build --dev --watch --serve",
|
"start": "npm run build.css && stencil build --dev --watch --serve",
|
||||||
"test": "npm run test.spec && npm run test.e2e",
|
"test": "npm run test.spec && npm run test.e2e",
|
||||||
"test.spec": "stencil test --spec --max-workers=2",
|
"test.spec": "stencil test --spec --max-workers=2",
|
||||||
|
@ -5,12 +5,15 @@
|
|||||||
// - It is probably the most well-known and widely used Design Tokens tool. It has also been regularly maintained for a long time.
|
// - It is probably the most well-known and widely used Design Tokens tool. It has also been regularly maintained for a long time.
|
||||||
// - It can easily scale to different necessities we might have in the future.
|
// - It can easily scale to different necessities we might have in the future.
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
const StyleDictionary = require('style-dictionary');
|
const StyleDictionary = require('style-dictionary');
|
||||||
|
|
||||||
const targetPath = './src/foundations/';
|
const targetPath = './src/foundations/';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
variablesPrefix,
|
variablesPrefix,
|
||||||
|
getRgbaValue,
|
||||||
hexToRgb,
|
hexToRgb,
|
||||||
generateShadowValue,
|
generateShadowValue,
|
||||||
generateFontFamilyValue,
|
generateFontFamilyValue,
|
||||||
@ -41,8 +44,8 @@ StyleDictionary.registerFormat({
|
|||||||
} else if (prop.attributes.category.match('font-family')) {
|
} else if (prop.attributes.category.match('font-family')) {
|
||||||
return generateFontFamilyValue(prop);
|
return generateFontFamilyValue(prop);
|
||||||
} else {
|
} else {
|
||||||
// TODO(ROU-4870): prevent colors with 8 characters to be created without a rgb transformation
|
|
||||||
const rgb = hexToRgb(prop.value);
|
const rgb = hexToRgb(prop.value);
|
||||||
|
prop.value = getRgbaValue(prop.value);
|
||||||
return ` --${variablesPrefix}-${prop.name}: ${prop.value};${
|
return ` --${variablesPrefix}-${prop.name}: ${prop.value};${
|
||||||
rgb ? `\n --${variablesPrefix}-${prop.name}-rgb: ${rgb.r}, ${rgb.g}, ${rgb.b};` : ``
|
rgb ? `\n --${variablesPrefix}-${prop.name}-rgb: ${rgb.r}, ${rgb.g}, ${rgb.b};` : ``
|
||||||
}`;
|
}`;
|
||||||
@ -137,6 +140,137 @@ StyleDictionary.registerFormat({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register the custom format to generate HTML
|
||||||
|
// Load the HTML template
|
||||||
|
const template = fs.readFileSync(path.join(__dirname, 'preview.template.html'), 'utf8');
|
||||||
|
|
||||||
|
StyleDictionary.registerFormat({
|
||||||
|
name: 'html/tokens',
|
||||||
|
formatter: function ({ dictionary }) {
|
||||||
|
// Function to extract numerical value from token name
|
||||||
|
const extractValue = (tokenName) => {
|
||||||
|
const match = tokenName.match(/-([0-9]+)/);
|
||||||
|
return match ? parseInt(match[1], 10) : Number.MAX_SAFE_INTEGER;
|
||||||
|
};
|
||||||
|
|
||||||
|
let colorTokens = `
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Color</th>
|
||||||
|
<th>Hex</th>
|
||||||
|
<th>Token Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
`;
|
||||||
|
let fontSizeTokens = '';
|
||||||
|
let boxShadowTokens = '';
|
||||||
|
let borderSizeTokens = '';
|
||||||
|
let borderRadiusTokens = '';
|
||||||
|
let borderStyleTokens = '';
|
||||||
|
let fontWeightTokens = '';
|
||||||
|
let letterSpacingTokens = '';
|
||||||
|
let spaceTokens = '';
|
||||||
|
|
||||||
|
// Collect border-radius and space tokens for separate sorting
|
||||||
|
let borderRadiusTokenList = [];
|
||||||
|
let spaceTokenList = [];
|
||||||
|
|
||||||
|
dictionary.allProperties.forEach((token) => {
|
||||||
|
if (token.attributes.category === 'color') {
|
||||||
|
colorTokens += `
|
||||||
|
<tr>
|
||||||
|
<td><div class="color-swatch" style="background-color: ${token.value};"></div></td>
|
||||||
|
<td>${token.value}</td>
|
||||||
|
<td>${token.name}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
} else if (token.attributes.category === 'font-size') {
|
||||||
|
fontSizeTokens += `
|
||||||
|
<div class="font-size-token" style="font-size: ${token.value};">
|
||||||
|
${token.name} (${token.value})
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else if (token.attributes.category.startsWith('Elevation')) {
|
||||||
|
const cssShadow = token.value.map(generateShadowValue).join(', ');
|
||||||
|
boxShadowTokens += `
|
||||||
|
<div class="shadow-token" style="box-shadow: ${cssShadow};">
|
||||||
|
${token.name}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else if (token.attributes.category === 'border-size' || token.attributes.category === 'border-width') {
|
||||||
|
borderSizeTokens += `
|
||||||
|
<div class="border-token" style="border-width: ${token.value};">
|
||||||
|
${token.name} (${token.value})
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else if (token.attributes.category === 'border-radius') {
|
||||||
|
borderRadiusTokenList.push(token); // Collect border-radius tokens
|
||||||
|
} else if (token.attributes.category === 'border-style') {
|
||||||
|
borderStyleTokens += `
|
||||||
|
<div class="border-token" style="border: 1px ${token.value} #000;">
|
||||||
|
${token.name} (${token.value})
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else if (token.attributes.category === 'font-weight') {
|
||||||
|
fontWeightTokens += `
|
||||||
|
<div class="weight-token" style="font-weight: ${token.value};">
|
||||||
|
${token.name} (${token.value})
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else if (token.attributes.category === 'letter-spacing') {
|
||||||
|
// Convert % to px
|
||||||
|
const letterSpacingValue = token.value.replace('%', '') + 'px';
|
||||||
|
letterSpacingTokens += `
|
||||||
|
<div class="letter-spacing-token" style="letter-spacing: ${letterSpacingValue};">
|
||||||
|
${token.name} (${letterSpacingValue})
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else if (token.attributes.category === 'space') {
|
||||||
|
spaceTokenList.push(token); // Collect space tokens
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort border-radius and space tokens
|
||||||
|
borderRadiusTokenList.sort((a, b) => extractValue(a.name) - extractValue(b.name));
|
||||||
|
spaceTokenList.sort((a, b) => extractValue(a.name) - extractValue(b.name));
|
||||||
|
|
||||||
|
// Generate HTML for sorted border-radius tokens
|
||||||
|
borderRadiusTokenList.forEach((token) => {
|
||||||
|
borderRadiusTokens += `
|
||||||
|
<div class="border-token" style="border-radius: ${token.value};">
|
||||||
|
${token.name} (${token.value})
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate HTML for sorted space tokens
|
||||||
|
spaceTokenList.forEach((token) => {
|
||||||
|
spaceTokens += `
|
||||||
|
<div class="spacing-wrapper">
|
||||||
|
<div class="space-token" style="margin: ${token.value};">
|
||||||
|
${token.name} (${token.value})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
colorTokens += '</tbody></table>';
|
||||||
|
|
||||||
|
return template
|
||||||
|
.replace('{{colorTokens}}', colorTokens)
|
||||||
|
.replace('{{fontSizeTokens}}', fontSizeTokens)
|
||||||
|
.replace('{{boxShadowTokens}}', boxShadowTokens)
|
||||||
|
.replace('{{borderSizeTokens}}', borderSizeTokens)
|
||||||
|
.replace('{{borderRadiusTokens}}', borderRadiusTokens)
|
||||||
|
.replace('{{borderStyleTokens}}', borderStyleTokens)
|
||||||
|
.replace('{{fontWeightTokens}}', fontWeightTokens)
|
||||||
|
.replace('{{letterSpacingTokens}}', letterSpacingTokens)
|
||||||
|
.replace('{{spaceTokens}}', spaceTokens);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Custom transform to ensure unique token names
|
// Custom transform to ensure unique token names
|
||||||
StyleDictionary.registerTransform({
|
StyleDictionary.registerTransform({
|
||||||
name: 'name/cti/kebab-unique',
|
name: 'name/cti/kebab-unique',
|
||||||
@ -209,6 +343,16 @@ StyleDictionary.extend({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
html: {
|
||||||
|
transformGroup: 'custom',
|
||||||
|
buildPath: targetPath,
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
destination: 'tokens.preview.html',
|
||||||
|
format: 'html/tokens',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fileHeader: {
|
fileHeader: {
|
||||||
myFileHeader: () => {
|
myFileHeader: () => {
|
||||||
|
63
core/scripts/tokens/preview.styles.css
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-swatch {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-size-token,
|
||||||
|
.weight-token,
|
||||||
|
.letter-spacing-token {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-token,
|
||||||
|
.shadow-token {
|
||||||
|
margin: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-token {
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacing-wrapper {
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacing-wrapper > div {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-wrapper:has(.spacing-wrapper) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
background-color: #ccc;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
52
core/scripts/tokens/preview.template.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||||
|
/>
|
||||||
|
<title>Design Tokens</title>
|
||||||
|
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||||
|
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||||
|
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||||
|
<link rel="stylesheet" href="../../scripts/tokens/preview.styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ion-app>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Design Tokens - Preview</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content class="ion-padding-horizontal">
|
||||||
|
<h1>Color Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{colorTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Font Size Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{fontSizeTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Font Weight Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{fontWeightTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Letter Spacing Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{letterSpacingTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Box Shadow Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{boxShadowTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Border Size Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{borderSizeTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Border Radius Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{borderRadiusTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Border Style Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{borderStyleTokens}}</div>
|
||||||
|
<hr />
|
||||||
|
<h1>Space Tokens</h1>
|
||||||
|
<div class="token-wrapper">{{spaceTokens}}</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,6 +1,18 @@
|
|||||||
const variablesPrefix = 'ionic'; // Variable that holds the prefix used on all css and scss variables generated
|
const variablesPrefix = 'ionic'; // Variable that holds the prefix used on all css and scss variables generated
|
||||||
|
|
||||||
// Generates translate an hex color value to rgb
|
// Generates a valid rgba() color
|
||||||
|
function getRgbaValue(propValue) {
|
||||||
|
// Check if its rgba color
|
||||||
|
const isRgba = hexToRgba(propValue);
|
||||||
|
// If it is, then compose rgba() color, otherwise use the normal color
|
||||||
|
if (isRgba !== null) {
|
||||||
|
return (propValue = `rgba(${isRgba.r}, ${isRgba.g}, ${isRgba.b},${isRgba.a})`);
|
||||||
|
} else {
|
||||||
|
return propValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translates an hex color value to rgb
|
||||||
function hexToRgb(hex) {
|
function hexToRgb(hex) {
|
||||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
return result
|
return result
|
||||||
@ -12,9 +24,24 @@ function hexToRgb(hex) {
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Translates an hex color value to rgba
|
||||||
|
function hexToRgba(hex) {
|
||||||
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return result
|
||||||
|
? {
|
||||||
|
r: parseInt(result[1], 16),
|
||||||
|
g: parseInt(result[2], 16),
|
||||||
|
b: parseInt(result[3], 16),
|
||||||
|
a: Math.round((parseInt(result[4], 16) * 100) / 255) / 100,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
// Generates a valid box-shadow value from a shadow Design Token structure
|
// Generates a valid box-shadow value from a shadow Design Token structure
|
||||||
function generateShadowValue(shadow) {
|
function generateShadowValue(shadow) {
|
||||||
return `${shadow.offsetX} ${shadow.offsetY} ${shadow.blur} ${shadow.spread} ${shadow.color}`;
|
const color = getRgbaValue(shadow.color);
|
||||||
|
|
||||||
|
return `${shadow.offsetX} ${shadow.offsetY} ${shadow.blur} ${shadow.spread} ${color}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a valid font-family value from a font-family Design Token structure
|
// Generates a valid font-family value from a font-family Design Token structure
|
||||||
@ -55,16 +82,19 @@ function getTypeMap(dictionary, type) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a rgb color value, based on a color Design Token
|
// Generates a final value, based if the Design Token is of type color or not
|
||||||
function generateRgbValue(prop) {
|
function generateValue(prop) {
|
||||||
const rgb = hexToRgb(prop.value);
|
const rgb = hexToRgb(prop.value);
|
||||||
|
|
||||||
let rgbDeclaration = '';
|
let rgbDeclaration = '';
|
||||||
|
|
||||||
// If the token is color, also add a rgb variable using the same color
|
|
||||||
if (rgb) {
|
if (rgb) {
|
||||||
|
// If the token is color, also add a rgb variable using the same color
|
||||||
rgbDeclaration = `\n$${variablesPrefix}-${prop.name}-rgb: var(--${variablesPrefix}-${prop.name}-rgb, ${rgb.r}, ${rgb.g}, ${rgb.b});`;
|
rgbDeclaration = `\n$${variablesPrefix}-${prop.name}-rgb: var(--${variablesPrefix}-${prop.name}-rgb, ${rgb.r}, ${rgb.g}, ${rgb.b});`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prop.value = getRgbaValue(prop.value);
|
||||||
|
|
||||||
return `$${variablesPrefix}-${prop.name}: var(--${variablesPrefix}-${prop.name}, ${prop.value});${rgbDeclaration}`;
|
return `$${variablesPrefix}-${prop.name}: var(--${variablesPrefix}-${prop.name}, ${prop.value});${rgbDeclaration}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,11 +165,13 @@ function generateSpaceUtilityClasses(prop, className) {
|
|||||||
// Export all methods to be used on the tokens.js script
|
// Export all methods to be used on the tokens.js script
|
||||||
module.exports = {
|
module.exports = {
|
||||||
variablesPrefix,
|
variablesPrefix,
|
||||||
|
getRgbaValue,
|
||||||
hexToRgb,
|
hexToRgb,
|
||||||
|
hexToRgba,
|
||||||
generateShadowValue,
|
generateShadowValue,
|
||||||
generateFontFamilyValue,
|
generateFontFamilyValue,
|
||||||
generateTypographyValue,
|
generateTypographyValue,
|
||||||
generateRgbValue,
|
generateRgbValue: generateValue,
|
||||||
generateColorUtilityClasses,
|
generateColorUtilityClasses,
|
||||||
generateFontUtilityClass,
|
generateFontUtilityClass,
|
||||||
generateSpaceUtilityClasses,
|
generateSpaceUtilityClasses,
|
||||||
|
24
core/src/components.d.ts
vendored
@ -335,6 +335,14 @@ export namespace Components {
|
|||||||
* The mode determines the platform behaviors of the component.
|
* The mode determines the platform behaviors of the component.
|
||||||
*/
|
*/
|
||||||
"mode"?: "ios" | "md";
|
"mode"?: "ios" | "md";
|
||||||
|
/**
|
||||||
|
* Set to `"soft"` for an avatar with slightly rounded corners, `"round"` for an avatar with fully rounded corners, or `"rectangular"` for an avatar without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
|
||||||
|
*/
|
||||||
|
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
"size"?: `xsmall` | 'small' | 'medium' | 'large' | 'xlarge';
|
||||||
/**
|
/**
|
||||||
* The theme determines the visual appearance of the component.
|
* The theme determines the visual appearance of the component.
|
||||||
*/
|
*/
|
||||||
@ -409,6 +417,10 @@ export namespace Components {
|
|||||||
* The mode determines the platform behaviors of the component.
|
* The mode determines the platform behaviors of the component.
|
||||||
*/
|
*/
|
||||||
"mode"?: "ios" | "md";
|
"mode"?: "ios" | "md";
|
||||||
|
/**
|
||||||
|
* Set to `"small"` for less height and width. Set to "medium" for slightly larger dimensions. Set to "large" for even greater height and width. Set to `"xlarge"` for the largest badge. Defaults to `"small"` for the `ionic` theme, undefined for all other themes.
|
||||||
|
*/
|
||||||
|
"size"?: 'small' | 'medium' | 'large' | 'xlarge';
|
||||||
/**
|
/**
|
||||||
* The theme determines the visual appearance of the component.
|
* The theme determines the visual appearance of the component.
|
||||||
*/
|
*/
|
||||||
@ -5563,6 +5575,14 @@ declare namespace LocalJSX {
|
|||||||
* The mode determines the platform behaviors of the component.
|
* The mode determines the platform behaviors of the component.
|
||||||
*/
|
*/
|
||||||
"mode"?: "ios" | "md";
|
"mode"?: "ios" | "md";
|
||||||
|
/**
|
||||||
|
* Set to `"soft"` for an avatar with slightly rounded corners, `"round"` for an avatar with fully rounded corners, or `"rectangular"` for an avatar without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
|
||||||
|
*/
|
||||||
|
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
"size"?: `xsmall` | 'small' | 'medium' | 'large' | 'xlarge';
|
||||||
/**
|
/**
|
||||||
* The theme determines the visual appearance of the component.
|
* The theme determines the visual appearance of the component.
|
||||||
*/
|
*/
|
||||||
@ -5641,6 +5661,10 @@ declare namespace LocalJSX {
|
|||||||
* The mode determines the platform behaviors of the component.
|
* The mode determines the platform behaviors of the component.
|
||||||
*/
|
*/
|
||||||
"mode"?: "ios" | "md";
|
"mode"?: "ios" | "md";
|
||||||
|
/**
|
||||||
|
* Set to `"small"` for less height and width. Set to "medium" for slightly larger dimensions. Set to "large" for even greater height and width. Set to `"xlarge"` for the largest badge. Defaults to `"small"` for the `ionic` theme, undefined for all other themes.
|
||||||
|
*/
|
||||||
|
"size"?: 'small' | 'medium' | 'large' | 'xlarge';
|
||||||
/**
|
/**
|
||||||
* The theme determines the visual appearance of the component.
|
* The theme determines the visual appearance of the component.
|
||||||
*/
|
*/
|
||||||
|
105
core/src/components/avatar/avatar.ionic.scss
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
@use "../../themes/ionic/ionic.globals.scss" as globals;
|
||||||
|
@import "./avatar";
|
||||||
|
|
||||||
|
// Ionic Avatar
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--padding-top: #{globals.$ionic-space-0};
|
||||||
|
--padding-bottom: #{globals.$ionic-space-0};
|
||||||
|
|
||||||
|
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-medium;
|
||||||
|
|
||||||
|
line-height: globals.$ionic-line-height-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(:not(.avatar-image)) {
|
||||||
|
@include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start));
|
||||||
|
|
||||||
|
border: globals.$ionic-border-size-025 globals.$ionic-border-style-solid globals.$ionic-color-neutral-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avatar Sizes
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
:host(.avatar-xsmall) {
|
||||||
|
--padding-end: #{globals.$ionic-space-050};
|
||||||
|
--padding-start: #{globals.$ionic-space-050};
|
||||||
|
|
||||||
|
width: globals.$ionic-scale-600;
|
||||||
|
height: globals.$ionic-scale-600;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-300;
|
||||||
|
|
||||||
|
line-height: globals.$ionic-line-height-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.avatar-small) {
|
||||||
|
--padding-end: #{globals.$ionic-space-150};
|
||||||
|
--padding-start: #{globals.$ionic-space-150};
|
||||||
|
|
||||||
|
width: globals.$ionic-scale-800;
|
||||||
|
height: globals.$ionic-scale-800;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-400;
|
||||||
|
|
||||||
|
line-height: globals.$ionic-line-height-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.avatar-medium) {
|
||||||
|
--padding-end: #{globals.$ionic-space-200};
|
||||||
|
--padding-start: #{globals.$ionic-space-200};
|
||||||
|
|
||||||
|
width: globals.$ionic-scale-1000;
|
||||||
|
height: globals.$ionic-scale-1000;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-450;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.avatar-large) {
|
||||||
|
--padding-end: #{globals.$ionic-space-250};
|
||||||
|
--padding-start: #{globals.$ionic-space-250};
|
||||||
|
|
||||||
|
width: globals.$ionic-scale-1200;
|
||||||
|
height: globals.$ionic-scale-1200;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.avatar-xlarge) {
|
||||||
|
--padding-end: #{globals.$ionic-space-300};
|
||||||
|
--padding-start: #{globals.$ionic-space-300};
|
||||||
|
|
||||||
|
width: globals.$ionic-scale-1400;
|
||||||
|
height: globals.$ionic-scale-1400;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-550;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avatar Shapes
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
:host(.avatar-xsmall.avatar-soft),
|
||||||
|
:host(.avatar-small.avatar-soft) {
|
||||||
|
--border-radius: #{globals.$ionic-border-radius-100};
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.avatar-soft) {
|
||||||
|
--border-radius: #{globals.$ionic-border-radius-200};
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.avatar-round) {
|
||||||
|
--border-radius: #{globals.$ionic-border-radius-full};
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.avatar-rectangular) {
|
||||||
|
--border-radius: #{globals.$ionic-border-radius-0};
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import type { ComponentInterface } from '@stencil/core';
|
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';
|
import { getIonTheme } from '../../global/ionic-global';
|
||||||
|
|
||||||
@ -12,17 +12,79 @@ import { getIonTheme } from '../../global/ionic-global';
|
|||||||
styleUrls: {
|
styleUrls: {
|
||||||
ios: 'avatar.ios.scss',
|
ios: 'avatar.ios.scss',
|
||||||
md: 'avatar.md.scss',
|
md: 'avatar.md.scss',
|
||||||
ionic: 'avatar.md.scss',
|
ionic: 'avatar.ionic.scss',
|
||||||
},
|
},
|
||||||
shadow: true,
|
shadow: true,
|
||||||
})
|
})
|
||||||
export class Avatar implements ComponentInterface {
|
export class Avatar implements ComponentInterface {
|
||||||
|
@Element() el!: HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
@Prop() size?: `xsmall` | 'small' | 'medium' | 'large' | 'xlarge';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to `"soft"` for an avatar with slightly rounded corners,
|
||||||
|
* `"round"` for an avatar with fully rounded corners, or `"rectangular"`
|
||||||
|
* for an avatar without rounded corners.
|
||||||
|
*
|
||||||
|
* Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
|
||||||
|
*/
|
||||||
|
@Prop() shape?: 'soft' | 'round' | 'rectangular';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
render() {
|
||||||
const theme = getIonTheme(this);
|
const theme = getIonTheme(this);
|
||||||
|
const size = this.getSize();
|
||||||
|
const shape = this.getShape();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Host
|
<Host
|
||||||
class={{
|
class={{
|
||||||
[theme]: true,
|
[theme]: true,
|
||||||
|
[`avatar-${size}`]: size !== undefined,
|
||||||
|
[`avatar-${shape}`]: shape !== undefined,
|
||||||
|
[`avatar-image`]: this.hasImage,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
185
core/src/components/avatar/test/shape/avatar.e2e.ts
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
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`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('rectangular', () => {
|
||||||
|
test('should not have visual regressions when containing text', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar shape="rectangular">AB</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-shape-rectangular-text`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an icon', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar shape="rectangular">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-shape-rectangular-icon`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an image', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar shape="rectangular">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-shape-rectangular-image`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('soft', () => {
|
||||||
|
test('should not have visual regressions when containing text', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<style>
|
||||||
|
#container {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="container">
|
||||||
|
<ion-avatar shape="soft" size="xsmall">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="soft" size="small">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="soft">AB</ion-avatar>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const container = page.locator('#container');
|
||||||
|
|
||||||
|
await expect(container).toHaveScreenshot(screenshot(`avatar-shape-soft-text`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an icon', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<style>
|
||||||
|
#container {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="container">
|
||||||
|
<ion-avatar shape="soft" size="xsmall">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar shape="soft" size="small">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar shape="soft">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const container = page.locator('#container');
|
||||||
|
|
||||||
|
await expect(container).toHaveScreenshot(screenshot(`avatar-shape-soft-icon`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an image', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<style>
|
||||||
|
#container {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="container">
|
||||||
|
<ion-avatar shape="soft" size="xsmall">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar shape="soft" size="small">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar shape="soft">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const container = page.locator('#container');
|
||||||
|
|
||||||
|
await expect(container).toHaveScreenshot(screenshot(`avatar-shape-soft-image`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
After Width: | Height: | Size: 630 B |
After Width: | Height: | Size: 528 B |
After Width: | Height: | Size: 561 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 585 B |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 758 B |
After Width: | Height: | Size: 770 B |
After Width: | Height: | Size: 706 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
73
core/src/components/avatar/test/shape/index.html
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<!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>Soft</h2>
|
||||||
|
<div class="container">
|
||||||
|
<ion-avatar shape="soft" size="xsmall">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="soft" size="small">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="soft" size="medium">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="soft" size="large">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="soft" size="xlarge">AB</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>
|
||||||
|
|
||||||
|
<h2>Rectangular</h2>
|
||||||
|
<div class="container">
|
||||||
|
<ion-avatar shape="rectangular" size="xsmall">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="rectangular" size="small">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="rectangular" size="medium">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="rectangular" size="large">AB</ion-avatar>
|
||||||
|
<ion-avatar shape="rectangular" size="xlarge">AB</ion-avatar>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
234
core/src/components/avatar/test/size/avatar.e2e.ts
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
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('xsmall', () => {
|
||||||
|
test('should not have visual regressions when containing text', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="xsmall">AB</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-xsmall-text`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an icon', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="xsmall">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-xsmall-icon`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an image', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="xsmall">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-xsmall-image`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('small', () => {
|
||||||
|
test('should not have visual regressions when containing text', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="small">AB</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-small-text`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an icon', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="small">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-small-icon`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an image', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="small">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-small-image`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('large', () => {
|
||||||
|
test('should not have visual regressions when containing text', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="large">AB</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-large-text`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an icon', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="large">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-large-icon`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an image', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="large">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-large-image`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('xlarge', () => {
|
||||||
|
test('should not have visual regressions when containing text', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="xlarge">AB</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-xlarge-text`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an icon', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="xlarge">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-xlarge-icon`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have visual regressions when containing an image', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-avatar size="xlarge">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg"/>
|
||||||
|
</ion-avatar>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatar = page.locator('ion-avatar');
|
||||||
|
|
||||||
|
await expect(avatar).toHaveScreenshot(screenshot(`avatar-size-xlarge-image`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 928 B |
After Width: | Height: | Size: 891 B |
After Width: | Height: | Size: 883 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 870 B |
After Width: | Height: | Size: 754 B |
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 703 B |
After Width: | Height: | Size: 655 B |
After Width: | Height: | Size: 676 B |
After Width: | Height: | Size: 994 B |
After Width: | Height: | Size: 888 B |
After Width: | Height: | Size: 968 B |
93
core/src/components/avatar/test/size/index.html
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<!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>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>Text</h2>
|
||||||
|
<div class="container">
|
||||||
|
<ion-avatar size="xsmall">AB</ion-avatar>
|
||||||
|
<ion-avatar size="small">AB</ion-avatar>
|
||||||
|
<ion-avatar size="medium">AB</ion-avatar>
|
||||||
|
<ion-avatar size="large">AB</ion-avatar>
|
||||||
|
<ion-avatar size="xlarge">AB</ion-avatar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Icons</h2>
|
||||||
|
<div class="container">
|
||||||
|
<ion-avatar size="xsmall">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="small">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="medium">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="large">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="xlarge">
|
||||||
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
|
</ion-avatar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Images</h2>
|
||||||
|
<div class="container">
|
||||||
|
<ion-avatar size="xsmall">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg" />
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="small">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg" />
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="medium">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg" />
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="large">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg" />
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-avatar size="xlarge">
|
||||||
|
<img src="/src/components/avatar/test/avatar.svg" />
|
||||||
|
</ion-avatar>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
68
core/src/components/badge/badge.ionic.scss
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
@use "../../themes/ionic/ionic.globals.scss" as globals;
|
||||||
|
@import "./badge";
|
||||||
|
|
||||||
|
// Ionic Badge
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
:host {
|
||||||
|
--padding-start: #{globals.$ionic-space-100};
|
||||||
|
--padding-end: #{globals.$ionic-space-100};
|
||||||
|
--padding-top: #{globals.$ionic-space-0};
|
||||||
|
--padding-bottom: #{globals.$ionic-space-0};
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
font-weight: globals.$ionic-font-weight-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Badge Sizes
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
/* Small Badge */
|
||||||
|
:host(.badge-small) {
|
||||||
|
min-width: globals.$ionic-scale-800;
|
||||||
|
height: globals.$ionic-scale-800;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-400;
|
||||||
|
|
||||||
|
line-height: globals.$ionic-line-height-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Medium Badge */
|
||||||
|
:host(.badge-medium) {
|
||||||
|
min-width: globals.$ionic-scale-1000;
|
||||||
|
height: globals.$ionic-scale-1000;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-450;
|
||||||
|
|
||||||
|
line-height: globals.$ionic-line-height-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Large Badge */
|
||||||
|
:host(.badge-large) {
|
||||||
|
--padding-start: #{globals.$ionic-space-200};
|
||||||
|
--padding-end: #{globals.$ionic-space-200};
|
||||||
|
|
||||||
|
min-width: globals.$ionic-scale-1200;
|
||||||
|
height: globals.$ionic-scale-1200;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-500;
|
||||||
|
|
||||||
|
line-height: globals.$ionic-line-height-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extra Large Badge */
|
||||||
|
:host(.badge-xlarge) {
|
||||||
|
--padding-start: #{globals.$ionic-space-200};
|
||||||
|
--padding-end: #{globals.$ionic-space-200};
|
||||||
|
|
||||||
|
min-width: globals.$ionic-scale-1400;
|
||||||
|
height: globals.$ionic-scale-1400;
|
||||||
|
|
||||||
|
font-size: globals.$ionic-font-size-550;
|
||||||
|
|
||||||
|
line-height: globals.$ionic-line-height-700;
|
||||||
|
}
|
@ -14,7 +14,7 @@ import type { Color } from '../../interface';
|
|||||||
styleUrls: {
|
styleUrls: {
|
||||||
ios: 'badge.ios.scss',
|
ios: 'badge.ios.scss',
|
||||||
md: 'badge.md.scss',
|
md: 'badge.md.scss',
|
||||||
ionic: 'badge.md.scss',
|
ionic: 'badge.ionic.scss',
|
||||||
},
|
},
|
||||||
shadow: true,
|
shadow: true,
|
||||||
})
|
})
|
||||||
@ -26,12 +26,36 @@ export class Badge implements ComponentInterface {
|
|||||||
*/
|
*/
|
||||||
@Prop({ reflect: true }) color?: Color;
|
@Prop({ reflect: true }) color?: Color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to `"small"` for less height and width. Set to "medium" for slightly larger dimensions. Set to "large" for even greater height and width. Set to `"xlarge"` for the largest badge.
|
||||||
|
* Defaults to `"small"` for the `ionic` theme, undefined for all other themes.
|
||||||
|
*/
|
||||||
|
@Prop() size?: 'small' | 'medium' | 'large' | 'xlarge';
|
||||||
|
|
||||||
|
private getSize(): string | undefined {
|
||||||
|
const theme = getIonTheme(this);
|
||||||
|
const { size } = this;
|
||||||
|
|
||||||
|
// TODO(ROU-10747): Remove theme check when sizes are defined for all themes.
|
||||||
|
if (theme !== 'ionic') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size === undefined) {
|
||||||
|
return 'small';
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const size = this.getSize();
|
||||||
const theme = getIonTheme(this);
|
const theme = getIonTheme(this);
|
||||||
return (
|
return (
|
||||||
<Host
|
<Host
|
||||||
class={createColorClasses(this.color, {
|
class={createColorClasses(this.color, {
|
||||||
[theme]: true,
|
[theme]: true,
|
||||||
|
[`badge-${size}`]: size !== undefined,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
61
core/src/components/badge/test/size/badge.e2e.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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('badge: size'), () => {
|
||||||
|
test('should render small badges', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-badge size="small">00</ion-badge>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const badge = page.locator('ion-badge');
|
||||||
|
|
||||||
|
await expect(badge).toHaveScreenshot(screenshot(`badge-size-small`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render medium badges', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-badge size="medium">00</ion-badge>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const badge = page.locator('ion-badge');
|
||||||
|
|
||||||
|
await expect(badge).toHaveScreenshot(screenshot(`badge-size-medium`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render large badges', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-badge size="large">00</ion-badge>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const badge = page.locator('ion-badge');
|
||||||
|
|
||||||
|
await expect(badge).toHaveScreenshot(screenshot(`badge-size-large`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render xlarge badges', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-badge size="xlarge">00</ion-badge>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const badge = page.locator('ion-badge');
|
||||||
|
|
||||||
|
await expect(badge).toHaveScreenshot(screenshot(`badge-size-xlarge`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
After Width: | Height: | Size: 561 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 501 B |
After Width: | Height: | Size: 924 B |
After Width: | Height: | Size: 473 B |
After Width: | Height: | Size: 459 B |
After Width: | Height: | Size: 536 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 632 B |
After Width: | Height: | Size: 1.1 KiB |