Skip to content
Merged
24 changes: 12 additions & 12 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -1039,7 +1039,7 @@ export namespace Components {
*/
"loadingSpinner"?: SpinnerTypes | null;
/**
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"loadingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -1422,7 +1422,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Optional text content to display in the loading indicator.
* Optional text content to display in the loading indicator. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -2221,15 +2221,15 @@ export namespace Components {
*/
"pullingIcon"?: SpinnerTypes | string | null;
/**
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"pullingText"?: string | IonicSafeString;
/**
* An animated SVG spinner that shows when refreshing begins
*/
"refreshingSpinner"?: SpinnerTypes | null;
/**
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"refreshingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -2990,7 +2990,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Message to be shown in the toast.
* Message to be shown in the toast. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -4147,7 +4147,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -5045,7 +5045,7 @@ declare namespace LocalJSX {
*/
"loadingSpinner"?: SpinnerTypes | null;
/**
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"loadingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -5420,7 +5420,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Optional text content to display in the loading indicator.
* Optional text content to display in the loading indicator. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -6188,15 +6188,15 @@ declare namespace LocalJSX {
*/
"pullingIcon"?: SpinnerTypes | string | null;
/**
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"pullingText"?: string | IonicSafeString;
/**
* An animated SVG spinner that shows when refreshing begins
*/
"refreshingSpinner"?: SpinnerTypes | null;
/**
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"refreshingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -6999,7 +6999,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Message to be shown in the toast.
* Message to be shown in the toast. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down
23 changes: 22 additions & 1 deletion core/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Listen, Method, Prop, Watch, forceUpdate, h } from '@stencil/core';

import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type {
AlertButton,
Expand All @@ -12,6 +13,7 @@ import type {
OverlayEventDetail,
OverlayInterface,
} from '../../interface';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import type { Gesture } from '../../utils/gesture';
import { createButtonActiveGesture } from '../../utils/gesture/button-active';
import { BACKDROP, dismiss, eventMethod, isCancel, prepareOverlay, present, safeCall } from '../../utils/overlays';
Expand Down Expand Up @@ -39,6 +41,7 @@ import { mdLeaveAnimation } from './animations/md.leave';
scoped: true,
})
export class Alert implements ComponentInterface, OverlayInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
private activeId?: string;
private inputType?: string;
private processedInputs: AlertInput[] = [];
Expand Down Expand Up @@ -93,6 +96,11 @@ export class Alert implements ComponentInterface, OverlayInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() message?: string | IonicSafeString;

Expand Down Expand Up @@ -579,6 +587,19 @@ export class Alert implements ComponentInterface, OverlayInterface {
);
}

private renderAlertMessage(msgId: string) {
const { customHTMLEnabled, message } = this;
if (customHTMLEnabled) {
return <div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(message)}></div>;
}

return (
<div id={msgId} class="alert-message">
{message}
</div>
);
}

render() {
const { overlayIndex, header, subHeader, message, htmlAttributes } = this;
const mode = getIonMode(this);
Expand Down Expand Up @@ -631,7 +652,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
)}
</div>

<div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(message)}></div>
{this.renderAlertMessage(msgId)}

{this.renderAlertInputs()}
{this.renderAlertButtons()}
Expand Down
40 changes: 40 additions & 0 deletions core/src/components/alert/test/alert.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { newSpecPage } from '@stencil/core/testing';
import { Alert } from '../alert';
import { config } from '../../../global/config';

describe('alert: custom html', () => {
it('should allow for custom html by default', async () => {
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});

const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});

const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});

const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Component, Host, Prop, h } from '@stencil/core';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type { SpinnerTypes } from '../../interface';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import type { IonicSafeString } from '../../utils/sanitization';
import { sanitizeDOMString } from '../../utils/sanitization';

Expand All @@ -15,6 +16,8 @@ import { sanitizeDOMString } from '../../utils/sanitization';
},
})
export class InfiniteScrollContent implements ComponentInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);

/**
* An animated SVG spinner that shows while loading.
*/
Expand All @@ -28,6 +31,11 @@ export class InfiniteScrollContent implements ComponentInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() loadingText?: string | IonicSafeString;

Expand All @@ -41,6 +49,15 @@ export class InfiniteScrollContent implements ComponentInterface {
}
}

private renderLoadingText() {
const { customHTMLEnabled, loadingText } = this;
if (customHTMLEnabled) {
return <div class="infinite-loading-text" innerHTML={sanitizeDOMString(loadingText)}></div>;
}

return <div class="infinite-loading-text">{this.loadingText}</div>;
}

render() {
const mode = getIonMode(this);
return (
Expand All @@ -58,9 +75,7 @@ export class InfiniteScrollContent implements ComponentInterface {
<ion-spinner name={this.loadingSpinner} />
</div>
)}
{this.loadingText !== undefined && (
<div class="infinite-loading-text" innerHTML={sanitizeDOMString(this.loadingText)} />
)}
{this.loadingText !== undefined && this.renderLoadingText()}
</div>
</Host>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { newSpecPage } from '@stencil/core/testing';
import { InfiniteScrollContent } from '../infinite-scroll-content';
import { config } from '../../../global/config';

describe('infinite-scroll-content: custom html', () => {
it('should allow for custom html by default', async () => {
const page = await newSpecPage({
components: [InfiniteScrollContent],
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text</button>"></ion-infinite-scroll-content>`,
});

const content = page.body.querySelector('.infinite-loading-text');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [InfiniteScrollContent],
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text</button>"></ion-infinite-scroll-content>`,
});

const content = page.body.querySelector('.infinite-loading-text');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [InfiniteScrollContent],
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text2</button>"></ion-infinite-scroll-content>`,
});

const content = page.body.querySelector('.infinite-loading-text');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
});
Loading