From 1089f5b1aba92863fdeefecb81bd6b1b0d8341be Mon Sep 17 00:00:00 2001
From: swimer11 <65334157+swimer11@users.noreply.github.com>
Date: Fri, 6 Dec 2024 17:21:59 -0800
Subject: [PATCH 1/4] feat(select): add helper and error text
---
core/src/components.d.ts | 16 ++
core/src/components/select/select.scss | 28 ++
core/src/components/select/select.tsx | 71 +++++
.../select/test/bottom-content/index.html | 81 ++++++
.../select/test/bottom-content/input.e2e.ts | 242 ++++++++++++++++++
packages/angular/src/directives/proxies.ts | 4 +-
packages/vue/src/proxies.ts | 2 +
7 files changed, 442 insertions(+), 2 deletions(-)
create mode 100644 core/src/components/select/test/bottom-content/index.html
create mode 100644 core/src/components/select/test/bottom-content/input.e2e.ts
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index 1bdfaa88545..a77c651c8ad 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -2755,6 +2755,10 @@ export namespace Components {
* If `true`, the user cannot interact with the select.
*/
"disabled": boolean;
+ /**
+ * Text that is placed under the select and displayed when an error is detected.
+ */
+ "errorText"?: string;
/**
* The toggle icon to show when the select is open. If defined, the icon rotation behavior in `md` mode will be disabled. If undefined, `toggleIcon` will be used for when the select is both open and closed.
*/
@@ -2763,6 +2767,10 @@ export namespace Components {
* The fill for the item. If `"solid"` the item will have a background. If `"outline"` the item will be transparent with a border. Only available in `md` mode.
*/
"fill"?: 'outline' | 'solid';
+ /**
+ * Text that is placed under the select and displayed when no error is detected.
+ */
+ "helperText"?: string;
/**
* The interface the select should use: `action-sheet`, `popover`, `alert`, or `modal`.
*/
@@ -7568,6 +7576,10 @@ declare namespace LocalJSX {
* If `true`, the user cannot interact with the select.
*/
"disabled"?: boolean;
+ /**
+ * Text that is placed under the select and displayed when an error is detected.
+ */
+ "errorText"?: string;
/**
* The toggle icon to show when the select is open. If defined, the icon rotation behavior in `md` mode will be disabled. If undefined, `toggleIcon` will be used for when the select is both open and closed.
*/
@@ -7576,6 +7588,10 @@ declare namespace LocalJSX {
* The fill for the item. If `"solid"` the item will have a background. If `"outline"` the item will be transparent with a border. Only available in `md` mode.
*/
"fill"?: 'outline' | 'solid';
+ /**
+ * Text that is placed under the select and displayed when no error is detected.
+ */
+ "helperText"?: string;
/**
* The interface the select should use: `action-sheet`, `popover`, `alert`, or `modal`.
*/
diff --git a/core/src/components/select/select.scss b/core/src/components/select/select.scss
index 8b12f01ec1c..a9f9758869a 100644
--- a/core/src/components/select/select.scss
+++ b/core/src/components/select/select.scss
@@ -340,6 +340,34 @@ button {
display: none;
}
+// Select Hint Text
+// ----------------------------------------------------------------
+
+/**
+ * Error text should only be shown when .ion-invalid is
+ * present on the input. Otherwise the helper text should
+ * be shown.
+ */
+ .input-bottom .error-text {
+ display: none;
+
+ color: var(--highlight-color-invalid);
+}
+
+.input-bottom .helper-text {
+ display: block;
+
+ color: #{$text-color-step-450};
+}
+
+:host(.ion-touched.ion-invalid) .input-bottom .error-text {
+ display: block;
+}
+
+:host(.ion-touched.ion-invalid) .input-bottom .helper-text {
+ display: none;
+}
+
// Select Native Wrapper
// ----------------------------------------------------------------
diff --git a/core/src/components/select/select.tsx b/core/src/components/select/select.tsx
index 3b4ef84f26f..d486cbf0c21 100644
--- a/core/src/components/select/select.tsx
+++ b/core/src/components/select/select.tsx
@@ -52,12 +52,15 @@ import type { SelectChangeEventDetail, SelectInterface, SelectCompareFn } from '
})
export class Select implements ComponentInterface {
private inputId = `ion-sel-${selectIds++}`;
+ private helperTextId = `${this.inputId}-helper-text`;
+ private errorTextId = `${this.inputId}-error-text`;
private overlay?: OverlaySelect;
private focusEl?: HTMLButtonElement;
private mutationO?: MutationObserver;
private inheritedAttributes: Attributes = {};
private nativeWrapperEl: HTMLElement | undefined;
private notchSpacerEl: HTMLElement | undefined;
+
private notchController?: NotchController;
@@ -98,6 +101,16 @@ export class Select implements ComponentInterface {
*/
@Prop() fill?: 'outline' | 'solid';
+ /**
+ * Text that is placed under the select and displayed when no error is detected.
+ */
+ @Prop() helperText?: string;
+
+ /**
+ * Text that is placed under the select and displayed when an error is detected.
+ */
+ @Prop() errorText?: string;
+
/**
* The interface the select should use: `action-sheet`, `popover`, `alert`, or `modal`.
*/
@@ -714,6 +727,36 @@ export class Select implements ComponentInterface {
return this.getText() !== '';
}
+ /**
+ * Renders the helper text or error text values
+ */
+ private renderHintText() {
+ const { helperText, errorText, helperTextId, errorTextId } = this;
+
+ return [
+
+ {helperText}
+
,
+
+ {errorText}
+
,
+ ];
+ }
+
+ private getHintTextID(): string | undefined {
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
+
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
+ return errorTextId;
+ }
+
+ if (helperText) {
+ return helperTextId;
+ }
+
+ return undefined;
+ }
+
private get childOpts() {
return Array.from(this.el.querySelectorAll('ion-select-option'));
}
@@ -812,6 +855,33 @@ export class Select implements ComponentInterface {
this.ionBlur.emit();
};
+ /**
+ * Responsible for rendering helper text and
+ * error text. This element should only
+ * be rendered if hint text is set.
+ */
+ private renderBottomContent() {
+ const { helperText, errorText } = this;
+
+ /**
+ * undefined and empty string values should
+ * be treated as not having helper/error text.
+ */
+ const hasHintText = !!helperText || !!errorText;
+ console.log(`HelperText: ${helperText}`);
+ console.log(`errorText: ${errorText}`);
+ if (!hasHintText) {
+ console.log("No text");
+ return;
+ }
+
+ return (
+