refactor(searchbar): update default styles for ionic theme (#29917)
Co-authored-by: Brandy Carney <brandyscarney@users.noreply.github.com>
@ -1915,6 +1915,8 @@ ion-searchbar,css-prop,--clear-button-color,md
|
||||
ion-searchbar,css-prop,--color,ionic
|
||||
ion-searchbar,css-prop,--color,ios
|
||||
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,ios
|
||||
ion-searchbar,css-prop,--icon-color,md
|
||||
|
@ -1,46 +1,63 @@
|
||||
@use "searchbar.common";
|
||||
@use "../../themes/ionic/ionic.globals.scss" as globals;
|
||||
@use "searchbar.common";
|
||||
|
||||
// Ionic Searchbar
|
||||
// --------------------------------------------------
|
||||
|
||||
: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};
|
||||
--border-radius: #{globals.$ionic-border-radius-800};
|
||||
--border-radius: #{globals.$ionic-border-radius-400};
|
||||
--box-shadow: none;
|
||||
--cancel-button-color: #{globals.$ionic-color-neutral-800};
|
||||
--clear-button-color: #{globals.$ionic-color-neutral-800};
|
||||
--color: #{globals.$ionic-color-neutral-800};
|
||||
--clear-button-color: #{globals.$ionic-color-neutral-1000};
|
||||
--color: #{globals.$ionic-color-neutral-1200};
|
||||
--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);
|
||||
|
||||
min-height: globals.$ionic-scale-1000;
|
||||
|
||||
contain: content;
|
||||
}
|
||||
|
||||
.searchbar-input-container {
|
||||
min-height: globals.$ionic-scale-1000;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@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%;
|
||||
|
||||
font-size: globals.$ionic-font-size-350;
|
||||
font-weight: globals.$ionic-font-weight-regular;
|
||||
min-height: globals.$ionic-scale-1200;
|
||||
|
||||
contain: strict;
|
||||
}
|
||||
@ -49,25 +66,94 @@
|
||||
// -----------------------------------------
|
||||
|
||||
.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;
|
||||
height: globals.$ionic-scale-600;
|
||||
background-color: transparent;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
// -----------------------------------------
|
||||
|
||||
: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;
|
||||
}
|
||||
|
@ -12,16 +12,18 @@
|
||||
<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>
|
||||
/* TODO(ROU-11256): Remove the border that is added through the testing styling */
|
||||
ion-searchbar button {
|
||||
border: none;
|
||||
padding: initial;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Searchbar - Basic</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<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>
|
||||
|
@ -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('should render searchbar', async ({ page }) => {
|
||||
await page.setContent(
|
||||
@ -143,19 +143,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
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 }) => {
|
||||
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('should align with the back button when used in a toolbar', async ({ page }, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.8 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: 1.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
86
core/src/components/searchbar/test/states/searchbar.e2e.ts
Normal 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`));
|
||||
});
|
||||
});
|
||||
});
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.6 KiB |