mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 10:01:59 +08:00
fix(react): Support for adding css classes via className
in Ionic React components (#18231)
* fix(react): adding classname to react props * fix(react): updating rtl to latest to fix ts error * fix(react): changes to support className
This commit is contained in:
@ -48,7 +48,7 @@
|
|||||||
"react-dom": "^16.7.0",
|
"react-dom": "^16.7.0",
|
||||||
"react-router": "^4.3.1",
|
"react-router": "^4.3.1",
|
||||||
"react-router-dom": "^4.3.1",
|
"react-router-dom": "^4.3.1",
|
||||||
"react-testing-library": "^5.5.3",
|
"react-testing-library": "^7.0.0",
|
||||||
"ts-jest": "^23.10.5",
|
"ts-jest": "^23.10.5",
|
||||||
"typescript": "^3.2.2"
|
"typescript": "^3.2.2"
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createOverlayComponent } from './createOverlayComponent';
|
import { createOverlayComponent } from './createOverlayComponent';
|
||||||
|
import { ReactProps } from './ReactProps';
|
||||||
|
|
||||||
export type ActionSheetOptions = Components.IonActionSheetAttributes;
|
export type ActionSheetOptions = Components.IonActionSheetAttributes;
|
||||||
|
|
||||||
const IonActionSheet = createOverlayComponent<ActionSheetOptions, HTMLIonActionSheetElement, HTMLIonActionSheetControllerElement>('ion-action-sheet', 'ion-action-sheet-controller')
|
const IonActionSheet = createOverlayComponent<ActionSheetOptions & ReactProps, HTMLIonActionSheetElement, HTMLIonActionSheetControllerElement>('ion-action-sheet', 'ion-action-sheet-controller')
|
||||||
export default IonActionSheet;
|
export default IonActionSheet;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createControllerComponent } from './createControllerComponent';
|
import { createControllerComponent } from './createControllerComponent';
|
||||||
|
import { ReactProps } from './ReactProps';
|
||||||
|
|
||||||
export type AlertOptions = Components.IonAlertAttributes;
|
export type AlertOptions = Components.IonAlertAttributes;
|
||||||
|
|
||||||
const IonAlert = createControllerComponent<AlertOptions, HTMLIonAlertElement, HTMLIonAlertControllerElement>('ion-alert', 'ion-alert-controller')
|
const IonAlert = createControllerComponent<AlertOptions & ReactProps, HTMLIonAlertElement, HTMLIonAlertControllerElement>('ion-alert', 'ion-alert-controller')
|
||||||
export default IonAlert;
|
export default IonAlert;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createControllerComponent } from './createControllerComponent';
|
import { createControllerComponent } from './createControllerComponent';
|
||||||
|
import { ReactProps } from './ReactProps';
|
||||||
|
|
||||||
export type LoadingOptions = Components.IonLoadingAttributes;
|
export type LoadingOptions = Components.IonLoadingAttributes;
|
||||||
|
|
||||||
const IonLoading = createControllerComponent<LoadingOptions, HTMLIonLoadingElement, HTMLIonLoadingControllerElement>('ion-loading', 'ion-loading-controller')
|
const IonLoading = createControllerComponent<LoadingOptions & ReactProps, HTMLIonLoadingElement, HTMLIonLoadingControllerElement>('ion-loading', 'ion-loading-controller')
|
||||||
export default IonLoading;
|
export default IonLoading;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createOverlayComponent } from './createOverlayComponent';
|
import { createOverlayComponent } from './createOverlayComponent';
|
||||||
import { Omit } from '../types';
|
import { Omit } from '../types';
|
||||||
|
import { ReactProps } from './ReactProps';
|
||||||
|
|
||||||
export type ModalOptions = Omit<Components.IonModalAttributes, 'component' | 'componentProps'> & {
|
export type ModalOptions = Omit<Components.IonModalAttributes, 'component' | 'componentProps'> & {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const IonModal = createOverlayComponent<ModalOptions, HTMLIonModalElement, HTMLIonModalControllerElement>('ion-modal', 'ion-modal-controller')
|
const IonModal = createOverlayComponent<ModalOptions & ReactProps, HTMLIonModalElement, HTMLIonModalControllerElement>('ion-modal', 'ion-modal-controller')
|
||||||
export default IonModal;
|
export default IonModal;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createOverlayComponent } from './createOverlayComponent';
|
import { createOverlayComponent } from './createOverlayComponent';
|
||||||
import { Omit } from '../types';
|
import { Omit } from '../types';
|
||||||
|
import { ReactProps } from './ReactProps';
|
||||||
|
|
||||||
export type PopoverOptions = Omit<Components.IonPopoverAttributes, 'component' | 'componentProps'> & {
|
export type PopoverOptions = Omit<Components.IonPopoverAttributes, 'component' | 'componentProps'> & {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const IonPopover = createOverlayComponent<PopoverOptions, HTMLIonPopoverElement, HTMLIonPopoverControllerElement>('ion-popover', 'ion-popover-controller')
|
const IonPopover = createOverlayComponent<PopoverOptions & ReactProps, HTMLIonPopoverElement, HTMLIonPopoverControllerElement>('ion-popover', 'ion-popover-controller')
|
||||||
export default IonPopover;
|
export default IonPopover;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createControllerComponent } from './createControllerComponent';
|
import { createControllerComponent } from './createControllerComponent';
|
||||||
|
import { ReactProps } from './ReactProps';
|
||||||
|
|
||||||
export type ToastOptions = Components.IonToastAttributes;
|
export type ToastOptions = Components.IonToastAttributes;
|
||||||
|
|
||||||
const IonToast = createControllerComponent<ToastOptions, HTMLIonToastElement, HTMLIonToastControllerElement>('ion-toast', 'ion-toast-controller')
|
const IonToast = createControllerComponent<ToastOptions & ReactProps, HTMLIonToastElement, HTMLIonToastControllerElement>('ion-toast', 'ion-toast-controller')
|
||||||
export default IonToast;
|
export default IonToast;
|
||||||
|
3
react/src/components/ReactProps.ts
Normal file
3
react/src/components/ReactProps.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface ReactProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createReactComponent } from '../createComponent';
|
import { createReactComponent } from '../createComponent';
|
||||||
import { render, fireEvent, cleanup } from 'react-testing-library';
|
import { render, fireEvent, cleanup, RenderResult } from 'react-testing-library';
|
||||||
|
import { IonButton } from '../index';
|
||||||
|
|
||||||
afterEach(cleanup);
|
afterEach(cleanup);
|
||||||
|
|
||||||
@ -47,3 +48,88 @@ describe('createComponent - ref', () => {
|
|||||||
expect(ionButtonRef.current).toEqual(ionButtonItem);
|
expect(ionButtonRef.current).toEqual(ionButtonItem);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when working with css classes', () => {
|
||||||
|
const myClass = 'my-class'
|
||||||
|
const myClass2 = 'my-class2'
|
||||||
|
const customClass = 'custom-class';
|
||||||
|
|
||||||
|
describe('when a class is added to className', () => {
|
||||||
|
let renderResult: RenderResult;
|
||||||
|
let button: HTMLElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
renderResult = render(
|
||||||
|
<IonButton className={myClass}>
|
||||||
|
Hello!
|
||||||
|
</IonButton>
|
||||||
|
);
|
||||||
|
button = renderResult.getByText(/Hello/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('then it should be in the class list', () => {
|
||||||
|
expect(button.classList.contains(myClass)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when a class is added to class list outside of React, then that class should still be in class list when rendered again', () => {
|
||||||
|
button.classList.add(customClass);
|
||||||
|
expect(button.classList.contains(customClass)).toBeTruthy();
|
||||||
|
renderResult.rerender(
|
||||||
|
<IonButton className={myClass}>
|
||||||
|
Hello!
|
||||||
|
</IonButton>
|
||||||
|
);
|
||||||
|
expect(button.classList.contains(customClass)).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when multiple classes are added to className', () => {
|
||||||
|
let renderResult: RenderResult;
|
||||||
|
let button: HTMLElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
renderResult = render(
|
||||||
|
<IonButton className={myClass + ' ' + myClass2}>
|
||||||
|
Hello!
|
||||||
|
</IonButton>
|
||||||
|
);
|
||||||
|
button = renderResult.getByText(/Hello/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('then both classes should be in class list', () => {
|
||||||
|
expect(button.classList.contains(myClass)).toBeTruthy();
|
||||||
|
expect(button.classList.contains(myClass2)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when one of the classes is removed, then only the remaining class should be in class list', () => {
|
||||||
|
expect(button.classList.contains(myClass)).toBeTruthy();
|
||||||
|
expect(button.classList.contains(myClass2)).toBeTruthy();
|
||||||
|
|
||||||
|
renderResult.rerender(
|
||||||
|
<IonButton className={myClass}>
|
||||||
|
Hello!
|
||||||
|
</IonButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(button.classList.contains(myClass)).toBeTruthy();
|
||||||
|
expect(button.classList.contains(myClass2)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when a custom class is added outside of React and one of the classes is removed, then only the remaining class and the custom class should be in class list', () => {
|
||||||
|
button.classList.add(customClass);
|
||||||
|
expect(button.classList.contains(myClass)).toBeTruthy();
|
||||||
|
expect(button.classList.contains(myClass2)).toBeTruthy();
|
||||||
|
expect(button.classList.contains(customClass)).toBeTruthy();
|
||||||
|
|
||||||
|
renderResult.rerender(
|
||||||
|
<IonButton className={myClass}>
|
||||||
|
Hello!
|
||||||
|
</IonButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(button.classList.contains(myClass)).toBeTruthy();
|
||||||
|
expect(button.classList.contains(myClass)).toBeTruthy();
|
||||||
|
expect(button.classList.contains(myClass2)).toBeFalsy();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
@ -68,7 +68,7 @@ describe('createControllerComponent - events', () => {
|
|||||||
duration: 2000,
|
duration: 2000,
|
||||||
children: 'ButtonNameA',
|
children: 'ButtonNameA',
|
||||||
onIonLoadingDidDismiss: onDidDismiss
|
onIonLoadingDidDismiss: onDidDismiss
|
||||||
});
|
}, expect.any(Object));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should dismiss component on hiding', async () => {
|
test('should dismiss component on hiding', async () => {
|
||||||
|
@ -65,7 +65,7 @@ describe('createOverlayComponent - events', () => {
|
|||||||
expect(attachEventPropsSpy).toHaveBeenCalledWith(element, {
|
expect(attachEventPropsSpy).toHaveBeenCalledWith(element, {
|
||||||
buttons: [],
|
buttons: [],
|
||||||
onIonActionSheetDidDismiss: onDidDismiss
|
onIonActionSheetDidDismiss: onDidDismiss
|
||||||
});
|
}, expect.any(Object));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should dismiss component on hiding', async () => {
|
test('should dismiss component on hiding', async () => {
|
||||||
|
@ -2,24 +2,24 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { dashToPascalCase, attachEventProps } from './utils';
|
import { dashToPascalCase, attachEventProps } from './utils';
|
||||||
|
|
||||||
export function createReactComponent<T extends object, E>(tagName: string) {
|
export function createReactComponent<PropType, ElementType>(tagName: string) {
|
||||||
const displayName = dashToPascalCase(tagName);
|
const displayName = dashToPascalCase(tagName);
|
||||||
|
|
||||||
type IonicReactInternalProps = {
|
type IonicReactInternalProps = {
|
||||||
forwardedRef?: React.RefObject<E>;
|
forwardedRef?: React.RefObject<ElementType>;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
type InternalProps = T & IonicReactInternalProps;
|
type InternalProps = PropType & IonicReactInternalProps;
|
||||||
|
|
||||||
type IonicReactExternalProps = {
|
type IonicReactExternalProps = {
|
||||||
ref?: React.RefObject<E>;
|
ref?: React.RefObject<ElementType>;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReactComponent extends React.Component<InternalProps> {
|
class ReactComponent extends React.Component<InternalProps> {
|
||||||
componentRef: React.RefObject<E>;
|
componentRef: React.RefObject<ElementType>;
|
||||||
|
|
||||||
constructor(props: T & IonicReactInternalProps) {
|
constructor(props: PropType & IonicReactInternalProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.componentRef = React.createRef();
|
this.componentRef = React.createRef();
|
||||||
}
|
}
|
||||||
@ -34,8 +34,7 @@ export function createReactComponent<T extends object, E>(tagName: string) {
|
|||||||
|
|
||||||
componentWillReceiveProps(props: InternalProps) {
|
componentWillReceiveProps(props: InternalProps) {
|
||||||
const node = ReactDOM.findDOMNode(this) as HTMLElement;
|
const node = ReactDOM.findDOMNode(this) as HTMLElement;
|
||||||
|
attachEventProps(node, props, this.props);
|
||||||
attachEventProps(node, props);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -52,10 +51,10 @@ export function createReactComponent<T extends object, E>(tagName: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function forwardRef(props: InternalProps, ref: React.RefObject<E>) {
|
function forwardRef(props: InternalProps, ref: React.RefObject<ElementType>) {
|
||||||
return <ReactComponent {...props} forwardedRef={ref} />;
|
return <ReactComponent {...props} forwardedRef={ref} />;
|
||||||
}
|
}
|
||||||
forwardRef.displayName = displayName;
|
forwardRef.displayName = displayName;
|
||||||
|
|
||||||
return React.forwardRef<E, T & IonicReactExternalProps>(forwardRef);
|
return React.forwardRef<ElementType, PropType & IonicReactExternalProps>(forwardRef);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ export function createControllerComponent<T extends object, E extends OverlayCom
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.element = await this.controllerElement.create(elementProps);
|
this.element = await this.controllerElement.create(elementProps);
|
||||||
attachEventProps(this.element, elementProps);
|
attachEventProps(this.element, elementProps, prevProps);
|
||||||
|
|
||||||
await this.element.present();
|
await this.element.present();
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ export function createOverlayComponent<T extends object, E extends OverlayCompon
|
|||||||
component: this.el,
|
component: this.el,
|
||||||
componentProps: {}
|
componentProps: {}
|
||||||
});
|
});
|
||||||
attachEventProps(this.element, elementProps);
|
attachEventProps(this.element, elementProps, prevProps);
|
||||||
|
|
||||||
await this.element.present();
|
await this.element.present();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { defineCustomElements } from '@ionic/core/loader';
|
|||||||
import { Components as IoniconsComponents } from 'ionicons';
|
import { Components as IoniconsComponents } from 'ionicons';
|
||||||
import { Components } from '@ionic/core';
|
import { Components } from '@ionic/core';
|
||||||
import { createReactComponent } from './createComponent';
|
import { createReactComponent } from './createComponent';
|
||||||
|
import { ReactProps } from './ReactProps';
|
||||||
export { AlertButton, AlertInput } from '@ionic/core';
|
export { AlertButton, AlertInput } from '@ionic/core';
|
||||||
|
|
||||||
// createControllerComponent
|
// createControllerComponent
|
||||||
@ -26,81 +27,81 @@ addIcons(ICON_PATHS);
|
|||||||
defineCustomElements(window);
|
defineCustomElements(window);
|
||||||
|
|
||||||
// ionicons
|
// ionicons
|
||||||
export const IonIcon = createReactComponent<IoniconsComponents.IonIconAttributes, HTMLIonIconElement>('ion-icon');
|
export const IonIcon = createReactComponent<IoniconsComponents.IonIconAttributes & ReactProps, HTMLIonIconElement>('ion-icon');
|
||||||
|
|
||||||
// createReactComponent
|
// createReactComponent
|
||||||
export const IonTabBarInner = createReactComponent<Components.IonTabBarAttributes, HTMLIonTabBarElement>('ion-tab-bar');
|
export const IonTabBarInner = createReactComponent<Components.IonTabBarAttributes & ReactProps, HTMLIonTabBarElement>('ion-tab-bar');
|
||||||
export const IonRouterOutletInner = createReactComponent<Components.IonRouterOutletAttributes, HTMLIonRouterOutletElement>('ion-router-outlet');
|
export const IonRouterOutletInner = createReactComponent<Components.IonRouterOutletAttributes & ReactProps, HTMLIonRouterOutletElement>('ion-router-outlet');
|
||||||
export const IonBackButtonInner = createReactComponent<Components.IonBackButtonAttributes, HTMLIonBackButtonElement>('ion-back-button');
|
export const IonBackButtonInner = createReactComponent<Components.IonBackButtonAttributes & ReactProps, HTMLIonBackButtonElement>('ion-back-button');
|
||||||
export const IonTab = createReactComponent<Components.IonTabAttributes, HTMLIonTabElement>('ion-tab');
|
export const IonTab = createReactComponent<Components.IonTabAttributes & ReactProps, HTMLIonTabElement>('ion-tab');
|
||||||
export const IonTabButton = createReactComponent<Components.IonTabButtonAttributes, HTMLIonTabButtonElement>('ion-tab-button');
|
export const IonTabButton = createReactComponent<Components.IonTabButtonAttributes & ReactProps, HTMLIonTabButtonElement>('ion-tab-button');
|
||||||
export const IonAnchor = createReactComponent<Components.IonAnchorAttributes, HTMLIonAnchorElement>('ion-anchor');
|
export const IonAnchor = createReactComponent<Components.IonAnchorAttributes & ReactProps, HTMLIonAnchorElement>('ion-anchor');
|
||||||
export const IonApp = createReactComponent<Components.IonAppAttributes, HTMLIonAppElement>('ion-app');
|
export const IonApp = createReactComponent<Components.IonAppAttributes & ReactProps, HTMLIonAppElement>('ion-app');
|
||||||
export const IonAvatar = createReactComponent<Components.IonAvatarAttributes, HTMLIonAvatarElement>('ion-avatar');
|
export const IonAvatar = createReactComponent<Components.IonAvatarAttributes & ReactProps, HTMLIonAvatarElement>('ion-avatar');
|
||||||
export const IonBackdrop = createReactComponent<Components.IonBackdropAttributes, HTMLIonBackdropElement>('ion-backdrop');
|
export const IonBackdrop = createReactComponent<Components.IonBackdropAttributes & ReactProps, HTMLIonBackdropElement>('ion-backdrop');
|
||||||
export const IonBadge = createReactComponent<Components.IonBadgeAttributes, HTMLIonBadgeElement>('ion-badge');
|
export const IonBadge = createReactComponent<Components.IonBadgeAttributes & ReactProps, HTMLIonBadgeElement>('ion-badge');
|
||||||
export const IonButton = createReactComponent<Components.IonButtonAttributes, HTMLIonButtonElement>('ion-button');
|
export const IonButton = createReactComponent<Components.IonButtonAttributes & ReactProps, HTMLIonButtonElement>('ion-button');
|
||||||
export const IonButtons = createReactComponent<Components.IonButtonsAttributes, HTMLIonButtonsElement>('ion-buttons');
|
export const IonButtons = createReactComponent<Components.IonButtonsAttributes & ReactProps, HTMLIonButtonsElement>('ion-buttons');
|
||||||
export const IonCard = createReactComponent<Components.IonCardAttributes, HTMLIonCardElement>('ion-card');
|
export const IonCard = createReactComponent<Components.IonCardAttributes & ReactProps, HTMLIonCardElement>('ion-card');
|
||||||
export const IonCardContent = createReactComponent<Components.IonCardContentAttributes, HTMLIonCardContentElement>('ion-card-content');
|
export const IonCardContent = createReactComponent<Components.IonCardContentAttributes & ReactProps, HTMLIonCardContentElement>('ion-card-content');
|
||||||
export const IonCardHeader = createReactComponent<Components.IonCardHeaderAttributes, HTMLIonCardHeaderElement>('ion-card-header');
|
export const IonCardHeader = createReactComponent<Components.IonCardHeaderAttributes & ReactProps, HTMLIonCardHeaderElement>('ion-card-header');
|
||||||
export const IonCardSubtitle = createReactComponent<Components.IonCardSubtitleAttributes, HTMLIonCardSubtitleElement>('ion-card-subtitle');
|
export const IonCardSubtitle = createReactComponent<Components.IonCardSubtitleAttributes & ReactProps, HTMLIonCardSubtitleElement>('ion-card-subtitle');
|
||||||
export const IonCardTitle = createReactComponent<Components.IonCardTitleAttributes, HTMLIonCardTitleElement>('ion-card-title');
|
export const IonCardTitle = createReactComponent<Components.IonCardTitleAttributes & ReactProps, HTMLIonCardTitleElement>('ion-card-title');
|
||||||
export const IonCheckbox = createReactComponent<Components.IonCheckboxAttributes, HTMLIonCheckboxElement>('ion-checkbox');
|
export const IonCheckbox = createReactComponent<Components.IonCheckboxAttributes & ReactProps, HTMLIonCheckboxElement>('ion-checkbox');
|
||||||
export const IonCol = createReactComponent<Components.IonColAttributes, HTMLIonColElement>('ion-col');
|
export const IonCol = createReactComponent<Components.IonColAttributes & ReactProps, HTMLIonColElement>('ion-col');
|
||||||
export const IonContent = createReactComponent<Components.IonContentAttributes, HTMLIonContentElement>('ion-content');
|
export const IonContent = createReactComponent<Components.IonContentAttributes & ReactProps, HTMLIonContentElement>('ion-content');
|
||||||
export const IonChip = createReactComponent<Components.IonChipAttributes, HTMLIonChipElement>('ion-chip');
|
export const IonChip = createReactComponent<Components.IonChipAttributes & ReactProps, HTMLIonChipElement>('ion-chip');
|
||||||
export const IonDatetime = createReactComponent<Components.IonDatetimeAttributes, HTMLIonDatetimeElement>('ion-datetime');
|
export const IonDatetime = createReactComponent<Components.IonDatetimeAttributes & ReactProps, HTMLIonDatetimeElement>('ion-datetime');
|
||||||
export const IonFab= createReactComponent<Components.IonFabAttributes, HTMLIonFabElement>('ion-fab');
|
export const IonFab= createReactComponent<Components.IonFabAttributes & ReactProps, HTMLIonFabElement>('ion-fab');
|
||||||
export const IonFabButton= createReactComponent<Components.IonFabButtonAttributes, HTMLIonFabButtonElement>('ion-fab-button');
|
export const IonFabButton= createReactComponent<Components.IonFabButtonAttributes & ReactProps, HTMLIonFabButtonElement>('ion-fab-button');
|
||||||
export const IonFabList = createReactComponent<Components.IonFabListAttributes, HTMLIonFabListElement>('ion-fab-list');
|
export const IonFabList = createReactComponent<Components.IonFabListAttributes & ReactProps, HTMLIonFabListElement>('ion-fab-list');
|
||||||
export const IonFooter = createReactComponent<Components.IonFooterAttributes, HTMLIonFooterElement>('ion-footer');
|
export const IonFooter = createReactComponent<Components.IonFooterAttributes & ReactProps, HTMLIonFooterElement>('ion-footer');
|
||||||
export const IonGrid = createReactComponent<Components.IonGridAttributes, HTMLIonGridElement>('ion-grid');
|
export const IonGrid = createReactComponent<Components.IonGridAttributes & ReactProps, HTMLIonGridElement>('ion-grid');
|
||||||
export const IonHeader = createReactComponent<Components.IonHeaderAttributes, HTMLIonHeaderElement>('ion-header');
|
export const IonHeader = createReactComponent<Components.IonHeaderAttributes & ReactProps, HTMLIonHeaderElement>('ion-header');
|
||||||
export const IonImg = createReactComponent<Components.IonImgAttributes, HTMLIonImgElement>('ion-img');
|
export const IonImg = createReactComponent<Components.IonImgAttributes & ReactProps, HTMLIonImgElement>('ion-img');
|
||||||
export const IonInfiniteScroll = createReactComponent<Components.IonInfiniteScrollAttributes, HTMLIonInfiniteScrollElement>('ion-infinite-scroll');
|
export const IonInfiniteScroll = createReactComponent<Components.IonInfiniteScrollAttributes & ReactProps, HTMLIonInfiniteScrollElement>('ion-infinite-scroll');
|
||||||
export const IonInput = createReactComponent<Components.IonInputAttributes, HTMLIonInputElement>('ion-input');
|
export const IonInput = createReactComponent<Components.IonInputAttributes & ReactProps, HTMLIonInputElement>('ion-input');
|
||||||
export const IonItem = createReactComponent<Components.IonItemAttributes, HTMLIonItemElement>('ion-item');
|
export const IonItem = createReactComponent<Components.IonItemAttributes & ReactProps, HTMLIonItemElement>('ion-item');
|
||||||
export const IonItemDivider = createReactComponent<Components.IonItemDividerAttributes, HTMLIonItemDividerElement>('ion-item-divider');
|
export const IonItemDivider = createReactComponent<Components.IonItemDividerAttributes & ReactProps, HTMLIonItemDividerElement>('ion-item-divider');
|
||||||
export const IonItemGroup = createReactComponent<Components.IonItemGroupAttributes, HTMLIonItemGroupElement>('ion-item-group');
|
export const IonItemGroup = createReactComponent<Components.IonItemGroupAttributes & ReactProps, HTMLIonItemGroupElement>('ion-item-group');
|
||||||
export const IonItemOption = createReactComponent<Components.IonItemOptionAttributes, HTMLIonItemOptionElement>('ion-item-option');
|
export const IonItemOption = createReactComponent<Components.IonItemOptionAttributes & ReactProps, HTMLIonItemOptionElement>('ion-item-option');
|
||||||
export const IonItemOptions = createReactComponent<Components.IonItemOptionsAttributes, HTMLIonItemOptionsElement>('ion-item-options');
|
export const IonItemOptions = createReactComponent<Components.IonItemOptionsAttributes & ReactProps, HTMLIonItemOptionsElement>('ion-item-options');
|
||||||
export const IonItemSliding = createReactComponent<Components.IonItemSlidingAttributes, HTMLIonItemSlidingElement>('ion-item-sliding');
|
export const IonItemSliding = createReactComponent<Components.IonItemSlidingAttributes & ReactProps, HTMLIonItemSlidingElement>('ion-item-sliding');
|
||||||
export const IonLabel = createReactComponent<Components.IonLabelAttributes, HTMLIonLabelElement>('ion-label');
|
export const IonLabel = createReactComponent<Components.IonLabelAttributes & ReactProps, HTMLIonLabelElement>('ion-label');
|
||||||
export const IonList = createReactComponent<Components.IonListAttributes, HTMLIonListElement>('ion-list');
|
export const IonList = createReactComponent<Components.IonListAttributes & ReactProps, HTMLIonListElement>('ion-list');
|
||||||
export const IonListHeader = createReactComponent<Components.IonListHeaderAttributes, HTMLIonListHeaderElement>('ion-list-header');
|
export const IonListHeader = createReactComponent<Components.IonListHeaderAttributes & ReactProps, HTMLIonListHeaderElement>('ion-list-header');
|
||||||
export const IonMenu = createReactComponent<Components.IonMenuAttributes, HTMLIonMenuElement>('ion-menu');
|
export const IonMenu = createReactComponent<Components.IonMenuAttributes & ReactProps, HTMLIonMenuElement>('ion-menu');
|
||||||
export const IonMenuButton = createReactComponent<Components.IonMenuButtonAttributes, HTMLIonMenuButtonElement>('ion-menu-button');
|
export const IonMenuButton = createReactComponent<Components.IonMenuButtonAttributes & ReactProps, HTMLIonMenuButtonElement>('ion-menu-button');
|
||||||
export const IonMenuToggle = createReactComponent<Components.IonMenuToggleAttributes, HTMLIonMenuToggleElement>('ion-menu-toggle');
|
export const IonMenuToggle = createReactComponent<Components.IonMenuToggleAttributes & ReactProps, HTMLIonMenuToggleElement>('ion-menu-toggle');
|
||||||
export const IonNote = createReactComponent<Components.IonNoteAttributes, HTMLIonNoteElement>('ion-note');
|
export const IonNote = createReactComponent<Components.IonNoteAttributes & ReactProps, HTMLIonNoteElement>('ion-note');
|
||||||
export const IonPicker = createReactComponent<Components.IonPickerAttributes, HTMLIonPickerElement>('ion-picker');
|
export const IonPicker = createReactComponent<Components.IonPickerAttributes & ReactProps, HTMLIonPickerElement>('ion-picker');
|
||||||
export const IonPickerColumn = createReactComponent<Components.IonPickerColumnAttributes, HTMLIonPickerColumnElement>('ion-picker-column');
|
export const IonPickerColumn = createReactComponent<Components.IonPickerColumnAttributes & ReactProps, HTMLIonPickerColumnElement>('ion-picker-column');
|
||||||
export const IonNav = createReactComponent<Components.IonNavAttributes, HTMLIonNavElement>('ion-nav');
|
export const IonNav = createReactComponent<Components.IonNavAttributes & ReactProps, HTMLIonNavElement>('ion-nav');
|
||||||
export const IonProgressBar = createReactComponent<Components.IonProgressBarAttributes, HTMLIonProgressBarElement>('ion-progress-bar');
|
export const IonProgressBar = createReactComponent<Components.IonProgressBarAttributes & ReactProps, HTMLIonProgressBarElement>('ion-progress-bar');
|
||||||
export const IonRadio = createReactComponent<Components.IonRadioAttributes, HTMLIonRadioElement>('ion-radio');
|
export const IonRadio = createReactComponent<Components.IonRadioAttributes & ReactProps, HTMLIonRadioElement>('ion-radio');
|
||||||
export const IonRadioGroup = createReactComponent<Components.IonRadioGroupAttributes, HTMLIonRadioGroupElement>('ion-radio-group');
|
export const IonRadioGroup = createReactComponent<Components.IonRadioGroupAttributes & ReactProps, HTMLIonRadioGroupElement>('ion-radio-group');
|
||||||
export const IonRange = createReactComponent<Components.IonRangeAttributes, HTMLIonRangeElement>('ion-range');
|
export const IonRange = createReactComponent<Components.IonRangeAttributes & ReactProps, HTMLIonRangeElement>('ion-range');
|
||||||
export const IonRefresher = createReactComponent<Components.IonRefresherAttributes, HTMLIonRefresherElement>('ion-refresher');
|
export const IonRefresher = createReactComponent<Components.IonRefresherAttributes & ReactProps, HTMLIonRefresherElement>('ion-refresher');
|
||||||
export const IonRefresherContent = createReactComponent<Components.IonRefresherContentAttributes, HTMLIonRefresherContentElement>('ion-refresher-content');
|
export const IonRefresherContent = createReactComponent<Components.IonRefresherContentAttributes & ReactProps, HTMLIonRefresherContentElement>('ion-refresher-content');
|
||||||
export const IonReorder = createReactComponent<Components.IonReorderAttributes, HTMLIonReorderElement>('ion-reorder');
|
export const IonReorder = createReactComponent<Components.IonReorderAttributes & ReactProps, HTMLIonReorderElement>('ion-reorder');
|
||||||
export const IonReorderGroup = createReactComponent<Components.IonReorderGroupAttributes, HTMLIonReorderGroupElement>('ion-reorder-group');
|
export const IonReorderGroup = createReactComponent<Components.IonReorderGroupAttributes & ReactProps, HTMLIonReorderGroupElement>('ion-reorder-group');
|
||||||
export const IonRippleEffect = createReactComponent<Components.IonRippleEffectAttributes, HTMLIonRippleEffectElement>('ion-ripple-effect');
|
export const IonRippleEffect = createReactComponent<Components.IonRippleEffectAttributes & ReactProps, HTMLIonRippleEffectElement>('ion-ripple-effect');
|
||||||
export const IonRow = createReactComponent<Components.IonRowAttributes, HTMLIonRowElement>('ion-row');
|
export const IonRow = createReactComponent<Components.IonRowAttributes & ReactProps, HTMLIonRowElement>('ion-row');
|
||||||
export const IonSearchbar= createReactComponent<Components.IonSearchbarAttributes, HTMLIonSearchbarElement>('ion-searchbar');
|
export const IonSearchbar= createReactComponent<Components.IonSearchbarAttributes & ReactProps, HTMLIonSearchbarElement>('ion-searchbar');
|
||||||
export const IonSegment= createReactComponent<Components.IonSegmentAttributes, HTMLIonSegmentElement>('ion-segment');
|
export const IonSegment= createReactComponent<Components.IonSegmentAttributes & ReactProps, HTMLIonSegmentElement>('ion-segment');
|
||||||
export const IonSegmentButton= createReactComponent<Components.IonSegmentButtonAttributes, HTMLIonSegmentButtonElement>('ion-segment-button');
|
export const IonSegmentButton= createReactComponent<Components.IonSegmentButtonAttributes & ReactProps, HTMLIonSegmentButtonElement>('ion-segment-button');
|
||||||
export const IonSelect = createReactComponent<Components.IonSelectAttributes, HTMLIonSelectElement>('ion-select');
|
export const IonSelect = createReactComponent<Components.IonSelectAttributes & ReactProps, HTMLIonSelectElement>('ion-select');
|
||||||
export const IonSelectOption = createReactComponent<Components.IonSelectOptionAttributes, HTMLIonSelectOptionElement>('ion-select-option');
|
export const IonSelectOption = createReactComponent<Components.IonSelectOptionAttributes & ReactProps, HTMLIonSelectOptionElement>('ion-select-option');
|
||||||
export const IonSelectPopover = createReactComponent<Components.IonSelectPopoverAttributes, HTMLIonSelectPopoverElement>('ion-select-popover');
|
export const IonSelectPopover = createReactComponent<Components.IonSelectPopoverAttributes & ReactProps, HTMLIonSelectPopoverElement>('ion-select-popover');
|
||||||
export const IonSkeletonText = createReactComponent<Components.IonSkeletonTextAttributes, HTMLIonSkeletonTextElement>('ion-skeleton-text');
|
export const IonSkeletonText = createReactComponent<Components.IonSkeletonTextAttributes & ReactProps, HTMLIonSkeletonTextElement>('ion-skeleton-text');
|
||||||
export const IonSlide = createReactComponent<Components.IonSlideAttributes, HTMLIonSlideElement>('ion-slide');
|
export const IonSlide = createReactComponent<Components.IonSlideAttributes & ReactProps, HTMLIonSlideElement>('ion-slide');
|
||||||
export const IonSlides = createReactComponent<Components.IonSlidesAttributes, HTMLIonSlidesElement>('ion-slides');
|
export const IonSlides = createReactComponent<Components.IonSlidesAttributes & ReactProps, HTMLIonSlidesElement>('ion-slides');
|
||||||
export const IonSpinner = createReactComponent<Components.IonSpinnerAttributes, HTMLIonSpinnerElement>('ion-spinner');
|
export const IonSpinner = createReactComponent<Components.IonSpinnerAttributes & ReactProps, HTMLIonSpinnerElement>('ion-spinner');
|
||||||
export const IonSplitPane = createReactComponent<Components.IonSplitPaneAttributes, HTMLIonSplitPaneElement>('ion-split-pane');
|
export const IonSplitPane = createReactComponent<Components.IonSplitPaneAttributes & ReactProps, HTMLIonSplitPaneElement>('ion-split-pane');
|
||||||
export const IonText = createReactComponent<Components.IonTextAttributes, HTMLIonTextElement>('ion-text');
|
export const IonText = createReactComponent<Components.IonTextAttributes & ReactProps, HTMLIonTextElement>('ion-text');
|
||||||
export const IonTextarea = createReactComponent<Components.IonTextareaAttributes, HTMLIonTextareaElement>('ion-textarea');
|
export const IonTextarea = createReactComponent<Components.IonTextareaAttributes & ReactProps, HTMLIonTextareaElement>('ion-textarea');
|
||||||
export const IonThumbnail = createReactComponent<Components.IonThumbnailAttributes, HTMLIonThumbnailElement>('ion-thumbnail');
|
export const IonThumbnail = createReactComponent<Components.IonThumbnailAttributes & ReactProps, HTMLIonThumbnailElement>('ion-thumbnail');
|
||||||
export const IonTitle = createReactComponent<Components.IonTitleAttributes, HTMLIonTitleElement>('ion-title');
|
export const IonTitle = createReactComponent<Components.IonTitleAttributes & ReactProps, HTMLIonTitleElement>('ion-title');
|
||||||
export const IonToggle = createReactComponent<Components.IonToggleAttributes, HTMLIonToggleElement>('ion-toggle');
|
export const IonToggle = createReactComponent<Components.IonToggleAttributes & ReactProps, HTMLIonToggleElement>('ion-toggle');
|
||||||
export const IonToolbar = createReactComponent<Components.IonToolbarAttributes, HTMLIonToolbarElement>('ion-toolbar');
|
export const IonToolbar = createReactComponent<Components.IonToolbarAttributes & ReactProps, HTMLIonToolbarElement>('ion-toolbar');
|
||||||
export const IonVirtualScroll = createReactComponent<Components.IonVirtualScrollAttributes, HTMLIonVirtualScrollElement>('ion-virtual-scroll');
|
export const IonVirtualScroll = createReactComponent<Components.IonVirtualScrollAttributes & ReactProps, HTMLIonVirtualScrollElement>('ion-virtual-scroll');
|
||||||
|
83
react/src/components/utils/attachEventProps.ts
Normal file
83
react/src/components/utils/attachEventProps.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
export function attachEventProps(node: HTMLElement, newProps: any, oldProps: any = {}) {
|
||||||
|
const className = getClassName(node.classList, newProps, oldProps);
|
||||||
|
if(className) {
|
||||||
|
node.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(newProps).forEach(name => {
|
||||||
|
if (name === 'children' || name === 'style' || name === 'ref' || name === 'className') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
|
||||||
|
const eventName = name.substring(2);
|
||||||
|
const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
|
||||||
|
|
||||||
|
if (!isCoveredByReact(eventNameLc)) {
|
||||||
|
syncEvent(node, eventNameLc, newProps[name]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(node as any)[name] = newProps[name];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClassName(classList: DOMTokenList, newProps: any, oldProps: any) {
|
||||||
|
// map the classes to Maps for performance
|
||||||
|
const currentClasses = arrayToMap(classList);
|
||||||
|
const incomingPropClasses = arrayToMap(newProps.className ? newProps.className.split(' ') : []);
|
||||||
|
const oldPropClasses = arrayToMap(oldProps.className ? oldProps.className.split(' ') : []);
|
||||||
|
const finalClassNames: string[] = [];
|
||||||
|
// loop through each of the current classes on the component
|
||||||
|
// to see if it should be a part of the classNames added
|
||||||
|
currentClasses.forEach((currentClass) => {
|
||||||
|
if (incomingPropClasses.has(currentClass)) {
|
||||||
|
// add it as its already included in classnames coming in from newProps
|
||||||
|
finalClassNames.push(currentClass);
|
||||||
|
incomingPropClasses.delete(currentClass);
|
||||||
|
}
|
||||||
|
else if (!oldPropClasses.has(currentClass)) {
|
||||||
|
// add it as it has NOT been removed by user
|
||||||
|
finalClassNames.push(currentClass);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
incomingPropClasses.forEach(s => finalClassNames.push(s));
|
||||||
|
return finalClassNames.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an event is supported in the current execution environment.
|
||||||
|
* @license Modernizr 3.0.0pre (Custom Build) | MIT
|
||||||
|
*/
|
||||||
|
export function isCoveredByReact(eventNameSuffix: string, doc: Document = document) {
|
||||||
|
const eventName = 'on' + eventNameSuffix;
|
||||||
|
let isSupported = eventName in doc;
|
||||||
|
|
||||||
|
if (!isSupported) {
|
||||||
|
const element = doc.createElement('div');
|
||||||
|
element.setAttribute(eventName, 'return;');
|
||||||
|
isSupported = typeof (<any>element)[eventName] === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
return isSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function syncEvent(node: Element, eventName: string, newEventHandler: (e: Event) => any) {
|
||||||
|
const eventStore = (node as any).__events || ((node as any).__events = {});
|
||||||
|
const oldEventHandler = eventStore[eventName];
|
||||||
|
|
||||||
|
// Remove old listener so they don't double up.
|
||||||
|
if (oldEventHandler) {
|
||||||
|
node.removeEventListener(eventName, oldEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind new listener.
|
||||||
|
node.addEventListener(eventName, eventStore[eventName] = function handler(e: Event) {
|
||||||
|
newEventHandler.call(this, e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayToMap(arr: string[] | DOMTokenList) {
|
||||||
|
const map = new Map<string, string>();
|
||||||
|
arr.forEach((s: string) => map.set(s, s));
|
||||||
|
return map;
|
||||||
|
}
|
@ -1,34 +1,4 @@
|
|||||||
/**
|
|
||||||
* Checks if an event is supported in the current execution environment.
|
|
||||||
* @license Modernizr 3.0.0pre (Custom Build) | MIT
|
|
||||||
*/
|
|
||||||
export function isCoveredByReact(eventNameSuffix: string, doc: Document = document) {
|
|
||||||
const eventName = 'on' + eventNameSuffix;
|
|
||||||
let isSupported = eventName in doc;
|
|
||||||
|
|
||||||
if (!isSupported) {
|
|
||||||
const element = doc.createElement('div');
|
|
||||||
element.setAttribute(eventName, 'return;');
|
|
||||||
isSupported = typeof (<any>element)[eventName] === 'function';
|
|
||||||
}
|
|
||||||
|
|
||||||
return isSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function syncEvent(node: Element, eventName: string, newEventHandler: (e: Event) => any) {
|
|
||||||
const eventStore = (node as any).__events || ((node as any).__events = {});
|
|
||||||
const oldEventHandler = eventStore[eventName];
|
|
||||||
|
|
||||||
// Remove old listener so they don't double up.
|
|
||||||
if (oldEventHandler) {
|
|
||||||
node.removeEventListener(eventName, oldEventHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind new listener.
|
|
||||||
node.addEventListener(eventName, eventStore[eventName] = function handler(e: Event) {
|
|
||||||
newEventHandler.call(this, e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dashToPascalCase = (str: string) => str.toLowerCase().split('-').map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)).join('');
|
export const dashToPascalCase = (str: string) => str.toLowerCase().split('-').map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)).join('');
|
||||||
|
|
||||||
@ -41,29 +11,11 @@ export function ensureElementInBody<E extends HTMLElement>(elementName: string):
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function attachEventProps<E extends HTMLElement>(node: E, props: any) {
|
|
||||||
Object.keys(props).forEach(name => {
|
|
||||||
if (name === 'children' || name === 'style' || name === 'ref') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
|
|
||||||
const eventName = name.substring(2);
|
|
||||||
const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
|
|
||||||
|
|
||||||
if (!isCoveredByReact(eventNameLc)) {
|
|
||||||
syncEvent(node, eventNameLc, props[name]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(node as any)[name] = props[name];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function generateUniqueId() {
|
export function generateUniqueId() {
|
||||||
return ([1e7].toString() + -1e3.toString() + -4e3.toString() + -8e3.toString() + -1e11.toString()).replace(/[018]/g, function(c: any) {
|
return ([1e7].toString() + -1e3.toString() + -4e3.toString() + -8e3.toString() + -1e11.toString()).replace(/[018]/g, function(c: any) {
|
||||||
const random = crypto.getRandomValues(new Uint8Array(1)) as Uint8Array;
|
const random = crypto.getRandomValues(new Uint8Array(1)) as Uint8Array;
|
||||||
return (c ^ random[0] & 15 >> c / 4).toString(16);
|
return (c ^ random[0] & 15 >> c / 4).toString(16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export * from './attachEventProps';
|
||||||
|
Reference in New Issue
Block a user