Compare commits
1 Commits
v7.5.6
...
sp/stencil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
395b14317f |
14
CHANGELOG.md
@@ -3,20 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** match MD spec on tablet ([#28501](https://github.com/ionic-team/ionic-framework/issues/28501)) ([6a2be9f](https://github.com/ionic-team/ionic-framework/commit/6a2be9fa3c12a893d98dc139a1575a6e7e3c7c26)), closes [#23977](https://github.com/ionic-team/ionic-framework/issues/23977)
|
||||
* **angular:** ng add @ionic/angular in standalone projects ([#28523](https://github.com/ionic-team/ionic-framework/issues/28523)) ([c07312e](https://github.com/ionic-team/ionic-framework/commit/c07312e5ed931f6f825ccf083c9dead9fa815843)), closes [#28514](https://github.com/ionic-team/ionic-framework/issues/28514)
|
||||
* **angular:** overlays are defined when using standalone controllers ([#28560](https://github.com/ionic-team/ionic-framework/issues/28560)) ([9453132](https://github.com/ionic-team/ionic-framework/commit/9453132aa8952b4adfa1326e61138b329e254f76)), closes [#28385](https://github.com/ionic-team/ionic-framework/issues/28385)
|
||||
* **datetime:** updating value with min scrolls to new value ([#28549](https://github.com/ionic-team/ionic-framework/issues/28549)) ([388d19e](https://github.com/ionic-team/ionic-framework/commit/388d19e04f83f85abd4602adb04cc71ac575764a)), closes [#28548](https://github.com/ionic-team/ionic-framework/issues/28548)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** match MD spec on tablet ([#28501](https://github.com/ionic-team/ionic-framework/issues/28501)) ([6a2be9f](https://github.com/ionic-team/ionic-framework/commit/6a2be9fa3c12a893d98dc139a1575a6e7e3c7c26)), closes [#23977](https://github.com/ionic-team/ionic-framework/issues/23977)
|
||||
* **datetime:** updating value with min scrolls to new value ([#28549](https://github.com/ionic-team/ionic-framework/issues/28549)) ([388d19e](https://github.com/ionic-team/ionic-framework/commit/388d19e04f83f85abd4602adb04cc71ac575764a)), closes [#28548](https://github.com/ionic-team/ionic-framework/issues/28548)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
|
||||
|
||||
1625
core/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -31,7 +31,7 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@stencil/core": "^4.7.2",
|
||||
"@stencil/core": "^4.6.0-dev.1698410852.c526078",
|
||||
"ionicons": "^7.2.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -64,7 +64,6 @@
|
||||
"jest": "^29.7.0",
|
||||
"jest-cli": "^29.7.0",
|
||||
"prettier": "^2.6.1",
|
||||
"puppeteer": "21.1.1",
|
||||
"rollup": "^2.26.4",
|
||||
"sass": "^1.33.0",
|
||||
"serve": "^14.0.1",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { AccordionGroup } from '../../accordion-group/accordion-group';
|
||||
import { Item } from '../../item/item';
|
||||
import { Accordion } from '../accordion';
|
||||
import { AccordionGroup } from '../../accordion-group/accordion-group.tsx';
|
||||
import { Item } from '../../item/item.tsx';
|
||||
import { Accordion } from '../accordion.tsx';
|
||||
|
||||
it('should open correct accordions when accordion group value is set', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -25,7 +25,7 @@ it('should open correct accordions when accordion group value is set', async ()
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
accordions.forEach((accordion) => {
|
||||
@@ -61,7 +61,7 @@ it('should open correct accordions when accordion value is set', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
accordions.forEach((accordion) => {
|
||||
@@ -97,7 +97,7 @@ it('should open more than one accordion when multiple="true"', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
accordions.forEach((accordion) => {
|
||||
@@ -133,7 +133,7 @@ it('should render with accordion open', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
expect(accordions[0].classList.contains('accordion-collapsed')).toEqual(false);
|
||||
@@ -162,7 +162,7 @@ it('should accept a string when multiple="true"', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
expect(accordions[0].classList.contains('accordion-collapsed')).toEqual(false);
|
||||
@@ -183,8 +183,8 @@ it('should set default values if not provided', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordion = accordionGroup.querySelector('ion-accordion')!;
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordion = accordionGroup.querySelector('ion-accordion');
|
||||
|
||||
/**
|
||||
* ID is determined via an auto incrementing counter
|
||||
|
||||
@@ -7,17 +7,10 @@ describe('action sheet: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [ActionSheet],
|
||||
template: () => (
|
||||
<ion-action-sheet
|
||||
htmlAttributes={{
|
||||
'data-testid': 'basic-action-sheet',
|
||||
}}
|
||||
overlayIndex={1}
|
||||
></ion-action-sheet>
|
||||
),
|
||||
template: () => <ion-action-sheet htmlAttributes={{ 'data-testid': 'basic-action-sheet' }}></ion-action-sheet>,
|
||||
});
|
||||
|
||||
const actionSheet = page.body.querySelector('ion-action-sheet')!;
|
||||
const actionSheet = page.body.querySelector('ion-action-sheet');
|
||||
|
||||
await expect(actionSheet.getAttribute('data-testid')).toBe('basic-action-sheet');
|
||||
});
|
||||
|
||||
@@ -52,18 +52,9 @@
|
||||
}
|
||||
|
||||
.alert-message {
|
||||
font-size: $alert-md-message-font-size;
|
||||
}
|
||||
max-height: $alert-md-content-max-height;
|
||||
|
||||
/**
|
||||
* MD Alerts on tablets can expand vertically up to
|
||||
* a total maximum height. We only want to set a max-height
|
||||
* on mobile phones.
|
||||
*/
|
||||
@include mobile-viewport() {
|
||||
.alert-message {
|
||||
max-height: $alert-md-content-max-height;
|
||||
}
|
||||
font-size: $alert-md-message-font-size;
|
||||
}
|
||||
|
||||
.alert-message:empty {
|
||||
@@ -111,24 +102,14 @@
|
||||
.alert-checkbox-group {
|
||||
position: relative;
|
||||
|
||||
max-height: $alert-md-content-max-height;
|
||||
|
||||
border-top: $alert-md-list-border-top;
|
||||
border-bottom: $alert-md-list-border-bottom;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* MD Alerts on tablets can expand vertically up to
|
||||
* a total maximum height. We only want to set a max-height
|
||||
* on mobile phones.
|
||||
*/
|
||||
@include mobile-viewport() {
|
||||
.alert-radio-group,
|
||||
.alert-checkbox-group {
|
||||
max-height: $alert-md-content-max-height;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-tappable {
|
||||
position: relative;
|
||||
|
||||
@@ -301,14 +282,3 @@
|
||||
.alert-button-inner {
|
||||
justify-content: $alert-md-button-group-justify-content;
|
||||
}
|
||||
|
||||
/**
|
||||
* MD alerts should scale up to 560px x 560px
|
||||
* on tablet dimensions.
|
||||
*/
|
||||
@include tablet-viewport() {
|
||||
:host {
|
||||
--max-width: #{$alert-md-max-width-tablet};
|
||||
--max-height: #{$alert-md-max-height-tablet};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,20 +10,6 @@ $alert-md-font-size: dynamic-font(14px) !default;
|
||||
/// @prop - Max width of the alert
|
||||
$alert-md-max-width: 280px !default;
|
||||
|
||||
/// @prop - Max width of the alert on a tablet
|
||||
/**
|
||||
* Large display requirements for MD Alert:
|
||||
* 1. Maintain a minimum of 48px distance from the leading and
|
||||
* trailing edges of the screen. (48px * 2 = 96px)
|
||||
* 2. The width can increase up to 560px.
|
||||
* 3. The height can increase up to 560px.
|
||||
* Source: https://m2.material.io/components/dialogs#behavior
|
||||
*/
|
||||
$alert-md-max-width-tablet: min(calc(100vw - 96px), 560px) !default;
|
||||
|
||||
/// @prop - Max width of the alert on a tablet
|
||||
$alert-md-max-height-tablet: min(calc(100vh - 96px), 560px) !default;
|
||||
|
||||
/// @prop - Border radius of the alert
|
||||
$alert-md-border-radius: 4px !default;
|
||||
|
||||
|
||||
@@ -84,15 +84,7 @@
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert has a maximum height in scenarios
|
||||
* such as the MD alert on tablet devices.
|
||||
* As a result, we need to make sure the inner
|
||||
* containers can scroll otherwise content
|
||||
* may be cut off.
|
||||
*/
|
||||
.alert-message,
|
||||
.alert-input-group {
|
||||
.alert-message {
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { Alert } from '../alert';
|
||||
import { config } from '../../../global/config';
|
||||
|
||||
describe('alert: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -10,7 +9,7 @@ describe('alert: custom html', () => {
|
||||
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.alert-message')!;
|
||||
const content = page.body.querySelector('.alert-message');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -22,7 +21,7 @@ describe('alert: custom html', () => {
|
||||
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.alert-message')!;
|
||||
const content = page.body.querySelector('.alert-message');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).not.toBe(null);
|
||||
});
|
||||
@@ -34,7 +33,7 @@ describe('alert: custom html', () => {
|
||||
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.alert-message')!;
|
||||
const content = page.body.querySelector('.alert-message');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test, Viewports } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* This behavior does not vary across directions.
|
||||
*/
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('alert: rendering - tablet'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setViewportSize(Viewports.tablet.portrait);
|
||||
await page.goto('/src/components/alert/test/basic', config);
|
||||
});
|
||||
|
||||
test('should expand width and height on larger displays with text', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#longMessage');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await button.click();
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-tablet-text'));
|
||||
});
|
||||
|
||||
test('should expand width and height on larger displays with checkboxes', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#checkbox');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await button.click();
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-tablet-checkboxes'));
|
||||
});
|
||||
|
||||
test('should expand width and height on larger displays with radios', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#radio');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await button.click();
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-tablet-radios'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
@@ -1,7 +1,7 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Breadcrumb } from '../../breadcrumb/breadcrumb';
|
||||
import { Breadcrumbs } from '../breadcrumbs';
|
||||
import { Breadcrumb } from '../../breadcrumb/breadcrumb.tsx';
|
||||
import { Breadcrumbs } from '../breadcrumbs.tsx';
|
||||
|
||||
it('should correctly provide the collapsed breadcrumbs in the event payload', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -18,8 +18,8 @@ it('should correctly provide the collapsed breadcrumbs in the event payload', as
|
||||
});
|
||||
|
||||
const onCollapsedClick = jest.fn((ev) => ev);
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs')!;
|
||||
const breadcrumb = page.body.querySelectorAll('ion-breadcrumb')!;
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs');
|
||||
const breadcrumb = page.body.querySelectorAll('ion-breadcrumb');
|
||||
|
||||
breadcrumbs.addEventListener('ionCollapsedClick', onCollapsedClick);
|
||||
|
||||
@@ -46,8 +46,8 @@ it('should exclude the separator from narrators', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const firstBreadcrumb = page.body.querySelector('ion-breadcrumb:first-of-type')!;
|
||||
const separator = firstBreadcrumb.shadowRoot!.querySelector('[part="separator"]')!;
|
||||
const firstBreadcrumb = page.body.querySelector('ion-breadcrumb:first-of-type');
|
||||
const separator = firstBreadcrumb.shadowRoot.querySelector('[part="separator"]');
|
||||
|
||||
expect(separator.getAttribute('aria-hidden')).toBe('true');
|
||||
});
|
||||
@@ -62,7 +62,7 @@ it('should have color attribute', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs')!;
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs');
|
||||
|
||||
expect(breadcrumbs.hasAttribute('color')).toBe(true);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Button } from '../../button';
|
||||
|
||||
describe('Button: Hidden Form Button', () => {
|
||||
@@ -16,7 +15,8 @@ describe('Button: Hidden Form Button', () => {
|
||||
return page.body.querySelectorAll('form button');
|
||||
};
|
||||
|
||||
const button = page.body.querySelector('ion-button')!;
|
||||
const form = page.body.querySelectorAll('form');
|
||||
const button = page.body.querySelector('ion-button');
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('ion-checkbox: disabled', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const checkbox = page.body.querySelector('ion-checkbox')!;
|
||||
const checkbox = page.body.querySelector('ion-checkbox');
|
||||
|
||||
expect(checkbox.checked).toBe(false);
|
||||
|
||||
|
||||
@@ -1132,7 +1132,7 @@ export class Datetime implements ComponentInterface {
|
||||
* so we need to re-init behavior with the new elements.
|
||||
*/
|
||||
componentDidRender() {
|
||||
const { presentation, prevPresentation, calendarBodyRef, minParts, preferWheel, forceRenderDate } = this;
|
||||
const { presentation, prevPresentation, calendarBodyRef, minParts, preferWheel } = this;
|
||||
|
||||
/**
|
||||
* TODO(FW-2165)
|
||||
@@ -1150,20 +1150,7 @@ export class Datetime implements ComponentInterface {
|
||||
const hasCalendarGrid = !preferWheel && ['date-time', 'time-date', 'date'].includes(presentation);
|
||||
if (minParts !== undefined && hasCalendarGrid && calendarBodyRef) {
|
||||
const workingMonth = calendarBodyRef.querySelector('.calendar-month:nth-of-type(1)');
|
||||
/**
|
||||
* We need to make sure the datetime is not in the process
|
||||
* of scrolling to a new datetime value if the value
|
||||
* is updated programmatically.
|
||||
* Otherwise, the datetime will appear to not scroll at all because
|
||||
* we are resetting the scroll position to the center of the view.
|
||||
* Prior to the datetime's value being updated programmatically,
|
||||
* the calendarBodyRef is scrolled such that the middle month is centered
|
||||
* in the view. The below code updates the scroll position so the middle
|
||||
* month is also centered in the view. Since the scroll position did not change,
|
||||
* the scroll callback in this file does not fire,
|
||||
* and the resolveForceDateScrolling promise never resolves.
|
||||
*/
|
||||
if (workingMonth && forceRenderDate === undefined) {
|
||||
if (workingMonth) {
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import { isSameDay, isBefore, isAfter } from '../utils/comparison';
|
||||
|
||||
describe('isSameDay()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference: DatetimeParts = { month: 1, day: 1, year: 2021 };
|
||||
const reference = { month: 1, day: 1, year: 2021 };
|
||||
|
||||
expect(isSameDay(reference, { month: 1, day: 1, year: 2021 })).toEqual(true);
|
||||
expect(isSameDay(reference, { month: 2, day: 1, year: 2021 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: 1, day: 2, year: 2021 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: 1, day: 1, year: 2022 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: 0, day: 0, year: 0 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: null, day: null, year: null } as any)).toEqual(false);
|
||||
expect(isSameDay(reference, { month: null, day: null, year: null })).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBefore()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference: DatetimeParts = { month: 1, day: 1, year: 2021 };
|
||||
const reference = { month: 1, day: 1, year: 2021 };
|
||||
|
||||
expect(isBefore(reference, { month: 1, day: 1, year: 2021 })).toEqual(false);
|
||||
expect(isBefore(reference, { month: 2, day: 1, year: 2021 })).toEqual(true);
|
||||
@@ -24,13 +23,13 @@ describe('isBefore()', () => {
|
||||
expect(isBefore(reference, { month: 1, day: 1, year: 2022 })).toEqual(true);
|
||||
expect(isBefore(reference, { month: 1, day: 1, year: 2020 })).toEqual(false);
|
||||
expect(isBefore(reference, { month: 0, day: 0, year: 0 })).toEqual(false);
|
||||
expect(isBefore(reference, { month: null, day: null, year: null } as any)).toEqual(false);
|
||||
expect(isBefore(reference, { month: null, day: null, year: null })).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAfter()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference: DatetimeParts = { month: 2, day: 2, year: 2021 };
|
||||
const reference = { month: 2, day: 2, year: 2021 };
|
||||
|
||||
expect(isAfter(reference, { month: 2, day: 2, year: 2021 })).toEqual(false);
|
||||
expect(isAfter(reference, { month: 2, day: 1, year: 2021 })).toEqual(true);
|
||||
@@ -43,6 +42,6 @@ describe('isAfter()', () => {
|
||||
* 2021 > undefined === false
|
||||
* 2021 > null === true
|
||||
*/
|
||||
expect(isAfter(reference, { month: null, day: null, year: null } as any)).toEqual(true);
|
||||
expect(isAfter(reference, { month: null, day: null, year: null })).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
generateMonths,
|
||||
getDaysOfWeek,
|
||||
@@ -365,7 +364,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -373,7 +372,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { hours } = generateTime('en-US', refValue, 'h23', minParts);
|
||||
|
||||
@@ -388,7 +387,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 22,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -396,7 +395,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { hours, minutes } = generateTime('en-US', refValue, 'h23', minParts);
|
||||
|
||||
@@ -412,7 +411,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 30,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -420,7 +419,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -428,7 +427,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 40,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { hours } = generateTime('en-US', refValue, 'h23', minParts, maxParts);
|
||||
|
||||
@@ -442,7 +441,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 0,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -450,7 +449,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
|
||||
|
||||
@@ -464,7 +463,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 12,
|
||||
minute: 0,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -472,7 +471,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
|
||||
|
||||
@@ -483,7 +482,7 @@ describe('generateTime()', () => {
|
||||
|
||||
describe('getToday', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.useFakeTimers('modern');
|
||||
// System time is zero based, 1 = February
|
||||
jest.setSystemTime(new Date(2022, 1, 21, 18, 30));
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
generateDayAriaLabel,
|
||||
getMonthAndDay,
|
||||
@@ -110,7 +109,7 @@ describe('getLocalizedDayPeriod', () => {
|
||||
|
||||
describe('getLocalizedTime', () => {
|
||||
it('should localize the time to PM', () => {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
const datetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -122,7 +121,7 @@ describe('getLocalizedTime', () => {
|
||||
});
|
||||
|
||||
it('should localize the time to AM', () => {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
const datetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -134,7 +133,7 @@ describe('getLocalizedTime', () => {
|
||||
});
|
||||
|
||||
it('should avoid Chromium bug when using 12 hour time in a 24 hour locale', () => {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
const datetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -145,12 +144,12 @@ describe('getLocalizedTime', () => {
|
||||
expect(getLocalizedTime('en-GB', datetimeParts, 'h12')).toEqual('12:00 am');
|
||||
});
|
||||
it('should parse time-only values correctly', () => {
|
||||
const datetimeParts: Partial<DatetimeParts> = {
|
||||
const datetimeParts = {
|
||||
hour: 22,
|
||||
minute: 40,
|
||||
};
|
||||
|
||||
expect(getLocalizedTime('en-US', datetimeParts as DatetimeParts, 'h12')).toEqual('10:40 PM');
|
||||
expect(getLocalizedTime('en-US', datetimeParts as DatetimeParts, 'h23')).toEqual('22:40');
|
||||
expect(getLocalizedTime('en-US', datetimeParts, 'h12')).toEqual('10:40 PM');
|
||||
expect(getLocalizedTime('en-US', datetimeParts, 'h23')).toEqual('22:40');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
getPreviousYear,
|
||||
getNextYear,
|
||||
@@ -104,31 +103,31 @@ describe('getInternalHourValue()', () => {
|
||||
|
||||
describe('calculateHourFromAMPM()', () => {
|
||||
it('should correctly convert from AM to PM', () => {
|
||||
expect(calculateHourFromAMPM({ hour: 12, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(12);
|
||||
expect(calculateHourFromAMPM({ hour: 1, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(13);
|
||||
expect(calculateHourFromAMPM({ hour: 2, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(14);
|
||||
expect(calculateHourFromAMPM({ hour: 3, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(15);
|
||||
expect(calculateHourFromAMPM({ hour: 4, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(16);
|
||||
expect(calculateHourFromAMPM({ hour: 5, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(17);
|
||||
expect(calculateHourFromAMPM({ hour: 6, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(18);
|
||||
expect(calculateHourFromAMPM({ hour: 7, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(19);
|
||||
expect(calculateHourFromAMPM({ hour: 8, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(20);
|
||||
expect(calculateHourFromAMPM({ hour: 9, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(21);
|
||||
expect(calculateHourFromAMPM({ hour: 10, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(22);
|
||||
expect(calculateHourFromAMPM({ hour: 11, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(23);
|
||||
expect(calculateHourFromAMPM({ hour: 12, ampm: 'am' }, 'pm')).toEqual(12);
|
||||
expect(calculateHourFromAMPM({ hour: 1, ampm: 'am' }, 'pm')).toEqual(13);
|
||||
expect(calculateHourFromAMPM({ hour: 2, ampm: 'am' }, 'pm')).toEqual(14);
|
||||
expect(calculateHourFromAMPM({ hour: 3, ampm: 'am' }, 'pm')).toEqual(15);
|
||||
expect(calculateHourFromAMPM({ hour: 4, ampm: 'am' }, 'pm')).toEqual(16);
|
||||
expect(calculateHourFromAMPM({ hour: 5, ampm: 'am' }, 'pm')).toEqual(17);
|
||||
expect(calculateHourFromAMPM({ hour: 6, ampm: 'am' }, 'pm')).toEqual(18);
|
||||
expect(calculateHourFromAMPM({ hour: 7, ampm: 'am' }, 'pm')).toEqual(19);
|
||||
expect(calculateHourFromAMPM({ hour: 8, ampm: 'am' }, 'pm')).toEqual(20);
|
||||
expect(calculateHourFromAMPM({ hour: 9, ampm: 'am' }, 'pm')).toEqual(21);
|
||||
expect(calculateHourFromAMPM({ hour: 10, ampm: 'am' }, 'pm')).toEqual(22);
|
||||
expect(calculateHourFromAMPM({ hour: 11, ampm: 'am' }, 'pm')).toEqual(23);
|
||||
|
||||
expect(calculateHourFromAMPM({ hour: 13, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(1);
|
||||
expect(calculateHourFromAMPM({ hour: 14, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(2);
|
||||
expect(calculateHourFromAMPM({ hour: 15, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(3);
|
||||
expect(calculateHourFromAMPM({ hour: 16, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(4);
|
||||
expect(calculateHourFromAMPM({ hour: 17, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(5);
|
||||
expect(calculateHourFromAMPM({ hour: 18, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(6);
|
||||
expect(calculateHourFromAMPM({ hour: 19, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(7);
|
||||
expect(calculateHourFromAMPM({ hour: 20, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(8);
|
||||
expect(calculateHourFromAMPM({ hour: 21, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(9);
|
||||
expect(calculateHourFromAMPM({ hour: 22, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(10);
|
||||
expect(calculateHourFromAMPM({ hour: 23, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(11);
|
||||
expect(calculateHourFromAMPM({ hour: 0, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(12);
|
||||
expect(calculateHourFromAMPM({ hour: 13, ampm: 'pm' }, 'am')).toEqual(1);
|
||||
expect(calculateHourFromAMPM({ hour: 14, ampm: 'pm' }, 'am')).toEqual(2);
|
||||
expect(calculateHourFromAMPM({ hour: 15, ampm: 'pm' }, 'am')).toEqual(3);
|
||||
expect(calculateHourFromAMPM({ hour: 16, ampm: 'pm' }, 'am')).toEqual(4);
|
||||
expect(calculateHourFromAMPM({ hour: 17, ampm: 'pm' }, 'am')).toEqual(5);
|
||||
expect(calculateHourFromAMPM({ hour: 18, ampm: 'pm' }, 'am')).toEqual(6);
|
||||
expect(calculateHourFromAMPM({ hour: 19, ampm: 'pm' }, 'am')).toEqual(7);
|
||||
expect(calculateHourFromAMPM({ hour: 20, ampm: 'pm' }, 'am')).toEqual(8);
|
||||
expect(calculateHourFromAMPM({ hour: 21, ampm: 'pm' }, 'am')).toEqual(9);
|
||||
expect(calculateHourFromAMPM({ hour: 22, ampm: 'pm' }, 'am')).toEqual(10);
|
||||
expect(calculateHourFromAMPM({ hour: 23, ampm: 'pm' }, 'am')).toEqual(11);
|
||||
expect(calculateHourFromAMPM({ hour: 0, ampm: 'pm' }, 'am')).toEqual(12);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -42,8 +42,7 @@ describe('parseDate()', () => {
|
||||
* See https://github.com/ionic-team/ionic-framework/commit/3fb4caf21ffac12f765c4c80bf1850e05d211c6a
|
||||
*/
|
||||
it('should return the correct time zone offset', () => {
|
||||
// Casting as any since `tzOffset` does not exist on DatetimeParts
|
||||
expect((parseDate('2022-12-15T13:47:30-02:00') as any)?.tzOffset).toEqual(undefined);
|
||||
expect(parseDate('2022-12-15T13:47:30-02:00').tzOffset).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should parse an array of dates', () => {
|
||||
@@ -163,8 +162,8 @@ describe('parseMinParts()', () => {
|
||||
minute: 4,
|
||||
hour: 2,
|
||||
};
|
||||
expect(parseMinParts(undefined as any, today)).toEqual(undefined);
|
||||
expect(parseMinParts(null as any, today)).toEqual(undefined);
|
||||
expect(parseMinParts(undefined, today)).toEqual(undefined);
|
||||
expect(parseMinParts(null, today)).toEqual(undefined);
|
||||
expect(parseMinParts('foo', today)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
@@ -226,8 +225,8 @@ describe('parseMaxParts()', () => {
|
||||
minute: 4,
|
||||
hour: 2,
|
||||
};
|
||||
expect(parseMaxParts(undefined as any, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(null as any, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(undefined, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(null, today)).toEqual(undefined);
|
||||
expect(parseMaxParts('foo', today)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,13 +84,13 @@ describe('isPrevMonthDisabled()', () => {
|
||||
// Date month and year is the same as min month and year
|
||||
expect(isPrevMonthDisabled({ month: 1, year: 2021, day: null }, { month: 1, year: 2021, day: null })).toEqual(true);
|
||||
// Date year is the same as min year (month not provided)
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 1, year: 2021, day: null }, { year: 2021, month: null, day: null } as any)
|
||||
).toEqual(true);
|
||||
expect(isPrevMonthDisabled({ month: 1, year: 2021, day: null }, { year: 2021, month: null, day: null })).toEqual(
|
||||
true
|
||||
);
|
||||
// Date year is less than the min year (month not provided)
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 5, year: 2021, day: null }, { year: 2022, month: null, day: null } as any)
|
||||
).toEqual(true);
|
||||
expect(isPrevMonthDisabled({ month: 5, year: 2021, day: null }, { year: 2022, month: null, day: null })).toEqual(
|
||||
true
|
||||
);
|
||||
|
||||
// Date is above the maximum bounds and the previous month does not does not fall within the
|
||||
// min-max range.
|
||||
@@ -118,12 +118,12 @@ describe('isPrevMonthDisabled()', () => {
|
||||
expect(isPrevMonthDisabled({ month: 12, year: 2021, day: null })).toEqual(false);
|
||||
// Date year is the same as min year,
|
||||
// but can navigate to a previous month without reducing the year.
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 12, year: 2021, day: null }, { year: 2021, month: null, day: null } as any)
|
||||
).toEqual(false);
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 2, year: 2021, day: null }, { year: 2021, month: null, day: null } as any)
|
||||
).toEqual(false);
|
||||
expect(isPrevMonthDisabled({ month: 12, year: 2021, day: null }, { year: 2021, month: null, day: null })).toEqual(
|
||||
false
|
||||
);
|
||||
expect(isPrevMonthDisabled({ month: 2, year: 2021, day: null }, { year: 2021, month: null, day: null })).toEqual(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { InfiniteScrollContent } from '../infinite-scroll-content';
|
||||
import { config } from '../../../global/config';
|
||||
|
||||
describe('infinite-scroll-content: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -10,7 +9,7 @@ describe('infinite-scroll-content: custom html', () => {
|
||||
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')!;
|
||||
const content = page.body.querySelector('.infinite-loading-text');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -22,7 +21,7 @@ describe('infinite-scroll-content: custom html', () => {
|
||||
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')!;
|
||||
const content = page.body.querySelector('.infinite-loading-text');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).not.toBe(null);
|
||||
});
|
||||
@@ -34,7 +33,7 @@ describe('infinite-scroll-content: custom html', () => {
|
||||
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')!;
|
||||
const content = page.body.querySelector('.infinite-loading-text');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Input } from '../input';
|
||||
|
||||
describe('input: rendering', () => {
|
||||
@@ -9,7 +8,7 @@ describe('input: rendering', () => {
|
||||
html: '<ion-input title="my title" tabindex="-1" data-form-type="password"></ion-input>',
|
||||
});
|
||||
|
||||
const nativeEl = page.body.querySelector('ion-input input')!;
|
||||
const nativeEl = page.body.querySelector('ion-input input');
|
||||
expect(nativeEl.getAttribute('title')).toBe('my title');
|
||||
expect(nativeEl.getAttribute('tabindex')).toBe('-1');
|
||||
expect(nativeEl.getAttribute('data-form-type')).toBe('password');
|
||||
@@ -64,9 +63,9 @@ describe('input: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
const input = page.body.querySelector('ion-input');
|
||||
|
||||
const labelText = input.querySelector('.label-text-wrapper')!;
|
||||
const labelText = input.querySelector('.label-text-wrapper');
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
@@ -78,9 +77,9 @@ describe('input: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
const input = page.body.querySelector('ion-input');
|
||||
|
||||
const labelText = input.querySelector('.label-text-wrapper')!;
|
||||
const labelText = input.querySelector('.label-text-wrapper');
|
||||
|
||||
expect(labelText.textContent).toBe('Label Slot Text');
|
||||
});
|
||||
@@ -92,9 +91,9 @@ describe('input: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
const input = page.body.querySelector('ion-input');
|
||||
|
||||
const labelText = input.querySelector('.label-text-wrapper')!;
|
||||
const labelText = input.querySelector('.label-text-wrapper');
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Item } from '../../../item/item';
|
||||
import { Input } from '../../input';
|
||||
import { Item } from '../../../item/item';
|
||||
|
||||
it('should render as modern when label is set asynchronously', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -13,7 +12,7 @@ it('should render as modern when label is set asynchronously', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
const input = page.body.querySelector('ion-input');
|
||||
|
||||
// Template should be modern
|
||||
expect(input.classList.contains('legacy-input')).toBe(false);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Radio } from '../../../radio/radio.tsx';
|
||||
import { RadioGroup } from '../../../radio-group/radio-group.tsx';
|
||||
import { Item } from '../../item.tsx';
|
||||
import { List } from '../../../list/list.tsx';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { List } from '../../../list/list';
|
||||
import { RadioGroup } from '../../../radio-group/radio-group';
|
||||
import { Radio } from '../../../radio/radio';
|
||||
import { Item } from '../../item';
|
||||
|
||||
describe('ion-item', () => {
|
||||
it('should not have a role when used without list', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -12,7 +11,7 @@ describe('ion-item', () => {
|
||||
html: `<ion-item>Hello World</ion-item>`,
|
||||
});
|
||||
|
||||
const item = page.body.querySelector('ion-item')!;
|
||||
const item = page.body.querySelector('ion-item');
|
||||
expect(item.getAttribute('role')).toBe(null);
|
||||
});
|
||||
|
||||
@@ -28,7 +27,7 @@ describe('ion-item', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const item = page.body.querySelector('ion-item')!;
|
||||
const item = page.body.querySelector('ion-item');
|
||||
expect(item.getAttribute('role')).toBe('listitem');
|
||||
});
|
||||
|
||||
@@ -46,7 +45,7 @@ describe('ion-item', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const item = page.body.querySelector('ion-item')!;
|
||||
const item = page.body.querySelector('ion-item');
|
||||
expect(item.getAttribute('role')).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,10 +7,10 @@ describe('loading: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Loading],
|
||||
template: () => <ion-loading overlayIndex={1} htmlAttributes={{ 'data-testid': 'basic-loading' }}></ion-loading>,
|
||||
template: () => <ion-loading htmlAttributes={{ 'data-testid': 'basic-loading' }}></ion-loading>,
|
||||
});
|
||||
|
||||
const loading = page.body.querySelector('ion-loading')!;
|
||||
const loading = page.body.querySelector('ion-loading');
|
||||
|
||||
await expect(loading.getAttribute('data-testid')).toBe('basic-loading');
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
// iOS Card Modal
|
||||
// --------------------------------------------------
|
||||
|
||||
@include mobile-viewport() {
|
||||
@media screen and (max-width: 767px) {
|
||||
@supports (width: max(0px, 1px)) {
|
||||
:host(.modal-card) {
|
||||
--height: calc(100% - max(30px, var(--ion-safe-area-top)) - 10px);
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include tablet-viewport() {
|
||||
@media screen and (min-width: 768px) {
|
||||
:host(.modal-card) {
|
||||
--width: calc(100% - 120px);
|
||||
--height: calc(100% - (120px + var(--ion-safe-area-top) + var(--ion-safe-area-bottom)));
|
||||
|
||||
@@ -15,8 +15,8 @@ describe('modal: a11y', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modalWrapper = modal.shadowRoot!.querySelector('.modal-wrapper')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modalWrapper = modal.shadowRoot.querySelector('.modal-wrapper');
|
||||
|
||||
await expect(modalWrapper.getAttribute('role')).toBe('alertdialog');
|
||||
});
|
||||
|
||||
@@ -7,10 +7,10 @@ describe('modal: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal htmlAttributes={{ 'data-testid': 'basic-modal' }} overlayIndex={1}></ion-modal>,
|
||||
template: () => <ion-modal htmlAttributes={{ 'data-testid': 'basic-modal' }}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await expect(modal.getAttribute('data-testid')).toBe('basic-modal');
|
||||
});
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { h, setMode } from '@stencil/core';
|
||||
import { h } from '@stencil/core';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { setMode } from '@stencil/core';
|
||||
|
||||
import { Content } from '../../../content/content';
|
||||
import { Modal } from '../../modal';
|
||||
import { Content } from '../../../content/content';
|
||||
|
||||
describe('modal: canDismiss', () => {
|
||||
describe('modal: regular modal', () => {
|
||||
it('should dismiss when canDismiss is true', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal overlayIndex={1} animated={false} canDismiss={true}></ion-modal>,
|
||||
template: () => <ion-modal animated={false} canDismiss={true}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -25,10 +26,10 @@ describe('modal: canDismiss', () => {
|
||||
it('should not dismiss when canDismiss is false', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal overlayIndex={1} animated={false} canDismiss={false}></ion-modal>,
|
||||
template: () => <ion-modal animated={false} canDismiss={false}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -43,7 +44,6 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -54,7 +54,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -69,7 +69,6 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -80,7 +79,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -95,24 +94,19 @@ describe('modal: canDismiss', () => {
|
||||
/**
|
||||
* Card modal is only available on iOS
|
||||
*/
|
||||
setMode(() => 'ios');
|
||||
setMode((elm) => 'ios');
|
||||
});
|
||||
it('should dismiss when canDismiss is true', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={true}
|
||||
>
|
||||
<ion-modal presentingElement={document.createElement('div')} animated={false} canDismiss={true}>
|
||||
<ion-content>Test Content</ion-content>
|
||||
</ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -126,18 +120,13 @@ describe('modal: canDismiss', () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={false}
|
||||
>
|
||||
<ion-modal presentingElement={document.createElement('div')} animated={false} canDismiss={false}>
|
||||
<ion-content>Test Content</ion-content>
|
||||
</ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -152,7 +141,6 @@ describe('modal: canDismiss', () => {
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
@@ -166,7 +154,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -181,7 +169,6 @@ describe('modal: canDismiss', () => {
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
@@ -195,7 +182,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -210,17 +197,11 @@ describe('modal: canDismiss', () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
canDismiss={true}
|
||||
></ion-modal>
|
||||
<ion-modal breakpoints={[0, 1]} initialBreakpoint={1} animated={false} canDismiss={true}></ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -234,17 +215,11 @@ describe('modal: canDismiss', () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
canDismiss={false}
|
||||
></ion-modal>
|
||||
<ion-modal breakpoints={[0, 1]} initialBreakpoint={1} animated={false} canDismiss={false}></ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -259,7 +234,6 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
@@ -272,7 +246,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -287,7 +261,6 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
@@ -300,7 +273,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -315,15 +288,15 @@ describe('modal: canDismiss', () => {
|
||||
const canDismiss = jest.fn();
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal overlayIndex={1} animated={false} canDismiss={canDismiss}></ion-modal>,
|
||||
template: () => <ion-modal animated={false} canDismiss={canDismiss}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
|
||||
await modal.dismiss('my data', 'my role');
|
||||
const returnValue = await modal.dismiss('my data', 'my role');
|
||||
|
||||
expect(canDismiss).toHaveBeenCalledWith('my data', 'my role');
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Config } from '../../../global/config';
|
||||
import type { ComponentProps } from '../../../interface';
|
||||
import { Nav } from '../nav';
|
||||
import type { NavOptions } from '../nav-interface';
|
||||
@@ -196,6 +197,7 @@ describe('NavController', () => {
|
||||
.insert(-1, null as any, null, null, trnsDone)
|
||||
.then(() => {
|
||||
fail('it should not succeed');
|
||||
done();
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
const hasCompleted = false;
|
||||
@@ -250,6 +252,7 @@ describe('NavController', () => {
|
||||
.pop(null, trnsDone)
|
||||
.then(() => {
|
||||
fail('it should not succeed');
|
||||
done();
|
||||
})
|
||||
.catch((err: any) => {
|
||||
const hasCompleted = false;
|
||||
@@ -816,10 +819,15 @@ describe('NavController', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
trnsDone = jest.fn();
|
||||
const config = new Config();
|
||||
config.reset({ animated: false });
|
||||
const page = await newSpecPage({
|
||||
components: [Nav],
|
||||
html: `<ion-nav></ion-nav>`,
|
||||
autoApplyChanges: true,
|
||||
context: {
|
||||
config,
|
||||
},
|
||||
});
|
||||
nav = page.rootInstance;
|
||||
});
|
||||
@@ -840,7 +848,7 @@ describe('NavController', () => {
|
||||
pause: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
onfinish: undefined,
|
||||
} as any;
|
||||
};
|
||||
|
||||
animation.play = () => {
|
||||
if (animation.onfinish) {
|
||||
|
||||
@@ -19,7 +19,6 @@ describe('picker-column', () => {
|
||||
text: 'Java',
|
||||
},
|
||||
],
|
||||
name: 'programmingLanguages',
|
||||
};
|
||||
|
||||
const page = await newSpecPage({
|
||||
@@ -27,8 +26,8 @@ describe('picker-column', () => {
|
||||
template: () => <ion-picker-column col={col}></ion-picker-column>,
|
||||
});
|
||||
|
||||
const firstOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(1)')!;
|
||||
const secondOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(2)')!;
|
||||
const firstOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(1)');
|
||||
const secondOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(2)');
|
||||
|
||||
expect(firstOption.getAttribute('aria-label')).toBe('C Sharp');
|
||||
expect(secondOption.getAttribute('aria-label')).toBe(null);
|
||||
|
||||
@@ -15,19 +15,18 @@ describe('picker-column: dynamic options', () => {
|
||||
|
||||
const page = await newSpecPage({
|
||||
components: [PickerColumnCmp],
|
||||
template: () => <ion-picker-column col={{ options: defaultOptions, name: 'animals' }}></ion-picker-column>,
|
||||
template: () => <ion-picker-column col={{ options: defaultOptions }}></ion-picker-column>,
|
||||
});
|
||||
|
||||
const pickerCol = page.body.querySelector('ion-picker-column')!;
|
||||
const pickerCol = page.body.querySelector('ion-picker-column');
|
||||
|
||||
pickerCol.col = {
|
||||
options: [...defaultOptions, { text: 'Carrot', value: 'carrot' }],
|
||||
name: 'vegetables',
|
||||
};
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
const pickerOpt = pickerCol.querySelector('.picker-opt:nth(2)')!;
|
||||
const pickerOpt = pickerCol.querySelector('.picker-opt:nth(2)');
|
||||
expect(pickerOpt.getAttribute('style')).toContain('transform');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,14 +5,14 @@ import { PickerColumnCmp } from '../picker-column';
|
||||
|
||||
describe('picker-column', () => {
|
||||
it('should add class to host of component', async () => {
|
||||
const col = { cssClass: 'test-class', options: [], name: 'col' };
|
||||
const col = { cssClass: 'test-class', options: [] };
|
||||
|
||||
const page = await newSpecPage({
|
||||
components: [PickerColumnCmp],
|
||||
template: () => <ion-picker-column col={col}></ion-picker-column>,
|
||||
});
|
||||
|
||||
const pickerCol = page.body.querySelector('ion-picker-column')!;
|
||||
const pickerCol = page.body.querySelector('ion-picker-column');
|
||||
expect(pickerCol.classList.contains('test-class')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,10 +7,10 @@ describe('popover: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Popover],
|
||||
template: () => <ion-popover overlayIndex={1} htmlAttributes={{ 'data-testid': 'basic-popover' }}></ion-popover>,
|
||||
template: () => <ion-popover htmlAttributes={{ 'data-testid': 'basic-popover' }}></ion-popover>,
|
||||
});
|
||||
|
||||
const popover = page.body.querySelector('ion-popover')!;
|
||||
const popover = page.body.querySelector('ion-popover');
|
||||
|
||||
await expect(popover.getAttribute('data-testid')).toBe('basic-popover');
|
||||
});
|
||||
|
||||
@@ -17,20 +17,20 @@ describe('isTriggerElement', () => {
|
||||
|
||||
describe('getIndexOfItem', () => {
|
||||
it('should return the correct index in an array of ion-items', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
|
||||
expect(getIndexOfItem(array, array[1])).toEqual(1);
|
||||
});
|
||||
|
||||
it('should return -1 when ion-item not found', () => {
|
||||
const el = document.createElement('ion-item');
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item']);
|
||||
|
||||
expect(getIndexOfItem(array, el)).toEqual(-1);
|
||||
});
|
||||
|
||||
it('should return -1 if a non-ion-item is passed in', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'div', 'ion-item']) as HTMLIonItemElement[];
|
||||
const array = createArrayOfElements(['ion-item', 'div', 'ion-item']);
|
||||
|
||||
expect(getIndexOfItem(array, array[1])).toEqual(-1);
|
||||
});
|
||||
@@ -38,24 +38,24 @@ describe('getIndexOfItem', () => {
|
||||
|
||||
describe('getNextItem', () => {
|
||||
it('should get the next item in an array of ion-items', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
expect(getNextItem(array, array[1])).toEqual(array[2]);
|
||||
});
|
||||
|
||||
it('should return undefined if there is no next item', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
expect(getNextItem(array, array[2])).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPrevItem', () => {
|
||||
it('should get the previous item in an array of ion-items', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
expect(getPrevItem(array, array[1])).toEqual(array[0]);
|
||||
});
|
||||
|
||||
it('should return undefined if there is no previous item', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
expect(getPrevItem(array, array[0])).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Radio } from '../radio.tsx';
|
||||
import { RadioGroup } from '../../radio-group/radio-group.tsx';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { RadioGroup } from '../../radio-group/radio-group';
|
||||
import { Radio } from '../radio';
|
||||
|
||||
describe('ion-radio', () => {
|
||||
it('should set a default value', async () => {
|
||||
const radio = new Radio();
|
||||
@@ -22,7 +21,7 @@ describe('ion-radio', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const radio = page.body.querySelector('ion-radio')!;
|
||||
const radio = page.root.querySelector('ion-radio');
|
||||
expect(radio.classList.contains('radio-checked')).toBe(false);
|
||||
|
||||
radio.value = 'a';
|
||||
@@ -44,8 +43,8 @@ describe('ion-radio: disabled', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const radio = page.body.querySelector('ion-radio')!;
|
||||
const radioGroup = page.body.querySelector('ion-radio-group')!;
|
||||
const radio = page.body.querySelector('ion-radio');
|
||||
const radioGroup = page.body.querySelector('ion-radio-group');
|
||||
|
||||
expect(radioGroup.value).toBe(undefined);
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Item } from '../../item/item';
|
||||
import { Range } from '../range';
|
||||
import { Item } from '../../item/item';
|
||||
|
||||
let sharedRange: Range;
|
||||
|
||||
let sharedRange;
|
||||
describe('Range', () => {
|
||||
beforeEach(() => {
|
||||
sharedRange = new Range();
|
||||
@@ -23,8 +21,7 @@ describe('Range', () => {
|
||||
];
|
||||
|
||||
valueTests.forEach((test) => {
|
||||
// Casting as any since we are accessing a private API on the range component
|
||||
expect((sharedRange as any).ensureValueInBounds(test[0])).toBe(test[1]);
|
||||
expect(sharedRange.ensureValueInBounds(test[0])).toBe(test[1]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,8 +58,7 @@ describe('Range', () => {
|
||||
];
|
||||
|
||||
valueTests.forEach((test) => {
|
||||
// Casting as any since we are accessing a private API on the range component
|
||||
expect((sharedRange as any).ensureValueInBounds(test[0])).toEqual(test[1]);
|
||||
expect(sharedRange.ensureValueInBounds(test[0])).toEqual(test[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -77,7 +73,7 @@ describe('range id', () => {
|
||||
</ion-range>`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.getAttribute('id')).toBe('my-custom-range');
|
||||
});
|
||||
});
|
||||
@@ -93,7 +89,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -108,7 +104,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -123,7 +119,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -138,7 +134,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -153,7 +149,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -168,7 +164,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -181,7 +177,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -195,7 +191,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -210,7 +206,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -225,7 +221,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
const range = page.body.querySelector('ion-range');
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { RefresherContent } from '../refresher-content';
|
||||
import { config } from '../../../global/config';
|
||||
|
||||
describe('refresher-content: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -10,11 +9,11 @@ describe('refresher-content: custom html', () => {
|
||||
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-refreshing-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
|
||||
});
|
||||
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text')!;
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text');
|
||||
expect(pullingContent.textContent).toContain('Custom Pulling Text');
|
||||
expect(pullingContent.querySelector('button.custom-pulling-html')).toBe(null);
|
||||
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text')!;
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
|
||||
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
|
||||
expect(refreshingContent.querySelector('button.custom-refreshing-html')).toBe(null);
|
||||
});
|
||||
@@ -26,11 +25,11 @@ describe('refresher-content: custom html', () => {
|
||||
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-refreshing-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
|
||||
});
|
||||
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text')!;
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text');
|
||||
expect(pullingContent.textContent).toContain('Custom Pulling Text');
|
||||
expect(pullingContent.querySelector('button.custom-pulling-html')).not.toBe(null);
|
||||
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text')!;
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
|
||||
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
|
||||
expect(refreshingContent.querySelector('button.custom-refreshing-html')).not.toBe(null);
|
||||
});
|
||||
@@ -42,11 +41,11 @@ describe('refresher-content: custom html', () => {
|
||||
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
|
||||
});
|
||||
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text')!;
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text');
|
||||
expect(pullingContent.textContent).toContain('Custom Pulling Text');
|
||||
expect(pullingContent.querySelector('button.custom-pulling-html')).toBe(null);
|
||||
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text')!;
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
|
||||
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
|
||||
expect(refreshingContent.querySelector('button.custom-refreshing-html')).toBe(null);
|
||||
});
|
||||
|
||||
@@ -203,17 +203,17 @@ describe('findChainForSegments', () => {
|
||||
describe('mergeParams', () => {
|
||||
it('should merge undefined', () => {
|
||||
expect(mergeParams(undefined, undefined)).toBeUndefined();
|
||||
expect(mergeParams(null as any, undefined)).toBeUndefined();
|
||||
expect(mergeParams(undefined, null as any)).toBeUndefined();
|
||||
expect(mergeParams(null as any, null as any)).toBeUndefined();
|
||||
expect(mergeParams(null, undefined)).toBeUndefined();
|
||||
expect(mergeParams(undefined, null)).toBeUndefined();
|
||||
expect(mergeParams(null, null)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should merge undefined with params', () => {
|
||||
const params = { data: '1' };
|
||||
expect(mergeParams(undefined, params)).toEqual(params);
|
||||
expect(mergeParams(null as any, params)).toEqual(params);
|
||||
expect(mergeParams(null, params)).toEqual(params);
|
||||
expect(mergeParams(params, undefined)).toEqual(params);
|
||||
expect(mergeParams(params, null as any)).toEqual(params);
|
||||
expect(mergeParams(params, null)).toEqual(params);
|
||||
});
|
||||
|
||||
it('should merge params with params', () => {
|
||||
@@ -253,44 +253,36 @@ describe('RouterSegments', () => {
|
||||
|
||||
describe('matchesRedirect', () => {
|
||||
it('should match empty redirect', () => {
|
||||
expect(matchesRedirect([''], { from: [''], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect([''], { from: ['*'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect([''], { from: [''], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect([''], { from: ['*'], to: [''] })).toBeTruthy();
|
||||
|
||||
expect(matchesRedirect([''], { from: ['hola'], to: { segments: [] } })).toBeFalsy();
|
||||
expect(matchesRedirect([''], { from: ['hola', '*'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect([''], { from: ['hola'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect([''], { from: ['hola', '*'], to: [''] })).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should match simple segment redirect', () => {
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['*'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', '*'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'hola'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', '*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'hola'], to: [''] })).toBeTruthy();
|
||||
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts', '*'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'adios'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts', '*'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'adios'], to: [''] })).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should match long route', () => {
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['*'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', '*'], to: { segments: [''] } })
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', '*'], to: { segments: [''] } })
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to'], to: { segments: [''] } })
|
||||
).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', '*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', '*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to'], to: [''] })).toBeTruthy();
|
||||
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login', '*'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login', '*'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path'], to: [''] })).toBeFalsy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path'], to: { segments: [''] } })
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to', '*'], to: { segments: [''] } })
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to', '*'], to: [''] })
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
@@ -18,21 +18,19 @@ describe('ionic-conference-app', () => {
|
||||
expect(getRouteIDs('/about', routes)).toEqual(['page-tabs', 'page-about']);
|
||||
expect(getRouteIDs('/tutorial', routes)).toEqual(['page-tutorial']);
|
||||
|
||||
expect(
|
||||
getRoutePath([{ id: 'PAGE-TABS' }, { id: 'tab-schedule' }, { id: 'page-schedule' }] as RouteID[], routes)
|
||||
).toEqual('/');
|
||||
expect(getRoutePath([{ id: 'PAGE-TABS' }, { id: 'tab-schedule' }, { id: 'page-schedule' }], routes)).toEqual('/');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }] as RouteID[], routes)).toEqual('/speaker');
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }], routes)).toEqual('/speaker');
|
||||
|
||||
expect(
|
||||
getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }, { id: 'page-speaker-list' }] as RouteID[], routes)
|
||||
).toEqual('/speaker');
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }, { id: 'page-speaker-list' }], routes)).toEqual(
|
||||
'/speaker'
|
||||
);
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'PAGE-MAP' }] as RouteID[], routes)).toEqual('/map');
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'PAGE-MAP' }], routes)).toEqual('/map');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'page-about' }] as RouteID[], routes)).toEqual('/about');
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'page-about' }], routes)).toEqual('/about');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tutorial' }] as RouteID[], routes)).toEqual('/tutorial');
|
||||
expect(getRoutePath([{ id: 'page-tutorial' }], routes)).toEqual('/tutorial');
|
||||
});
|
||||
|
||||
let win: Window;
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('searchbar: rendering', () => {
|
||||
html: '<ion-searchbar name="search"></ion-searchbar>',
|
||||
});
|
||||
|
||||
const nativeEl = page.body.querySelector('ion-searchbar input')!;
|
||||
const nativeEl = page.body.querySelector('ion-searchbar input');
|
||||
expect(nativeEl.getAttribute('name')).toBe('search');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,14 +9,14 @@ it('should disable segment buttons added to disabled segment async', async () =>
|
||||
html: `<ion-segment disabled="true"></ion-segment>`,
|
||||
});
|
||||
|
||||
const segment = page.body.querySelector('ion-segment')!;
|
||||
const segment = page.body.querySelector('ion-segment');
|
||||
segment.innerHTML = `
|
||||
<ion-segment-button>
|
||||
<ion-label>Segment Button</ion-label>
|
||||
</ion-segment-button>`;
|
||||
await page.waitForChanges();
|
||||
|
||||
const segmentButton = page.body.querySelector('ion-segment-button')!;
|
||||
const segmentButton = page.body.querySelector('ion-segment-button');
|
||||
expect(segmentButton.disabled).toBe(true);
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ it('should set checked state when value is set asynchronously', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const segmentButton = page.body.querySelector('ion-segment-button')!;
|
||||
const segmentButton = page.root.querySelector('ion-segment-button');
|
||||
|
||||
expect(segmentButton.classList.contains('segment-button-checked')).toBe(false);
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ describe('ion-select', () => {
|
||||
template: () => <ion-select value="my value" name="my name" disabled={true}></ion-select>,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
const select = page.body.querySelector('ion-select');
|
||||
|
||||
const hiddenInput = select.querySelector<HTMLInputElement>('input[type="hidden"]')!;
|
||||
const hiddenInput = select.querySelector('input[type="hidden"]');
|
||||
expect(hiddenInput).not.toBe(null);
|
||||
|
||||
expect(hiddenInput.value).toBe('my value');
|
||||
@@ -28,10 +28,10 @@ describe('ion-select', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
const select = page.body.querySelector('ion-select');
|
||||
|
||||
const propEl = select.shadowRoot!.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot!.querySelector('slot[name="label"]');
|
||||
const propEl = select.shadowRoot.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot.querySelector('slot[name="label"]');
|
||||
|
||||
expect(propEl).not.toBe(null);
|
||||
expect(slotEl).toBe(null);
|
||||
@@ -44,10 +44,10 @@ describe('ion-select', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
const select = page.body.querySelector('ion-select');
|
||||
|
||||
const propEl = select.shadowRoot!.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot!.querySelector('slot[name="label"]');
|
||||
const propEl = select.shadowRoot.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot.querySelector('slot[name="label"]');
|
||||
|
||||
expect(propEl).toBe(null);
|
||||
expect(slotEl).not.toBe(null);
|
||||
@@ -60,10 +60,10 @@ describe('ion-select', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
const select = page.body.querySelector('ion-select');
|
||||
|
||||
const propEl = select.shadowRoot!.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot!.querySelector('slot[name="label"]');
|
||||
const propEl = select.shadowRoot.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot.querySelector('slot[name="label"]');
|
||||
|
||||
expect(propEl).not.toBe(null);
|
||||
expect(slotEl).toBe(null);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Textarea } from '../textarea';
|
||||
|
||||
it('should inherit attributes', async () => {
|
||||
@@ -8,7 +7,7 @@ it('should inherit attributes', async () => {
|
||||
html: '<ion-textarea title="my title" tabindex="-1" data-form-type="password"></ion-textarea>',
|
||||
});
|
||||
|
||||
const nativeEl = page.body.querySelector('ion-textarea textarea')!;
|
||||
const nativeEl = page.body.querySelector('ion-textarea textarea');
|
||||
expect(nativeEl.getAttribute('title')).toBe('my title');
|
||||
expect(nativeEl.getAttribute('tabindex')).toBe('-1');
|
||||
expect(nativeEl.getAttribute('data-form-type')).toBe('password');
|
||||
@@ -32,9 +31,9 @@ describe('textarea: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const textarea = page.body.querySelector('ion-textarea')!;
|
||||
const textarea = page.body.querySelector('ion-textarea');
|
||||
|
||||
const labelText = textarea.querySelector('.label-text-wrapper')!;
|
||||
const labelText = textarea.querySelector('.label-text-wrapper');
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
@@ -46,9 +45,9 @@ describe('textarea: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const textarea = page.body.querySelector('ion-textarea')!;
|
||||
const textarea = page.body.querySelector('ion-textarea');
|
||||
|
||||
const labelText = textarea.querySelector('.label-text-wrapper')!;
|
||||
const labelText = textarea.querySelector('.label-text-wrapper');
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Slot');
|
||||
});
|
||||
@@ -60,9 +59,9 @@ describe('textarea: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const textarea = page.body.querySelector('ion-textarea')!;
|
||||
const textarea = page.body.querySelector('ion-textarea');
|
||||
|
||||
const labelText = textarea.querySelector('.label-text-wrapper')!;
|
||||
const labelText = textarea.querySelector('.label-text-wrapper');
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { h } from '@stencil/core';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { Toast } from '../toast';
|
||||
import { config } from '../../../global/config';
|
||||
import { toastController } from '../../../utils/overlays';
|
||||
|
||||
describe('toast: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -11,8 +11,8 @@ describe('toast: custom html', () => {
|
||||
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const content = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -24,8 +24,8 @@ describe('toast: custom html', () => {
|
||||
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const content = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).not.toBe(null);
|
||||
});
|
||||
@@ -37,8 +37,8 @@ describe('toast: custom html', () => {
|
||||
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const content = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -56,9 +56,9 @@ describe('toast: a11y smoke test', () => {
|
||||
html: `<ion-toast message="Message" header="Header"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const header = toast.shadowRoot!.querySelector('.toast-header')!;
|
||||
const message = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const header = toast.shadowRoot.querySelector('.toast-header');
|
||||
const message = toast.shadowRoot.querySelector('.toast-message');
|
||||
|
||||
expect(header.getAttribute('aria-hidden')).toBe('true');
|
||||
expect(message.getAttribute('aria-hidden')).toBe('true');
|
||||
@@ -74,7 +74,7 @@ describe('toast: a11y smoke test', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
|
||||
/**
|
||||
* Wait for present method to resolve
|
||||
@@ -83,8 +83,8 @@ describe('toast: a11y smoke test', () => {
|
||||
await toast.present();
|
||||
await page.waitForChanges();
|
||||
|
||||
const header = toast.shadowRoot!.querySelector('.toast-header')!;
|
||||
const message = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
const header = toast.shadowRoot.querySelector('.toast-header');
|
||||
const message = toast.shadowRoot.querySelector('.toast-message');
|
||||
|
||||
expect(header.getAttribute('aria-hidden')).toBe(null);
|
||||
expect(message.getAttribute('aria-hidden')).toBe(null);
|
||||
@@ -98,7 +98,7 @@ describe('toast: duration config', () => {
|
||||
html: `<ion-toast></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
|
||||
expect(toast.duration).toBe(0);
|
||||
});
|
||||
@@ -111,7 +111,7 @@ describe('toast: duration config', () => {
|
||||
html: `<ion-toast></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
|
||||
expect(toast.duration).toBe(5000);
|
||||
});
|
||||
@@ -121,10 +121,10 @@ describe('toast: htmlAttributes', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Toast],
|
||||
template: () => <ion-toast overlayIndex={1} htmlAttributes={{ 'data-testid': 'basic-toast' }}></ion-toast>,
|
||||
template: () => <ion-toast htmlAttributes={{ 'data-testid': 'basic-toast' }}></ion-toast>,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
|
||||
await expect(toast.getAttribute('data-testid')).toBe('basic-toast');
|
||||
});
|
||||
@@ -134,12 +134,12 @@ describe('toast: button cancel', () => {
|
||||
it('should render the cancel button with part button-cancel', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Toast],
|
||||
template: () => <ion-toast overlayIndex={1} buttons={[{ text: 'Cancel', role: 'cancel' }]}></ion-toast>,
|
||||
template: () => <ion-toast buttons={[{ text: 'Cancel', role: 'cancel' }]}></ion-toast>,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
|
||||
const buttonCancel = toast.shadowRoot!.querySelector('.toast-button-cancel')!;
|
||||
const buttonCancel = toast?.shadowRoot?.querySelector('.toast-button-cancel');
|
||||
|
||||
expect(buttonCancel.getAttribute('part')).toBe('button cancel');
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('ion-toggle: disabled', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toggle = page.body.querySelector('ion-toggle')!;
|
||||
const toggle = page.body.querySelector('ion-toggle');
|
||||
|
||||
expect(toggle.checked).toBe(false);
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { IonicConfig } from '../../interface';
|
||||
import { Config } from '../config';
|
||||
|
||||
describe('Config', () => {
|
||||
it('should get a value from the config', () => {
|
||||
const config = new Config();
|
||||
config.reset({ mode: 'ios' } as IonicConfig);
|
||||
expect(config.get('mode')).toEqual('ios');
|
||||
expect(config.getBoolean('mode')).toBe(false);
|
||||
config.reset({ name: 'Doc Brown' });
|
||||
expect(config.get('name')).toEqual('Doc Brown');
|
||||
expect(config.getBoolean('name')).toBe(false);
|
||||
});
|
||||
|
||||
it('should get a boolean value', () => {
|
||||
@@ -19,14 +18,14 @@ describe('Config', () => {
|
||||
bool4: 'hola',
|
||||
bool5: 0,
|
||||
bool6: 1,
|
||||
} as any);
|
||||
expect(config.getBoolean('bool0' as any)).toEqual(false);
|
||||
expect(config.getBoolean('bool1' as any)).toEqual(false);
|
||||
expect(config.getBoolean('bool2' as any)).toEqual(true);
|
||||
expect(config.getBoolean('bool3' as any)).toEqual(true);
|
||||
expect(config.getBoolean('bool4' as any)).toEqual(false);
|
||||
expect(config.getBoolean('bool5' as any)).toEqual(false);
|
||||
expect(config.getBoolean('bool6' as any)).toEqual(true);
|
||||
});
|
||||
expect(config.getBoolean('bool0')).toEqual(false);
|
||||
expect(config.getBoolean('bool1')).toEqual(false);
|
||||
expect(config.getBoolean('bool2')).toEqual(true);
|
||||
expect(config.getBoolean('bool3')).toEqual(true);
|
||||
expect(config.getBoolean('bool4')).toEqual(false);
|
||||
expect(config.getBoolean('bool5')).toEqual(false);
|
||||
expect(config.getBoolean('bool6')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should get a number value', () => {
|
||||
@@ -37,12 +36,12 @@ describe('Config', () => {
|
||||
nu2: '200',
|
||||
nu3: '2.3',
|
||||
nu4: -100.2,
|
||||
} as any);
|
||||
expect(config.getNumber('nu0' as any)).toEqual(0);
|
||||
expect(config.getNumber('nu1' as any)).toEqual(-1);
|
||||
expect(config.getNumber('nu2' as any)).toEqual(200);
|
||||
expect(config.getNumber('nu3' as any)).toEqual(2.3);
|
||||
expect(config.getNumber('nu4' as any)).toEqual(-100.2);
|
||||
});
|
||||
expect(config.getNumber('nu0')).toEqual(0);
|
||||
expect(config.getNumber('nu1')).toEqual(-1);
|
||||
expect(config.getNumber('nu2')).toEqual(200);
|
||||
expect(config.getNumber('nu3')).toEqual(2.3);
|
||||
expect(config.getNumber('nu4')).toEqual(-100.2);
|
||||
});
|
||||
|
||||
it('should not get fallback', () => {
|
||||
@@ -57,29 +56,29 @@ describe('Config', () => {
|
||||
nu0: '0',
|
||||
nu1: 0,
|
||||
nu2: 10,
|
||||
} as any);
|
||||
expect(config.get('text0' as any, 'HEY')).toEqual('');
|
||||
expect(config.get('text1' as any, 'HEY')).toEqual('hola');
|
||||
});
|
||||
expect(config.get('text0', 'HEY')).toEqual('');
|
||||
expect(config.get('text1', 'HEY')).toEqual('hola');
|
||||
|
||||
expect(config.getBoolean('bool0' as any, true)).toEqual(false);
|
||||
expect(config.getBoolean('bool1' as any, true)).toEqual(false);
|
||||
expect(config.getBoolean('bool0', true)).toEqual(false);
|
||||
expect(config.getBoolean('bool1', true)).toEqual(false);
|
||||
|
||||
expect(config.getNumber('nu0' as any, 100)).toEqual(0);
|
||||
expect(config.getNumber('nu1' as any, 100)).toEqual(0);
|
||||
expect(config.getNumber('nu2' as any, 100)).toEqual(10);
|
||||
expect(config.getNumber('nu0', 100)).toEqual(0);
|
||||
expect(config.getNumber('nu1', 100)).toEqual(0);
|
||||
expect(config.getNumber('nu2', 100)).toEqual(10);
|
||||
});
|
||||
|
||||
it('should get fallback', () => {
|
||||
const config = new Config();
|
||||
expect(config.get('text0' as any, 'HEY')).toEqual('HEY');
|
||||
expect(config.getBoolean('bool0' as any, true)).toEqual(true);
|
||||
expect(config.getNumber('nu0' as any, 100)).toEqual(100);
|
||||
expect(config.get('text0', 'HEY')).toEqual('HEY');
|
||||
expect(config.getBoolean('bool0', true)).toEqual(true);
|
||||
expect(config.getNumber('nu0', 100)).toEqual(100);
|
||||
});
|
||||
|
||||
it('should set value', () => {
|
||||
const config = new Config();
|
||||
expect(config.get('text0' as any, 'HEY')).toEqual('HEY');
|
||||
config.set('text0' as any, 'hola');
|
||||
expect(config.get('text0' as any, 'HEY')).toEqual('hola');
|
||||
expect(config.get('text0', 'HEY')).toEqual('HEY');
|
||||
config.set('text0', 'hola');
|
||||
expect(config.get('text0', 'HEY')).toEqual('hola');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,38 +1,3 @@
|
||||
|
||||
/**
|
||||
* A heuristic that applies CSS to tablet
|
||||
* viewports.
|
||||
*
|
||||
* Usage:
|
||||
* @include tablet-viewport() {
|
||||
* :host {
|
||||
* background-color: green;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@mixin tablet-viewport() {
|
||||
@media screen and (min-width: 768px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A heuristic that applies CSS to mobile
|
||||
* viewports (i.e. phones, not tablets).
|
||||
*
|
||||
* Usage:
|
||||
* @include mobile-viewport() {
|
||||
* :host {
|
||||
* background-color: blue;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@mixin mobile-viewport() {
|
||||
@media screen and (max-width: 767px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin input-cover() {
|
||||
@include position(0, null, null, 0);
|
||||
@include margin(0);
|
||||
@@ -252,7 +217,7 @@
|
||||
$restSelectors: append($restSelectors, $selector, comma);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Supported by Chrome.
|
||||
@if length($hostContextSelectors) > 0 {
|
||||
@at-root #{$hostContextSelectors} {
|
||||
|
||||
@@ -108,7 +108,7 @@ describe('Animation Class', () => {
|
||||
animation.play();
|
||||
|
||||
animation.progressStart();
|
||||
animation.progressEnd(1, 0);
|
||||
animation.progressEnd(1);
|
||||
|
||||
expect(animation.isRunning()).toEqual(true);
|
||||
});
|
||||
@@ -125,9 +125,9 @@ describe('Animation Class', () => {
|
||||
await animation.play();
|
||||
|
||||
animation.progressStart();
|
||||
animation.progressEnd(0, 0);
|
||||
animation.progressEnd(0);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
await new Promise((resolve) => {
|
||||
animation.onFinish(() => {
|
||||
expect(animation.isRunning()).toEqual(false);
|
||||
resolve();
|
||||
@@ -161,8 +161,8 @@ describe('Animation Class', () => {
|
||||
const el = document.createElement('p');
|
||||
animation.addElement(el);
|
||||
|
||||
animation.addElement(null as any);
|
||||
animation.addElement(undefined as any);
|
||||
animation.addElement(null);
|
||||
animation.addElement(undefined);
|
||||
|
||||
expect(animation.elements.length).toEqual(1);
|
||||
});
|
||||
@@ -188,8 +188,8 @@ describe('Animation Class', () => {
|
||||
});
|
||||
|
||||
it('should not error when trying to add null or undefined', () => {
|
||||
animation.addAnimation(null as any);
|
||||
animation.addAnimation(undefined as any);
|
||||
animation.addAnimation(null);
|
||||
animation.addAnimation(undefined);
|
||||
|
||||
expect(animation.childAnimations.length).toEqual(0);
|
||||
});
|
||||
@@ -312,7 +312,7 @@ describe('Animation Class', () => {
|
||||
animation.progressStart(true);
|
||||
expect(animation.getEasing()).toEqual('linear');
|
||||
|
||||
animation.progressEnd(0, 0);
|
||||
animation.progressEnd();
|
||||
expect(animation.getEasing()).toEqual('ease-in-out');
|
||||
});
|
||||
|
||||
@@ -428,15 +428,9 @@ describe('cubic-bezier conversion', () => {
|
||||
[1, 1],
|
||||
];
|
||||
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.5), [
|
||||
0.16,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.97), [
|
||||
0.56,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.33), [
|
||||
0.11,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.5), [0.16]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.97), [0.56]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.33), [0.11]);
|
||||
});
|
||||
|
||||
it('cubic-bezier(1, 0, 0.68, 0.28)', () => {
|
||||
@@ -447,15 +441,9 @@ describe('cubic-bezier conversion', () => {
|
||||
[1, 1],
|
||||
];
|
||||
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.08), [
|
||||
0.6,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.5), [
|
||||
0.84,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.94), [
|
||||
0.98,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.08), [0.6]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.5), [0.84]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.94), [0.98]);
|
||||
});
|
||||
|
||||
it('cubic-bezier(0.4, 0, 0.6, 1)', () => {
|
||||
@@ -466,15 +454,9 @@ describe('cubic-bezier conversion', () => {
|
||||
[1, 1],
|
||||
];
|
||||
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.39), [
|
||||
0.43,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.03), [
|
||||
0.11,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.89), [
|
||||
0.78,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.39), [0.43]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.03), [0.11]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.89), [0.78]);
|
||||
});
|
||||
|
||||
it('cubic-bezier(0, 0, 0.2, 1)', () => {
|
||||
@@ -485,15 +467,9 @@ describe('cubic-bezier conversion', () => {
|
||||
[1, 1],
|
||||
];
|
||||
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.95), [
|
||||
0.71,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.1), [
|
||||
0.03,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 0.7), [
|
||||
0.35,
|
||||
]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.95), [0.71]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.1), [0.03]);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 0.7), [0.35]);
|
||||
});
|
||||
|
||||
it('cubic-bezier(0.32, 0.72, 0, 1) (with out of bounds progression)', () => {
|
||||
@@ -504,8 +480,8 @@ describe('cubic-bezier conversion', () => {
|
||||
[1, 1],
|
||||
];
|
||||
|
||||
expect(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 1.32)[0]).toBeUndefined();
|
||||
expect(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], -0.32)[0]).toBeUndefined();
|
||||
expect(getTimeGivenProgression(...equation, 1.32)[0]).toBeUndefined();
|
||||
expect(getTimeGivenProgression(...equation, -0.32)[0]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('cubic-bezier(0.21, 1.71, 0.88, 0.9) (multiple solutions)', () => {
|
||||
@@ -516,10 +492,7 @@ describe('cubic-bezier conversion', () => {
|
||||
[1, 1],
|
||||
];
|
||||
|
||||
shouldApproximatelyEqual(
|
||||
getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 1.02),
|
||||
[0.35, 0.87]
|
||||
);
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 1.02), [0.35, 0.87]);
|
||||
});
|
||||
|
||||
it('cubic-bezier(0.32, 0.72, 0, 1) (with out of bounds progression)', () => {
|
||||
@@ -530,8 +503,8 @@ describe('cubic-bezier conversion', () => {
|
||||
[1, 1],
|
||||
];
|
||||
|
||||
expect(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], 1.32)).toEqual([]);
|
||||
expect(getTimeGivenProgression(equation[0], equation[1], equation[2], equation[3], -0.32)).toEqual([]);
|
||||
expect(getTimeGivenProgression(...equation, 1.32)).toEqual([]);
|
||||
expect(getTimeGivenProgression(...equation, -0.32)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,8 +16,8 @@ const mockVisualViewport = (
|
||||
win: Window,
|
||||
visualViewport: any = { width: 320, height: 568 },
|
||||
layoutViewport = { innerWidth: 320, innerHeight: 568 }
|
||||
) => {
|
||||
(win as any).visualViewport = {
|
||||
): any => {
|
||||
win.visualViewport = {
|
||||
width: 320,
|
||||
height: 568,
|
||||
offsetTop: 0,
|
||||
@@ -29,32 +29,26 @@ const mockVisualViewport = (
|
||||
onscroll: undefined,
|
||||
};
|
||||
|
||||
(win as any).visualViewport = Object.assign(win.visualViewport!, visualViewport);
|
||||
win.visualViewport = Object.assign(win.visualViewport, visualViewport);
|
||||
win = Object.assign(win, layoutViewport);
|
||||
|
||||
const mockDispatchEvent = jest.fn();
|
||||
|
||||
win.dispatchEvent = mockDispatchEvent;
|
||||
win.dispatchEvent = jest.fn();
|
||||
|
||||
trackViewportChanges(win);
|
||||
|
||||
return {
|
||||
win,
|
||||
mockDispatchEvent,
|
||||
};
|
||||
return win;
|
||||
};
|
||||
|
||||
const mockCapacitor = (win: Window) => {
|
||||
(win as any).Capacitor = {
|
||||
win.Capacitor = {
|
||||
isPluginAvailable: () => false,
|
||||
};
|
||||
};
|
||||
|
||||
const resizeVisualViewport = (win: Window, visualViewport: any = {}) => {
|
||||
(win as any).visualViewport = Object.assign((win as any).visualViewport, visualViewport);
|
||||
win.visualViewport = Object.assign(win.visualViewport, visualViewport);
|
||||
|
||||
if (win.visualViewport!.onresize) {
|
||||
win.visualViewport!.onresize({} as any);
|
||||
if (win.visualViewport.onresize) {
|
||||
win.visualViewport.onresize();
|
||||
} else {
|
||||
trackViewportChanges(win);
|
||||
}
|
||||
@@ -93,64 +87,62 @@ describe('Keyboard Assist Tests', () => {
|
||||
|
||||
describe('setKeyboardOpen()', () => {
|
||||
it('should dispatch the keyboard open event on the window', () => {
|
||||
const mockDispatchEvent = jest.fn();
|
||||
window.dispatchEvent = mockDispatchEvent;
|
||||
window.dispatchEvent = jest.fn();
|
||||
|
||||
setKeyboardOpen(window);
|
||||
|
||||
expect(mockDispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(mockDispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
expect(window.dispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(window.dispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setKeyboardClose()', () => {
|
||||
it('should dispatch the keyboard close event on the window', () => {
|
||||
const mockDispatchEvent = jest.fn();
|
||||
window.dispatchEvent = mockDispatchEvent;
|
||||
window.dispatchEvent = jest.fn();
|
||||
|
||||
setKeyboardClose(window);
|
||||
|
||||
expect(mockDispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(mockDispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_CLOSE);
|
||||
expect(window.dispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(window.dispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_CLOSE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('keyboardDidOpen()', () => {
|
||||
beforeEach(() => {
|
||||
resetKeyboardAssist();
|
||||
resetKeyboardAssist(window);
|
||||
mockVisualViewport(window);
|
||||
});
|
||||
|
||||
it('should return true when visual viewport height < layout viewport height and meets or exceeds the keyboard threshold', () => {
|
||||
resizeVisualViewport(window, { height: 200 });
|
||||
|
||||
expect(keyboardDidOpen()).toEqual(true);
|
||||
expect(keyboardDidOpen(window)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true if the layout and visual viewports resize', () => {
|
||||
resizeLayoutViewport(window, { width: 320, height: 300 });
|
||||
resizeVisualViewport(window, { width: 320, height: 300 });
|
||||
|
||||
expect(keyboardDidOpen()).toEqual(true);
|
||||
expect(keyboardDidOpen(window)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return false when visual viewport height < layout viewport heigh but does not meet the keyboard threshold', () => {
|
||||
resizeVisualViewport(window, { height: 500 });
|
||||
|
||||
expect(keyboardDidOpen()).toEqual(false);
|
||||
expect(keyboardDidOpen(window)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false on orientation change', () => {
|
||||
resizeVisualViewport(window, { width: 320, height: 250 });
|
||||
resizeVisualViewport(window, { width: 250, height: 320 });
|
||||
|
||||
expect(keyboardDidOpen()).toEqual(false);
|
||||
expect(keyboardDidOpen(window)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false when both the visual and layout viewports change', () => {
|
||||
resizeVisualViewport(window, { width: 250, height: 320 });
|
||||
resizeVisualViewport(window, { width: 250, height: 320 }, { innerWidth: 250, innerHeight: 320 });
|
||||
|
||||
expect(keyboardDidOpen()).toEqual(false);
|
||||
expect(keyboardDidOpen(window)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return true when the keyboard shows even if the user is zoomed in', () => {
|
||||
@@ -160,13 +152,13 @@ describe('Keyboard Assist Tests', () => {
|
||||
// User taps input and keyboard appears
|
||||
resizeVisualViewport(window, { width: 160, height: 184, scale: 2 });
|
||||
|
||||
expect(keyboardDidOpen()).toEqual(true);
|
||||
expect(keyboardDidOpen(window)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('keyboardDidClose()', () => {
|
||||
beforeEach(() => {
|
||||
resetKeyboardAssist();
|
||||
resetKeyboardAssist(window);
|
||||
mockVisualViewport(window);
|
||||
});
|
||||
|
||||
@@ -230,66 +222,54 @@ describe('Keyboard Assist Tests', () => {
|
||||
});
|
||||
|
||||
describe('Keyboard Assist Integration', () => {
|
||||
let mockDispatchEvent: jest.Mock<any, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
resetKeyboardAssist();
|
||||
mockDispatchEvent = mockVisualViewport(window).mockDispatchEvent;
|
||||
resetKeyboardAssist(window);
|
||||
mockVisualViewport(window);
|
||||
startKeyboardAssist(window);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockDispatchEvent.mockReset();
|
||||
});
|
||||
|
||||
it('should properly set the keyboard to be open', () => {
|
||||
resizeVisualViewport(window, { width: 320, height: 350 });
|
||||
|
||||
expect(mockDispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(mockDispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
expect(window.dispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(window.dispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
});
|
||||
|
||||
it('should properly set the keyboard to be closed', () => {
|
||||
resizeVisualViewport(window, { width: 320, height: 350 });
|
||||
resizeVisualViewport(window, { width: 320, height: 568 });
|
||||
|
||||
expect(mockDispatchEvent.mock.calls.length).toEqual(2);
|
||||
expect(mockDispatchEvent.mock.calls[1][0].type).toEqual(KEYBOARD_DID_CLOSE);
|
||||
expect(window.dispatchEvent.mock.calls.length).toEqual(2);
|
||||
expect(window.dispatchEvent.mock.calls[1][0].type).toEqual(KEYBOARD_DID_CLOSE);
|
||||
});
|
||||
|
||||
it('should properly set the keyboard to be resized', () => {
|
||||
resizeVisualViewport(window, { width: 320, height: 350 });
|
||||
resizeVisualViewport(window, { width: 320, height: 360 });
|
||||
|
||||
expect(mockDispatchEvent.mock.calls.length).toEqual(2);
|
||||
expect(mockDispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
expect(mockDispatchEvent.mock.calls[1][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
expect(window.dispatchEvent.mock.calls.length).toEqual(2);
|
||||
expect(window.dispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
expect(window.dispatchEvent.mock.calls[1][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
});
|
||||
|
||||
it('should not set keyboard open on orientation change', () => {
|
||||
resizeVisualViewport(window, { width: 568, height: 320 });
|
||||
expect(mockDispatchEvent.mock.calls.length).toEqual(0);
|
||||
expect(window.dispatchEvent.mock.calls.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Keyboard Assist with Capacitor', () => {
|
||||
let mockDispatchEvent: jest.Mock<any, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
resetKeyboardAssist();
|
||||
resetKeyboardAssist(window);
|
||||
mockCapacitor(window);
|
||||
mockDispatchEvent = mockVisualViewport(window).mockDispatchEvent;
|
||||
mockVisualViewport(window);
|
||||
startKeyboardAssist(window);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockDispatchEvent.mockReset();
|
||||
});
|
||||
|
||||
it('should attach visual viewport listeners when Capacitor is available but the Keyboard plugin is not', () => {
|
||||
resizeVisualViewport(window, { width: 320, height: 350 });
|
||||
|
||||
expect(mockDispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(mockDispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
expect(window.dispatchEvent.mock.calls.length).toEqual(1);
|
||||
expect(window.dispatchEvent.mock.calls[0][0].type).toEqual(KEYBOARD_DID_OPEN);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('sanitizeDOMString', () => {
|
||||
});
|
||||
|
||||
const enableSanitizer = (enable = true) => {
|
||||
(window as any).Ionic = {};
|
||||
(window as any).Ionic.config = {};
|
||||
(window as any).Ionic.config.sanitizerEnabled = enable;
|
||||
window.Ionic = {};
|
||||
window.Ionic.config = {};
|
||||
window.Ionic.config.sanitizerEnabled = enable;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Item } from '../../components/item/item';
|
||||
import { Label } from '../../components/label/label';
|
||||
import { Toggle } from '../../components/toggle/toggle';
|
||||
import { Item } from '../../components/item/item.tsx';
|
||||
import { Label } from '../../components/label/label.tsx';
|
||||
import { Toggle } from '../../components/toggle/toggle.tsx';
|
||||
import { getAriaLabel } from '../helpers';
|
||||
|
||||
describe('getAriaLabel()', () => {
|
||||
@@ -17,7 +17,7 @@ describe('getAriaLabel()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toggle = page.body.querySelector('ion-toggle')!;
|
||||
const toggle = page.body.querySelector('ion-toggle');
|
||||
|
||||
const { label, labelId, labelText } = getAriaLabel(toggle, 'ion-tg-0');
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('getAriaLabel()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toggle = page.body.querySelector('ion-toggle')!;
|
||||
const toggle = page.body.querySelector('ion-toggle');
|
||||
|
||||
const { label, labelId, labelText } = getAriaLabel(toggle, 'ion-tg-0');
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('getAriaLabel()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toggle = page.body.querySelector('ion-toggle')!;
|
||||
const toggle = page.body.querySelector('ion-toggle');
|
||||
|
||||
const { labelId, labelText } = getAriaLabel(toggle, 'ion-tg-0');
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('getAriaLabel()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toggle = page.body.querySelector('ion-toggle')!;
|
||||
const toggle = page.body.querySelector('ion-toggle');
|
||||
|
||||
const { labelId, labelText } = getAriaLabel(toggle, 'ion-tg-0');
|
||||
|
||||
@@ -87,7 +87,7 @@ describe('getAriaLabel()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toggle = page.body.querySelector('ion-toggle')!;
|
||||
const toggle = page.body.querySelector('ion-toggle');
|
||||
|
||||
const { labelId, labelText } = getAriaLabel(toggle, 'ion-tg-0');
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { BackButtonEvent } from '../../../src/interface';
|
||||
import { startHardwareBackButton } from '../hardware-back-button';
|
||||
|
||||
describe('Hardware Back Button', () => {
|
||||
@@ -6,7 +5,7 @@ describe('Hardware Back Button', () => {
|
||||
it('should call handler', () => {
|
||||
const cbSpy = jest.fn();
|
||||
document.addEventListener('ionBackButton', (ev) => {
|
||||
(ev as BackButtonEvent).detail.register(0, cbSpy);
|
||||
ev.detail.register(0, cbSpy);
|
||||
});
|
||||
|
||||
dispatchBackButtonEvent();
|
||||
@@ -17,8 +16,8 @@ describe('Hardware Back Button', () => {
|
||||
const cbSpy = jest.fn();
|
||||
const cbSpyTwo = jest.fn();
|
||||
document.addEventListener('ionBackButton', (ev) => {
|
||||
(ev as BackButtonEvent).detail.register(100, cbSpy);
|
||||
(ev as BackButtonEvent).detail.register(99, cbSpyTwo);
|
||||
ev.detail.register(100, cbSpy);
|
||||
ev.detail.register(99, cbSpyTwo);
|
||||
});
|
||||
|
||||
dispatchBackButtonEvent();
|
||||
@@ -30,8 +29,8 @@ describe('Hardware Back Button', () => {
|
||||
const cbSpy = jest.fn();
|
||||
const cbSpyTwo = jest.fn();
|
||||
document.addEventListener('ionBackButton', (ev) => {
|
||||
(ev as BackButtonEvent).detail.register(100, cbSpy);
|
||||
(ev as BackButtonEvent).detail.register(100, cbSpyTwo);
|
||||
ev.detail.register(100, cbSpy);
|
||||
ev.detail.register(100, cbSpyTwo);
|
||||
});
|
||||
|
||||
dispatchBackButtonEvent();
|
||||
@@ -40,13 +39,13 @@ describe('Hardware Back Button', () => {
|
||||
});
|
||||
|
||||
it('should call multiple callbacks', () => {
|
||||
const cbSpy = (processNextHandler: () => void) => {
|
||||
const cbSpy = (processNextHandler) => {
|
||||
processNextHandler();
|
||||
};
|
||||
const cbSpyTwo = jest.fn();
|
||||
document.addEventListener('ionBackButton', (ev) => {
|
||||
(ev as BackButtonEvent).detail.register(100, cbSpy);
|
||||
(ev as BackButtonEvent).detail.register(99, cbSpyTwo);
|
||||
ev.detail.register(100, cbSpy);
|
||||
ev.detail.register(99, cbSpyTwo);
|
||||
});
|
||||
|
||||
dispatchBackButtonEvent();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Modal } from '../../../components/modal/modal';
|
||||
import { Nav } from '../../../components/nav/nav';
|
||||
import { RouterOutlet } from '../../../components/router-outlet/router-outlet';
|
||||
import { Modal } from '../../../components/modal/modal';
|
||||
|
||||
import { setRootAriaHidden } from '../../overlays';
|
||||
|
||||
describe('setRootAriaHidden()', () => {
|
||||
@@ -14,7 +15,7 @@ describe('setRootAriaHidden()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const routerOutlet = page.body.querySelector('ion-router-outlet')!;
|
||||
const routerOutlet = page.body.querySelector('ion-router-outlet');
|
||||
|
||||
expect(routerOutlet.hasAttribute('aria-hidden')).toEqual(false);
|
||||
|
||||
@@ -33,7 +34,7 @@ describe('setRootAriaHidden()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const nav = page.body.querySelector('ion-nav')!;
|
||||
const nav = page.body.querySelector('ion-nav');
|
||||
|
||||
expect(nav.hasAttribute('aria-hidden')).toEqual(false);
|
||||
|
||||
@@ -53,8 +54,8 @@ describe('setRootAriaHidden()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const containerRoot = page.body.querySelector('#ion-view-container-root')!;
|
||||
const notContainerRoot = page.body.querySelector('#not-container-root')!;
|
||||
const containerRoot = page.body.querySelector('#ion-view-container-root');
|
||||
const notContainerRoot = page.body.querySelector('#not-container-root');
|
||||
|
||||
expect(containerRoot.hasAttribute('aria-hidden')).toEqual(false);
|
||||
expect(notContainerRoot.hasAttribute('aria-hidden')).toEqual(false);
|
||||
@@ -89,8 +90,8 @@ describe('setRootAriaHidden()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const routerOutlet = page.body.querySelector('ion-router-outlet')!;
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const routerOutlet = page.body.querySelector('ion-router-outlet');
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
|
||||
await modal.present();
|
||||
|
||||
@@ -108,9 +109,9 @@ describe('setRootAriaHidden()', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const routerOutlet = page.body.querySelector('ion-router-outlet')!;
|
||||
const modalOne = page.body.querySelector<HTMLIonModalElement>('ion-modal#one')!;
|
||||
const modalTwo = page.body.querySelector<HTMLIonModalElement>('ion-modal#two')!;
|
||||
const routerOutlet = page.body.querySelector('ion-router-outlet');
|
||||
const modalOne = page.body.querySelector('ion-modal#one');
|
||||
const modalTwo = page.body.querySelector('ion-modal#two');
|
||||
|
||||
await modalOne.present();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('componentOnReady()', () => {
|
||||
);
|
||||
|
||||
const component = document.createElement('hello-world');
|
||||
componentOnReady(component, (el: HTMLElement) => {
|
||||
componentOnReady(component, (el) => {
|
||||
expect(el).toBe(component);
|
||||
done();
|
||||
});
|
||||
@@ -39,7 +39,7 @@ describe('componentOnReady()', () => {
|
||||
);
|
||||
|
||||
const component = document.createElement('hello-world');
|
||||
componentOnReady(component, (el: HTMLElement) => {
|
||||
componentOnReady(component, (el) => {
|
||||
expect(el).toBe(component);
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
done();
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
"src",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
"node_modules",
|
||||
"**/test/**/*.spec.ts",
|
||||
"**/test/**/*.spec.tsx",
|
||||
"**/test/**/e2e.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ionic/docs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ionic/docs
|
||||
|
||||
4
docs/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@ionic/docs",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/docs",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/docs",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"description": "Pre-packaged API documentation for the Ionic docs.",
|
||||
"main": "core.json",
|
||||
"types": "core.d.ts",
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"docs",
|
||||
"packages/*"
|
||||
],
|
||||
"version": "7.5.6"
|
||||
"version": "7.5.5"
|
||||
}
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular-server
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular-server
|
||||
|
||||
6
packages/angular-server/package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/angular-server",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/angular-server",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ionic/core": "^7.5.6"
|
||||
"@ionic/core": "^7.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-eslint/eslint-plugin": "^14.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular-server",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"description": "Angular SSR Module for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -62,6 +62,6 @@
|
||||
},
|
||||
"prettier": "@ionic/prettier-config",
|
||||
"dependencies": {
|
||||
"@ionic/core": "^7.5.6"
|
||||
"@ionic/core": "^7.5.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ng add @ionic/angular in standalone projects ([#28523](https://github.com/ionic-team/ionic-framework/issues/28523)) ([c07312e](https://github.com/ionic-team/ionic-framework/commit/c07312e5ed931f6f825ccf083c9dead9fa815843)), closes [#28514](https://github.com/ionic-team/ionic-framework/issues/28514)
|
||||
* **angular:** overlays are defined when using standalone controllers ([#28560](https://github.com/ionic-team/ionic-framework/issues/28560)) ([9453132](https://github.com/ionic-team/ionic-framework/commit/9453132aa8952b4adfa1326e61138b329e254f76)), closes [#28385](https://github.com/ionic-team/ionic-framework/issues/28385)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export { AlertController } from './providers/alert-controller';
|
||||
export { LoadingController } from './providers/loading-controller';
|
||||
export { MenuController } from './providers/menu-controller';
|
||||
export { PickerController } from './providers/picker-controller';
|
||||
export { DomController } from './providers/dom-controller';
|
||||
export { NavController } from './providers/nav-controller';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { AlertOptions } from '@ionic/core/components';
|
||||
import { alertController } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-alert.js';
|
||||
|
||||
import { OverlayBaseController } from '../utils/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -10,6 +10,5 @@ import { defineCustomElement } from '@ionic/core/components/ion-alert.js';
|
||||
export class AlertController extends OverlayBaseController<AlertOptions, HTMLIonAlertElement> {
|
||||
constructor() {
|
||||
super(alertController);
|
||||
defineCustomElement();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { LoadingOptions } from '@ionic/core/components';
|
||||
import { loadingController } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-loading.js';
|
||||
|
||||
import { OverlayBaseController } from '../utils/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -10,6 +10,5 @@ import { defineCustomElement } from '@ionic/core/components/ion-loading.js';
|
||||
export class LoadingController extends OverlayBaseController<LoadingOptions, HTMLIonLoadingElement> {
|
||||
constructor() {
|
||||
super(loadingController);
|
||||
defineCustomElement();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { PickerOptions } from '@ionic/core/components';
|
||||
import { pickerController } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-picker.js';
|
||||
|
||||
import { OverlayBaseController } from '../utils/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -10,6 +10,5 @@ import { defineCustomElement } from '@ionic/core/components/ion-picker.js';
|
||||
export class PickerController extends OverlayBaseController<PickerOptions, HTMLIonPickerElement> {
|
||||
constructor() {
|
||||
super(pickerController);
|
||||
defineCustomElement();
|
||||
}
|
||||
}
|
||||
2969
packages/angular/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "7.5.6",
|
||||
"version": "7.5.5",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/core": "^7.5.6",
|
||||
"@ionic/core": "^7.5.5",
|
||||
"ionicons": "^7.0.0",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
@@ -61,12 +61,11 @@
|
||||
"zone.js": ">=0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/core": "^17.0.0",
|
||||
"@angular-devkit/schematics": "^17.0.0",
|
||||
"@angular-devkit/core": "^14.0.0",
|
||||
"@angular-devkit/schematics": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^14.0.0",
|
||||
"@angular-eslint/template-parser": "^14.0.0",
|
||||
"@angular/cli": "^14.0.0",
|
||||
"@angular/common": "^14.0.0",
|
||||
"@angular/compiler": "^14.0.0",
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
@@ -77,7 +76,7 @@
|
||||
"@angular/router": "^14.0.0",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
"@schematics/angular": "^17.0.0",
|
||||
"@schematics/angular": "^14.0.0",
|
||||
"@types/node": "12.12.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
|
||||
@@ -20,6 +20,9 @@ export * from './directives/validators';
|
||||
|
||||
// PROVIDERS
|
||||
export {
|
||||
AlertController,
|
||||
LoadingController,
|
||||
PickerController,
|
||||
DomController,
|
||||
NavController,
|
||||
Config,
|
||||
@@ -32,14 +35,11 @@ export {
|
||||
ViewDidEnter,
|
||||
ViewDidLeave,
|
||||
} from '@ionic/angular/common';
|
||||
export { AlertController } from './providers/alert-controller';
|
||||
export { AnimationController } from './providers/animation-controller';
|
||||
export { ActionSheetController } from './providers/action-sheet-controller';
|
||||
export { GestureController } from './providers/gesture-controller';
|
||||
export { LoadingController } from './providers/loading-controller';
|
||||
export { MenuController } from './providers/menu-controller';
|
||||
export { ModalController } from './providers/modal-controller';
|
||||
export { PickerController } from './providers/picker-controller';
|
||||
export { PopoverController } from './providers/popover-controller';
|
||||
export { ToastController } from './providers/toast-controller';
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { AlertOptions } from '@ionic/core';
|
||||
import { alertController } from '@ionic/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AlertController extends OverlayBaseController<AlertOptions, HTMLIonAlertElement> {
|
||||
constructor() {
|
||||
super(alertController);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { LoadingOptions } from '@ionic/core';
|
||||
import { loadingController } from '@ionic/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LoadingController extends OverlayBaseController<LoadingOptions, HTMLIonLoadingElement> {
|
||||
constructor() {
|
||||
super(loadingController);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { PickerOptions } from '@ionic/core';
|
||||
import { pickerController } from '@ionic/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PickerController extends OverlayBaseController<PickerOptions, HTMLIonPickerElement> {
|
||||
constructor() {
|
||||
super(pickerController);
|
||||
}
|
||||
}
|
||||
@@ -4,244 +4,3 @@
|
||||
|
||||
/* To quickly generate your own theme, check out the color generator */
|
||||
/* https://ionicframework.com/docs/theming/color-generator */
|
||||
|
||||
/** Ionic CSS Variables **/
|
||||
:root {
|
||||
/** primary **/
|
||||
--ion-color-primary: #3880ff;
|
||||
--ion-color-primary-rgb: 56, 128, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3171e0;
|
||||
--ion-color-primary-tint: #4c8dff;
|
||||
|
||||
/** secondary **/
|
||||
--ion-color-secondary: #3dc2ff;
|
||||
--ion-color-secondary-rgb: 61, 194, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #36abe0;
|
||||
--ion-color-secondary-tint: #50c8ff;
|
||||
|
||||
/** tertiary **/
|
||||
--ion-color-tertiary: #5260ff;
|
||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #4854e0;
|
||||
--ion-color-tertiary-tint: #6370ff;
|
||||
|
||||
/** success **/
|
||||
--ion-color-success: #2dd36f;
|
||||
--ion-color-success-rgb: 45, 211, 111;
|
||||
--ion-color-success-contrast: #ffffff;
|
||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-success-shade: #28ba62;
|
||||
--ion-color-success-tint: #42d77d;
|
||||
|
||||
/** warning **/
|
||||
--ion-color-warning: #ffc409;
|
||||
--ion-color-warning-rgb: 255, 196, 9;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0ac08;
|
||||
--ion-color-warning-tint: #ffca22;
|
||||
|
||||
/** danger **/
|
||||
--ion-color-danger: #eb445a;
|
||||
--ion-color-danger-rgb: 235, 68, 90;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #cf3c4f;
|
||||
--ion-color-danger-tint: #ed576b;
|
||||
|
||||
/** dark **/
|
||||
--ion-color-dark: #222428;
|
||||
--ion-color-dark-rgb: 34, 36, 40;
|
||||
--ion-color-dark-contrast: #ffffff;
|
||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-dark-shade: #1e2023;
|
||||
--ion-color-dark-tint: #383a3e;
|
||||
|
||||
/** medium **/
|
||||
--ion-color-medium: #92949c;
|
||||
--ion-color-medium-rgb: 146, 148, 156;
|
||||
--ion-color-medium-contrast: #ffffff;
|
||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-medium-shade: #808289;
|
||||
--ion-color-medium-tint: #9d9fa6;
|
||||
|
||||
/** light **/
|
||||
--ion-color-light: #f4f5f8;
|
||||
--ion-color-light-rgb: 244, 245, 248;
|
||||
--ion-color-light-contrast: #000000;
|
||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-light-shade: #d7d8da;
|
||||
--ion-color-light-tint: #f5f6f9;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66, 140, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80, 200, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47, 223, 117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255, 213, 52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255, 73, 97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244, 245, 248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152, 154, 162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34, 36, 40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0, 0, 0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18, 18, 18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
/*
|
||||
* For more information on dynamic font scaling, visit the documentation:
|
||||
* https://ionicframework.com/docs/layout/dynamic-font-scaling
|
||||
*/
|
||||
--ion-dynamic-font: var(--ion-default-dynamic-font);
|
||||
}
|
||||
|
||||
@@ -12,19 +12,10 @@ import {
|
||||
url,
|
||||
} from '@angular-devkit/schematics';
|
||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||
import { addRootProvider } from '@schematics/angular/utility';
|
||||
import { getWorkspace } from '@schematics/angular/utility/workspace';
|
||||
|
||||
import { addIonicModuleImportToNgModule } from '../utils/ast';
|
||||
|
||||
import {
|
||||
addArchitectBuilder,
|
||||
addAsset,
|
||||
addCli,
|
||||
addSchematics,
|
||||
addStyle,
|
||||
getDefaultAngularAppName,
|
||||
} from './../utils/config';
|
||||
import { addModuleImportToRootModule } from './../utils/ast';
|
||||
import { addArchitectBuilder, addAsset, addStyle, getDefaultAngularAppName } from './../utils/config';
|
||||
import { addPackageToPackageJson } from './../utils/package';
|
||||
import { Schema as IonAddOptions } from './schema';
|
||||
|
||||
@@ -42,53 +33,9 @@ function addIonicAngularToolkitToPackageJson(): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the @ionic/angular-toolkit schematics and cli configuration to the project's `angular.json` file.
|
||||
* @param projectName The name of the project.
|
||||
*/
|
||||
function addIonicAngularToolkitToAngularJson(): Rule {
|
||||
return (host: Tree) => {
|
||||
addCli(host, '@ionic/angular-toolkit');
|
||||
addSchematics(host, '@ionic/angular-toolkit:component', {
|
||||
styleext: 'scss',
|
||||
});
|
||||
addSchematics(host, '@ionic/angular-toolkit:page', {
|
||||
styleext: 'scss',
|
||||
});
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the `IonicModule.forRoot()` usage to the project's `AppModule`.
|
||||
* If the project does not use modules this will operate as a noop.
|
||||
* @param projectSourceRoot The source root path of the project.
|
||||
*/
|
||||
function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
|
||||
return (host: Tree) => {
|
||||
const appModulePath = `${projectSourceRoot}/app/app.module.ts`;
|
||||
if (host.exists(appModulePath)) {
|
||||
addIonicModuleImportToNgModule(host, appModulePath);
|
||||
}
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the `provideIonicAngular` usage to the project's app config.
|
||||
* If the project does not use an app config this will operate as a noop.
|
||||
* @param projectName The name of the project.
|
||||
* @param projectSourceRoot The source root path of the project.
|
||||
*/
|
||||
function addProvideIonicAngular(projectName: string, projectSourceRoot: Path): Rule {
|
||||
return (host: Tree) => {
|
||||
const appConfig = `${projectSourceRoot}/app/app.config.ts`;
|
||||
if (host.exists(appConfig)) {
|
||||
return addRootProvider(
|
||||
projectName,
|
||||
({ code, external }) => code`${external('provideIonicAngular', '@ionic/angular/standalone')}({})`
|
||||
);
|
||||
}
|
||||
addModuleImportToRootModule(host, projectSourceRoot, 'IonicModule.forRoot()', '@ionic/angular');
|
||||
return host;
|
||||
};
|
||||
}
|
||||
@@ -116,49 +63,15 @@ function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicons(projectName: string, projectSourceRoot: Path): Rule {
|
||||
function addIonicons(projectName: string): Rule {
|
||||
return (host: Tree) => {
|
||||
const hasAppModule = host.exists(`${projectSourceRoot}/app/app.module.ts`);
|
||||
|
||||
if (hasAppModule) {
|
||||
/**
|
||||
* Add Ionicons to the `angular.json` file only if the project
|
||||
* is using the lazy build of `@ionic/angular` with modules.
|
||||
*/
|
||||
const ioniconsGlob = {
|
||||
glob: '**/*.svg',
|
||||
input: 'node_modules/ionicons/dist/ionicons/svg',
|
||||
output: './svg',
|
||||
};
|
||||
addAsset(host, projectName, 'build', ioniconsGlob);
|
||||
addAsset(host, projectName, 'test', ioniconsGlob);
|
||||
}
|
||||
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicConfig(projectSourceRoot: string): Rule {
|
||||
return (host: Tree) => {
|
||||
const ionicConfig = 'ionic.config.json';
|
||||
if (!host.exists(ionicConfig)) {
|
||||
const hasAppModule = host.exists(`${projectSourceRoot}/app/app.module.ts`);
|
||||
const type = hasAppModule ? 'angular' : 'angular-standalone';
|
||||
|
||||
host.create(
|
||||
ionicConfig,
|
||||
JSON.stringify(
|
||||
{
|
||||
name: 'ionic-app',
|
||||
app_id: '',
|
||||
type,
|
||||
integrations: {},
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
}
|
||||
const ioniconsGlob = {
|
||||
glob: '**/*.svg',
|
||||
input: 'node_modules/ionicons/dist/ionicons/svg',
|
||||
output: './svg',
|
||||
};
|
||||
addAsset(host, projectName, 'build', ioniconsGlob);
|
||||
addAsset(host, projectName, 'test', ioniconsGlob);
|
||||
return host;
|
||||
};
|
||||
}
|
||||
@@ -216,13 +129,10 @@ export default function ngAdd(options: IonAddOptions): Rule {
|
||||
// @ionic/angular
|
||||
addIonicAngularToPackageJson(),
|
||||
addIonicAngularToolkitToPackageJson(),
|
||||
addIonicAngularToolkitToAngularJson(),
|
||||
addIonicAngularModuleToAppModule(sourcePath),
|
||||
addProvideIonicAngular(options.project, sourcePath),
|
||||
addIonicBuilder(options.project),
|
||||
addIonicStyles(options.project, sourcePath),
|
||||
addIonicons(options.project, sourcePath),
|
||||
addIonicConfig(sourcePath),
|
||||
addIonicons(options.project),
|
||||
mergeWith(rootTemplateSource),
|
||||
// install freshly added dependencies
|
||||
installNodeDeps(),
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicsException } from '@angular-devkit/schematics';
|
||||
import { addSymbolToNgModuleMetadata, insertImport } from '@schematics/angular/utility/ast-utils';
|
||||
import { applyToUpdateRecorder } from '@schematics/angular/utility/change';
|
||||
import { normalize } from '@angular-devkit/core';
|
||||
import { Tree, SchematicsException } from '@angular-devkit/schematics';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import { addImportToModule } from './devkit-utils/ast-utils';
|
||||
import { InsertChange } from './devkit-utils/change';
|
||||
|
||||
/**
|
||||
* Reads file given path and returns TypeScript source file.
|
||||
*/
|
||||
function getSourceFile(host: Tree, path: string): ts.SourceFile {
|
||||
export function getSourceFile(host: Tree, path: string): ts.SourceFile {
|
||||
const buffer = host.read(path);
|
||||
if (!buffer) {
|
||||
throw new SchematicsException(`Could not find file for path: ${path}`);
|
||||
@@ -20,17 +21,32 @@ function getSourceFile(host: Tree, path: string): ts.SourceFile {
|
||||
/**
|
||||
* Import and add module to root app module.
|
||||
*/
|
||||
export function addIonicModuleImportToNgModule(host: Tree, modulePath: string): void {
|
||||
export function addModuleImportToRootModule(
|
||||
host: Tree,
|
||||
projectSourceRoot: string,
|
||||
moduleName: string,
|
||||
importSrc: string
|
||||
): void {
|
||||
addModuleImportToModule(host, normalize(`${projectSourceRoot}/app/app.module.ts`), moduleName, importSrc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import and add module to specific module path.
|
||||
* @param host the tree we are updating
|
||||
* @param modulePath src location of the module to import
|
||||
* @param moduleName name of module to import
|
||||
* @param src src location to import
|
||||
*/
|
||||
export function addModuleImportToModule(host: Tree, modulePath: string, moduleName: string, src: string): void {
|
||||
const moduleSource = getSourceFile(host, modulePath);
|
||||
const changes = addImportToModule(moduleSource, modulePath, moduleName, src);
|
||||
const recorder = host.beginUpdate(modulePath);
|
||||
const moduleSource = getSourceFile(host, modulePath) as any;
|
||||
|
||||
const ionicModuleChange = insertImport(moduleSource, modulePath, 'IonicModule', '@ionic/angular');
|
||||
|
||||
applyToUpdateRecorder(recorder, [ionicModuleChange]);
|
||||
|
||||
const metadataChange = addSymbolToNgModuleMetadata(moduleSource, modulePath, 'imports', 'IonicModule.forRoot({})');
|
||||
|
||||
applyToUpdateRecorder(recorder, metadataChange);
|
||||
changes.forEach((change) => {
|
||||
if (change instanceof InsertChange) {
|
||||
recorder.insertLeft(change.pos, change.toAdd);
|
||||
}
|
||||
});
|
||||
|
||||
host.commitUpdate(recorder);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
import type { JsonObject } from '@angular-devkit/core';
|
||||
import { WorkspaceDefinition } from '@angular-devkit/core/src/workspace';
|
||||
import { Tree, SchematicsException } from '@angular-devkit/schematics';
|
||||
import type { SchematicOptions } from '@angular/cli/lib/config/workspace-schema';
|
||||
import { parse } from 'jsonc-parser';
|
||||
|
||||
const ANGULAR_JSON_PATH = 'angular.json';
|
||||
const CONFIG_PATH = 'angular.json';
|
||||
|
||||
export function readConfig<T extends JsonObject = JsonObject>(host: Tree): T {
|
||||
return host.readJson(ANGULAR_JSON_PATH) as T;
|
||||
// TODO(FW-2827): types
|
||||
|
||||
export function readConfig(host: Tree): any {
|
||||
const sourceText = host.read(CONFIG_PATH)?.toString('utf-8');
|
||||
return JSON.parse(sourceText);
|
||||
}
|
||||
|
||||
export function writeConfig(host: Tree, config: JsonObject): void {
|
||||
host.overwrite(ANGULAR_JSON_PATH, JSON.stringify(config, null, 2));
|
||||
export function writeConfig(host: Tree, config: JSON): void {
|
||||
host.overwrite(CONFIG_PATH, JSON.stringify(config, null, 2));
|
||||
}
|
||||
|
||||
function isAngularBrowserProject(projectConfig: any): boolean {
|
||||
if (projectConfig.projectType === 'application') {
|
||||
const buildConfig = projectConfig.architect.build;
|
||||
// Angular 16 and lower
|
||||
const legacyAngularBuilder = buildConfig.builder === '@angular-devkit/build-angular:browser';
|
||||
// Angular 17+
|
||||
const modernAngularBuilder = buildConfig.builder === '@angular-devkit/build-angular:application';
|
||||
|
||||
return legacyAngularBuilder || modernAngularBuilder;
|
||||
return buildConfig.builder === '@angular-devkit/build-angular:browser';
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -42,7 +38,7 @@ export function getDefaultAngularAppName(config: any): string {
|
||||
return projectNames[0];
|
||||
}
|
||||
|
||||
function getAngularJson(config: any, projectName: string): any | never {
|
||||
export function getAngularAppConfig(config: any, projectName: string): any | never {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (!config.projects.hasOwnProperty(projectName)) {
|
||||
throw new SchematicsException(`Could not find project: ${projectName}`);
|
||||
@@ -63,8 +59,8 @@ function getAngularJson(config: any, projectName: string): any | never {
|
||||
|
||||
export function addStyle(host: Tree, projectName: string, stylePath: string): void {
|
||||
const config = readConfig(host);
|
||||
const angularJson = getAngularJson(config, projectName);
|
||||
angularJson.architect.build.options.styles.push({
|
||||
const appConfig = getAngularAppConfig(config, projectName);
|
||||
appConfig.architect.build.options.styles.push({
|
||||
input: stylePath,
|
||||
});
|
||||
writeConfig(host, config);
|
||||
@@ -77,8 +73,8 @@ export function addAsset(
|
||||
asset: string | { glob: string; input: string; output: string }
|
||||
): void {
|
||||
const config = readConfig(host);
|
||||
const angularJson = getAngularJson(config, projectName);
|
||||
const target = angularJson.architect[architect];
|
||||
const appConfig = getAngularAppConfig(config, projectName);
|
||||
const target = appConfig.architect[architect];
|
||||
if (target) {
|
||||
target.options.assets.push(asset);
|
||||
writeConfig(host, config);
|
||||
@@ -92,48 +88,11 @@ export function addArchitectBuilder(
|
||||
builderOpts: any
|
||||
): void | never {
|
||||
const config = readConfig(host);
|
||||
const angularJson = getAngularJson(config, projectName);
|
||||
angularJson.architect[builderName] = builderOpts;
|
||||
const appConfig = getAngularAppConfig(config, projectName);
|
||||
appConfig.architect[builderName] = builderOpts;
|
||||
writeConfig(host, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the angular.json to add an additional schematic collection
|
||||
* to the CLI configuration.
|
||||
*/
|
||||
export function addCli(host: Tree, collectionName: string): void | never {
|
||||
const angularJson = readConfig<any>(host);
|
||||
|
||||
if (angularJson.cli === undefined) {
|
||||
angularJson.cli = {};
|
||||
}
|
||||
|
||||
if (angularJson.cli.schematicCollections === undefined) {
|
||||
angularJson.cli.schematicCollections = [];
|
||||
}
|
||||
|
||||
angularJson.cli.schematicCollections.push(collectionName);
|
||||
|
||||
writeConfig(host, angularJson);
|
||||
}
|
||||
|
||||
// TODO(FW-5639): can remove [property: string]: any; when upgrading @angular/cli dev-dep to v16 or later
|
||||
export function addSchematics(
|
||||
host: Tree,
|
||||
schematicName: string,
|
||||
schematicOpts: SchematicOptions & { [property: string]: any }
|
||||
): void | never {
|
||||
const angularJson = readConfig<any>(host);
|
||||
|
||||
if (angularJson.schematics === undefined) {
|
||||
angularJson.schematics = {};
|
||||
}
|
||||
|
||||
angularJson.schematics[schematicName] = schematicOpts;
|
||||
|
||||
writeConfig(host, angularJson);
|
||||
}
|
||||
|
||||
export function getWorkspacePath(host: Tree): string {
|
||||
const possibleFiles = ['/angular.json', '/.angular.json'];
|
||||
const path = possibleFiles.filter((path) => host.exists(path))[0];
|
||||
|
||||
579
packages/angular/src/schematics/utils/devkit-utils/ast-utils.ts
Normal file
@@ -0,0 +1,579 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import { Change, InsertChange, NoopChange } from './change';
|
||||
|
||||
/**
|
||||
* Add Import `import { symbolName } from fileName` if the import doesn't exit
|
||||
* already. Assumes fileToEdit can be resolved and accessed.
|
||||
* @param fileToEdit (file we want to add import to)
|
||||
* @param symbolName (item to import)
|
||||
* @param fileName (path to the file)
|
||||
* @param isDefault (if true, import follows style for importing default exports)
|
||||
* @return Change
|
||||
*/
|
||||
export function insertImport(
|
||||
source: ts.SourceFile,
|
||||
fileToEdit: string,
|
||||
symbolName: string,
|
||||
fileName: string,
|
||||
isDefault = false
|
||||
): Change {
|
||||
const rootNode = source;
|
||||
const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
|
||||
|
||||
// get nodes that map to import statements from the file fileName
|
||||
const relevantImports = allImports.filter((node) => {
|
||||
// StringLiteral of the ImportDeclaration is the import file (fileName in this case).
|
||||
const importFiles = node
|
||||
.getChildren()
|
||||
.filter((child) => child.kind === ts.SyntaxKind.StringLiteral)
|
||||
.map((n) => (n as ts.StringLiteral).text);
|
||||
|
||||
return importFiles.filter((file) => file === fileName).length === 1;
|
||||
});
|
||||
|
||||
if (relevantImports.length > 0) {
|
||||
let importsAsterisk = false;
|
||||
// imports from import file
|
||||
const imports: ts.Node[] = [];
|
||||
relevantImports.forEach((n) => {
|
||||
Array.prototype.push.apply(imports, findNodes(n, ts.SyntaxKind.Identifier));
|
||||
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
|
||||
importsAsterisk = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if imports * from fileName, don't add symbolName
|
||||
if (importsAsterisk) {
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
const importTextNodes = imports.filter((n) => (n as ts.Identifier).text === symbolName);
|
||||
|
||||
// insert import if it's not there
|
||||
if (importTextNodes.length === 0) {
|
||||
const fallbackPos =
|
||||
findNodes(relevantImports[0], ts.SyntaxKind.CloseBraceToken)[0].getStart() ||
|
||||
findNodes(relevantImports[0], ts.SyntaxKind.FromKeyword)[0].getStart();
|
||||
|
||||
return insertAfterLastOccurrence(imports, `, ${symbolName}`, fileToEdit, fallbackPos);
|
||||
}
|
||||
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
// no such import declaration exists
|
||||
const useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral).filter(
|
||||
(n: ts.StringLiteral) => n.text === 'use strict'
|
||||
);
|
||||
let fallbackPos = 0;
|
||||
if (useStrict.length > 0) {
|
||||
fallbackPos = useStrict[0].end;
|
||||
}
|
||||
const open = isDefault ? '' : '{ ';
|
||||
const close = isDefault ? '' : ' }';
|
||||
// if there are no imports or 'use strict' statement, insert import at beginning of file
|
||||
const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
|
||||
const separator = insertAtBeginning ? '' : ';\n';
|
||||
const toInsert =
|
||||
`${separator}import ${open}${symbolName}${close}` + ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
|
||||
|
||||
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all nodes from the AST in the subtree of node of SyntaxKind kind.
|
||||
* @param node
|
||||
* @param kind
|
||||
* @param max The maximum number of items to return.
|
||||
* @return all nodes of kind, or [] if none is found
|
||||
*/
|
||||
export function findNodes(node: ts.Node, kind: ts.SyntaxKind, max = Infinity): ts.Node[] {
|
||||
if (!node || max == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const arr: ts.Node[] = [];
|
||||
if (node.kind === kind) {
|
||||
arr.push(node);
|
||||
max--;
|
||||
}
|
||||
if (max > 0) {
|
||||
for (const child of node.getChildren()) {
|
||||
findNodes(child, kind, max).forEach((node) => {
|
||||
if (max > 0) {
|
||||
arr.push(node);
|
||||
}
|
||||
max--;
|
||||
});
|
||||
|
||||
if (max <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the nodes from a source.
|
||||
* @param sourceFile The source file object.
|
||||
* @returns {Observable<ts.Node>} An observable of all the nodes in the source.
|
||||
*/
|
||||
export function getSourceNodes(sourceFile: ts.SourceFile): ts.Node[] {
|
||||
const nodes: ts.Node[] = [sourceFile];
|
||||
const result = [];
|
||||
|
||||
while (nodes.length > 0) {
|
||||
const node = nodes.shift();
|
||||
|
||||
if (node) {
|
||||
result.push(node);
|
||||
if (node.getChildCount(sourceFile) >= 0) {
|
||||
nodes.unshift(...node.getChildren());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function findNode(node: ts.Node, kind: ts.SyntaxKind, text: string): ts.Node | null {
|
||||
if (node.kind === kind && node.getText() === text) {
|
||||
// throw new Error(node.getText());
|
||||
return node;
|
||||
}
|
||||
|
||||
let foundNode: ts.Node | null = null;
|
||||
ts.forEachChild(node, (childNode) => {
|
||||
foundNode = foundNode || findNode(childNode, kind, text);
|
||||
});
|
||||
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for sorting nodes.
|
||||
* @return function to sort nodes in increasing order of position in sourceFile
|
||||
*/
|
||||
function nodesByPosition(first: ts.Node, second: ts.Node): number {
|
||||
return first.getStart() - second.getStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert `toInsert` after the last occurence of `ts.SyntaxKind[nodes[i].kind]`
|
||||
* or after the last of occurence of `syntaxKind` if the last occurence is a sub child
|
||||
* of ts.SyntaxKind[nodes[i].kind] and save the changes in file.
|
||||
*
|
||||
* @param nodes insert after the last occurence of nodes
|
||||
* @param toInsert string to insert
|
||||
* @param file file to insert changes into
|
||||
* @param fallbackPos position to insert if toInsert happens to be the first occurence
|
||||
* @param syntaxKind the ts.SyntaxKind of the subchildren to insert after
|
||||
* @return Change instance
|
||||
* @throw Error if toInsert is first occurence but fall back is not set
|
||||
*/
|
||||
export function insertAfterLastOccurrence(
|
||||
nodes: ts.Node[],
|
||||
toInsert: string,
|
||||
file: string,
|
||||
fallbackPos: number,
|
||||
syntaxKind?: ts.SyntaxKind
|
||||
): Change {
|
||||
// sort() has a side effect, so make a copy so that we won't overwrite the parent's object.
|
||||
let lastItem = [...nodes].sort(nodesByPosition).pop();
|
||||
if (!lastItem) {
|
||||
throw new Error();
|
||||
}
|
||||
if (syntaxKind) {
|
||||
lastItem = findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop();
|
||||
}
|
||||
if (!lastItem && fallbackPos == undefined) {
|
||||
throw new Error(`tried to insert ${toInsert} as first occurence with no fallback position`);
|
||||
}
|
||||
const lastItemPosition: number = lastItem ? lastItem.getEnd() : fallbackPos;
|
||||
|
||||
return new InsertChange(file, lastItemPosition, toInsert);
|
||||
}
|
||||
|
||||
export function getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): string | null {
|
||||
if (node.kind == ts.SyntaxKind.Identifier) {
|
||||
return (node as ts.Identifier).text;
|
||||
} else if (node.kind == ts.SyntaxKind.StringLiteral) {
|
||||
return (node as ts.StringLiteral).text;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _angularImportsFromNode(
|
||||
node: ts.ImportDeclaration,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_sourceFile: ts.SourceFile
|
||||
): { [name: string]: string } {
|
||||
const ms = node.moduleSpecifier;
|
||||
let modulePath: string;
|
||||
switch (ms.kind) {
|
||||
case ts.SyntaxKind.StringLiteral:
|
||||
modulePath = (ms as ts.StringLiteral).text;
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!modulePath.startsWith('@angular/')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (node.importClause) {
|
||||
if (node.importClause.name) {
|
||||
// This is of the form `import Name from 'path'`. Ignore.
|
||||
return {};
|
||||
} else if (node.importClause.namedBindings) {
|
||||
const nb = node.importClause.namedBindings;
|
||||
if (nb.kind == ts.SyntaxKind.NamespaceImport) {
|
||||
// This is of the form `import * as name from 'path'`. Return `name.`.
|
||||
return {
|
||||
[(nb as ts.NamespaceImport).name.text + '.']: modulePath,
|
||||
};
|
||||
} else {
|
||||
// This is of the form `import {a,b,c} from 'path'`
|
||||
const namedImports = nb as ts.NamedImports;
|
||||
|
||||
return namedImports.elements
|
||||
.map((is: ts.ImportSpecifier) => (is.propertyName ? is.propertyName.text : is.name.text))
|
||||
.reduce((acc: { [name: string]: string }, curr: string) => {
|
||||
acc[curr] = modulePath;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
} else {
|
||||
// This is of the form `import 'path';`. Nothing to do.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function getDecoratorMetadata(source: ts.SourceFile, identifier: string, module: string): ts.Node[] {
|
||||
const angularImports: { [name: string]: string } = findNodes(source, ts.SyntaxKind.ImportDeclaration)
|
||||
.map((node: ts.ImportDeclaration) => _angularImportsFromNode(node, source))
|
||||
.reduce((acc: { [name: string]: string }, current: { [name: string]: string }) => {
|
||||
for (const key of Object.keys(current)) {
|
||||
acc[key] = current[key];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return getSourceNodes(source)
|
||||
.filter((node) => {
|
||||
return (
|
||||
node.kind == ts.SyntaxKind.Decorator && (node as ts.Decorator).expression.kind == ts.SyntaxKind.CallExpression
|
||||
);
|
||||
})
|
||||
.map((node) => (node as ts.Decorator).expression as ts.CallExpression)
|
||||
.filter((expr) => {
|
||||
if (expr.expression.kind == ts.SyntaxKind.Identifier) {
|
||||
const id = expr.expression as ts.Identifier;
|
||||
|
||||
return id.getFullText(source) == identifier && angularImports[id.getFullText(source)] === module;
|
||||
} else if (expr.expression.kind == ts.SyntaxKind.PropertyAccessExpression) {
|
||||
// This covers foo.NgModule when importing * as foo.
|
||||
const paExpr = expr.expression as ts.PropertyAccessExpression;
|
||||
// If the left expression is not an identifier, just give up at that point.
|
||||
if (paExpr.expression.kind !== ts.SyntaxKind.Identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const id = paExpr.name.text;
|
||||
const moduleId = (paExpr.expression as ts.Identifier).getText(source);
|
||||
|
||||
return id === identifier && angularImports[moduleId + '.'] === module;
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.filter((expr) => expr.arguments[0] && expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression)
|
||||
.map((expr) => expr.arguments[0] as ts.ObjectLiteralExpression);
|
||||
}
|
||||
|
||||
function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration | undefined {
|
||||
if (ts.isClassDeclaration(node)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
return node.parent && findClassDeclarationParent(node.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a source file with @NgModule class(es), find the name of the first @NgModule class.
|
||||
*
|
||||
* @param source source file containing one or more @NgModule
|
||||
* @returns the name of the first @NgModule, or `undefined` if none is found
|
||||
*/
|
||||
export function getFirstNgModuleName(source: ts.SourceFile): string | undefined {
|
||||
// First, find the @NgModule decorators.
|
||||
const ngModulesMetadata = getDecoratorMetadata(source, 'NgModule', '@angular/core');
|
||||
if (ngModulesMetadata.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Then walk parent pointers up the AST, looking for the ClassDeclaration parent of the NgModule
|
||||
// metadata.
|
||||
const moduleClass = findClassDeclarationParent(ngModulesMetadata[0]);
|
||||
if (!moduleClass?.name) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Get the class name of the module ClassDeclaration.
|
||||
return moduleClass.name.text;
|
||||
}
|
||||
|
||||
export function addSymbolToNgModuleMetadata(
|
||||
source: ts.SourceFile,
|
||||
ngModulePath: string,
|
||||
metadataField: string,
|
||||
symbolName: string,
|
||||
importPath: string | null = null
|
||||
): Change[] {
|
||||
const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
|
||||
let node: any = nodes[0]; // tslint:disable-line:no-any
|
||||
|
||||
// Find the decorator declaration.
|
||||
if (!node) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get all the children property assignment of object literals.
|
||||
const matchingProperties: ts.ObjectLiteralElement[] = (node as ts.ObjectLiteralExpression).properties
|
||||
.filter((prop) => prop.kind == ts.SyntaxKind.PropertyAssignment)
|
||||
// Filter out every fields that's not "metadataField". Also handles string literals
|
||||
// (but not expressions).
|
||||
.filter((prop: ts.PropertyAssignment) => {
|
||||
const name = prop.name;
|
||||
switch (name.kind) {
|
||||
case ts.SyntaxKind.Identifier:
|
||||
return (name as ts.Identifier).getText(source) == metadataField;
|
||||
case ts.SyntaxKind.StringLiteral:
|
||||
return (name as ts.StringLiteral).text == metadataField;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Get the last node of the array literal.
|
||||
if (!matchingProperties) {
|
||||
return [];
|
||||
}
|
||||
if (matchingProperties.length == 0) {
|
||||
// We haven't found the field in the metadata declaration. Insert a new field.
|
||||
const expr = node as ts.ObjectLiteralExpression;
|
||||
let position: number;
|
||||
let toInsert: string;
|
||||
if (expr.properties.length == 0) {
|
||||
position = expr.getEnd() - 1;
|
||||
toInsert = ` ${metadataField}: [${symbolName}]\n`;
|
||||
} else {
|
||||
node = expr.properties[expr.properties.length - 1];
|
||||
position = node.getEnd();
|
||||
// Get the indentation of the last element, if any.
|
||||
const text = node.getFullText(source);
|
||||
const matches = text.match(/^\r?\n\s*/);
|
||||
if (matches.length > 0) {
|
||||
toInsert = `,${matches[0]}${metadataField}: [${symbolName}]`;
|
||||
} else {
|
||||
toInsert = `, ${metadataField}: [${symbolName}]`;
|
||||
}
|
||||
}
|
||||
if (importPath !== null) {
|
||||
return [
|
||||
new InsertChange(ngModulePath, position, toInsert),
|
||||
insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath),
|
||||
];
|
||||
} else {
|
||||
return [new InsertChange(ngModulePath, position, toInsert)];
|
||||
}
|
||||
}
|
||||
const assignment = matchingProperties[0] as ts.PropertyAssignment;
|
||||
|
||||
// If it's not an array, nothing we can do really.
|
||||
if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const arrLiteral = assignment.initializer as ts.ArrayLiteralExpression;
|
||||
if (arrLiteral.elements.length == 0) {
|
||||
// Forward the property.
|
||||
node = arrLiteral;
|
||||
} else {
|
||||
node = arrLiteral.elements;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
console.log('No app module found. Please add your new class to your component.');
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Array.isArray(node)) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const nodeArray = node as {} as ts.Node[];
|
||||
const symbolsArray = nodeArray.map((node) => node.getText());
|
||||
if (symbolsArray.includes(symbolName)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
node = node[node.length - 1];
|
||||
}
|
||||
|
||||
let toInsert: string;
|
||||
let position = node.getEnd();
|
||||
if (node.kind == ts.SyntaxKind.ObjectLiteralExpression) {
|
||||
// We haven't found the field in the metadata declaration. Insert a new
|
||||
// field.
|
||||
const expr = node as ts.ObjectLiteralExpression;
|
||||
if (expr.properties.length == 0) {
|
||||
position = expr.getEnd() - 1;
|
||||
toInsert = ` ${metadataField}: [${symbolName}]\n`;
|
||||
} else {
|
||||
node = expr.properties[expr.properties.length - 1];
|
||||
position = node.getEnd();
|
||||
// Get the indentation of the last element, if any.
|
||||
const text = node.getFullText(source);
|
||||
if (text.match('^\r?\r?\n')) {
|
||||
toInsert = `,${text.match(/^\r?\n\s+/)[0]}${metadataField}: [${symbolName}]`;
|
||||
} else {
|
||||
toInsert = `, ${metadataField}: [${symbolName}]`;
|
||||
}
|
||||
}
|
||||
} else if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
|
||||
// We found the field but it's empty. Insert it just before the `]`.
|
||||
position--;
|
||||
toInsert = `${symbolName}`;
|
||||
} else {
|
||||
// Get the indentation of the last element, if any.
|
||||
const text = node.getFullText(source);
|
||||
if (text.match(/^\r?\n/)) {
|
||||
toInsert = `,${text.match(/^\r?\n(\r?)\s+/)[0]}${symbolName}`;
|
||||
} else {
|
||||
toInsert = `, ${symbolName}`;
|
||||
}
|
||||
}
|
||||
if (importPath !== null) {
|
||||
return [
|
||||
new InsertChange(ngModulePath, position, toInsert),
|
||||
insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath),
|
||||
];
|
||||
}
|
||||
|
||||
return [new InsertChange(ngModulePath, position, toInsert)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom function to insert a declaration (component, pipe, directive)
|
||||
* into NgModule declarations. It also imports the component.
|
||||
*/
|
||||
export function addDeclarationToModule(
|
||||
source: ts.SourceFile,
|
||||
modulePath: string,
|
||||
classifiedName: string,
|
||||
importPath: string
|
||||
): Change[] {
|
||||
return addSymbolToNgModuleMetadata(source, modulePath, 'declarations', classifiedName, importPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom function to insert an NgModule into NgModule imports. It also imports the module.
|
||||
*/
|
||||
export function addImportToModule(
|
||||
source: ts.SourceFile,
|
||||
modulePath: string,
|
||||
classifiedName: string,
|
||||
importPath: string
|
||||
): Change[] {
|
||||
return addSymbolToNgModuleMetadata(source, modulePath, 'imports', classifiedName, importPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom function to insert a provider into NgModule. It also imports it.
|
||||
*/
|
||||
export function addProviderToModule(
|
||||
source: ts.SourceFile,
|
||||
modulePath: string,
|
||||
classifiedName: string,
|
||||
importPath: string
|
||||
): Change[] {
|
||||
return addSymbolToNgModuleMetadata(source, modulePath, 'providers', classifiedName, importPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom function to insert an export into NgModule. It also imports it.
|
||||
*/
|
||||
export function addExportToModule(
|
||||
source: ts.SourceFile,
|
||||
modulePath: string,
|
||||
classifiedName: string,
|
||||
importPath: string
|
||||
): Change[] {
|
||||
return addSymbolToNgModuleMetadata(source, modulePath, 'exports', classifiedName, importPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom function to insert an export into NgModule. It also imports it.
|
||||
*/
|
||||
export function addBootstrapToModule(
|
||||
source: ts.SourceFile,
|
||||
modulePath: string,
|
||||
classifiedName: string,
|
||||
importPath: string
|
||||
): Change[] {
|
||||
return addSymbolToNgModuleMetadata(source, modulePath, 'bootstrap', classifiedName, importPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom function to insert an entryComponent into NgModule. It also imports it.
|
||||
*/
|
||||
export function addEntryComponentToModule(
|
||||
source: ts.SourceFile,
|
||||
modulePath: string,
|
||||
classifiedName: string,
|
||||
importPath: string
|
||||
): Change[] {
|
||||
return addSymbolToNgModuleMetadata(source, modulePath, 'entryComponents', classifiedName, importPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an import already exists.
|
||||
*/
|
||||
export function isImported(source: ts.SourceFile, classifiedName: string, importPath: string): boolean {
|
||||
const allNodes = getSourceNodes(source);
|
||||
const matchingNodes = allNodes
|
||||
.filter((node) => node.kind === ts.SyntaxKind.ImportDeclaration)
|
||||
.filter((imp: ts.ImportDeclaration) => imp.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
|
||||
.filter((imp: ts.ImportDeclaration) => {
|
||||
return (imp.moduleSpecifier as ts.StringLiteral).text === importPath;
|
||||
})
|
||||
.filter((imp: ts.ImportDeclaration) => {
|
||||
if (!imp.importClause) {
|
||||
return false;
|
||||
}
|
||||
const nodes = findNodes(imp.importClause, ts.SyntaxKind.ImportSpecifier).filter(
|
||||
(n) => n.getText() === classifiedName
|
||||
);
|
||||
|
||||
return nodes.length > 0;
|
||||
});
|
||||
|
||||
return matchingNodes.length > 0;
|
||||
}
|
||||
123
packages/angular/src/schematics/utils/devkit-utils/change.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
export interface Host {
|
||||
write(path: string, content: string): Promise<void>;
|
||||
read(path: string): Promise<string>;
|
||||
}
|
||||
|
||||
export interface Change {
|
||||
apply(host: Host): Promise<void>;
|
||||
|
||||
// The file this change should be applied to. Some changes might not apply to
|
||||
// a file (maybe the config).
|
||||
readonly path: string | null;
|
||||
|
||||
// The order this change should be applied. Normally the position inside the file.
|
||||
// Changes are applied from the bottom of a file to the top.
|
||||
readonly order: number;
|
||||
|
||||
// The description of this change. This will be outputted in a dry or verbose run.
|
||||
readonly description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation that does nothing.
|
||||
*/
|
||||
export class NoopChange implements Change {
|
||||
description = 'No operation.';
|
||||
order = Infinity;
|
||||
path = null;
|
||||
apply(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will add text to the source code.
|
||||
*/
|
||||
export class InsertChange implements Change {
|
||||
order: number;
|
||||
description: string;
|
||||
|
||||
constructor(public path: string, public pos: number, public toAdd: string) {
|
||||
if (pos < 0) {
|
||||
throw new Error('Negative positions are invalid');
|
||||
}
|
||||
this.description = `Inserted ${toAdd} into position ${pos} of ${path}`;
|
||||
this.order = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does not insert spaces if there is none in the original string.
|
||||
*/
|
||||
apply(host: Host): Promise<void> {
|
||||
return host.read(this.path).then((content) => {
|
||||
const prefix = content.substring(0, this.pos);
|
||||
const suffix = content.substring(this.pos);
|
||||
|
||||
return host.write(this.path, `${prefix}${this.toAdd}${suffix}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will remove text from the source code.
|
||||
*/
|
||||
export class RemoveChange implements Change {
|
||||
order: number;
|
||||
description: string;
|
||||
|
||||
constructor(public path: string, private pos: number, private toRemove: string) {
|
||||
if (pos < 0) {
|
||||
throw new Error('Negative positions are invalid');
|
||||
}
|
||||
this.description = `Removed ${toRemove} into position ${pos} of ${path}`;
|
||||
this.order = pos;
|
||||
}
|
||||
|
||||
apply(host: Host): Promise<void> {
|
||||
return host.read(this.path).then((content) => {
|
||||
const prefix = content.substring(0, this.pos);
|
||||
const suffix = content.substring(this.pos + this.toRemove.length);
|
||||
|
||||
// TODO: throw error if toRemove doesn't match removed string.
|
||||
return host.write(this.path, `${prefix}${suffix}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will replace text from the source code.
|
||||
*/
|
||||
export class ReplaceChange implements Change {
|
||||
order: number;
|
||||
description: string;
|
||||
|
||||
constructor(public path: string, private pos: number, private oldText: string, private newText: string) {
|
||||
if (pos < 0) {
|
||||
throw new Error('Negative positions are invalid');
|
||||
}
|
||||
this.description = `Replaced ${oldText} into position ${pos} of ${path} with ${newText}`;
|
||||
this.order = pos;
|
||||
}
|
||||
|
||||
apply(host: Host): Promise<void> {
|
||||
return host.read(this.path).then((content) => {
|
||||
const prefix = content.substring(0, this.pos);
|
||||
const suffix = content.substring(this.pos + this.oldText.length);
|
||||
const text = content.substring(this.pos, this.pos + this.oldText.length);
|
||||
|
||||
if (text !== this.oldText) {
|
||||
return Promise.reject(new Error(`Invalid replace: "${text}" != "${this.oldText}".`));
|
||||
}
|
||||
|
||||
// TODO: throw error if oldText doesn't match removed string.
|
||||
return host.write(this.path, `${prefix}${this.newText}${suffix}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
### Devkit Utils
|
||||
|
||||
These are utility files copied over from `@angular-devkit`.
|
||||
They are not exported so they need to be manually copied over.
|
||||
Please do not edit directly.
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import { findNodes, insertAfterLastOccurrence } from './ast-utils';
|
||||
import { Change, NoopChange } from './change';
|
||||
|
||||
/**
|
||||
* Add Import `import { symbolName } from fileName` if the import doesn't exit
|
||||
* already. Assumes fileToEdit can be resolved and accessed.
|
||||
* @param fileToEdit (file we want to add import to)
|
||||
* @param symbolName (item to import)
|
||||
* @param fileName (path to the file)
|
||||
* @param isDefault (if true, import follows style for importing default exports)
|
||||
* @return Change
|
||||
*/
|
||||
|
||||
export function insertImport(
|
||||
source: ts.SourceFile,
|
||||
fileToEdit: string,
|
||||
symbolName: string,
|
||||
fileName: string,
|
||||
isDefault = false
|
||||
): Change {
|
||||
const rootNode = source;
|
||||
const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
|
||||
|
||||
// get nodes that map to import statements from the file fileName
|
||||
const relevantImports = allImports.filter((node) => {
|
||||
// StringLiteral of the ImportDeclaration is the import file (fileName in this case).
|
||||
const importFiles = node
|
||||
.getChildren()
|
||||
.filter((child) => child.kind === ts.SyntaxKind.StringLiteral)
|
||||
.map((n) => (n as ts.StringLiteral).text);
|
||||
|
||||
return importFiles.filter((file) => file === fileName).length === 1;
|
||||
});
|
||||
|
||||
if (relevantImports.length > 0) {
|
||||
let importsAsterisk = false;
|
||||
// imports from import file
|
||||
const imports: ts.Node[] = [];
|
||||
relevantImports.forEach((n) => {
|
||||
Array.prototype.push.apply(imports, findNodes(n, ts.SyntaxKind.Identifier));
|
||||
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
|
||||
importsAsterisk = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if imports * from fileName, don't add symbolName
|
||||
if (importsAsterisk) {
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
const importTextNodes = imports.filter((n) => (n as ts.Identifier).text === symbolName);
|
||||
|
||||
// insert import if it's not there
|
||||
if (importTextNodes.length === 0) {
|
||||
const fallbackPos =
|
||||
findNodes(relevantImports[0], ts.SyntaxKind.CloseBraceToken)[0].getStart() ||
|
||||
findNodes(relevantImports[0], ts.SyntaxKind.FromKeyword)[0].getStart();
|
||||
|
||||
return insertAfterLastOccurrence(imports, `, ${symbolName}`, fileToEdit, fallbackPos);
|
||||
}
|
||||
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
// no such import declaration exists
|
||||
const useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral).filter(
|
||||
(n: ts.StringLiteral) => n.text === 'use strict'
|
||||
);
|
||||
let fallbackPos = 0;
|
||||
if (useStrict.length > 0) {
|
||||
fallbackPos = useStrict[0].end;
|
||||
}
|
||||
const open = isDefault ? '' : '{ ';
|
||||
const close = isDefault ? '' : ' }';
|
||||
// if there are no imports or 'use strict' statement, insert import at beginning of file
|
||||
const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
|
||||
const separator = insertAtBeginning ? '' : ';\n';
|
||||
const toInsert =
|
||||
`${separator}import ${open}${symbolName}${close}` + ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
|
||||
|
||||
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
|
||||
}
|
||||
@@ -6,16 +6,16 @@ export { IonRouterLink, IonRouterLinkWithHref } from './navigation/router-link-d
|
||||
export { IonTabs } from './navigation/tabs';
|
||||
export { provideIonicAngular } from './providers/ionic-angular';
|
||||
export { ActionSheetController } from './providers/action-sheet-controller';
|
||||
export { AlertController } from './providers/alert-controller';
|
||||
export { AnimationController } from './providers/animation-controller';
|
||||
export { GestureController } from './providers/gesture-controller';
|
||||
export { LoadingController } from './providers/loading-controller';
|
||||
export { MenuController } from './providers/menu-controller';
|
||||
export { ModalController } from './providers/modal-controller';
|
||||
export { PickerController } from './providers/picker-controller';
|
||||
export { PopoverController } from './providers/popover-controller';
|
||||
export { ToastController } from './providers/toast-controller';
|
||||
export {
|
||||
AlertController,
|
||||
LoadingController,
|
||||
PickerController,
|
||||
DomController,
|
||||
NavController,
|
||||
Config,
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { ActionSheetOptions } from '@ionic/core/components';
|
||||
import { actionSheetController } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-action-sheet.js';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -10,6 +9,5 @@ import { defineCustomElement } from '@ionic/core/components/ion-action-sheet.js'
|
||||
export class ActionSheetController extends OverlayBaseController<ActionSheetOptions, HTMLIonActionSheetElement> {
|
||||
constructor() {
|
||||
super(actionSheetController);
|
||||
defineCustomElement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Injector, Injectable, EnvironmentInjector, inject } from '@angular/core
|
||||
import { AngularDelegate, OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { ModalOptions } from '@ionic/core/components';
|
||||
import { modalController } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-modal.js';
|
||||
|
||||
@Injectable()
|
||||
export class ModalController extends OverlayBaseController<ModalOptions, HTMLIonModalElement> {
|
||||
@@ -12,7 +11,6 @@ export class ModalController extends OverlayBaseController<ModalOptions, HTMLIon
|
||||
|
||||
constructor() {
|
||||
super(modalController);
|
||||
defineCustomElement();
|
||||
}
|
||||
|
||||
create(opts: ModalOptions): Promise<HTMLIonModalElement> {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Injector, inject, EnvironmentInjector } from '@angular/core';
|
||||
import { AngularDelegate, OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { PopoverOptions } from '@ionic/core/components';
|
||||
import { popoverController } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-popover.js';
|
||||
|
||||
export class PopoverController extends OverlayBaseController<PopoverOptions, HTMLIonPopoverElement> {
|
||||
private angularDelegate = inject(AngularDelegate);
|
||||
@@ -11,7 +10,6 @@ export class PopoverController extends OverlayBaseController<PopoverOptions, HTM
|
||||
|
||||
constructor() {
|
||||
super(popoverController);
|
||||
defineCustomElement();
|
||||
}
|
||||
|
||||
create(opts: PopoverOptions): Promise<HTMLIonPopoverElement> {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { ToastOptions } from '@ionic/core/components';
|
||||
import { toastController } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-toast.js';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -10,6 +9,5 @@ import { defineCustomElement } from '@ionic/core/components/ion-toast.js';
|
||||
export class ToastController extends OverlayBaseController<ToastOptions, HTMLIonToastElement> {
|
||||
constructor() {
|
||||
super(toastController);
|
||||
defineCustomElement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
describe('Providers', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/lazy/providers');
|
||||
});
|
||||
})
|
||||
|
||||
it('should load all providers', () => {
|
||||
cy.get('#is-loaded').should('have.text', 'true');
|
||||
@@ -26,7 +26,7 @@ describe('Providers', () => {
|
||||
cy.visit('/lazy/providers?firstParam=abc&secondParam=true');
|
||||
|
||||
cy.get('#query-params').should('have.text', 'firstParam: abc, firstParam: true');
|
||||
});
|
||||
})
|
||||
|
||||
// https://github.com/ionic-team/ionic-framework/issues/28337
|
||||
it('should register menus correctly', () => {
|
||||
@@ -39,22 +39,5 @@ describe('Providers', () => {
|
||||
|
||||
cy.get('ion-action-sheet').should('be.visible');
|
||||
});
|
||||
|
||||
it('should open an alert', () => {
|
||||
cy.get('button#open-alert').click();
|
||||
|
||||
cy.get('ion-alert').should('be.visible');
|
||||
});
|
||||
|
||||
it('should open a loading-indicator', () => {
|
||||
cy.get('button#open-loading').click();
|
||||
|
||||
cy.get('ion-loading').should('be.visible');
|
||||
});
|
||||
|
||||
it('should open a picker', () => {
|
||||
cy.get('button#open-picker').click();
|
||||
|
||||
cy.get('ion-picker').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||