Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
137 changes: 112 additions & 25 deletions core/src/components/searchbar/searchbar.ionic.scss
Original file line number Diff line number Diff line change
@@ -1,46 +1,57 @@
@use "searchbar.common";
@use "../../themes/ionic/ionic.globals.scss" as globals;
@use "searchbar.common";
@use "./searchbar.ionic.vars" as searchbarVars;

// 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;
@include globals.position(
searchbarVars.$searchbar-ionic-input-search-icon-size,
null,
null,
searchbarVars.$searchbar-ionic-input-search-icon-size
);

width: searchbarVars.$searchbar-ionic-input-search-icon-size;
height: searchbarVars.$searchbar-ionic-input-search-icon-size;
}

// Searchbar Input Field
// -----------------------------------------

.searchbar-input {
@include globals.padding(globals.$ionic-space-300);

height: 100%;
@include globals.padding(
globals.$ionic-space-300,
searchbarVars.$searchbar-ionic-padding-end,
globals.$ionic-space-300,
searchbarVars.$searchbar-ionic-padding-start
);

font-size: globals.$ionic-font-size-350;
font-weight: globals.$ionic-font-weight-regular;
min-height: globals.$ionic-scale-1200;

contain: strict;
}
Expand All @@ -49,25 +60,101 @@
// -----------------------------------------

.searchbar-clear-button {
@include globals.position(50%, globals.$ionic-space-200, null, null);
@include globals.position(
searchbarVars.$searchbar-ionic-input-clear-icon-size,
searchbarVars.$searchbar-ionic-input-clear-icon-size,
null,
null
);

position: absolute;
width: searchbarVars.$searchbar-ionic-input-clear-icon-size;
height: searchbarVars.$searchbar-ionic-input-clear-icon-size;

background-color: transparent;

width: globals.$ionic-scale-600;
height: globals.$ionic-scale-600;
font-size: globals.$ionic-font-size-400;

contain: strict;
}

// Searchbar Cancel Icon
// -----------------------------------------

transform: translateY(-50%);
.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) {
--icon-color: #{globals.$ionic-color-neutral-900};
}

/* 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 {
display: block;
}

: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;
}
23 changes: 23 additions & 0 deletions core/src/components/searchbar/searchbar.ionic.vars.scss
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually do we need this file since we are already using tokens?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this file because I wanted the padding logic separate so it would be easier to read. Plus, if one of them changes like gap then the dev wouldn't have to worry about updating padding start or end. Thoughts?

Copy link
Member

@brandyscarney brandyscarney Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t feel strongly about this, but I believe we wanted to avoid creating var files for the Ionic theme since we already have tokens. @BenOsodrac do you have any thoughts on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up removing the file and added comments: 739abe1

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@use "../../themes/ionic/ionic.globals.scss" as globals;

// Ionic Searchbar
// --------------------------------------------------

/// @prop - Size of the searchbar input search icon
$searchbar-ionic-input-search-icon-size: globals.$ionic-scale-400;

/// @prop - Size of the searchbar input clear icon
$searchbar-ionic-input-clear-icon-size: globals.$ionic-scale-400;

/// @prop - Gap between searchbar elements
$searchbar-ionic-gap: globals.$ionic-space-200;

/// @prop - Padding start of the searchbar
$searchbar-ionic-padding-start: calc(
globals.$ionic-space-400 + $searchbar-ionic-input-search-icon-size + $searchbar-ionic-gap
);

/// @prop - Padding end of the searchbar
$searchbar-ionic-padding-end: calc(
globals.$ionic-space-400 + $searchbar-ionic-input-clear-icon-size + $searchbar-ionic-gap
);
14 changes: 8 additions & 6 deletions core/src/components/searchbar/test/basic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
/* 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>
Expand Down
17 changes: 2 additions & 15 deletions core/src/components/searchbar/test/basic/searchbar.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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 }) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this to the states test folder.

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(
`
Expand Down Expand Up @@ -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({
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
86 changes: 86 additions & 0 deletions core/src/components/searchbar/test/states/searchbar.e2e.ts
Original file line number Diff line number Diff line change
@@ -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`));
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading