Skip to content

Commit 0c457b7

Browse files
committed
docs(component-guide): add information on new input components
1 parent 0094604 commit 0c457b7

File tree

1 file changed

+171
-1
lines changed

1 file changed

+171
-1
lines changed

docs/component-guide.md

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,18 @@
1414
* [Switch](#switch)
1515
* [Accordion](#accordion)
1616
- [Rendering Anchor or Button](#rendering-anchor-or-button)
17-
* [Example Components](#example-components-1)
17+
* [Example Components](#example-components-4)
1818
* [Component Structure](#component-structure-1)
1919
- [Converting Scoped to Shadow](#converting-scoped-to-shadow)
2020
- [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)
2129

2230
## Button States
2331

@@ -749,3 +757,165 @@ class={{
749757
transform-origin: right center;
750758
}
751759
```
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

Comments
 (0)