Skip to content

Commit

Permalink
feat(searchbar): add shapes for ionic theme (#30210)
Browse files Browse the repository at this point in the history
  • Loading branch information
thetaPC authored Feb 25, 2025
1 parent a314950 commit fc552ad
Show file tree
Hide file tree
Showing 17 changed files with 119 additions and 2 deletions.
1 change: 1 addition & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1886,6 +1886,7 @@ ion-searchbar,prop,mode,"ios" | "md",undefined,false,false
ion-searchbar,prop,name,string,this.inputId,false,false
ion-searchbar,prop,placeholder,string,'Search',false,false
ion-searchbar,prop,searchIcon,string | undefined,undefined,false,false
ion-searchbar,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
ion-searchbar,prop,showCancelButton,"always" | "focus" | "never",'never',false,false
ion-searchbar,prop,showClearButton,"always" | "focus" | "never",'always',false,false
ion-searchbar,prop,spellcheck,boolean,false,false,false
Expand Down
8 changes: 8 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3107,6 +3107,10 @@ export namespace Components {
* Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information.
*/
"setFocus": () => Promise<void>;
/**
* Set to `"soft"` for a searchbar with slightly rounded corners, `"round"` for a searchbar with fully rounded corners, or `"rectangular"` for a searchbar without rounded corners. Defaults to `"round"` for the ionic theme, and `undefined` for all other themes.
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state.
*/
Expand Down Expand Up @@ -8514,6 +8518,10 @@ declare namespace LocalJSX {
* The icon to use as the search icon. Defaults to `"search-outline"` in the `"ios"` theme and `"search-sharp"` in the `"md"` and `"ionic"` themes.
*/
"searchIcon"?: string;
/**
* Set to `"soft"` for a searchbar with slightly rounded corners, `"round"` for a searchbar with fully rounded corners, or `"rectangular"` for a searchbar without rounded corners. Defaults to `"round"` for the ionic theme, and `undefined` for all other themes.
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state.
*/
Expand Down
15 changes: 15 additions & 0 deletions core/src/components/searchbar/searchbar.ionic.scss
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,18 @@
cursor: default;
pointer-events: none;
}

// Searchbar Shapes
// --------------------------------------------------

:host(.searchbar-shape-soft) {
--border-radius: #{globals.$ion-border-radius-200};
}

:host(.searchbar-shape-round) {
--border-radius: #{globals.$ion-border-radius-400};
}

:host(.searchbar-shape-rectangular) {
--border-radius: #{globals.$ion-border-radius-0};
}
26 changes: 26 additions & 0 deletions core/src/components/searchbar/searchbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ export class Searchbar implements ComponentInterface {
*/
@Prop({ mutable: true }) value?: string | null = '';

/**
* Set to `"soft"` for a searchbar with slightly rounded corners,
* `"round"` for a searchbar with fully rounded corners,
* or `"rectangular"` for a searchbar without rounded corners.
* Defaults to `"round"` for the ionic theme, and `undefined` for all other themes.
*/
@Prop() shape?: 'soft' | 'round' | 'rectangular';

/**
* Emitted when the `value` of the `ion-searchbar` element has changed.
*/
Expand Down Expand Up @@ -612,6 +620,22 @@ export class Searchbar implements ComponentInterface {
return true;
}

private getShape() {
const theme = getIonTheme(this);
const { shape } = this;

// TODO(ROU-11677): Remove theme check when shapes are defined for all themes.
if (theme !== 'ionic') {
return undefined;
}

if (shape === undefined) {
return 'round';
}

return shape;
}

/**
* Get the icon to use for the clear icon.
* If an icon is set on the component, use that.
Expand Down Expand Up @@ -698,6 +722,7 @@ export class Searchbar implements ComponentInterface {
const animated = this.animated && config.getBoolean('animated', true);
const theme = getIonTheme(this);
const shouldShowCancelButton = this.shouldShowCancelButton();
const shape = this.getShape();

const cancelButton = this.showCancelButton !== 'never' && (
<button
Expand Down Expand Up @@ -734,6 +759,7 @@ export class Searchbar implements ComponentInterface {
'searchbar-has-focus': this.focused,
'searchbar-should-show-clear': this.shouldShowClearButton(),
'searchbar-should-show-cancel': this.shouldShowCancelButton(),
[`searchbar-shape-${shape}`]: shape !== undefined,
})}
>
<div class="searchbar-input-container">
Expand Down
34 changes: 34 additions & 0 deletions core/src/components/searchbar/test/shape/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Searchbar - Shape</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<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>
</head>

<body>
<ion-app>
<ion-content class="ion-padding">
<h5 class="ion-padding-start ion-padding-top">Search - Default</h5>
<ion-searchbar></ion-searchbar>

<h5 class="ion-padding-start ion-padding-top">Search - Round</h5>
<ion-searchbar shape="round"></ion-searchbar>

<h5 class="ion-padding-start ion-padding-top">Search - Soft</h5>
<ion-searchbar shape="soft"></ion-searchbar>

<h5 class="ion-padding-start ion-padding-top">Search - Rectangular</h5>
<ion-searchbar shape="rectangular"></ion-searchbar>
</ion-content>
</ion-app>
</body>
</html>
32 changes: 32 additions & 0 deletions core/src/components/searchbar/test/shape/searchbar.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

/**
* This behavior only applies to the `ionic` theme.
* This behavior does not vary across directions.
*/
configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('searchbar: shape'), () => {
['soft', 'round', 'rectangular'].forEach((shape) => {
test(`${shape} - should not have visual regressions`, async ({ page }) => {
await page.setContent(
`
<style>
/* Background styles to show the border radius */
:root {
background: #000;
}
</style>
<ion-searchbar shape="${shape}"></ion-searchbar>
`,
config
);

const searchbar = page.locator('ion-searchbar');

await expect(searchbar).toHaveScreenshot(screenshot(`searchbar-shape-${shape}`));
});
});
});
});
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.
4 changes: 2 additions & 2 deletions packages/angular/src/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1900,15 +1900,15 @@ export declare interface IonRow extends Components.IonRow {}


@ProxyCmp({
inputs: ['animated', 'autocapitalize', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'maxlength', 'minlength', 'mode', 'name', 'placeholder', 'searchIcon', 'showCancelButton', 'showClearButton', 'spellcheck', 'theme', 'type', 'value'],
inputs: ['animated', 'autocapitalize', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'maxlength', 'minlength', 'mode', 'name', 'placeholder', 'searchIcon', 'shape', 'showCancelButton', 'showClearButton', 'spellcheck', 'theme', 'type', 'value'],
methods: ['setFocus', 'getInputElement']
})
@Component({
selector: 'ion-searchbar',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['animated', 'autocapitalize', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'maxlength', 'minlength', 'mode', 'name', 'placeholder', 'searchIcon', 'showCancelButton', 'showClearButton', 'spellcheck', 'theme', 'type', 'value'],
inputs: ['animated', 'autocapitalize', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'maxlength', 'minlength', 'mode', 'name', 'placeholder', 'searchIcon', 'shape', 'showCancelButton', 'showClearButton', 'spellcheck', 'theme', 'type', 'value'],
})
export class IonSearchbar {
protected el: HTMLElement;
Expand Down
1 change: 1 addition & 0 deletions packages/vue/src/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ export const IonSearchbar = /*@__PURE__*/ defineContainer<JSX.IonSearchbar, JSX.
'spellcheck',
'type',
'value',
'shape',
'ionInput',
'ionChange',
'ionCancel',
Expand Down

0 comments on commit fc552ad

Please sign in to comment.