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,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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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({
|
||||||
|
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 |