|
14 | 14 | * [Switch](#switch) |
15 | 15 | * [Accordion](#accordion) |
16 | 16 | - [Rendering Anchor or Button](#rendering-anchor-or-button) |
17 | | - * [Example Components](#example-components-1) |
| 17 | + * [Example Components](#example-components-4) |
18 | 18 | * [Component Structure](#component-structure-1) |
19 | 19 | - [Converting Scoped to Shadow](#converting-scoped-to-shadow) |
20 | 20 | - [RTL](#rtl) |
| 21 | +- [Adding New Components with Native Input Support](#adding-new-components-with-native-input-support) |
| 22 | + * [Angular Integration](#angular-integration) |
| 23 | + * [Angular Tests](#angular-tests) |
| 24 | + * [Vue Integration](#vue-integration) |
| 25 | + * [Vue Tests](#vue-tests) |
| 26 | + * [React Integration](#react-integration) |
| 27 | + * [React Tests](#react-tests) |
| 28 | + * [Interface Exports](#interface-exports) |
21 | 29 |
|
22 | 30 | ## Button States |
23 | 31 |
|
@@ -749,3 +757,165 @@ class={{ |
749 | 757 | transform-origin: right center; |
750 | 758 | } |
751 | 759 | ``` |
| 760 | +
|
| 761 | +## Adding New Components with Native Input Support |
| 762 | +
|
| 763 | +When creating a new component that renders native input elements (such as `<input>` or `<textarea>`), there are several steps required to ensure proper form integration across all frameworks (Angular, React, and Vue). |
| 764 | +
|
| 765 | +### Angular Integration |
| 766 | +
|
| 767 | +#### Value Accessors |
| 768 | +
|
| 769 | +For Angular integration, you should use one of the existing value accessors based on your component's needs. Choose the one that most closely matches your component's behavior: |
| 770 | +
|
| 771 | +- For text input (handles string values): Use [`TextValueAccessorDirective`](/packages/angular/src/directives/control-value-accessors/text-value-accessor.ts) which handles `ion-input:not([type=number])`, `ion-input-otp[type=text]`, `ion-textarea`, and `ion-searchbar` |
| 772 | +- For numeric input (converts string to number): Use [`NumericValueAccessorDirective`](/packages/angular/src/directives/control-value-accessors/numeric-value-accessor.ts) which handles `ion-input[type=number]`, `ion-input-otp:not([type=text])`, and `ion-range` |
| 773 | +- For boolean input (handles true/false): Use [`BooleanValueAccessorDirective`](/packages/angular/src/directives/control-value-accessors/boolean-value-accessor.ts) which handles `ion-checkbox` and `ion-toggle` |
| 774 | +- For select-like input (handles option selection): Use [`SelectValueAccessorDirective`](/packages/angular/src/directives/control-value-accessors/select-value-accessor.ts) which handles `ion-select`, `ion-radio-group`, `ion-segment`, and `ion-datetime` |
| 775 | +
|
| 776 | +These value accessors are already set up in the `@ionic/angular` package and handle all the necessary form integration. You don't need to create a new value accessor unless your component has unique requirements that aren't covered by these existing ones. |
| 777 | +
|
| 778 | +For example, if your component renders a text input, it should be included in the `TextValueAccessorDirective` selector in [`text-value-accessor.ts`](/packages/angular/src/directives/control-value-accessors/text-value-accessor.ts): |
| 779 | +
|
| 780 | +```diff |
| 781 | +@Directive({ |
| 782 | +- selector: 'ion-input:not([type=number]),ion-input-otp[type=text],ion-textarea,ion-searchbar, |
| 783 | ++ selector: 'ion-input:not([type=number]),ion-input-otp[type=text],ion-textarea,ion-searchbar,ion-new-component', |
| 784 | + providers: [ |
| 785 | + { |
| 786 | + provide: NG_VALUE_ACCESSOR, |
| 787 | + useExisting: TextValueAccessorDirective, |
| 788 | + multi: true, |
| 789 | + }, |
| 790 | + ], |
| 791 | +}) |
| 792 | +``` |
| 793 | +
|
| 794 | +#### Standalone Directive |
| 795 | +
|
| 796 | +For standalone components, create a directive in the [standalone package](/packages/angular/standalone/src/directives). Look at the implementation of the most similar existing component as a reference: |
| 797 | +
|
| 798 | +- For text/numeric inputs: See [ion-input](/packages/angular/standalone/src/directives/input.ts) or [ion-input-otp](/packages/angular/standalone/src/directives/input-otp.ts) |
| 799 | +- For boolean inputs: See [ion-checkbox](/packages/angular/standalone/src/directives/checkbox.ts) or [ion-toggle](/packages/angular/standalone/src/directives/toggle.ts) |
| 800 | +- For select-like inputs: See [ion-select](/packages/angular/standalone/src/directives/select.ts) or [ion-radio-group](/packages/angular/standalone/src/directives/radio-group.ts) |
| 801 | +
|
| 802 | +After creating the directive, you need to export it in two places: |
| 803 | +
|
| 804 | +1. First, add your component to the directives export group in [`packages/angular/standalone/src/directives/index.ts`](/packages/angular/standalone/src/directives/index.ts): |
| 805 | +
|
| 806 | +```typescript |
| 807 | +export { IonNewComponent } from './new-component'; |
| 808 | +``` |
| 809 | +
|
| 810 | +2. Then, add it to the main standalone package's index file in [`packages/angular/standalone/src/index.ts`](/packages/angular/standalone/src/index.ts): |
| 811 | +
|
| 812 | +```typescript |
| 813 | +export { |
| 814 | + IonCheckbox, |
| 815 | + IonDatetime, |
| 816 | + IonInput, |
| 817 | + IonInputOtp, |
| 818 | + IonNewComponent, // Add your new component here |
| 819 | + // ... other components |
| 820 | +} from './directives'; |
| 821 | +``` |
| 822 | +
|
| 823 | +This ensures your component is properly exported and available for use in standalone Angular applications. |
| 824 | +
|
| 825 | +### Angular Tests |
| 826 | +
|
| 827 | +Add tests for the new component to the existing Angular test files: |
| 828 | +
|
| 829 | +- **(Lazy) Inputs** |
| 830 | + - [`inputs.component.html`](/packages/angular/test/base/src/app/lazy/inputs/inputs.component.html) |
| 831 | + - [`inputs.component.ts`](/packages/angular/test/base/src/app/lazy/inputs/inputs.component.ts) |
| 832 | + - [`inputs.spec.ts`](/packages/angular/test/base/e2e/src/lazy/inputs.spec.ts) |
| 833 | +- **(Lazy) Form** |
| 834 | + - [`form.component.html`](/packages/angular/test/base/src/app/lazy/form/form.component.html) |
| 835 | + - [`form.component.ts`](/packages/angular/test/base/src/app/lazy/form/form.component.ts) |
| 836 | + - [`form.spec.ts`](/packages/angular/test/base/e2e/src/lazy/form.spec.ts) |
| 837 | +- **(Standalone) Value Accessors** |
| 838 | + - [`value-accessors/`](/packages/angular/test/base/src/app/standalone/value-accessors) |
| 839 | + - [`value-accessors.spec.ts`](/packages/angular/test/base/e2e/src/standalone/value-accessors.spec.ts) |
| 840 | +
|
| 841 | +These files contain tests for form integration and input behavior. Review how similar components are tested and add the new component to the relevant test files. |
| 842 | +
|
| 843 | +### Vue Integration |
| 844 | +
|
| 845 | +#### Output Target Configuration |
| 846 | +
|
| 847 | +Update the `vueOutputTarget` configuration in `stencil.config.ts` to include the new component: |
| 848 | +
|
| 849 | +```typescript |
| 850 | +vueOutputTarget({ |
| 851 | + // ... other config |
| 852 | + componentModels: [ |
| 853 | + { |
| 854 | + elements: ['ion-new-component'], |
| 855 | + targetAttr: 'value', // or 'checked' for boolean inputs |
| 856 | + event: 'ion-input', // or 'ion-change' depending on the component |
| 857 | + } |
| 858 | + ] |
| 859 | +}) |
| 860 | +``` |
| 861 | +
|
| 862 | +Choose the `targetAttr` and `event` based on your component's behavior: |
| 863 | +- For text/numeric inputs: Use `targetAttr: 'value'` and `event: 'ion-input'` (like `ion-input` and `ion-textarea`) |
| 864 | +- For boolean inputs: Use `targetAttr: 'checked'` and `event: 'ion-change'` (like `ion-checkbox` and `ion-toggle`) |
| 865 | +- For select-like inputs: Use `targetAttr: 'value'` and `event: 'ion-change'` (like `ion-select` and `ion-radio-group`) |
| 866 | +
|
| 867 | +Look at similar components in the [Vue output target configuration](/core/stencil.config.ts) to see which values they use. |
| 868 | +
|
| 869 | +### Vue Tests |
| 870 | +
|
| 871 | +- **Inputs** |
| 872 | + - [`Inputs.vue`](/packages/vue/test/base/src/views/Inputs.vue) |
| 873 | + - [`inputs.cy.js`](/packages/vue/test/base/tests/e2e/specs/inputs.cy.js) |
| 874 | +
|
| 875 | +These files contain tests for input behavior. Review how similar components are tested and add the new component to the relevant test files. |
| 876 | +
|
| 877 | +### React Integration |
| 878 | +
|
| 879 | +React components are automatically generated from the core component definitions. No additional configuration is needed. |
| 880 | +
|
| 881 | +### React Tests |
| 882 | +
|
| 883 | +- **Inputs** |
| 884 | + - [`Inputs.tsx`](/packages/react/test/base/src/pages/Inputs.tsx) |
| 885 | + - [`inputs.cy.ts`](/packages/react/test/base/tests/e2e/specs/components/inputs.cy.ts) |
| 886 | +
|
| 887 | +These files contain tests for input behavior. Review how similar components are tested and add the new component to the relevant test files. |
| 888 | +
|
| 889 | +### Interface Exports |
| 890 | +
|
| 891 | +Add your component's interfaces to the framework packages: |
| 892 | +
|
| 893 | +1. Angular ([`packages/angular/src/index.ts`](/packages/angular/src/index.ts)): |
| 894 | +```typescript |
| 895 | +export { |
| 896 | + NewComponentCustomEvent, |
| 897 | + NewComponentChangeEventDetail, |
| 898 | + NewComponentInputEventDetail, |
| 899 | + // ... other event interfaces |
| 900 | +} from '@ionic/core'; |
| 901 | +``` |
| 902 | +
|
| 903 | +2. React ([`packages/react/src/components/index.ts`](/packages/react/src/components/index.ts)): |
| 904 | +```typescript |
| 905 | +export { |
| 906 | + NewComponentCustomEvent, |
| 907 | + NewComponentChangeEventDetail, |
| 908 | + NewComponentInputEventDetail, |
| 909 | + // ... other event interfaces |
| 910 | +} from '@ionic/core/components'; |
| 911 | +``` |
| 912 | +
|
| 913 | +3. Vue ([`packages/vue/src/index.ts`](/packages/vue/src/index.ts)): |
| 914 | +```typescript |
| 915 | +export { |
| 916 | + NewComponentCustomEvent, |
| 917 | + NewComponentChangeEventDetail, |
| 918 | + NewComponentInputEventDetail, |
| 919 | + // ... other event interfaces |
| 920 | +} from '@ionic/core/components'; |
| 921 | +``` |
0 commit comments