From a3cd204f616606ccffc35082655e55fdfb19fe28 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Wed, 29 Nov 2023 16:53:33 -0500 Subject: [PATCH 1/6] fix(overlays): trigger is configured on load (#28526) Issue number: resolves #28524 --------- ## What is the current behavior? Watchers in Stencil are constructed sometime between `connectedCallback` and `componentDidLoad`. If a property is set/changed during that time it is possible for the callback associated with the watcher to not fire because the watcher has not been setup yet. This is most often with `dist-custom-elements` and frameworks such as Angular when using a binding (i.e. `[trigger]` instead of `trigger`) ## What is the new behavior? - The trigger callback associated with the watcher is manually called in `componentDidLoad` for each overlay. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information Dev build: `7.5.5-dev.11699974376.13a15397` Note: This is a timing related bug due to a behavior in Stencil, so I did not write automated tests. However, I manually verified that this issue a) reproduces on `main` and b) is fixed with this dev build for each overlay component. --- core/src/components/action-sheet/action-sheet.tsx | 11 +++++++++++ core/src/components/alert/alert.tsx | 11 +++++++++++ core/src/components/loading/loading.tsx | 11 +++++++++++ core/src/components/modal/modal.tsx | 11 +++++++++++ core/src/components/picker/picker.tsx | 11 +++++++++++ core/src/components/popover/popover.tsx | 11 +++++++++++ core/src/components/toast/toast.tsx | 11 +++++++++++ 7 files changed, 77 insertions(+) diff --git a/core/src/components/action-sheet/action-sheet.tsx b/core/src/components/action-sheet/action-sheet.tsx index 813e38c7f9..984e24cdaa 100644 --- a/core/src/components/action-sheet/action-sheet.tsx +++ b/core/src/components/action-sheet/action-sheet.tsx @@ -337,6 +337,17 @@ export class ActionSheet implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } render() { diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index 4568e45a38..90a1c281bb 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -376,6 +376,17 @@ export class Alert implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } /** diff --git a/core/src/components/loading/loading.tsx b/core/src/components/loading/loading.tsx index 0892b082d1..05e40669d9 100644 --- a/core/src/components/loading/loading.tsx +++ b/core/src/components/loading/loading.tsx @@ -225,6 +225,17 @@ export class Loading implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } disconnectedCallback() { diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index 3c8a091901..ef6114b18b 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -368,6 +368,17 @@ export class Modal implements ComponentInterface, OverlayInterface { raf(() => this.present()); } this.breakpointsChanged(this.breakpoints); + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } /** diff --git a/core/src/components/picker/picker.tsx b/core/src/components/picker/picker.tsx index 19d03d2f8b..4232288919 100644 --- a/core/src/components/picker/picker.tsx +++ b/core/src/components/picker/picker.tsx @@ -209,6 +209,17 @@ export class Picker implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } /** diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index 75e16ad70d..21dce20a5d 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -370,6 +370,17 @@ export class Popover implements ComponentInterface, PopoverInterface { this.dismiss(undefined, undefined, false); }); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.configureTriggerInteraction(); } /** diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index b7945400b5..0a4fc420fc 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -288,6 +288,17 @@ export class Toast implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } /** From 60303aad23f823488afc8f8824e9c72e3ab86acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Le=20Ma=C3=AEtre?= Date: Fri, 1 Dec 2023 16:30:13 +0100 Subject: [PATCH 2/6] fix(vue): nav component accepts kebab-case component properties (#28615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue number: resolves #28611 --------- ## What is the current behavior? It's not possible to pass props that are not camelCase to the `IonNav` component. ## What is the new behavior? - It is now possible to set a props with kebab-case instead of camelCase (for example, `root-params` instead of `rootParams`) ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information ⚠️ This is my first PR for ionic so I hope I didn't miss important steps into the process. I also checked on my project that the fix is working well. Thank you! 🙂 --------- Co-authored-by: Sean Perkins --- packages/vue/src/components/IonNav.ts | 35 ++++++++++++++- packages/vue/test/base/src/components/Nav.vue | 18 +++----- .../vue/test/base/src/components/NavRoot.vue | 45 +++++++++---------- .../base/tests/e2e/specs/navigation.cy.js | 6 +++ 4 files changed, 66 insertions(+), 38 deletions(-) diff --git a/packages/vue/src/components/IonNav.ts b/packages/vue/src/components/IonNav.ts index 437734d159..47124a19ff 100644 --- a/packages/vue/src/components/IonNav.ts +++ b/packages/vue/src/components/IonNav.ts @@ -4,7 +4,7 @@ import { defineComponent, h, shallowRef } from "vue"; import { VueDelegate } from "../framework-delegate"; -export const IonNav = /*@__PURE__*/ defineComponent(() => { +export const IonNav = /*@__PURE__*/ defineComponent((props) => { defineCustomElement(); const views = shallowRef([]); @@ -19,8 +19,39 @@ export const IonNav = /*@__PURE__*/ defineComponent(() => { const delegate = VueDelegate(addView, removeView); return () => { - return h("ion-nav", { delegate }, views.value); + return h("ion-nav", { ...props, delegate }, views.value); }; }); IonNav.name = "IonNav"; + +/** + * The default values follow what is defined at + * https://ionicframework.com/docs/api/nav#properties + * otherwise the default values on the Web Component + * may be overridden. For example, if the default animated value + * is not `true` below, then Vue would default the prop to `false` + * which would override the Web Component default of `true`. + */ +IonNav.props = { + animated: { + type: Boolean, + default: true, + }, + animation: { + type: Function, + default: undefined, + }, + root: { + type: [Function, Object, String], + default: undefined, + }, + rootParams: { + type: Object, + default: undefined, + }, + swipeGesture: { + type: Boolean, + default: undefined, + }, +}; diff --git a/packages/vue/test/base/src/components/Nav.vue b/packages/vue/test/base/src/components/Nav.vue index f4ca025094..69dc6d7175 100644 --- a/packages/vue/test/base/src/components/Nav.vue +++ b/packages/vue/test/base/src/components/Nav.vue @@ -1,16 +1,12 @@ - diff --git a/packages/vue/test/base/src/components/NavRoot.vue b/packages/vue/test/base/src/components/NavRoot.vue index ec2fe1c513..ffd18e6765 100644 --- a/packages/vue/test/base/src/components/NavRoot.vue +++ b/packages/vue/test/base/src/components/NavRoot.vue @@ -8,11 +8,14 @@ - Go to Nav Child + + Go to Nav Child - diff --git a/packages/vue/test/base/tests/e2e/specs/navigation.cy.js b/packages/vue/test/base/tests/e2e/specs/navigation.cy.js index d8f75e7163..25788bf759 100644 --- a/packages/vue/test/base/tests/e2e/specs/navigation.cy.js +++ b/packages/vue/test/base/tests/e2e/specs/navigation.cy.js @@ -10,4 +10,10 @@ describe('Navigation', () => { cy.get('#nav-child-content').should('have.text', 'Custom Title'); }); + + it('nav should support kebab-case root-params', () => { + cy.get('#open-nav-modal').click(); + + cy.get('#nav-root-params').should('have.text', 'Message: Hello World!'); + }); }); From 1705d064cc041e99f432a27207f3aab7fa62c778 Mon Sep 17 00:00:00 2001 From: Sean Perkins <13732623+sean-perkins@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:11:41 -0500 Subject: [PATCH 3/6] fix(react): router creates new view instances of parameterized routes (#28616) Issue number: Resolves #26524 --------- ## Definitions **Parameterized routes**: A route that includes one or more variables in the path segments, such as `/form/:index`. ## What is the current behavior? When an application routes from a parameterized route, to an intermediary route, to the same parameterized route, but with a different value/url, Ionic's routing logic is incorrectly reusing the view item from the first instance of the parameterized route instead of calculating that the matched path is different. This results in the wrong view item being recycled and rendered. Another way of representing it: - User navigates to `/form/0` which resolves `FormPage` - User enters `0` into the form and submits the form - User navigates to `/link`, which resolves `LinkPage` - User navigates to `/form/1`, which resolves `FormPage` - However, instead of creating a new instance of `FormPage` it is reusing the instance of `FormPage` from `/form/0` which includes the form having `0` in the input. - The user now sees a "new view", but with cached data in the form. This is not expected or desired. ## What is the new behavior? - Ionic's routing logic will validate if the entering view item matches the match route data before reusing it. This results in new instances of the view item being constructed when using parameterized routes. https://github.com/ionic-team/ionic-framework/assets/13732623/e7e3d03f-2848-4429-9f60-9074d0761e45 ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information Dev-build: `7.5.8-dev.11701383555.17254408` --- .../src/ReactRouter/ReactRouterViewStack.tsx | 65 ++++++++++--------- .../src/ReactRouter/StackManager.tsx | 27 ++++---- .../src/ReactRouter/utils/matchPath.ts | 47 ++++++++++++++ .../test/base/src/pages/routing/Details.tsx | 7 +- .../test/base/tests/e2e/specs/routing.cy.js | 35 ++++++++++ 5 files changed, 131 insertions(+), 50 deletions(-) create mode 100644 packages/react-router/src/ReactRouter/utils/matchPath.ts diff --git a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx index fa9e002102..9203679020 100644 --- a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx +++ b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx @@ -1,7 +1,8 @@ import type { RouteInfo, ViewItem } from '@ionic/react'; import { IonRoute, ViewLifeCycleManager, ViewStacks, generateId } from '@ionic/react'; import React from 'react'; -import { matchPath } from 'react-router'; + +import { matchPath } from './utils/matchPath'; export class ReactRouterViewStack extends ViewStacks { constructor() { @@ -23,21 +24,16 @@ export class ReactRouterViewStack extends ViewStacks { ionRoute: false, }; - const matchProps = { - exact: reactElement.props.exact, - path: reactElement.props.path || reactElement.props.from, - component: reactElement.props.component, - }; - - const match = matchPath(routeInfo.pathname, matchProps); - if (reactElement.type === IonRoute) { viewItem.ionRoute = true; viewItem.disableIonPageManagement = reactElement.props.disableIonPageManagement; } viewItem.routeData = { - match, + match: matchPath({ + pathname: routeInfo.pathname, + componentProps: reactElement.props, + }), childProps: reactElement.props, }; @@ -106,7 +102,7 @@ export class ReactRouterViewStack extends ViewStacks { } findLeavingViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string, mustBeIonRoute = true) { - const { viewItem } = this.findViewItemByPath(routeInfo.lastPathname!, outletId, false, mustBeIonRoute); + const { viewItem } = this.findViewItemByPath(routeInfo.lastPathname!, outletId, mustBeIonRoute); return viewItem; } @@ -115,7 +111,10 @@ export class ReactRouterViewStack extends ViewStacks { return viewItem; } - private findViewItemByPath(pathname: string, outletId?: string, forceExact?: boolean, mustBeIonRoute?: boolean) { + /** + * Returns the matching view item and the match result for a given pathname. + */ + private findViewItemByPath(pathname: string, outletId?: string, mustBeIonRoute?: boolean) { let viewItem: ViewItem | undefined; let match: ReturnType | undefined; let viewStack: ViewItem[]; @@ -140,16 +139,24 @@ export class ReactRouterViewStack extends ViewStacks { if (mustBeIonRoute && !v.ionRoute) { return false; } - const matchProps = { - exact: forceExact ? true : v.routeData.childProps.exact, - path: v.routeData.childProps.path || v.routeData.childProps.from, - component: v.routeData.childProps.component, - }; - const myMatch = matchPath(pathname, matchProps); - if (myMatch) { - viewItem = v; - match = myMatch; - return true; + + match = matchPath({ + pathname, + componentProps: v.routeData.childProps, + }); + + if (match) { + /** + * Even though we have a match from react-router, we do not know if the match + * is for this specific view item. + * + * To validate this, we need to check if the path and url match the view item's route data. + */ + const hasParameter = match.path.includes(':'); + if (!hasParameter || (hasParameter && match.url === v.routeData?.match?.url)) { + viewItem = v; + return true; + } } return false; } @@ -171,13 +178,9 @@ export class ReactRouterViewStack extends ViewStacks { } } -function matchComponent(node: React.ReactElement, pathname: string, forceExact?: boolean) { - const matchProps = { - exact: forceExact ? true : node.props.exact, - path: node.props.path || node.props.from, - component: node.props.component, - }; - const match = matchPath(pathname, matchProps); - - return match; +function matchComponent(node: React.ReactElement, pathname: string) { + return matchPath({ + pathname, + componentProps: node.props, + }); } diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 1acfd48468..708de65139 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -1,9 +1,9 @@ import type { RouteInfo, StackContextState, ViewItem } from '@ionic/react'; import { RouteManagerContext, StackContext, generateId, getConfig } from '@ionic/react'; import React from 'react'; -import { matchPath } from 'react-router-dom'; import { clonePageElement } from './clonePageElement'; +import { matchPath } from './utils/matchPath'; // TODO(FW-2959): types @@ -433,12 +433,10 @@ export default StackManager; function matchRoute(node: React.ReactNode, routeInfo: RouteInfo) { let matchedNode: React.ReactNode; React.Children.forEach(node as React.ReactElement, (child: React.ReactElement) => { - const matchProps = { - exact: child.props.exact, - path: child.props.path || child.props.from, - component: child.props.component, - }; - const match = matchPath(routeInfo.pathname, matchProps); + const match = matchPath({ + pathname: routeInfo.pathname, + componentProps: child.props, + }); if (match) { matchedNode = child; } @@ -459,12 +457,11 @@ function matchRoute(node: React.ReactNode, routeInfo: RouteInfo) { } function matchComponent(node: React.ReactElement, pathname: string, forceExact?: boolean) { - const matchProps = { - exact: forceExact ? true : node.props.exact, - path: node.props.path || node.props.from, - component: node.props.component, - }; - const match = matchPath(pathname, matchProps); - - return match; + return matchPath({ + pathname, + componentProps: { + ...node.props, + exact: forceExact, + }, + }); } diff --git a/packages/react-router/src/ReactRouter/utils/matchPath.ts b/packages/react-router/src/ReactRouter/utils/matchPath.ts new file mode 100644 index 0000000000..891eda08bb --- /dev/null +++ b/packages/react-router/src/ReactRouter/utils/matchPath.ts @@ -0,0 +1,47 @@ +import { matchPath as reactRouterMatchPath } from 'react-router'; + +interface MatchPathOptions { + /** + * The pathname to match against. + */ + pathname: string; + /** + * The props to match against, they are identical to the matching props `Route` accepts. + */ + componentProps: { + path?: string; + from?: string; + component?: any; + exact?: boolean; + }; +} + +/** + * @see https://v5.reactrouter.com/web/api/matchPath + */ +export const matchPath = ({ + pathname, + componentProps, +}: MatchPathOptions): false | ReturnType => { + const { exact, component } = componentProps; + + const path = componentProps.path || componentProps.from; + /*** + * The props to match against, they are identical + * to the matching props `Route` accepts. It could also be a string + * or an array of strings as shortcut for `{ path }`. + */ + const matchProps = { + exact, + path, + component, + }; + + const match = reactRouterMatchPath(pathname, matchProps); + + if (!match) { + return false; + } + + return match; +}; diff --git a/packages/react-router/test/base/src/pages/routing/Details.tsx b/packages/react-router/test/base/src/pages/routing/Details.tsx index 17905911a4..94aeeb6dba 100644 --- a/packages/react-router/test/base/src/pages/routing/Details.tsx +++ b/packages/react-router/test/base/src/pages/routing/Details.tsx @@ -24,10 +24,6 @@ const Details: React.FC = () => { return () => console.log('Home Details unmount'); }, []); - // useIonViewWillEnter(() => { - // console.log('IVWE Details') - // }) - const nextId = parseInt(id, 10) + 1; return ( @@ -58,6 +54,9 @@ const Details: React.FC = () => { Go to Settings Details 1 +
+
+ ); diff --git a/packages/react-router/test/base/tests/e2e/specs/routing.cy.js b/packages/react-router/test/base/tests/e2e/specs/routing.cy.js index 950a27d157..1289bb3065 100644 --- a/packages/react-router/test/base/tests/e2e/specs/routing.cy.js +++ b/packages/react-router/test/base/tests/e2e/specs/routing.cy.js @@ -309,6 +309,41 @@ describe('Routing Tests', () => { cy.ionPageDoesNotExist('home-details-page-1'); cy.ionPageVisible('home-page'); }); + + it('should mount new view item instances of parameterized routes', () => { + cy.visit(`http://localhost:${port}/routing/tabs/home/details/1`); + + cy.get('div.ion-page[data-pageid=home-details-page-1]') + .get('[data-testid="details-input"]') + .should('have.value', ''); + + cy.get('div.ion-page[data-pageid=home-details-page-1] [data-testid="details-input"]').type('1'); + + cy.ionNav('ion-button', 'Go to Details 2'); + cy.ionPageVisible('home-details-page-2'); + + cy.get('div.ion-page[data-pageid=home-details-page-2] [data-testid="details-input"]').should('have.value', ''); + + cy.get('div.ion-page[data-pageid=home-details-page-2] [data-testid="details-input"]').type('2'); + + cy.ionNav('ion-button', 'Go to Details 3'); + cy.ionPageVisible('home-details-page-3'); + + cy.get('div.ion-page[data-pageid=home-details-page-3] [data-testid="details-input"]').should('have.value', ''); + + cy.get('div.ion-page[data-pageid=home-details-page-3] [data-testid="details-input"]').type('3'); + + cy.ionBackClick('home-details-page-3'); + cy.ionPageVisible('home-details-page-2'); + + cy.get('div.ion-page[data-pageid=home-details-page-2] [data-testid="details-input"]').should('have.value', '2'); + + cy.ionBackClick('home-details-page-2'); + cy.ionPageVisible('home-details-page-1'); + + cy.get('div.ion-page[data-pageid=home-details-page-1] [data-testid="details-input"]').should('have.value', '1'); + }); + /* Tests to add: Test that lifecycle events fire From fe3c3d500a2afd716ebde81a75b357b7c79d4920 Mon Sep 17 00:00:00 2001 From: Shawn Taylor Date: Mon, 4 Dec 2023 08:24:34 -0500 Subject: [PATCH 4/6] docs(input, searchbar, textarea): improve docs for managing focus (#28614) Issue number: Related to #18132 --------- ## What is the current behavior? The documentation about the `autofocus` prop is unclear and does not accurately reflect how it actually works across browsers and devices. ## What is the new behavior? - The documentation for `autofocus` and `setFocus` are more detailed. - The documentation links to the relevant page in the docs. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information --- core/src/components.d.ts | 14 +++++++------- core/src/components/input/input.tsx | 6 +++++- core/src/components/searchbar/searchbar.tsx | 2 ++ core/src/components/textarea/textarea.tsx | 6 +++++- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 14d14454de..885ff656c6 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1162,7 +1162,7 @@ export namespace Components { */ "autocorrect": 'on' | 'off'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus": boolean; /** @@ -1274,7 +1274,7 @@ export namespace Components { */ "required": boolean; /** - * Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. + * Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information. */ "setFocus": () => Promise; /** @@ -2601,7 +2601,7 @@ export namespace Components { */ "searchIcon"?: string; /** - * Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. + * Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information. */ "setFocus": () => Promise; /** @@ -2950,7 +2950,7 @@ export namespace Components { */ "autocapitalize": string; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus": boolean; /** @@ -3050,7 +3050,7 @@ export namespace Components { */ "rows"?: number; /** - * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`. + * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`. See [managing focus](/docs/developing/managing-focus) for more information. */ "setFocus": () => Promise; /** @@ -5854,7 +5854,7 @@ declare namespace LocalJSX { */ "autocorrect"?: 'on' | 'off'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus"?: boolean; /** @@ -7699,7 +7699,7 @@ declare namespace LocalJSX { */ "autocapitalize"?: string; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus"?: boolean; /** diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 1c54046718..92bf5fdeff 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -95,7 +95,9 @@ export class Input implements ComponentInterface { @Prop() autocorrect: 'on' | 'off' = 'off'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. + * + * This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ @Prop() autofocus = false; @@ -424,6 +426,8 @@ export class Input implements ComponentInterface { * * Developers who wish to focus an input when an overlay is presented * should call `setFocus` after `didPresent` has resolved. + * + * See [managing focus](/docs/developing/managing-focus) for more information. */ @Method() async setFocus() { diff --git a/core/src/components/searchbar/searchbar.tsx b/core/src/components/searchbar/searchbar.tsx index 84ae9b2acc..21fed733d2 100644 --- a/core/src/components/searchbar/searchbar.tsx +++ b/core/src/components/searchbar/searchbar.tsx @@ -257,6 +257,8 @@ export class Searchbar implements ComponentInterface { * * Developers who wish to focus an input when an overlay is presented * should call `setFocus` after `didPresent` has resolved. + * + * See [managing focus](/docs/developing/managing-focus) for more information. */ @Method() async setFocus() { diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx index 6159f64790..31ec5eb616 100644 --- a/core/src/components/textarea/textarea.tsx +++ b/core/src/components/textarea/textarea.tsx @@ -93,7 +93,9 @@ export class Textarea implements ComponentInterface { @Prop() autocapitalize = 'none'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. + * + * This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ @Prop() autofocus = false; @@ -372,6 +374,8 @@ export class Textarea implements ComponentInterface { /** * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global * `textarea.focus()`. + * + * See [managing focus](/docs/developing/managing-focus) for more information. */ @Method() async setFocus() { From 48474880903774d4e3efcb332a95d4c5ea470ea9 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Mon, 4 Dec 2023 16:31:12 -0500 Subject: [PATCH 5/6] chore(ci): update labeler workflow for labeler@v5 (#28627) Issue number: N/A --------- ## What is the current behavior? The labeler action is currently failing: https://github.com/ionic-team/ionic-framework/actions/runs/7090913880/job/19298918578?pr=28622. This is happening due to a breaking change in v5 of the action. We currently pull from `main` for this action so we are now receiving v5 of the action: https://github.com/ionic-team/ionic-framework/blob/fe3c3d500a2afd716ebde81a75b357b7c79d4920/.github/workflows/label.yml#L16 ## What is the new behavior? - This PR updates the action explicitly to v5 so we don't unexpectedly take on breaking changes - This PR also updates the labeler yaml file to account for the v5 breaking changes ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information --- .github/labeler.yml | 15 ++++++++------- .github/workflows/label.yml | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 545025db58..7ea70aaf7f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -6,16 +6,17 @@ # https://github.com/actions/labeler 'package: core': - - core/**/* +- changed-files: + - any-glob-to-any-file: ['core/**/*'] 'package: angular': - - packages/angular/**/* - - packages/angular-*/**/* +- changed-files: + - any-glob-to-any-file: ['packages/angular/**/*', 'packages/angular-*/**/*'] 'package: react': - - packages/react/**/* - - packages/react-*/**/* +- changed-files: + - any-glob-to-any-file: ['packages/react/**/*', 'packages/react-*/**/*'] 'package: vue': - - packages/vue/**/* - - packages/vue-*/**/* +- changed-files: + - any-glob-to-any-file: ['packages/vue/**/*', 'packages/vue-*/**/*'] diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 0241c7fa5b..a4e35060df 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -13,7 +13,7 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: actions/labeler@main + - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true From e2cbc9c15fbc1c24993267cb0acb56db69cac8ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 19:58:11 -0500 Subject: [PATCH 6/6] chore(deps): Bump @stencil/core from 4.8.0 to 4.8.1 in /core (#28632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [@stencil/core](https://github.com/ionic-team/stencil) from 4.8.0 to 4.8.1.
Release notes

Sourced from @​stencil/core's releases.

🍹 v4.8.1 (2023-12-04)

Bug Fixes

  • runtime: apply nonce to data styles before DOM insert (#5112) (df46fdc), closes #5102
  • runtime: call form-associated lifecycle callbacks w/ this (#5104) (1ac8aa3)
  • testing: re-add Puppeteer asElement() calls (#5114) (0c843f8), closes #5113
Changelog

Sourced from @​stencil/core's changelog.

🍹 4.8.1 (2023-12-04)

Bug Fixes

  • runtime: apply nonce to data styles before DOM insert (#5112) (df46fdc), closes #5102
  • runtime: call form-associated lifecycle callbacks w/ this (#5104) (1ac8aa3)
  • testing: re-add Puppeteer asElement() calls (#5114) (0c843f8), closes #5113
Commits
  • 28f794e Release v4.8.1 (#5130)
  • 2fd398a chore(deps): update dependency @​rollup/pluginutils to v5.1.0 (#5126)
  • 84feb5a chore(deps): update typescript-eslint to v6.13.1 (#5128)
  • cf4a701 fix(compiler): revert changes to style import transformer (#5125)
  • df32ceb chore(deps): update dependency fs-extra to v11.2.0 (#5127)
  • 4cdb725 chore(deps): update dependency dts-bundle-generator to v9 (#5129)
  • 3ef8fa0 chore(deps): update dependency @​types/eslint to v8.44.8 (#5122)
  • 1342eec chore(deps): update dependency @​types/node to v20.10.1 (#5123)
  • 7591dce fix(compiler): ensure not to import duplicate style identifier (#5119)
  • 7302e3f chore(deps): update node.js to v20.10.0 (#5117)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@stencil/core&package-manager=npm_and_yarn&previous-version=4.8.0&new-version=4.8.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/package-lock.json | 14 +++++++------- core/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/package-lock.json b/core/package-lock.json index 08c47b5014..256d5876d6 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -9,7 +9,7 @@ "version": "7.5.7", "license": "MIT", "dependencies": { - "@stencil/core": "^4.8.0", + "@stencil/core": "^4.8.1", "ionicons": "^7.2.1", "tslib": "^2.1.0" }, @@ -1825,9 +1825,9 @@ } }, "node_modules/@stencil/core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.0.tgz", - "integrity": "sha512-iNEaMiEt9oFZXSjZ7qkdlBpPTzWzBgWM7go8nI8a7V6ZOkmdVhhT0xGQD7OY13v5ZuDoIw5IsGvbIAGefhIhhQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.1.tgz", + "integrity": "sha512-KG1H10j24rlyxIqOI4CG8/h9T7ObTv7giW2H3u1qXV4KKrLykDOpMcLzpqNXqL2Fki3s1QvHyl/oaRvi5waWVw==", "bin": { "stencil": "bin/stencil" }, @@ -12184,9 +12184,9 @@ "requires": {} }, "@stencil/core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.0.tgz", - "integrity": "sha512-iNEaMiEt9oFZXSjZ7qkdlBpPTzWzBgWM7go8nI8a7V6ZOkmdVhhT0xGQD7OY13v5ZuDoIw5IsGvbIAGefhIhhQ==" + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.1.tgz", + "integrity": "sha512-KG1H10j24rlyxIqOI4CG8/h9T7ObTv7giW2H3u1qXV4KKrLykDOpMcLzpqNXqL2Fki3s1QvHyl/oaRvi5waWVw==" }, "@stencil/react-output-target": { "version": "0.5.3", diff --git a/core/package.json b/core/package.json index df64eb1499..79e803f515 100644 --- a/core/package.json +++ b/core/package.json @@ -31,7 +31,7 @@ "loader/" ], "dependencies": { - "@stencil/core": "^4.8.0", + "@stencil/core": "^4.8.1", "ionicons": "^7.2.1", "tslib": "^2.1.0" },