refactor(searchbar): update default styles for ionic theme (#29917)

Co-authored-by: Brandy Carney <brandyscarney@users.noreply.github.com>
This commit is contained in:
Maria Hutt
2024-10-04 16:43:32 -07:00
committed by GitHub
parent c295a089c2
commit ab61c122e4
43 changed files with 209 additions and 46 deletions

View File

@ -1915,6 +1915,8 @@ ion-searchbar,css-prop,--clear-button-color,md
ion-searchbar,css-prop,--color,ionic ion-searchbar,css-prop,--color,ionic
ion-searchbar,css-prop,--color,ios ion-searchbar,css-prop,--color,ios
ion-searchbar,css-prop,--color,md ion-searchbar,css-prop,--color,md
ion-searchbar,css-prop,--focus-ring-color,ionic
ion-searchbar,css-prop,--focus-ring-width,ionic
ion-searchbar,css-prop,--icon-color,ionic ion-searchbar,css-prop,--icon-color,ionic
ion-searchbar,css-prop,--icon-color,ios ion-searchbar,css-prop,--icon-color,ios
ion-searchbar,css-prop,--icon-color,md ion-searchbar,css-prop,--icon-color,md

View File

@ -1,46 +1,63 @@
@use "searchbar.common";
@use "../../themes/ionic/ionic.globals.scss" as globals; @use "../../themes/ionic/ionic.globals.scss" as globals;
@use "searchbar.common";
// Ionic Searchbar // Ionic Searchbar
// -------------------------------------------------- // --------------------------------------------------
:host { :host {
/**
* @prop --focus-ring-color: The color of the ring around the focused element.
* @prop --focus-ring-width: The width of the ring around the focused element.
*/
--background: #{globals.$ionic-color-neutral-100}; --background: #{globals.$ionic-color-neutral-100};
--border-radius: #{globals.$ionic-border-radius-800}; --border-radius: #{globals.$ionic-border-radius-400};
--box-shadow: none; --box-shadow: none;
--cancel-button-color: #{globals.$ionic-color-neutral-800}; --cancel-button-color: #{globals.$ionic-color-neutral-800};
--clear-button-color: #{globals.$ionic-color-neutral-800}; --clear-button-color: #{globals.$ionic-color-neutral-1000};
--color: #{globals.$ionic-color-neutral-800}; --color: #{globals.$ionic-color-neutral-1200};
--icon-color: #{globals.$ionic-color-neutral-800}; --icon-color: #{globals.$ionic-color-neutral-800};
--placeholder-color: #{globals.$ionic-color-neutral-800};
--focus-ring-color: #{globals.$ionic-state-focus-1};
--focus-ring-width: #{globals.$ionic-border-size-050};
@include globals.typography(globals.$ionic-body-md-regular);
@include globals.padding(0); @include globals.padding(0);
min-height: globals.$ionic-scale-1000;
contain: content;
}
.searchbar-input-container {
min-height: globals.$ionic-scale-1000;
} }
// Searchbar Search Icon // Searchbar Search Icon
// ----------------------------------------- // -----------------------------------------
.searchbar-search-icon { .searchbar-search-icon {
display: none; // Position is based on the size of the search icon.
@include globals.position(globals.$ionic-scale-400, null, null, globals.$ionic-scale-400);
width: globals.$ionic-scale-400;
height: globals.$ionic-scale-400;
} }
// Searchbar Input Field // Searchbar Input Field
// ----------------------------------------- // -----------------------------------------
.searchbar-input { .searchbar-input {
@include globals.padding(globals.$ionic-space-300); /**
* Padding start is based on
* desired padding from design,
* the size of the search icon,
* and the gap between the icon and the input.
*
* Padding end is based on
* desired padding from design,
* the size of the clear icon,
* and the gap between the icon and the input.
*/
@include globals.padding(
globals.$ionic-space-300,
calc(globals.$ionic-space-400 + globals.$ionic-scale-400 + globals.$ionic-space-200),
globals.$ionic-space-300,
calc(globals.$ionic-space-400 + globals.$ionic-scale-400 + globals.$ionic-space-200)
);
height: 100%; min-height: globals.$ionic-scale-1200;
font-size: globals.$ionic-font-size-350;
font-weight: globals.$ionic-font-weight-regular;
contain: strict; contain: strict;
} }
@ -49,25 +66,94 @@
// ----------------------------------------- // -----------------------------------------
.searchbar-clear-button { .searchbar-clear-button {
@include globals.position(50%, globals.$ionic-space-200, null, null); // Position is based on the size of the clear icon.
@include globals.position(globals.$ionic-scale-400, globals.$ionic-scale-400, null, null);
position: absolute; width: globals.$ionic-scale-400;
height: globals.$ionic-scale-400;
width: globals.$ionic-scale-600; background-color: transparent;
height: globals.$ionic-scale-600;
transform: translateY(-50%); font-size: globals.$ionic-scale-400;
contain: strict;
}
// Searchbar Cancel Icon
// -----------------------------------------
.searchbar-cancel-button {
/**
* The left edge of the cancel button
* should align with the left edge
* of the back button if the searchbar
* is used in a toolbar.
*/
@include globals.position(0, null, null, 9px);
background-color: transparent; background-color: transparent;
font-size: globals.$ionic-font-size-400; font-size: globals.$ionic-font-size-400;
}
contain: strict; // Searchbar Search & Clear Icon & Cancel Icon
// -----------------------------------------
.searchbar-search-icon,
.searchbar-clear-button,
.searchbar-cancel-button {
position: absolute;
}
// Clear Icon & Cancel Icon
// -----------------------------------------
.searchbar-clear-button:focus-visible,
.searchbar-cancel-button:focus-visible ion-icon {
@include globals.border-radius(globals.$ionic-border-radius-100);
outline: globals.$ionic-border-size-050 globals.$ionic-border-style-solid globals.$ionic-state-focus-1;
opacity: 1;
} }
// Searchbar in Toolbar // Searchbar in Toolbar
// ----------------------------------------- // -----------------------------------------
:host-context(ion-toolbar) { :host-context(ion-toolbar) {
min-height: globals.$ionic-scale-1000; min-height: globals.$ionic-scale-1200;
}
// Searchbar States
// --------------------------------------------------
/* Hover */
:host(:hover) {
--background: #{globals.$ionic-color-neutral-200};
}
/* Focus */
:host(.searchbar-has-focus) .searchbar-input {
outline: var(--focus-ring-width) globals.$ionic-border-style-solid var(--focus-ring-color);
}
:host(.searchbar-has-focus) .searchbar-search-icon,
:host(.searchbar-has-focus) .searchbar-cancel-button,
:host(.searchbar-should-show-cancel) .searchbar-cancel-button {
display: block;
}
:host(.searchbar-has-focus) .searchbar-cancel-button + .searchbar-search-icon,
:host(.searchbar-should-show-cancel) .searchbar-cancel-button + .searchbar-search-icon {
display: none;
}
/* Disabled */
:host(.searchbar-disabled) {
--color: #{globals.$ionic-color-neutral-500};
--icon-color: #{globals.$ionic-color-neutral-500};
--placeholder-color: #{globals.$ionic-color-neutral-500};
cursor: default;
pointer-events: none;
} }

View File

@ -12,16 +12,18 @@
<script src="../../../../../scripts/testing/scripts.js"></script> <script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script> <script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script> <script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
/* TODO(ROU-11256): Remove the border that is added through the testing styling */
ion-searchbar button {
border: none;
padding: initial;
}
</style>
</head> </head>
<body> <body>
<ion-app> <ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Searchbar - Basic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content id="content"> <ion-content id="content">
<h5 class="ion-padding-start ion-padding-top">Search - Default</h5> <h5 class="ion-padding-start ion-padding-top">Search - Default</h5>
<ion-searchbar id="basic" value="test" type="tel" show-cancel-button="focus" debounce="500"> </ion-searchbar> <ion-searchbar id="basic" value="test" type="tel" show-cancel-button="focus" debounce="500"> </ion-searchbar>

View File

@ -95,7 +95,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
}); });
}); });
configs().forEach(({ title, screenshot, config }) => { configs({ modes: ['md', 'ios', 'ionic-md'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('searchbar: rendering'), () => { test.describe(title('searchbar: rendering'), () => {
test('should render searchbar', async ({ page }) => { test('should render searchbar', async ({ page }) => {
await page.setContent( await page.setContent(
@ -143,19 +143,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
await expect(searchbar).toHaveScreenshot(screenshot(`searchbar-color`)); await expect(searchbar).toHaveScreenshot(screenshot(`searchbar-color`));
}); });
test('should render disabled searchbar', async ({ page }) => {
await page.setContent(
`
<ion-searchbar disabled="true"></ion-searchbar>
`,
config
);
const searchbar = page.locator('ion-searchbar');
await expect(searchbar).toHaveScreenshot(screenshot(`searchbar-disabled`));
});
test('should render custom search icon', async ({ page }) => { test('should render custom search icon', async ({ page }) => {
await page.setContent( await page.setContent(
` `
@ -199,7 +186,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c
}); });
}); });
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { configs({ modes: ['md', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('searchbar: cancel button alignment'), () => { test.describe(title('searchbar: cancel button alignment'), () => {
test('should align with the back button when used in a toolbar', async ({ page }, testInfo) => { test('should align with the back button when used in a toolbar', async ({ page }, testInfo) => {
testInfo.annotations.push({ testInfo.annotations.push({

View File

@ -0,0 +1,86 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* This behavior does not vary across directions.
*/
configs({ modes: ['md', 'ios', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('searchbar: disabled'), () => {
test('should render disabled searchbar', async ({ page }) => {
await page.setContent(
`
<ion-searchbar disabled="true"></ion-searchbar>
`,
config
);
const searchbar = page.locator('ion-searchbar');
await expect(searchbar).toHaveScreenshot(screenshot(`searchbar-state-disabled`));
});
});
});
/**
* This behavior is only applicable to the `ionic-md` mode.
* This behavior does not vary across directions.
*/
configs({ modes: ['ionic-md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('searchbar: focused'), () => {
test('should render focus ring on the component', async ({ page, pageUtils }) => {
await page.setContent(
`
<style>
/* Add padding to the container to prevent the focus ring from being clipped */
#container {
padding: 5px;
}
</style>
<div id="container">
<ion-searchbar></ion-searchbar>
</div>
`,
config
);
await pageUtils.pressKeys('Tab'); // Focused on the input
const container = page.locator('#container');
await expect(container).toHaveScreenshot(screenshot(`searchbar-state-focused`));
});
test('should render focus ring on the cancel button', async ({ page, pageUtils }) => {
await page.setContent(
`
<ion-searchbar show-cancel-button="always"></ion-searchbar>
`,
config
);
await pageUtils.pressKeys('Tab'); // Focused on the input
await pageUtils.pressKeys('Tab'); // Focused on the cancel button
const searchbar = page.locator('ion-searchbar');
await expect(searchbar).toHaveScreenshot(screenshot(`searchbar-state-focused-cancel-button`));
});
test('should render focus ring on the clear button', async ({ page, pageUtils }) => {
await page.setContent(
`
<ion-searchbar show-clear-button="always" value="Filled text"></ion-searchbar>
`,
config
);
await pageUtils.pressKeys('Tab'); // Focused on the input
await pageUtils.pressKeys('Tab'); // Focused on the clear button
const searchbar = page.locator('ion-searchbar');
await expect(searchbar).toHaveScreenshot(screenshot(`searchbar-state-focused-clear-button`));
});
});
});