Compare commits

..

84 Commits

Author SHA1 Message Date
Liam DeBeasi
f3fb09ab44 merge release-5.9.4
5.9.4
2022-04-27 11:54:50 -04:00
Liam DeBeasi
7947a26ba4 5.9.4 2022-04-27 11:40:57 -04:00
Sean Perkins
ffb056d50e fix(core): inherit aria attributes on host elements (#25156) (#25169) 2022-04-27 10:59:58 -04:00
Liam DeBeasi
2d9724947d merge release-5.9.3
Release 5.9.3
2021-12-15 10:29:24 -05:00
Liam DeBeasi
02ef5ae179 5.9.3 2021-12-15 10:10:07 -05:00
Sean Perkins
4aab72b061 fix(vue): tabs no longer get unmounted when navigating back to a tabs context (#24337)
resolves #24332

Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
2021-12-15 09:55:29 -05:00
Liam DeBeasi
9c9e28ccc9 perf(content): remove global click listener to improve interaction performance (#24360)
resolves #24359
2021-12-15 09:55:22 -05:00
Liam DeBeasi
1c2875044a fix(vue): improve query params handling in tabs (#24355)
resolves #24353
2021-12-15 09:55:05 -05:00
Liam DeBeasi
d665ace5c4 chore(): create 5.9.x history
chore(): create 5.9.x history
2021-12-08 09:06:27 -05:00
Liam DeBeasi
672ab80807 merge release-5.9.2
5.9.2
2021-12-07 09:43:26 -05:00
Liam DeBeasi
da3b93b4a2 5.9.2 2021-12-07 09:21:00 -05:00
Sean Perkins
5e5054d369 fix(router): popping route now accounts for route params (#24315) 2021-12-06 17:25:40 -05:00
Anant Sharma
8f188eaae7 fix(react): properly check for custom elements to avoid errors in unit tests (#24156)
resolves #24149
2021-12-06 17:06:17 -05:00
Amanda Smith
f6a00ea954 fix(popover): handle scrolling in content so header can be sticky (#24294) 2021-12-06 10:45:37 -06:00
Djakson
b083ae4e58 docs(): fix typo in popover docs (#24318) 2021-12-06 10:26:03 -05:00
Sean Perkins
f7bd4c02c3 docs(back-button): update icon prop to include src and name approach (#24307) 2021-12-03 12:52:42 -05:00
Liam DeBeasi
047d3c7772 fix(vue): switching between tabs preserves query string (#24297)
resolves #23699
2021-12-02 10:04:26 -05:00
Sean Perkins
4b26feaa47 fix(react): present and dismiss hooks return promises (#24299) 2021-12-01 15:16:55 -05:00
Amanda Smith
e41b0e0cf2 fix(content): ensure fixed slot renders on top of content in iOS (#24300) 2021-12-01 13:47:39 -06:00
Will Martin
7f61b06895 build(commitizen): do not run on main, next, or release branches (#24258) 2021-11-25 09:17:23 -05:00
Sean Perkins
89e4bc56a1 fix(slides): update swiper instance after initialization (#24257) 2021-11-24 11:52:48 -05:00
Yuhongjie
fb96ab5a26 fix(vue): ionic lifecycle hooks now run when using vue 3.2 setup syntax (#24253)
resolves #23824

Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
2021-11-23 17:04:24 -05:00
Liam DeBeasi
6f01c3b73d chore(): update vue test app (#24262) 2021-11-23 16:35:54 -05:00
Ryan Waskiewicz
07d83ccd24 chore(ci): fix typo for test-vue-e2e step (#24256) 2021-11-23 14:28:58 -05:00
Sean Perkins
816096f897 fix(angular): strict type usage (#24221) 2021-11-22 15:59:12 -05:00
Liam DeBeasi
615dcc0461 merge release-5.9.1
5.9.1
2021-11-17 12:01:48 -05:00
Liam DeBeasi
c2603e9cff 5.9.1 2021-11-17 11:48:52 -05:00
Liam DeBeasi
1d983fa4b3 fix(angular): build is now in correct directory (#24236)
* fix(): revert ded build changes

* fix(); revert stuff

* chore(): revert dev build

* chore(): fix merge
2021-11-17 11:40:56 -05:00
Liam DeBeasi
23b4fc5385 merge release-5.9.0
5.9.0
2021-11-17 10:29:56 -05:00
Liam DeBeasi
11425fbb35 build(): ensure react router gets latest version of core 2021-11-17 15:16:22 +00:00
Liam DeBeasi
2d70427608 build(): publish to prod but not dev builds (#24232) 2021-11-17 09:47:24 -05:00
Liam DeBeasi
bd8c3e881f 5.9.0 2021-11-17 09:27:51 -05:00
Sean Perkins
0566ec0da3 fix(input): date type in ion-input now aligns correctly on iOS 15 (#24217) 2021-11-12 15:13:37 -05:00
Sean Perkins
b2f2a4b71b docs(): docs reference main instead of master (#24216) 2021-11-12 14:31:18 -05:00
Liam DeBeasi
64dd070d67 build(dev-build): correctly pass secrets to publish task (#24194) 2021-11-10 12:52:00 -05:00
Liam DeBeasi
928dfa52ca build(dev-build): add GitHub actions for dev builds (#24193) 2021-11-09 13:25:22 -05:00
Liam DeBeasi
d0b61307c6 feat(slides): add support for Swiper 7 (#24190) 2021-11-09 12:50:56 -05:00
Amanda Smith
579d11824d chore(ionitron): remove reference to old Slack channel (#24189) 2021-11-09 09:18:20 -06:00
Liam DeBeasi
7c43589b0a fix(vue): canGoBack method now works correctly (#24188)
resolves #24109
2021-11-09 09:45:12 -05:00
Liam DeBeasi
642255e514 fix(action-sheet): safe area is now accounted for in MD mode (#24176)
resolves #24175
2021-11-08 08:58:20 -05:00
Matt Netkow
2485b303da docs(virtual-scroll): fix url reference paths (#24172) 2021-11-05 12:28:21 -04:00
Liam DeBeasi
9e2373e15e docs(github-actions): do not run screenshot test on forks (#24164) 2021-11-04 16:08:21 -04:00
Liam DeBeasi
e8fe117c34 docs(): update table of contents for contributing guide (#24148) 2021-11-01 17:54:25 -04:00
Liam DeBeasi
811a34493d docs(react): fix infinite scroll example to remove extra imports (#24147) 2021-11-01 17:47:07 -04:00
Liam DeBeasi
5bcb39f82b docs(): update contributing guide for angular, react, and vue (#24141)
resolves #24137
2021-11-01 17:37:48 -04:00
Liam DeBeasi
22be16f3f0 test(): allow core to use cached node modules (#24146) 2021-11-01 17:31:26 -04:00
Liam DeBeasi
fd59acd691 test(): add way of manually busting node module cache (#24145) 2021-11-01 17:00:25 -04:00
Liam DeBeasi
b5dbb3984c test(): migrate tests to GitHub Actions (#24029) 2021-11-01 15:41:00 -04:00
Liam DeBeasi
794bfbbc57 docs(react): add examples for infinite scroll (#24130) 2021-10-28 16:23:26 -04:00
Liam DeBeasi
351c30ce42 merge release-5.8.5
Release 5.8.5
2021-10-27 09:15:12 -04:00
Liam DeBeasi
8605c467d1 merge release-5.8.5
5.8.5
2021-10-27 09:14:49 -04:00
Liam DeBeasi
1b0669eebe 5.8.5 2021-10-27 09:01:14 -04:00
Amanda Smith
bdb268aa12 fix(menu): added focus trapping, improved compatibility with screen readers (#24076)
* fix(menu): add basic accessibility features

* fix(menu): add focus trapping

* test(menu): add test for focus trapping

* style(menu): lint fixes

* fix(menu): focus first element inside instead of whole menu

* test(menu): fix focus trap test to account for new behavior

* refactor(menu): pull focus handler into its own prop

* test(menu): add a11y testing

* fix(menu): prevent nested aria landmark from header inside menu

* fix(menu): revert switch to nav element

* fix(menu): remove unnecessary import from test

* fix(menu): allow for custom aria-label

* fix(menu): move nested ARIA role logic to header for flexibility

* fix(item): only add focusable class if it actually is focusable

* fix(menu): allow focusing of menu itself, for a11y on menus with no focusable children

* fix(item): move isFocusable logic to state for better reactivity

* perf(item): only grab one focusable child

* fix(menu): hide page content from screen readers when menu is open

* fix(menu): fallback to focusing host element

* docs(menu): add comments

Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
2021-10-27 08:51:46 -04:00
Dmitriy Fishman
2916810a2b docs(): fix a typo in the readme (#24087) 2021-10-19 10:08:22 -04:00
Liam DeBeasi
24659a527a fix(vue): mount correct views when navigating (#24056)
resolves #23914
2021-10-14 11:28:36 -04:00
Liam DeBeasi
a09d7d4ab6 fix(vue): back button now selects correct route when navigating from view multiple times (#24060)
resolves #23987
2021-10-13 09:07:53 -04:00
Ely Lucas
ea34e50430 test(react): add react e2e app 2021-10-12 15:10:03 -06:00
Liam DeBeasi
4075ea6941 merge release-5.8.4
5.8.4
2021-10-11 12:09:01 -04:00
Liam DeBeasi
2a2829c17e 5.8.4 2021-10-11 11:31:16 -04:00
Liam DeBeasi
e001f24f2c fix(angular): setup config properly (#24054)
resolves #24051 #24052
2021-10-11 11:22:08 -04:00
Amanda Smith
68a7e43345 fix(back-button): pass aria-label to native element (#24027) 2021-10-08 12:08:32 -05:00
Liam DeBeasi
e92fecb08c merge release-5.8.3
Release 5.8.3
2021-10-08 13:03:04 -04:00
Liam DeBeasi
a63474a621 5.8.3 2021-10-07 16:50:46 -04:00
Liam DeBeasi
f98151c5d1 5.8.3 2021-10-07 20:50:28 +00:00
Ely Lucas
655631ddf0 fix(react): fix to regression in overlay hooks dimiss method (#24038)
resolves #24030
2021-10-07 15:18:10 -04:00
Liam DeBeasi
3b9b9082b8 merge release-5.8.2
Release 5.8.2
2021-10-06 10:24:24 -04:00
Liam DeBeasi
11332e446c merge release-5.8.2
5.8.2
2021-10-06 10:24:14 -04:00
Liam DeBeasi
0774cca2cd merge release-5.8.1
Release 5.8.1
2021-09-22 10:48:57 -04:00
Liam DeBeasi
6c366aaf87 merge release-5.8.0
Release 5.8.0
2021-09-15 11:37:12 -04:00
Liam DeBeasi
6876fd089f merge release-5.7.0
Release 5.7.0
2021-09-01 10:07:42 -04:00
Liam DeBeasi
22a8842ac2 merge release-5.6.14
Release 5.6.14
2021-08-18 09:33:30 -04:00
Liam DeBeasi
2d5faa75db merge release-5.6.13
Release 5.6.13
2021-08-04 10:25:37 -04:00
Liam DeBeasi
cab2a5103f merge release-5.6.12
Release 5.6.12
2021-07-21 09:37:04 -04:00
Liam DeBeasi
d36050918a merge release-5.6.11
Release 5.6.11
2021-07-01 12:03:38 -04:00
Liam DeBeasi
64f128be07 merge release-5.6.10
Release 5.6.10
2021-06-22 10:01:58 -04:00
Liam DeBeasi
87999e3c7a merge release-5.6.9
Release 5.6.9
2021-06-08 09:38:37 -04:00
Liam DeBeasi
bb4554211d merge release-5.6.8
Release 5.6.8
2021-05-27 16:01:36 -04:00
Liam DeBeasi
f71109b088 merge release-5.6.7
Release 5.6.7
2021-05-13 10:00:26 -04:00
Liam DeBeasi
44e18bd795 merge release-5.6.6
Release 5.6.6
2021-04-29 10:31:45 -04:00
Liam DeBeasi
f4d265eb60 merge release-5.6.5
Release 5.6.5
2021-04-22 13:37:44 -04:00
Liam DeBeasi
1e8dfb7d85 merge release-5.6.4
Release 5.6.4
2021-04-08 13:14:26 -04:00
Liam DeBeasi
9f023c92c4 merge release-5.6.3
Release 5.6.3
2021-03-23 11:21:16 -04:00
Liam DeBeasi
694d47b794 merge release-5.6.2
Release 5.6.2
2021-03-22 17:07:08 -04:00
Liam DeBeasi
b87c555a6e merge release-5.6.1
Release 5.6.1
2021-03-18 09:36:55 -04:00
206 changed files with 71179 additions and 23928 deletions

View File

@@ -1,565 +0,0 @@
version: 2.1
orbs:
cypress: cypress-io/cypress@1.27.0
aliases:
- &restore-cache
keys:
- dependency-cache-{{ checksum "package.json" }}-7
- &save-cache
key: dependency-cache-{{ checksum "package.json" }}-7
paths:
- node_modules
- &restore-cache-core
keys:
- dependency-cache-{{ checksum "core/package.json" }}-7
- &save-cache-core
key: dependency-cache-{{ checksum "core/package.json" }}-7
paths:
- core/node_modules
- &restore-cache-core-stencil
keys:
- stencil-cache-6
- &save-cache-core-stencil
key: stencil-cache-6
paths:
- core/.stencil
- core/screenshot/images
defaults: &defaults
docker:
- image: circleci/node:latest-browsers
working_directory: /tmp/workspace
environment:
NODE_ENV: development
jobs:
puppeteer-dependencies:
<<: *defaults
steps:
- run:
name: Install headless Chrome dependencies
command: |
sudo apt-get install -yq \
gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 \
libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates \
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
- run:
name: Install Puppeteer with Chromium
command: |
npm i puppeteer
build:
<<: *defaults
steps:
- checkout
- restore_cache: *restore-cache
- run: npm install --legacy-peer-deps
- save_cache: *save-cache
- persist_to_workspace:
root: /tmp/workspace
paths:
- node_modules
build-core:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- restore_cache: *restore-cache-core
- restore_cache: *restore-cache-core-stencil
- run:
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/core
- save_cache: *save-cache-core
- run:
command: npm run build -- --ci
working_directory: /tmp/workspace/core
- save_cache: *save-cache-core-stencil
- persist_to_workspace:
root: /tmp/workspace
paths:
- core/node_modules
- core/dist
- core/components
- core/css
- core/hydrate
- core/loader
build-angular:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/angular
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/angular
- run:
command: npm run build
working_directory: /tmp/workspace/angular
- persist_to_workspace:
root: /tmp/workspace
paths:
- angular/node_modules
- angular/dist
build-angular-server:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/packages/angular-server
- run:
command: npm run build.prod
working_directory: /tmp/workspace/packages/angular-server
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/angular-server/dist
build-react:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react
- run:
command: npm run build
working_directory: /tmp/workspace/packages/react
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/react/node_modules
- packages/react/dist
- packages/react/css
build-react-router:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/packages/react-router
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react-router
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link @ionic/react
working_directory: /tmp/workspace/packages/react-router
- run:
command: npm run build
working_directory: /tmp/workspace/packages/react-router
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/react-router/node_modules
- packages/react-router/dist
build-vue:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/vue
- run:
command: npm run build
working_directory: /tmp/workspace/packages/vue
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/vue/node_modules
- packages/vue/dist
- packages/vue/css
build-vue-router:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
working_directory: /tmp/workspace/packages/vue-router
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link @ionic/vue
working_directory: /tmp/workspace/packages/vue-router
- run:
command: npm run build
working_directory: /tmp/workspace/packages/vue-router
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/vue-router/dist
test-core-clean-build:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: Checking clean build
command: git diff --exit-code
working_directory: /tmp/workspace/core
test-core-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/core
test-core-e2e:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run test.e2e -- --ci --no-build
working_directory: /tmp/workspace/core
test-core-spec:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run test.spec -- --ci
working_directory: /tmp/workspace/core
test-core-treeshake:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run test.treeshake -- --ci
working_directory: /tmp/workspace/core
test-core-screenshot:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: Run Screenshot
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --no-build || true
working_directory: /tmp/workspace/core
test-core-screenshot-master:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: Run Screenshot
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --update-screenshot --no-build || true
working_directory: /tmp/workspace/core
test-angular-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/angular
test-react-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/react
test-react-router-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/react-router
test-vue-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/vue
test-vue-router-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/vue-router
test-react-spec:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react
- run:
command: npm run test.spec
working_directory: /tmp/workspace/packages/react
test-react-router-spec:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link @ionic/react
working_directory: /tmp/workspace/packages/react-router
- run:
command: npm run test.spec
working_directory: /tmp/workspace/packages/react-router
install-react-test-app:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/react-router/test-app npm install
working_directory: /tmp/workspace/packages/react-router/test-app
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/react-router/test-app
test-react-e2e:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run sync
working_directory: /tmp/workspace/packages/react-router/test-app
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/react-router/test-app npm run e2e
working_directory: /tmp/workspace/packages/react-router/test-app
test-vue-router-spec:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link @ionic/vue
working_directory: /tmp/workspace/packages/vue-router
- run:
command: npm run test.spec
working_directory: /tmp/workspace/packages/vue-router
test-angular-e2e:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/angular/test/test-app
- run:
command: npm run test
working_directory: /tmp/workspace/angular/test/test-app
install-vue-test-app:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/vue/test-app npm install
working_directory: /tmp/workspace/packages/vue/test-app
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/vue/test-app
test-vue-spec-and-e2e:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run sync
working_directory: /tmp/workspace/packages/vue/test-app
- run:
command: npm run test:unit
working_directory: /tmp/workspace/packages/vue/test-app
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/vue/test-app npm run test:e2e
working_directory: /tmp/workspace/packages/vue/test-app
workflows:
version: 2
build:
jobs:
- puppeteer-dependencies
- build
- build-core:
requires: [build]
- test-core-clean-build:
requires: [build-core]
- test-core-lint:
requires: [build-core]
- test-core-e2e:
requires: [puppeteer-dependencies, build-core]
- test-core-spec:
requires: [build-core]
# Adam requested we skip this test for now
# since it is failing on ES5 code which
# will be removed in Ionic Framework v6
#- test-core-treeshake:
# requires: [build-core]
- test-core-screenshot:
requires: [build-core]
filters:
branches:
ignore: master
- test-core-screenshot-master:
requires: [build-core]
filters:
branches:
only: master
- build-angular:
requires: [build-core]
- build-angular-server:
requires: [build-angular]
- build-react:
requires: [build-core]
- build-react-router:
requires: [build-core, build-react]
- test-react-lint:
requires: [build-react]
- test-react-router-lint:
requires: [build-react-router]
- test-react-spec:
requires: [build-react]
- test-react-router-spec:
requires: [build-react-router]
- install-react-test-app:
requires: [build-core]
- test-react-e2e:
requires: [install-react-test-app, build-react, build-react-router]
- build-vue:
requires: [build-core]
- build-vue-router:
requires: [build-vue]
- test-vue-lint:
requires: [build-vue]
- test-vue-router-lint:
requires: [build-vue-router]
- test-vue-router-spec:
requires: [build-vue-router]
- install-vue-test-app:
requires: [build-core]
- test-vue-spec-and-e2e:
requires: [install-vue-test-app, build-vue, build-vue-router]
- test-angular-lint:
requires: [build-angular]
- test-angular-e2e:
requires:
- build-angular
- build-angular-server

View File

@@ -356,9 +356,9 @@ ion-ripple-effect {
### Example Components
- [ion-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/button)
- [ion-back-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/back-button)
- [ion-menu-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/menu-button)
- [ion-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/button)
- [ion-back-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/back-button)
- [ion-menu-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/menu-button)
### References
@@ -372,7 +372,7 @@ ion-ripple-effect {
#### Example Components
- [ion-checkbox](https://github.com/ionic-team/ionic/tree/master/core/src/components/checkbox)
- [ion-checkbox](https://github.com/ionic-team/ionic/tree/main/core/src/components/checkbox)
#### VoiceOver
@@ -498,7 +498,7 @@ This is a compromise we have to make in order for it to work with the other scre
#### Example Components
- [ion-toggle](https://github.com/ionic-team/ionic/tree/master/core/src/components/toggle)
- [ion-toggle](https://github.com/ionic-team/ionic/tree/main/core/src/components/toggle)
#### Voiceover
@@ -631,11 +631,11 @@ Certain components can render an `<a>` or a `<button>` depending on the presence
### Example Components
- [ion-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/button)
- [ion-card](https://github.com/ionic-team/ionic/tree/master/core/src/components/card)
- [ion-fab-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/fab-button)
- [ion-item-option](https://github.com/ionic-team/ionic/tree/master/core/src/components/item-option)
- [ion-item](https://github.com/ionic-team/ionic/tree/master/core/src/components/item)
- [ion-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/button)
- [ion-card](https://github.com/ionic-team/ionic/tree/main/core/src/components/card)
- [ion-fab-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/fab-button)
- [ion-item-option](https://github.com/ionic-team/ionic/tree/main/core/src/components/item-option)
- [ion-item](https://github.com/ionic-team/ionic/tree/main/core/src/components/item)
### Component Structure

View File

@@ -15,6 +15,12 @@ Thanks for your interest in contributing to the Ionic Framework! :tada:
+ [Modifying Tests](#modifying-tests)
- [Screenshot Tests](#screenshot-tests)
+ [Building Changes](#building-changes)
* [Angular, React, and Vue](#angular-react-and-vue)
+ [Modifying Files](#modifying-files)
+ [Preview Changes](#preview-changes-1)
+ [Lint Changes](#lint-changes-1)
+ [Modifying Tests](#modifying-tests-1)
+ [Building Changes](#building-changes-1)
* [Submit Pull Request](#submit-pull-request)
- [Commit Message Guidelines](#commit-message-guidelines)
* [Commit Message Format](#commit-message-format)
@@ -30,7 +36,7 @@ Thanks for your interest in contributing to the Ionic Framework! :tada:
## Contributing Etiquette
Please see our [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/master/CODE_OF_CONDUCT.md) for information on our rules of conduct.
Please see our [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/main/CODE_OF_CONDUCT.md) for information on our rules of conduct.
## Creating an Issue
@@ -87,7 +93,7 @@ Without a reliable code reproduction, it is unlikely we will be able to resolve
1. [Download the installer](https://nodejs.org/) for the LTS version of Node.js. This is the best way to also [install npm](https://blog.npmjs.org/post/85484771375/how-to-install-npm#_=_).
2. Fork this repository.
3. Clone your fork.
4. Create a new branch from master for your change.
4. Create a new branch from main for your change.
5. Navigate into the directory of the package you wish to modify (core, angular, etc.).
6. Run `npm install` to install dependencies for this package.
7. Follow the steps for the specific package below.
@@ -160,17 +166,67 @@ Without a reliable code reproduction, it is unlikely we will be able to resolve
3. Make sure the build has finished before committing. If you made changes to the documentation, properties, methods, or anything else that requires an update to a generate file, this needs to be committed.
4. After the changes have been pushed, publish the branch and [create a pull request](#creating-a-pull-request).
### Angular, React, and Vue
#### Modifying Files
1. Locate the files inside the relevant root directory:
- Angular: `/angular/src`
- React: `/packages/react/src`
- Vue: `/packages/vue/src`
2. Make your changes to the files. If the change is overly complex or out of the ordinary, add comments so we can understand the changes.
3. Run lint on the directory and make sure there are no errors.
4. Build the project.
5. After the build is finished, commit the changes. Please follow the [commit message format](#commit-message-format) for every commit.
6. [Submit a Pull Request](#submit-pull-request) of your changes.
#### Preview Changes
1. Run `npm run start` inside of the relevant test app directory. This will sync your previously built changes into a test Ionic app:
- Angular: `/angular/test-app`
- React: `/packages/react/test-app`
- Vue: `/packages/vue/test-app`
2. In a browser, navigate to the page you wish to test.
3. Alternatively, create a new page if you need to test something that is not already there.
#### Lint Changes
1. Run `npm run lint` to lint the TypeScript in the relevant directory:
- Angular: `/angular/src`
- React: `/packages/react/src`
- Vue: `/packages/vue/src`
2. If there are lint errors, run `npm run lint.fix` to automatically fix any errors. Repeat step 1 to ensure the errors have been fixed, and manually fix them if not.
#### Modifying Tests
1. Locate the test to modify inside the relevant test app directory:
- Angular: `/angular/test-app/e2e/src`
- React: `/packages/react/test-app/cypress/integration`
- Vue: `/packages/vue/test-app/tests/e2e`
2. If a test exists, modify the test by adding an example to reproduce the problem fixed or feature added.
3. If a new test is needed, copy an existing test, rename it, and edit the content in the test file.
4. Run `npm run test` to run your tests.
#### Building Changes
1. Once all changes have been made, run `npm run build` inside of the root directory. This will add your changes to any auto-generated files, if necessary.
2. Review the changes and, if everything looks correct, [commit](#commit-message-format) the changes.
3. Make sure the build has finished before committing. If you made changes to the documentation, properties, methods, or anything else that requires an update to a generate file, this needs to be committed.
4. After the changes have been pushed, publish the branch and [create a pull request](#creating-a-pull-request).
### Submit Pull Request
1. [Create a new pull request](https://github.com/ionic-team/ionic/compare) with the `master` branch as the `base`. You may need to click on `compare across forks` to find your changes.
1. [Create a new pull request](https://github.com/ionic-team/ionic/compare) with the `main` branch as the `base`. You may need to click on `compare across forks` to find your changes.
2. See the [Creating a pull request from a fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) GitHub help article for more information.
3. Please fill out the provided Pull Request template to the best of your ability and include any issues that are related.
## Commit Message Guidelines
We have very precise rules over how our git commit messages should be formatted. This leads to readable messages that are easy to follow when looking through the project history. We also use the git commit messages to generate our [changelog](https://github.com/ionic-team/ionic/blob/master/CHANGELOG.md). Our format closely resembles Angular's [commit message guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit).
We have very precise rules over how our git commit messages should be formatted. This leads to readable messages that are easy to follow when looking through the project history. We also use the git commit messages to generate our [changelog](https://github.com/ionic-team/ionic/blob/main/CHANGELOG.md). Our format closely resembles Angular's [commit message guidelines](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit).
### Commit Message Format

62
.github/PROCESS.md vendored
View File

@@ -95,81 +95,81 @@ Issues with this label are not automatically closed and locked, so we manually c
We have two long-living branches:
- `master`: completed features, bug fixes, refactors, chores
- `main`: completed features, bug fixes, refactors, chores
- `stable`: the latest release
The overall flow:
1. Feature, refactor, and bug fix branches are created from `master`
1. When a feature, refactor, or fix is complete it is merged into `master`
1. A release branch is created from `master`
1. When the release branch is done it is merged into `master` and `stable`
1. Feature, refactor, and bug fix branches are created from `main`
1. When a feature, refactor, or fix is complete it is merged into `main`
1. A release branch is created from `main`
1. When the release branch is done it is merged into `main` and `stable`
1. If an issue in `stable` is detected a hotfix branch is created from `stable`
1. Once the hotfix is complete it is merged to both `master` and `stable`
1. All branches should follow the syntax of `{type}-{details}` where `{type}` is the type of branch (`hotfix`, `release`, or one of the [commit types](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format)) and `{details}` is a few hyphen separated words explaining the branch
1. Once the hotfix is complete it is merged to both `main` and `stable`
1. All branches should follow the syntax of `{type}-{details}` where `{type}` is the type of branch (`hotfix`, `release`, or one of the [commit types](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format)) and `{details}` is a few hyphen separated words explaining the branch
### Stable and Master Branches
### Stable and Main Branches
#### Stable Branch
Branches created from `stable`:
The following branch should be merged back to **both** `master` and `stable`:
The following branch should be merged back to **both** `main` and `stable`:
- A `hotfix` branch (e.g. `hotfix-missing-export`): a bug fix that is fixing a regression or issue with a published release
A `hotfix` branch should be the **only** branch that is created from stable.
#### Master Branch
#### Main Branch
Branches created from `master`:
Branches created from `main`:
The following branches should be merged back to `master` via a pull request:
The following branches should be merged back to `main` via a pull request:
1. A feature branch (e.g. `feat-desktop-support`): an addition to the API that is not a bug fix or regression fix
1. A bug fix branch (e.g. `fix-tab-color`): a bug fix that is not fixing a regression or issue with a published release
1. All other types listed in the [commit message types](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format): `docs`, `style`, `refactor`, `perf`, `test`, `chore`
1. All other types listed in the [commit message types](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format): `docs`, `style`, `refactor`, `perf`, `test`, `chore`
The following branch should be merged back to **both** `master` and `stable`:
The following branch should be merged back to **both** `main` and `stable`:
1. A `release` branch (e.g. `release-4.1.x`): contains all fixes and (optionally) features that are tested and should go into the release
### Feature Branches
Each new feature should reside in its own branch, based on the `master` branch. When a feature is complete, it should go into a pull request that gets merged back into `master`. A pull request adding a feature should be approved by two team members. Features should never interact directly with `stable`.
Each new feature should reside in its own branch, based on the `main` branch. When a feature is complete, it should go into a pull request that gets merged back into `main`. A pull request adding a feature should be approved by two team members. Features should never interact directly with `stable`.
### Release Branches
Once `master` has acquired enough features for a release (or a predetermined release date is approaching), fork a release branch off of `master`. Creating this branch starts the next release cycle, so no new features can be added after this point - only bug fixes, documentation generation, and other release-oriented tasks should go in this branch.
Once `main` has acquired enough features for a release (or a predetermined release date is approaching), fork a release branch off of `main`. Creating this branch starts the next release cycle, so no new features can be added after this point - only bug fixes, documentation generation, and other release-oriented tasks should go in this branch.
Once the release is ready to ship, it will get merged into `stable` and `master`, then the release branch will be deleted. Its important to merge back into `master` because critical updates may have been added to the release branch and they need to be accessible to new features. This should be done in a pull request after review.
Once the release is ready to ship, it will get merged into `stable` and `main`, then the release branch will be deleted. Its important to merge back into `main` because critical updates may have been added to the release branch and they need to be accessible to new features. This should be done in a pull request after review.
See the [steps for releasing](#releasing) below for detailed information on how to publish a release.
### Version Branches
Once a release has shipped and the release branch has been merged into `stable` and `master` it should also be merged into its corresponding version branch. These version branches allow us to ship updates for specific versions of the framework (i.e. Lets us ship a bug fix that only affects 4.2.x).
Once a release has shipped and the release branch has been merged into `stable` and `main` it should also be merged into its corresponding version branch. These version branches allow us to ship updates for specific versions of the framework (i.e. Lets us ship a bug fix that only affects 4.2.x).
Patch releases should be merged into their corresponding version branches. For example, a `release-4.1.1` branch should be merged into the `4.1.x` version branch and a `release-5.0.1` branch should be merged into the `5.0.x` version branch.
When releasing a major version such as `5.0.0 ` or a minor version such as `4.1.0` , the version branch will not exist. The version branch should be created once the release branch has been merged into `stable` and `master`. For example, when releasing `4.1.0`, the `release-4.1.0` release branch should be merged into `stable` and `master` and then the `4.1.x` version branch should be created off the latest `stable`.
When releasing a major version such as `5.0.0 ` or a minor version such as `4.1.0` , the version branch will not exist. The version branch should be created once the release branch has been merged into `stable` and `main`. For example, when releasing `4.1.0`, the `release-4.1.0` release branch should be merged into `stable` and `main` and then the `4.1.x` version branch should be created off the latest `stable`.
### Hotfix Branches
Maintenance or “hotfix” branches are used to quickly patch production releases. This is the only branch that should fork directly off of `stable`. As soon as the fix is complete, it should be merged into both `stable` and `master` (or the current release branch).
Maintenance or “hotfix” branches are used to quickly patch production releases. This is the only branch that should fork directly off of `stable`. As soon as the fix is complete, it should be merged into both `stable` and `main` (or the current release branch).
### Examples
#### Making a Change
1. Create a branch from `master`.
1. Create a branch from `main`.
1. Make changes. Limit your changes to a "unit of work", meaning don't include irrelevant changes that may confuse and delay the change.
1. Push changes.
1. Create a PR with the base of `master`.
1. Create a PR with the base of `main`.
1. Have someone approve your change (optional right now--at your discretion).
<img width="236" alt="image" src="https://user-images.githubusercontent.com/236501/47031893-913e0480-d136-11e8-9d9a-4b6297a4d7ba.png">
@@ -182,13 +182,13 @@ Maintenance or “hotfix” branches are used to quickly patch production releas
<img width="192" alt="Squash and merge button" src="https://user-images.githubusercontent.com/236501/47031620-da418900-d135-11e8-91ff-e84f2478b2b3.png">
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `master` become permanent.
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `main` become permanent.
<img width="672" alt="Squash and merge confirmation" src="https://user-images.githubusercontent.com/236501/47031753-31dff480-d136-11e8-9116-03934961bdc2.png">
1. Confirm squash and merge into `master`.
1. Confirm squash and merge into `main`.
#### Updating from `master`
#### Updating from `main`
1. Pull the latest changes locally.
1. Merge the changes, fixing any conflicts.
@@ -204,7 +204,7 @@ OR
#### Hotfixes
Hotfixes bypass `master` and should only be used for urgent fixes that can't wait for the next release to be ready.
Hotfixes bypass `main` and should only be used for urgent fixes that can't wait for the next release to be ready.
1. Create a branch from `stable`.
1. Make changes.
@@ -215,13 +215,13 @@ Hotfixes bypass `master` and should only be used for urgent fixes that can't wai
<img width="192" alt="Squash and merge button" src="https://user-images.githubusercontent.com/236501/47031620-da418900-d135-11e8-91ff-e84f2478b2b3.png">
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `master` become permanent.
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `main` become permanent.
<img width="672" alt="Squash and merge confirmation" src="https://user-images.githubusercontent.com/236501/47031753-31dff480-d136-11e8-9116-03934961bdc2.png">
1. Confirm squash and merge into `stable`.
1. CI builds `stable`, performing the release.
1. Create a PR to merge `stable` into `master`.
1. Create a PR to merge `stable` into `main`.
1. Click **Merge pull request**. Use the dropdown to select this option if necessary.
<img width="191" alt="Merge pull request button" src="https://user-images.githubusercontent.com/236501/47032669-8be1b980-d138-11e8-9a90-d1518c223184.png">
@@ -229,7 +229,7 @@ Hotfixes bypass `master` and should only be used for urgent fixes that can't wai
## Releasing
1. Create the release branch from `master`, for example: `release-4.5.0`.
1. Create the release branch from `main`, for example: `release-4.5.0`.
1. For major or minor releases, create a version branch based off the latest version branch. For example, if releasing 4.5.0, create a branch called `4.5.x` based off `4.4.x`.
@@ -244,7 +244,7 @@ Hotfixes bypass `master` and should only be used for urgent fixes that can't wai
1. Run `npm run release.prepare`
- Select the version based on the type of commits and the [Ionic Versioning](https://ionicframework.com/docs/intro/versioning)
- After the process completes, verify the version number in all packages (`core`, `docs`, `angular`)
- Verify the changelog commits are accurate and follow the [proper format]((https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format))
- Verify the changelog commits are accurate and follow the [proper format]((https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format))
- For major or minor releases, ensure that the version number has an associated title (for example: `4.5.0 Boron`)
- Commit these changes with the version number as the message, e.g. `git commit -m "4.5.0"`
@@ -258,4 +258,4 @@ Hotfixes bypass `master` and should only be used for urgent fixes that can't wai
1. Rewrite the commit message to `merge release-[VERSION]` with the proper release branch. For example, if this release is for `4.5.0`, the message would be `merge release-4.5.0`.
1. Submit a pull request from the release branch into `master`. Merge this pull request using the same commit format in the last step, to ensure any changes made on the release branch get added to future releases.
1. Submit a pull request from the release branch into `main`. Merge this pull request using the same commit format in the last step, to ensure any changes made on the release branch get added to future releases.

View File

@@ -41,8 +41,7 @@ closeAndLock:
- label: "ionitron: support"
message: >
Thanks for the issue! This issue appears to be a support request. We use this issue tracker exclusively for
bug reports and feature requests. Please use our [forum](https://forum.ionicframework.com) or our
[slack channel](https://ionicworldwide.herokuapp.com/) for questions about the framework.
bug reports and feature requests. Please use our [forum](https://forum.ionicframework.com) for questions about the framework.
Thank you for using Ionic!

View File

@@ -0,0 +1,22 @@
name: 'Build Ionic Angular Server'
description: 'Build Ionic Angular Server'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Install Angular Server Dependencies
run: npm install --legacy-peer-deps
shell: bash
working-directory: ./packages/angular-server
- name: Build
run: npm run build.prod
shell: bash
working-directory: ./packages/angular-server
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-angular-server
output: packages/angular-server/AngularServerBuild.zip
paths: packages/angular-server/dist

View File

@@ -0,0 +1,53 @@
name: 'Build Ionic Angular'
description: 'Build Ionic Angular'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json')}}-v1
- name: Cache Angular Node Modules
uses: actions/cache@v2
env:
cache-name: angular-node-modules
with:
path: ./angular/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./angular/package-lock.json')}}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Install Angular Dependencies
run: npm install
shell: bash
working-directory: ./angular
- name: Link @ionic/core
run: npm link
shell: bash
working-directory: ./core
- name: Link @ionic/core in @ionic/angular
run: npm link @ionic/core
shell: bash
working-directory: ./angular
- name: Lint
run: npm run lint
shell: bash
working-directory: ./angular
- name: Build
run: npm run build
shell: bash
working-directory: ./angular
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-angular
output: ./angular/AngularBuild.zip
paths: ./angular/dist

View File

@@ -0,0 +1,30 @@
name: 'Build Ionic Core'
description: 'Build Ionic Core'
runs:
using: 'composite'
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- name: Install Dependencies
run: npm install
working-directory: ./core
shell: bash
- name: Build Core
run: npm run build -- --ci
working-directory: ./core
shell: bash
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-core
output: core/CoreBuild.zip
paths: core/dist core/components core/css core/hydrate core/loader

View File

@@ -0,0 +1,51 @@
name: 'Build Ionic React Router'
description: 'Build Ionic React Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react
path: ./packages/react
filename: ReactBuild.zip
- name: Install Dependencies
run: npm install --legacy-peer-deps
shell: bash
working-directory: ./packages/react-router
- name: Sync
run: npm run sync
shell: bash
working-directory: ./packages/react-router
- name: Lint
run: npm run lint
shell: bash
working-directory: ./packages/react-router
- name: Build
run: npm run build
shell: bash
working-directory: ./packages/react-router
- name: Test Spec
run: npm run test.spec
shell: bash
working-directory: ./packages/react-router
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-react-router
output: packages/react-router/ReactRouterBuild.zip
paths: packages/react-router/dist

View File

@@ -0,0 +1,46 @@
name: 'Build Ionic React'
description: 'Build Ionic React'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Install React Dependencies
run: npm install --legacy-peer-deps
shell: bash
working-directory: ./packages/react
- name: Sync
run: npm run sync
shell: bash
working-directory: ./packages/react
- name: Lint
run: npm run lint
shell: bash
working-directory: ./packages/react
- name: Build
run: npm run build
shell: bash
working-directory: ./packages/react
- name: Test Spec
run: npm run test.spec
shell: bash
working-directory: ./packages/react
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-react
output: packages/react/ReactBuild.zip
paths: packages/react/dist packages/react/css

View File

@@ -0,0 +1,51 @@
name: 'Build Ionic Vue Router'
description: 'Builds Ionic Vue Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-vue
path: ./packages/vue
filename: VueBuild.zip
- name: Install Vue Router Dependencies
run: npm install
shell: bash
working-directory: ./packages/vue-router
- name: Sync
run: npm run sync
shell: bash
working-directory: ./packages/vue-router
- name: Lint
run: npm run lint
shell: bash
working-directory: ./packages/vue-router
- name: Build
run: npm run build
shell: bash
working-directory: ./packages/vue-router
- name: Test Spec
run: npm run test.spec
shell: bash
working-directory: ./packages/vue-router
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-vue-router
output: ./packages/vue-router/VueRouterBuild.zip
paths: packages/vue-router/dist

View File

@@ -0,0 +1,42 @@
name: 'Build Ionic Vue'
description: 'Build Ionic Vue'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Install Vue Dependencies
run: npm install
shell: bash
working-directory: ./packages/vue
- name: Sync
run: npm run sync
shell: bash
working-directory: ./packages/vue
- name: Lint
run: npm run lint
shell: bash
working-directory: ./packages/vue
- name: Build
run: npm run build
shell: bash
working-directory: ./packages/vue
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-vue
output: packages/vue/VueBuild.zip
paths: packages/vue/dist packages/vue/css

View File

@@ -0,0 +1,19 @@
name: 'Ionic Framework Archive Download'
description: 'Downloads and decompresses an archive from a previous job'
inputs:
path:
description: 'Input archive name'
filename:
description: 'Input file name'
name:
description: 'Archive name'
runs:
using: 'composite'
steps:
- uses: actions/download-artifact@v2
with:
name: ${{ inputs.name }}
path: ${{ inputs.path }}
- name: Exract Archive
run: unzip -q -o ${{ inputs.path }}/${{ inputs.filename }}
shell: bash

View File

@@ -0,0 +1,39 @@
name: 'Test Angular E2E'
description: 'Test Angular E2E'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-angular
path: ./angular
filename: AngularBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-angular-server
path: ./packages/angular-server
filename: AngularServerBuild.zip
- name: Install Dependencies
run: npm install --legacy-peer-deps
shell: bash
working-directory: ./angular/test/test-app
- name: Run Tests
run: npm run test
shell: bash
working-directory: ./angular/test/test-app

View File

@@ -0,0 +1,18 @@
name: 'Test Core Clean Build'
description: 'Test Core Clean Build'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Check Diff
run: git diff --exit-code
shell: bash
working-directory: ./core

View File

@@ -0,0 +1,25 @@
name: 'Test Core E2E'
description: 'Test Core E2E'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Test
run: npm run test.e2e -- --ci --no-build
shell: bash
working-directory: ./core

View File

@@ -0,0 +1,20 @@
name: 'Test Core Lint'
description: 'Test Core Lint'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- name: Lint
run: npm run lint
shell: bash
working-directory: ./core

View File

@@ -0,0 +1,33 @@
name: 'Test Core Screenshot Main'
description: 'Test Core Screenshot Main'
inputs:
access-key-id:
description: 'AWS_ACCESS_KEY_ID'
secret-access-key:
description: 'AWS_SECRET_ACCESS_KEY'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Test
run: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --update-screenshot --no-build || true
shell: bash
env:
AWS_ACCESS_KEY_ID: ${{ inputs.access-key-id }}
AWS_SECRET_ACCESS_KEY: ${{ inputs.secret-access-key }}
working-directory: ./core

View File

@@ -0,0 +1,33 @@
name: 'Test Core Screenshot'
description: 'Test Core Screenshot'
inputs:
access-key-id:
description: 'AWS_ACCESS_KEY_ID'
secret-access-key:
description: 'AWS_SECRET_ACCESS_KEY'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Test
run: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --no-build || true
shell: bash
env:
AWS_ACCESS_KEY_ID: ${{ inputs.access-key-id }}
AWS_SECRET_ACCESS_KEY: ${{ inputs.secret-access-key }}
working-directory: ./core

View File

@@ -0,0 +1,25 @@
name: 'Test Core Spec'
description: 'Test Core Spec'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Test
run: npm run test.spec -- --ci
shell: bash
working-directory: ./core

View File

@@ -0,0 +1,37 @@
name: 'Test React E2E'
description: 'Test React E2E'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react
path: ./packages/react
filename: ReactBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react-router
path: ./packages/react-router
filename: ReactRouterBuild.zip
- uses: cypress-io/github-action@v2
with:
browser: chrome
headless: true
start: npm run start.ci
working-directory: ./packages/react/test-app

View File

@@ -0,0 +1,37 @@
name: 'Test React Router E2E'
description: 'Test React Router '
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react
path: ./packages/react
filename: ReactBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react-router
path: ./packages/react-router
filename: ReactRouterBuild.zip
- uses: cypress-io/github-action@v2
with:
browser: chrome
headless: true
start: npm run start.ci
working-directory: ./packages/react-router/test-app

View File

@@ -0,0 +1,47 @@
name: 'Test Vue E2E'
description: 'Test Vue E2E'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Cache Core Node Modules
uses: actions/cache@v2
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-vue
path: ./packages/vue
filename: VueBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-vue-router
path: ./packages/vue-router
filename: VueRouterBuild.zip
- name: Install Dependencies
run: npm install
shell: bash
working-directory: ./packages/vue/test-app
- name: Sync
run: npm run sync
shell: bash
working-directory: ./packages/vue/test-app
- name: Run Spec Tests
run: npm run test:unit
shell: bash
working-directory: ./packages/vue/test-app
- name: Run E2E Tests
run: npm run test:e2e
shell: bash
working-directory: ./packages/vue/test-app

View File

@@ -0,0 +1,19 @@
name: 'Ionic Framework Archive Upload'
description: 'Compresses and uploads an archive to be reused across jobs'
inputs:
paths:
description: 'Paths to files or directories to archive'
output:
description: 'Output file name'
name:
description: 'Archive name'
runs:
using: 'composite'
steps:
- name: Create Archive
run: zip -q -r ${{ inputs.output }} ${{ inputs.paths }}
shell: bash
- uses: actions/upload-artifact@v2
with:
name: ${{ inputs.name }}
path: ${{ inputs.output }}

132
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,132 @@
name: 'Ionic Framework Build'
on:
pull_request:
branches: [ '**' ]
jobs:
build-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/build-core
test-core-clean-build:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-core-clean-build
test-core-lint:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-core-lint
test-core-spec:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-core-spec
test-core-e2e:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-core-e2e
test-core-screenshot:
needs: [build-core]
runs-on: ubuntu-latest
if: github.ref != 'refs/heads/main' && !github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-core-screenshot
with:
access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
test-core-screenshot-main:
needs: [build-core]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-core-screenshot-main
with:
access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
build-vue:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/build-vue
build-vue-router:
needs: [build-vue]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/build-vue-router
test-vue-e2e:
needs: [build-vue, build-vue-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-vue-e2e
build-angular:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/build-angular
build-angular-server:
needs: [build-angular]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/build-angular-server
test-angular-e2e:
needs: [build-angular, build-angular-server]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-angular-e2e
build-react:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/build-react
build-react-router:
needs: [build-react]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/build-react-router
test-react-router-e2e:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-react-router-e2e
test-react-e2e:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/actions/test-react-e2e

18
.github/workflows/dev-build.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: 'Ionic Dev Build'
on:
workflow_dispatch:
jobs:
dev-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 15.x
- name: Create Dev Build
run: npm run release.dev -- --skip-prompt
shell: bash
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -44,7 +44,7 @@ async function askNpmTag(version) {
type: 'list',
name: 'npmTag',
message: 'Select npm tag or specify a new tag',
choices: ['latest', 'next', 'v4-lts']
choices: ['latest', 'next', 'v4-lts', 'v5-lts']
.concat([
new inquirer.Separator(),
{

View File

@@ -127,7 +127,7 @@ async function publishGithub(version, gitTag, changelog, npmTag) {
let branch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']);
if (!branch) {
branch = 'master';
branch = 'main';
}
await octokit.repos.createRelease({

View File

@@ -1,3 +1,99 @@
## [5.9.4](https://github.com/ionic-team/ionic/compare/v5.9.3...v5.9.4) (2022-04-27)
### Bug Fixes
* **core:** inherit aria attributes on host elements ([#25156](https://github.com/ionic-team/ionic/issues/25156)) ([#25169](https://github.com/ionic-team/ionic/issues/25169)) ([ffb056d](https://github.com/ionic-team/ionic/commit/ffb056d50e126a1b89f5133de1e7516d0c29a61a))
## [5.9.3](https://github.com/ionic-team/ionic/compare/v5.9.2...v5.9.3) (2021-12-15)
### Bug Fixes
* **vue:** improve query params handling in tabs ([#24355](https://github.com/ionic-team/ionic/issues/24355)) ([1c28750](https://github.com/ionic-team/ionic/commit/1c2875044ad4d93fdca866017159a89f4dc8872d)), closes [#24353](https://github.com/ionic-team/ionic/issues/24353)
* **vue:** tabs no longer get unmounted when navigating back to a tabs context ([#24337](https://github.com/ionic-team/ionic/issues/24337)) ([4aab72b](https://github.com/ionic-team/ionic/commit/4aab72b06159729d2dcd18b2ef0b76f693e5a74e)), closes [#24332](https://github.com/ionic-team/ionic/issues/24332)
### Performance Improvements
* **content:** remove global click listener to improve interaction performance ([#24360](https://github.com/ionic-team/ionic/issues/24360)) ([9c9e28c](https://github.com/ionic-team/ionic/commit/9c9e28ccc9f899c403c757d911ac02d9099415af)), closes [#24359](https://github.com/ionic-team/ionic/issues/24359)
## [5.9.2](https://github.com/ionic-team/ionic/compare/v5.9.1...v5.9.2) (2021-12-07)
### Bug Fixes
* **angular:** improve typing when compiling with legacy View Engine ([#24221](https://github.com/ionic-team/ionic/issues/24221)) ([816096f](https://github.com/ionic-team/ionic/commit/816096f89747e943a4a273175d384189f25e4628))
* **content:** ensure fixed slot renders on top of content in iOS ([#24300](https://github.com/ionic-team/ionic/issues/24300)) ([e41b0e0](https://github.com/ionic-team/ionic/commit/e41b0e0cf2a794972d7f4d8943a0bec3d1e08016)), closes [#24286](https://github.com/ionic-team/ionic-framework/issues/24286)
* **popover:** improve scrolling in popover when using header and footer ([#24294](https://github.com/ionic-team/ionic/issues/24294)) ([f6a00ea](https://github.com/ionic-team/ionic/commit/f6a00ea9544aa70620b5f8f65a7702fa3bedd974))
* **react:** present and dismiss hooks return promises ([#24299](https://github.com/ionic-team/ionic/issues/24299)) ([4b26fea](https://github.com/ionic-team/ionic/commit/4b26feaa47efed4806aba565a52554db232b99e2)), closes [#24293](https://github.com/ionic-team/ionic-framework/issues/24293)
* **react:** properly check for custom elements to avoid errors in unit tests ([#24156](https://github.com/ionic-team/ionic/issues/24156)) ([8f188ea](https://github.com/ionic-team/ionic/commit/8f188eaae7422c9e81053868b9dd93b4ac738e98)), closes [#24149](https://github.com/ionic-team/ionic/issues/24149)
* **router:** popping route now accounts for route params ([#24315](https://github.com/ionic-team/ionic/issues/24315)) ([5e5054d](https://github.com/ionic-team/ionic/commit/5e5054d369ad68c9ac43e12439d71fb42d6ca26b)), closes [#24223](https://github.com/ionic-team/ionic-framework/issues/24223)
* **slides:** update swiper instance after initialization ([#24257](https://github.com/ionic-team/ionic/issues/24257)) ([89e4bc5](https://github.com/ionic-team/ionic/commit/89e4bc56a1c3cd4fb26fc5514f38c6a01f047297)), closes [#19638](https://github.com/ionic-team/ionic-framework/issues/19638)
* **vue:** ionic lifecycle hooks now run when using vue 3.2 setup syntax ([#24253](https://github.com/ionic-team/ionic/issues/24253)) ([fb96ab5](https://github.com/ionic-team/ionic/commit/fb96ab5a26d87818a8b64ee82df0020355054183)), closes [#23824](https://github.com/ionic-team/ionic/issues/23824)
* **vue:** switching between tabs preserves query string ([#24297](https://github.com/ionic-team/ionic/issues/24297)) ([047d3c7](https://github.com/ionic-team/ionic/commit/047d3c77729db08e4fd84f426f6c5c2af0eacc52)), closes [#23699](https://github.com/ionic-team/ionic/issues/23699)
## [5.9.1](https://github.com/ionic-team/ionic/compare/v5.9.0...v5.9.1) (2021-11-17)
### Bug Fixes
* **angular:** build is now in correct directory ([#24236](https://github.com/ionic-team/ionic/issues/24236)) ([1d983fa](https://github.com/ionic-team/ionic/commit/1d983fa4b3ef0457dc192f376e380c77b611d058))
# [5.9.0](https://github.com/ionic-team/ionic/compare/v5.8.5...v5.9.0) (2021-11-17)
### Bug Fixes
* **action-sheet:** safe area is now accounted for in MD mode ([#24176](https://github.com/ionic-team/ionic/issues/24176)) ([642255e](https://github.com/ionic-team/ionic/commit/642255e514fd67238d9bd8ea90781111687c6d03)), closes [#24175](https://github.com/ionic-team/ionic/issues/24175)
* **input:** date type in ion-input now aligns correctly on iOS 15 ([#24217](https://github.com/ionic-team/ionic/issues/24217)) ([0566ec0](https://github.com/ionic-team/ionic/commit/0566ec0da3b8a66a1a1ebb1b235e7297ec483c79))
* **vue:** canGoBack method now works correctly ([#24188](https://github.com/ionic-team/ionic/issues/24188)) ([7c43589](https://github.com/ionic-team/ionic/commit/7c43589b0a486f71ee2ae5a4cdcd73071fcd31b9)), closes [#24109](https://github.com/ionic-team/ionic/issues/24109)
### Features
* **slides:** add support for Swiper 7 ([#24190](https://github.com/ionic-team/ionic/issues/24190)) ([d0b6130](https://github.com/ionic-team/ionic/commit/d0b61307c6b7ff1589646c43f989260b59db1473))
## [5.8.5](https://github.com/ionic-team/ionic/compare/v5.8.4...v5.8.5) (2021-10-27)
### Bug Fixes
* **menu:** added focus trapping, improved compatibility with screen readers ([#24076](https://github.com/ionic-team/ionic/issues/24076)) ([bdb268a](https://github.com/ionic-team/ionic/commit/bdb268aa12c5bf411c96529672486d35e018cefa))
* **vue:** back button now selects correct route when navigating from view multiple times ([#24060](https://github.com/ionic-team/ionic/issues/24060)) ([a09d7d4](https://github.com/ionic-team/ionic/commit/a09d7d4ab6dd0d90204015eaaf232ed190753c56)), closes [#23987](https://github.com/ionic-team/ionic/issues/23987)
* **vue:** mount correct views when navigating ([#24056](https://github.com/ionic-team/ionic/issues/24056)) ([24659a5](https://github.com/ionic-team/ionic/commit/24659a527abe0c70df7e8ae6da3dcb4017bf500c)), closes [#23914](https://github.com/ionic-team/ionic/issues/23914)
## [5.8.4](https://github.com/ionic-team/ionic/compare/v5.8.3...v5.8.4) (2021-10-11)
### Bug Fixes
* **angular:** setup config properly ([#24054](https://github.com/ionic-team/ionic/issues/24054)) ([e001f24](https://github.com/ionic-team/ionic/commit/e001f24f2ca99abbc98b923dd1a132cc83b3b23f)), closes [#24051](https://github.com/ionic-team/ionic/issues/24051) [#24052](https://github.com/ionic-team/ionic/issues/24052)
* **back-button:** pass aria-label to native element ([#24027](https://github.com/ionic-team/ionic/issues/24027)) ([68a7e43](https://github.com/ionic-team/ionic/commit/68a7e43345a0261fdeed6054198c5a22fbbcb584))
## [5.8.3](https://github.com/ionic-team/ionic/compare/v5.8.2...v5.8.3) (2021-10-07)
### Bug Fixes
* **react:** overlay hooks dismiss method now works ([#24038](https://github.com/ionic-team/ionic/issues/24038)) ([655631d](https://github.com/ionic-team/ionic/commit/655631ddf059ce58066d5384d0ae186d7abc09a9)), closes [#24030](https://github.com/ionic-team/ionic/issues/24030)
## [5.8.2](https://github.com/ionic-team/ionic/compare/v5.8.1...v5.8.2) (2021-10-06)

View File

@@ -30,11 +30,11 @@ an [issue](https://github.com/ionic-team/ionic/issues/new) on this repository.
### Contributing
Thanks for your interest in contributing! Read up on our guidelines for
[contributing](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md)
[contributing](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md)
and then look through our issues with a [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
label.
Please note that this project is released with a [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/master/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
Please note that this project is released with a [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/main/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
### Examples

View File

@@ -137,7 +137,7 @@ The back button is no longer added by default to a navigation bar. It should be
</ion-toolbar>
```
See the [back button documentation](https://github.com/ionic-team/ionic/blob/master/core/src/components/back-button) for more usage examples.
See the [back button documentation](https://github.com/ionic-team/ionic/blob/main/core/src/components/back-button) for more usage examples.
## Button
@@ -507,7 +507,7 @@ _In the following examples, `{breakpoint}` refers to the optional screen breakpo
- `push-{breakpoint}-{value}` attributes have been renamed to `push-{breakpoint}=“{value}”`
- `pull-{breakpoint}-{value}` attributes have been renamed to `pull-{breakpoint}=“{value}”`
Customizing the padding and width of a grid should now be done with CSS variables. For more information, see [Grid Layout](https://github.com/ionic-team/ionic-docs/blob/master/src/pages/layout/grid.md).
Customizing the padding and width of a grid should now be done with CSS variables. For more information, see [Grid Layout](https://github.com/ionic-team/ionic-docs/blob/main/src/pages/layout/grid.md).
## Icon
@@ -1689,7 +1689,7 @@ The tab attribute defines the route to be shown upon clicking on this tab.
</ion-tabs>
```
See more usage examples in the [Tabs](https://github.com/ionic-team/ionic/blob/master/core/src/components/tabs) documentation.
See more usage examples in the [Tabs](https://github.com/ionic-team/ionic/blob/main/core/src/components/tabs) documentation.
## Text / Typography

View File

@@ -16,11 +16,11 @@ Ionic Angular specific building blocks on top of [@ionic/core](https://www.npmjs
## License
* [MIT](https://raw.githubusercontent.com/ionic-team/ionic/master/LICENSE)
* [MIT](https://raw.githubusercontent.com/ionic-team/ionic/main/LICENSE)
## Testing ng-add in ionic
1. Pull the latest from master
1. Pull the latest from `main`
2. Build ionic/angular: `npm run build`
3. Run `npm link` from `ionic/angular/dist` directory
4. Create a blank angular project

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "5.8.2",
"version": "5.9.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "5.8.2",
"version": "5.9.4",
"license": "MIT",
"dependencies": {
"@ionic/core": "5.8.1",
"@ionic/core": "5.9.3",
"tslib": "^1.9.3"
},
"devDependencies": {
@@ -204,9 +204,9 @@
}
},
"node_modules/@ionic/core": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.8.1.tgz",
"integrity": "sha512-twAPHhrGMYvHW0NgebMBfN70xDUkFxW+Zcxeus7jrhuJh1uWp/3MwawLi4InCwpyuFQiBo8yAaqC1hB1wTZmLA==",
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.3.tgz",
"integrity": "sha512-WM50vVxAAw+MQYqWXKUK4usBgkr7iQ9UWSb6t59mG4ZSy/fPAb7ZIdAjxY0U5i1ykk6A7Ur4B9ZJMpC/a7nnug==",
"dependencies": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.3",
@@ -5156,9 +5156,9 @@
}
},
"@ionic/core": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.8.1.tgz",
"integrity": "sha512-twAPHhrGMYvHW0NgebMBfN70xDUkFxW+Zcxeus7jrhuJh1uWp/3MwawLi4InCwpyuFQiBo8yAaqC1hB1wTZmLA==",
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.3.tgz",
"integrity": "sha512-WM50vVxAAw+MQYqWXKUK4usBgkr7iQ9UWSb6t59mG4ZSy/fPAb7ZIdAjxY0U5i1ykk6A7Ur4B9ZJMpC/a7nnug==",
"requires": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.3",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "5.8.2",
"version": "5.9.4",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@@ -42,7 +42,7 @@
"validate": "npm i && npm run lint && npm run test && npm run build"
},
"dependencies": {
"@ionic/core": "5.8.2",
"@ionic/core": "5.9.4",
"tslib": "^1.9.3"
},
"peerDependencies": {

View File

@@ -1,5 +1,5 @@
import { NgZone } from '@angular/core';
import { initialize } from '@ionic/core';
import { setupConfig } from '@ionic/core';
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
import { Config } from './providers/config';
@@ -11,7 +11,7 @@ export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
const win: IonicWindow | undefined = doc.defaultView as any;
if (win && typeof (window as any) !== 'undefined') {
initialize({
setupConfig({
...config,
_zoneGate: (h: any) => zone.run(h)
});

View File

@@ -86,11 +86,11 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
*/
const formControl = ngControl.control;
if (formControl) {
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'] as const;
methodsToPatch.forEach(method => {
if (formControl[method]) {
const oldFn = formControl[method].bind(formControl);
formControl[method] = (...params) => {
formControl[method] = (...params: any[]) => {
oldFn(...params);
setIonicClasses(this.el);
};

View File

@@ -305,7 +305,7 @@ export class StackController {
const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
if (typeof (requestAnimationFrame as any) === 'function') {
return new Promise<any>(resolve => {
return new Promise<void>(resolve => {
requestAnimationFrame(() => {
cleanup(activeRoute, views, viewsSnapshot, location);
resolve();

View File

@@ -43,7 +43,7 @@ export * from './types/ionic-lifecycle-hooks';
export { IonicModule } from './ionic-module';
// UTILS
export { IonicSafeString, getPlatforms, isPlatform, createAnimation, IonicSwiper } from '@ionic/core';
export { IonicSafeString, getPlatforms, isPlatform, createAnimation, IonicSwiper, IonicSlides } from '@ionic/core';
// CORE TYPES
export {

View File

@@ -35,7 +35,7 @@ Additionally, within this package is a `dist/ionic.js` file and accompanying `di
## Framework Bindings
The `@ionic/core` package can by used in simple HTML, or by vanilla JavaScript without any framework at all. Ionic also has packages that make it easier to integrate Ionic into a framework's traditional ecosystem and patterns. (However, at the lowest-level framework bindings are still just using Ionic Core and Web Components).
The `@ionic/core` package can be used in simple HTML, or by vanilla JavaScript without any framework at all. Ionic also has packages that make it easier to integrate Ionic into a framework's traditional ecosystem and patterns. (However, at the lowest-level framework bindings are still just using Ionic Core and Web Components).
* [@ionic/angular](https://www.npmjs.com/package/@ionic/angular)

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "5.8.2",
"version": "5.9.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "5.8.2",
"version": "5.9.4",
"license": "MIT",
"dependencies": {
"@stencil/core": "^2.4.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "5.8.2",
"version": "5.9.4",
"description": "Base components for Ionic",
"keywords": [
"ionic",

View File

@@ -176,7 +176,7 @@ export namespace Components {
*/
"disabled": boolean;
/**
* The icon name to use for the back button.
* The built-in named SVG icon name or the exact `src` of an SVG file to use for the back button.
*/
"icon"?: string | null;
/**
@@ -3511,7 +3511,7 @@ declare namespace LocalJSX {
*/
"disabled"?: boolean;
/**
* The icon name to use for the back button.
* The built-in named SVG icon name or the exact `src` of an SVG file to use for the back button.
*/
"icon"?: string | null;
/**

View File

@@ -22,11 +22,6 @@
text-align: $action-sheet-ios-text-align;
}
.action-sheet-wrapper {
@include margin(var(--ion-safe-area-top, 0), auto, var(--ion-safe-area-bottom, 0), auto);
}
// iOS Action Sheet Container
// ---------------------------------------------------

View File

@@ -67,7 +67,7 @@
.action-sheet-wrapper {
@include position(null, 0, 0, 0);
@include margin(auto);
@include margin(var(--ion-safe-area-top, 0), auto, var(--ion-safe-area-bottom, 0), auto);
@include transform(translate3d(0, 100%, 0));
display: block;

View File

@@ -2,8 +2,9 @@ import { Component, ComponentInterface, Element, Host, Prop, h } from '@stencil/
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import { AnimationBuilder, Color } from '../../interface';
import { ButtonInterface } from '../../utils/element-interface';
import type { AnimationBuilder, Color } from '../../interface';
import type { ButtonInterface } from '../../utils/element-interface';
import { inheritAriaAttributes } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
/**
@@ -22,6 +23,7 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme';
shadow: true
})
export class BackButton implements ComponentInterface, ButtonInterface {
private inheritedAttributes: { [k: string]: any } = {};
@Element() el!: HTMLElement;
@@ -43,7 +45,8 @@ export class BackButton implements ComponentInterface, ButtonInterface {
@Prop({ reflect: true }) disabled = false;
/**
* The icon name to use for the back button.
* The built-in named SVG icon name or the exact `src` of an SVG file
* to use for the back button.
*/
@Prop() icon?: string | null;
@@ -64,6 +67,8 @@ export class BackButton implements ComponentInterface, ButtonInterface {
@Prop() routerAnimation: AnimationBuilder | undefined;
componentWillLoad() {
this.inheritedAttributes = inheritAriaAttributes(this.el);
if (this.defaultHref === undefined) {
this.defaultHref = config.get('backButtonDefaultHref');
}
@@ -115,9 +120,10 @@ export class BackButton implements ComponentInterface, ButtonInterface {
}
render() {
const { color, defaultHref, disabled, type, hasIconOnly, backButtonIcon, backButtonText } = this;
const { color, defaultHref, disabled, type, hasIconOnly, backButtonIcon, backButtonText, inheritedAttributes } = this;
const showBackButton = defaultHref !== undefined;
const mode = getIonMode(this);
const ariaLabel = inheritedAttributes['aria-label'] || backButtonText || 'back';
return (
<Host
@@ -139,7 +145,7 @@ export class BackButton implements ComponentInterface, ButtonInterface {
disabled={disabled}
class="button-native"
part="native"
aria-label={backButtonText || 'back'}
aria-label={ariaLabel}
>
<span class="button-inner">
{backButtonIcon && <ion-icon part="icon" icon={backButtonIcon} aria-hidden="true" lazy={false}></ion-icon>}

View File

@@ -315,7 +315,7 @@ export default defineComponent({
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |
| `defaultHref` | `default-href` | The url to navigate back to by default when there is no history. | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the user cannot interact with the button. | `boolean` | `false` |
| `icon` | `icon` | The icon name to use for the back button. | `null \| string \| undefined` | `undefined` |
| `icon` | `icon` | The built-in named SVG icon name or the exact `src` of an SVG file to use for the back button. | `null \| string \| undefined` | `undefined` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `routerAnimation` | -- | When using a router, it specifies the transition animation when navigating to another page. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |
| `text` | `text` | The text to display in the back button. | `null \| string \| undefined` | `undefined` |

View File

@@ -18,8 +18,8 @@
<ion-content>
<h3>Default</h3>
<p>
<ion-back-button></ion-back-button>
<ion-back-button text="Back"></ion-back-button>
<ion-back-button aria-label="back button"></ion-back-button>
<ion-back-button text="Back" aria-label="back button"></ion-back-button>
<ion-back-button icon="add"></ion-back-button>
<ion-back-button disabled text="Text Only" icon=""></ion-back-button>
<ion-back-button icon="heart" text="Love" color="danger"></ion-back-button>

View File

@@ -1,9 +1,9 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { AnimationBuilder, Color, RouterDirection } from '../../interface';
import { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
import { hasShadowDom, inheritAttributes } from '../../utils/helpers';
import type { AnimationBuilder, Color, RouterDirection } from '../../interface';
import type { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
import { hasShadowDom, inheritAriaAttributes } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
/**
@@ -135,7 +135,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
this.inToolbar = !!this.el.closest('ion-buttons');
this.inListHeader = !!this.el.closest('ion-list-header');
this.inItem = !!this.el.closest('ion-item') || !!this.el.closest('ion-item-divider');
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
private get hasIconOnly() {

View File

@@ -140,10 +140,37 @@
}
:host(.content-sizing) {
display: flex;
flex-direction: column;
/**
* This resolves a sizing issue in popovers where extra long content
* would overflow the popover's height, preventing scrolling. It's a
* quirk of flexbox that forces the content to shrink to fit.
*
* overflow: hidden can't be used here because it prevents the visual
* effect from showing on translucent headers.
*/
min-height: 0;
contain: none;
}
:host(.content-sizing) .inner-scroll {
position: relative;
/**
* Because the outer content has display: flex here (to help enable
* scrolling in a popover), offsetting via `top` (such as when using
* a translucent header) creates white space under the content. Use
* a negative margin instead to keep the bottom in place. (A similar
* thing happens with `bottom` and footers.)
*/
top: 0;
bottom: 0;
margin-top: calc(var(--offset-top) * -1);
margin-bottom: calc(var(--offset-bottom) * -1);
}
.transition-effect {
@@ -199,4 +226,15 @@
::slotted([slot="fixed"]) {
position: absolute;
/**
* When presenting ion-content inside of an ion-modal, the .inner-scroll
* element is composited. In WebKit, the fixed content is not composited
* causing it to appear under the main scrollable content as a result.
* The fixed content is correctly composited in other browsers. Adding
* the translateZ forces the fixed content to be composited so it correctly
* shows on top of the scrollable content. Setting a negative z-index will
* still allow the fixed content to appear under the scroll content if specified.
*/
transform: translateZ(0);
}

View File

@@ -119,14 +119,6 @@ export class Content implements ComponentInterface {
this.resize();
}
@Listen('click', { capture: true })
onClick(ev: Event) {
if (this.isScrolling) {
ev.preventDefault();
ev.stopPropagation();
}
}
private shouldForceOverscroll() {
const { forceOverscroll } = this;
const mode = getIonMode(this);
@@ -374,10 +366,17 @@ const getPageElement = (el: HTMLElement) => {
if (tabs) {
return tabs;
}
const page = el.closest('ion-app,ion-page,.ion-page,page-inner');
/**
* If we're in a popover, we need to use its wrapper so we can account for space
* between the popover and the edges of the screen. But if the popover contains
* its own page element, we should use that instead.
*/
const page = el.closest('ion-app, ion-page, .ion-page, page-inner, .popover-content');
if (page) {
return page;
}
return getParentElement(el);
};

View File

@@ -1,7 +1,8 @@
import { Component, ComponentInterface, Element, Host, Prop, h, writeTask } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { inheritAttributes } from '../../utils/helpers';
import { inheritAriaAttributes } from '../../utils/helpers';
import { hostContext } from '../../utils/theme';
import { cloneElement, createHeaderIndex, handleContentScroll, handleToolbarIntersection, setHeaderActive, setToolbarBackgroundOpacity } from './header.utils';
@@ -45,7 +46,7 @@ export class Header implements ComponentInterface {
@Prop() translucent = false;
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['role']);
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
async componentDidLoad() {
@@ -154,9 +155,12 @@ export class Header implements ComponentInterface {
const mode = getIonMode(this);
const collapse = this.collapse || 'none';
// banner role must be at top level, so remove role if inside a menu
const roleType = hostContext('ion-menu', this.el) ? 'none' : 'banner';
return (
<Host
role="banner"
role={roleType}
class={{
[mode]: true,

View File

@@ -2,10 +2,6 @@
The `ion-infinite-scroll-content` component is the default child used by the `ion-infinite-scroll`. It displays an infinite scroll spinner that looks best based on the platform and changes the look depending on the infinite scroll's state. The default spinner can be changed and text can be added by setting the `loadingSpinner` and `loadingText` properties.
## React
The `ion-infinite-scroll-content` component is not supported in React.
<!-- Auto Generated Below -->
@@ -39,6 +35,25 @@ The `ion-infinite-scroll-content` component is not supported in React.
```
### React
```tsx
import React from 'react';
import { IonContent, IonInfiniteScroll, IonInfiniteScrollContent } from '@ionic/react';
export const InfiniteScrollExample: React.FC = () => (
<IonContent>
<IonInfiniteScroll>
<IonInfiniteScrollContent
loadingSpinner="bubbles"
loadingText="Loading more data...">
</IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonContent>
);
```
### Stencil
```tsx

View File

@@ -0,0 +1,15 @@
```tsx
import React from 'react';
import { IonContent, IonInfiniteScroll, IonInfiniteScrollContent } from '@ionic/react';
export const InfiniteScrollExample: React.FC = () => (
<IonContent>
<IonInfiniteScroll>
<IonInfiniteScrollContent
loadingSpinner="bubbles"
loadingText="Loading more data...">
</IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonContent>
);
```

View File

@@ -111,6 +111,104 @@ function toggleInfiniteScroll() {
```
### React
```tsx
import {
IonButton,
IonContent,
IonHeader,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonItem,
IonLabel,
IonList,
IonPage,
IonTitle,
IonToolbar,
useIonViewWillEnter
} from '@ionic/react';
import { useState } from 'react';
const InfiniteScrollExample: React.FC = () => {
const [data, setData] = useState<string[]>([]);
const [isInfiniteDisabled, setInfiniteDisabled] = useState(false);
const pushData = () => {
const max = data.length + 20;
const min = max - 20;
const newData = [];
for (let i = min; i < max; i++) {
newData.push('Item' + i);
}
setData([
...data,
...newData
]);
}
const loadData = (ev: any) => {
setTimeout(() => {
pushData();
console.log('Loaded data');
ev.target.complete();
if (data.length == 1000) {
setInfiniteDisabled(true);
}
}, 500);
}
useIonViewWillEnter(() => {
pushData();
});
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Blank</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Blank</IonTitle>
</IonToolbar>
</IonHeader>
<IonButton onClick={() => setInfiniteDisabled(!isInfiniteDisabled)} expand="block">
Toggle Infinite Scroll
</IonButton>
<IonList>
{data.map((item, index) => {
return (
<IonItem key={index}>
<IonLabel>{item}</IonLabel>
</IonItem>
)
})}
</IonList>
<IonInfiniteScroll
onIonInfinite={loadData}
threshold="100px"
disabled={isInfiniteDisabled}
>
<IonInfiniteScrollContent
loadingSpinner="bubbles"
loadingText="Loading more data..."
></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonContent>
</IonPage>
);
};
export default InfiniteScrollExample;
```
### Stencil
```tsx

View File

@@ -0,0 +1,94 @@
```tsx
import {
IonButton,
IonContent,
IonHeader,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonItem,
IonLabel,
IonList,
IonPage,
IonTitle,
IonToolbar,
useIonViewWillEnter
} from '@ionic/react';
import { useState } from 'react';
const InfiniteScrollExample: React.FC = () => {
const [data, setData] = useState<string[]>([]);
const [isInfiniteDisabled, setInfiniteDisabled] = useState(false);
const pushData = () => {
const max = data.length + 20;
const min = max - 20;
const newData = [];
for (let i = min; i < max; i++) {
newData.push('Item' + i);
}
setData([
...data,
...newData
]);
}
const loadData = (ev: any) => {
setTimeout(() => {
pushData();
console.log('Loaded data');
ev.target.complete();
if (data.length == 1000) {
setInfiniteDisabled(true);
}
}, 500);
}
useIonViewWillEnter(() => {
pushData();
});
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Blank</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Blank</IonTitle>
</IonToolbar>
</IonHeader>
<IonButton onClick={() => setInfiniteDisabled(!isInfiniteDisabled)} expand="block">
Toggle Infinite Scroll
</IonButton>
<IonList>
{data.map((item, index) => {
return (
<IonItem key={index}>
<IonLabel>{item}</IonLabel>
</IonItem>
)
})}
</IonList>
<IonInfiniteScroll
onIonInfinite={loadData}
threshold="100px"
disabled={isInfiniteDisabled}
>
<IonInfiniteScrollContent
loadingSpinner="bubbles"
loadingText="Loading more data..."
></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonContent>
</IonPage>
);
};
export default InfiniteScrollExample;
```

View File

@@ -1,8 +1,14 @@
import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, State, Watch, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { AutocompleteTypes, Color, InputChangeEventDetail, StyleEventDetail, TextFieldTypes } from '../../interface';
import { debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
import type {
AutocompleteTypes,
Color,
InputChangeEventDetail,
StyleEventDetail,
TextFieldTypes,
} from '../../interface';
import { debounceEvent, findItemLabel, inheritAriaAttributes, inheritAttributes } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';
/**
@@ -234,7 +240,10 @@ export class Input implements ComponentInterface {
}
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label', 'tabindex', 'title']);
this.inheritedAttributes = {
...inheritAriaAttributes(this.el),
...inheritAttributes(this.el, ['tabindex', 'title']),
};
}
connectedCallback() {

View File

@@ -35,6 +35,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
@Element() el!: HTMLIonItemElement;
@State() multipleInputs = false;
@State() focusable = true;
/**
* The color to use from your application's color palette.
@@ -173,7 +174,10 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
}
componentDidLoad() {
raf(() => this.setMultipleInputs());
raf(() => {
this.setMultipleInputs();
this.focusable = this.isFocusable();
});
}
// If the item contains multiple clickable elements and/or inputs, then the item
@@ -217,6 +221,11 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
return (this.isClickable() || this.hasCover());
}
private isFocusable(): boolean {
const focusableChild = this.el.querySelector('.ion-focusable');
return (this.canActivate() || focusableChild !== null);
}
private getFirstInput(): HTMLIonInputElement | HTMLIonTextareaElement {
const inputs = this.el.querySelectorAll('ion-input, ion-textarea') as NodeListOf<HTMLIonInputElement | HTMLIonTextareaElement>;
return inputs[0];
@@ -289,7 +298,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
'in-list': hostContext('ion-list', this.el),
'item-multiple-inputs': this.multipleInputs,
'ion-activatable': canActivate,
'ion-focusable': true,
'ion-focusable': this.focusable
})
}}
>

View File

@@ -2,9 +2,9 @@ import { Component, ComponentInterface, Element, Host, Listen, Prop, State, h }
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import { Color } from '../../interface';
import { ButtonInterface } from '../../utils/element-interface';
import { inheritAttributes } from '../../utils/helpers';
import type { Color } from '../../interface';
import type { ButtonInterface } from '../../utils/element-interface';
import { inheritAriaAttributes } from '../../utils/helpers';
import { menuController } from '../../utils/menu-controller';
import { createColorClasses, hostContext } from '../../utils/theme';
import { updateVisibility } from '../menu-toggle/menu-toggle-util';
@@ -58,7 +58,7 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
@Prop() type: 'submit' | 'reset' | 'button' = 'button';
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
componentDidLoad() {

View File

@@ -5,13 +5,14 @@ import { getIonMode } from '../../global/ionic-global';
import { Animation, Gesture, GestureDetail, MenuChangeEventDetail, MenuI, Side } from '../../interface';
import { getTimeGivenProgression } from '../../utils/animation/cubic-bezier';
import { GESTURE_CONTROLLER } from '../../utils/gesture';
import { assert, clamp, isEndSide as isEnd } from '../../utils/helpers';
import { assert, clamp, inheritAriaAttributes, isEndSide as isEnd } from '../../utils/helpers';
import { menuController } from '../../utils/menu-controller';
const iosEasing = 'cubic-bezier(0.32,0.72,0,1)';
const mdEasing = 'cubic-bezier(0.0,0.0,0.2,1)';
const iosEasingReverse = 'cubic-bezier(1, 0, 0.68, 0.28)';
const mdEasingReverse = 'cubic-bezier(0.4, 0, 0.6, 1)';
const focusableQueryString = '[tabindex]:not([tabindex^="-"]), input:not([type=hidden]):not([tabindex^="-"]), textarea:not([tabindex^="-"]), button:not([tabindex^="-"]), select:not([tabindex^="-"]), .ion-focusable:not([tabindex^="-"])';
/**
* @part container - The container for the menu content.
@@ -39,6 +40,11 @@ export class Menu implements ComponentInterface, MenuI {
backdropEl?: HTMLElement;
menuInnerEl?: HTMLElement;
contentEl?: HTMLElement;
lastFocus?: HTMLElement;
private inheritedAttributes: { [k: string]: any } = {};
private handleFocus = (ev: Event) => this.trapKeyboardFocus(ev, document);
@Element() el!: HTMLIonMenuElement;
@@ -159,6 +165,7 @@ export class Menu implements ComponentInterface, MenuI {
const el = this.el;
const parent = el.parentNode as any;
if (this.contentId === undefined) {
console.warn(`[DEPRECATED][ion-menu] Using the [main] attribute is deprecated, please use the "contentId" property instead:
BEFORE:
@@ -205,6 +212,10 @@ AFTER:
this.updateState();
}
componentWillLoad() {
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
async componentDidLoad() {
this.ionMenuChange.emit({ disabled: this.disabled, open: this._isOpen });
this.updateState();
@@ -246,6 +257,13 @@ AFTER:
}
}
@Listen('keydown')
onKeydown(ev: KeyboardEvent) {
if (ev.key === 'Escape') {
this.close();
}
}
/**
* Returns `true` is the menu is open.
*/
@@ -301,6 +319,65 @@ AFTER:
return menuController._setOpen(this, shouldOpen, animated);
}
private focusFirstDescendant() {
const { el } = this;
const firstInput = el.querySelector(focusableQueryString) as HTMLElement | null;
if (firstInput) {
firstInput.focus();
} else {
el.focus();
}
}
private focusLastDescendant() {
const { el } = this;
const inputs = Array.from(el.querySelectorAll<HTMLElement>(focusableQueryString));
const lastInput = inputs.length > 0 ? inputs[inputs.length - 1] : null;
if (lastInput) {
lastInput.focus();
} else {
el.focus();
}
}
private trapKeyboardFocus(ev: Event, doc: Document) {
const target = ev.target as HTMLElement | null;
if (!target) { return; }
/**
* If the target is inside the menu contents, let the browser
* focus as normal and keep a log of the last focused element.
*/
if (this.el.contains(target)) {
this.lastFocus = target;
} else {
/**
* Otherwise, we are about to have focus go out of the menu.
* Wrap the focus to either the first or last element.
*/
/**
* Once we call `focusFirstDescendant`, another focus event
* will fire, which will cause `lastFocus` to be updated
* before we can run the code after that. We cache the value
* here to avoid that.
*/
this.focusFirstDescendant();
/**
* If the cached last focused element is the same as the now-
* active element, that means the user was on the first element
* already and pressed Shift + Tab, so we need to wrap to the
* last descendant.
*/
if (this.lastFocus === doc.activeElement) {
this.focusLastDescendant();
}
}
}
async _setOpen(shouldOpen: boolean, animated = true): Promise<boolean> {
// If the menu is disabled or it is currently being animated, let's do nothing
if (!this._isActive() || this.isAnimating || shouldOpen === this._isOpen) {
@@ -479,6 +556,16 @@ AFTER:
// this places the menu into the correct location before it animates in
// this css class doesn't actually kick off any animations
this.el.classList.add(SHOW_MENU);
/**
* We add a tabindex here so that focus trapping
* still works even if the menu does not have
* any focusable elements slotted inside. The
* focus trapping utility will fallback to focusing
* the menu so focus does not leave when the menu
* is open.
*/
this.el.setAttribute('tabindex', '0');
if (this.backdropEl) {
this.backdropEl.classList.add(SHOW_BACKDROP);
}
@@ -505,19 +592,51 @@ AFTER:
}
if (isOpen) {
// add css class
// add css class and hide content behind menu from screen readers
if (this.contentEl) {
this.contentEl.classList.add(MENU_CONTENT_OPEN);
/**
* When the menu is open and overlaying the main
* content, the main content should not be announced
* by the screenreader as the menu is the main
* focus. This is useful with screenreaders that have
* "read from top" gestures that read the entire
* page from top to bottom when activated.
*/
this.contentEl.setAttribute('aria-hidden', 'true');
}
// emit open event
this.ionDidOpen.emit();
// focus menu content for screen readers
if (this.menuInnerEl) {
this.focusFirstDescendant();
}
// setup focus trapping
document.addEventListener('focus', this.handleFocus, true);
} else {
// remove css classes
// remove css classes and unhide content from screen readers
this.el.classList.remove(SHOW_MENU);
/**
* Remove tabindex from the menu component
* so that is cannot be tabbed to.
*/
this.el.removeAttribute('tabindex');
if (this.contentEl) {
this.contentEl.classList.remove(MENU_CONTENT_OPEN);
/**
* Remove aria-hidden so screen readers
* can announce the main content again
* now that the menu is not the main focus.
*/
this.contentEl.removeAttribute('aria-hidden');
}
if (this.backdropEl) {
this.backdropEl.classList.remove(SHOW_BACKDROP);
}
@@ -528,6 +647,9 @@ AFTER:
// emit close event
this.ionDidClose.emit();
// undo focus trapping so multiple menus don't collide
document.removeEventListener('focus', this.handleFocus, true);
}
}
@@ -561,12 +683,13 @@ AFTER:
}
render() {
const { isEndSide, type, disabled, isPaneVisible } = this;
const { isEndSide, type, disabled, isPaneVisible, inheritedAttributes } = this;
const mode = getIonMode(this);
return (
<Host
role="navigation"
aria-label={inheritedAttributes['aria-label'] || 'menu'}
class={{
[mode]: true,
[`menu-type-${type}`]: true,

View File

@@ -0,0 +1,15 @@
import { newE2EPage } from '@stencil/core/testing';
import { AxePuppeteer } from '@axe-core/puppeteer';
test('menu: axe', async () => {
const page = await newE2EPage({
url: '/src/components/menu/test/a11y?ionic:_testing=true'
});
const menu = await page.find('ion-menu');
await menu.callMethod('open');
await menu.waitForVisible();
const results = await new AxePuppeteer(page).analyze();
expect(results.violations.length).toEqual(0);
});

View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Segment - a11y</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link href="../../../../../css/core.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<main>
<h1>Menu</h1>
<ion-menu menu-id="menu" content-id="main-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-button>Button</ion-button>
</ion-item>
<ion-item>
<ion-button>Button 2</ion-button>
</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
</main>
</body>
</html>

View File

@@ -1,6 +1,11 @@
import { testMenu } from '../test.utils';
import { newE2EPage } from '@stencil/core/testing';
const DIRECTORY = 'basic';
const getActiveElementID = async (page) => {
const activeElement = await page.evaluateHandle(() => document.activeElement);
return await page.evaluate(el => el && el.id, activeElement);
}
test('menu: start menu', async () => {
await testMenu(DIRECTORY, '#start-menu', 'first');
@@ -14,6 +19,21 @@ test('menu: end menu', async () => {
await testMenu(DIRECTORY, '#end-menu');
});
test('menu: focus trap', async () => {
const page = await newE2EPage({ url: '/src/components/menu/test/basic?ionic:_testing=true' });
await page.click('#open-first');
const menu = await page.find('#start-menu');
await menu.waitForVisible();
let activeElID = await getActiveElementID(page);
expect(activeElID).toEqual('start-menu-button');
await page.keyboard.press('Tab');
activeElID = await getActiveElementID(page);
expect(activeElID).toEqual('start-menu-button');
});
/**
* RTL Tests
*/

View File

@@ -24,7 +24,7 @@
<body>
<ion-app>
<ion-menu side="start" menu-id="first" id="start-menu" content-id="main" class="menu-part">
<ion-menu side="start" menu-id="first" id="start-menu" content-id="main" class="menu-part" aria-label="start menu">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Start Menu</ion-title>
@@ -32,6 +32,9 @@
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-button id="start-menu-button">Button</ion-button>
</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
@@ -82,7 +85,7 @@
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button expand="block" onclick="openFirst()">Open Start Menu</ion-button>
<ion-button expand="block" id="open-first" onclick="openFirst()">Open Start Menu</ion-button>
<ion-button expand="block" onclick="openEnd()">Open End Menu</ion-button>
<ion-button expand="block" onclick="openCustom()">Open Custom Menu</ion-button>
</ion-content>

View File

@@ -75,5 +75,9 @@
--ion-safe-area-right: 0px;
--ion-safe-area-bottom: 0px;
--ion-safe-area-left: 0px;
}
display: flex;
flex-direction: column;
overflow: hidden;
}

View File

@@ -4,7 +4,7 @@ A Popover is a dialog that appears on top of the current page. It can be used fo
## Presenting
To present a popover, call the `present` method on a popover instance. In order to position the popover relative to the element clicked, a click event needs to be passed into the options of the the `present` method. If the event is not passed, the popover will be positioned in the center of the viewport.
To present a popover, call the `present` method on a popover instance. In order to position the popover relative to the element clicked, a click event needs to be passed into the options of the `present` method. If the event is not passed, the popover will be positioned in the center of the viewport.
## Customization

View File

@@ -57,6 +57,14 @@ test('popover: custom class', async () => {
await testPopover(DIRECTORY, '#custom-class-popover');
});
test('popover: header', async () => {
await testPopover(DIRECTORY, '#header-popover');
});
test('popover: translucent header', async () => {
await testPopover(DIRECTORY, '#translucent-header-popover');
});
/**
* RTL Tests
*/
@@ -81,6 +89,14 @@ test('popover:rtl: custom class', async () => {
await testPopover(DIRECTORY, '#custom-class-popover', true);
});
test('popover:rtl: header', async () => {
await testPopover(DIRECTORY, '#header-popover', true);
});
test('popover:rtl: translucent header', async () => {
await testPopover(DIRECTORY, '#translucent-header-popover', true);
});
test('popover: htmlAttributes', async () => {
const page = await newE2EPage({ url: '/src/components/popover/test/basic?ionic:_testing=true' });

View File

@@ -34,6 +34,8 @@
<ion-button id="long-list-popover" expand="block" color="secondary" onclick="presentPopover({ component: 'list-page', event: event })">Show Long List Popover</ion-button>
<ion-button id="no-event-popover" expand="block" color="danger" onclick="presentPopover({ component: 'profile-page' })">No Event Popover</ion-button>
<ion-button id="custom-class-popover" expand="block" color="tertiary" onclick="presentPopover({ component: 'translucent-page', event: event, cssClass: 'my-custom-class' })">Custom Class Popover</ion-button>
<ion-button id="header-popover" expand="block" onclick="presentPopover({ component: 'header-page' })">Popover With Header</ion-button>
<ion-button id="translucent-header-popover" expand="block" onclick="presentPopover({ component: 'translucent-header-page' })">Popover With Translucent Header</ion-button>
</ion-content>
<ion-footer>
@@ -126,6 +128,56 @@
}
customElements.define('translucent-page', TranslucentPage);
class HeaderPage extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Header</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding" color="primary">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.In rutrum tortor lacus, ac interdum ipsum bibendum vel.Aenean non nibh gravida, ullamcorper mi at, tempor nulla.Proin malesuada tellus ut ullamcorper accumsan.Donec semper justo vulputate neque tempus ultricies.Proin non aliquet ipsum.Praesent mauris sem, facilisis eu justo nec, euismod imperdiet tellus.Duis eget justo congue, lacinia orci sed, fermentum urna.Quisque sed massa faucibus, interdum dolor rhoncus, molestie erat.Proin suscipit ante non mauris volutpat egestas.Donec a ultrices ligula.Mauris in felis vel dui consectetur viverra.Nam vitae quam in arcu aliquam aliquam.Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Cras non velit nisl.Donec viverra, magna quis vestibulum volutpat, metus ante tincidunt augue, non porta nisi mi sit amet neque.Proin dapibus eros vitae nibh tincidunt, blandit rhoncus est porttitor.
</ion-content>
`;
}
}
customElements.define('header-page', HeaderPage);
class TranslucentHeaderPage extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `
<ion-header translucent>
<ion-toolbar>
<ion-title>Header</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding" fullscreen color="primary">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.In rutrum tortor lacus, ac interdum ipsum bibendum vel.Aenean non nibh gravida, ullamcorper mi at, tempor nulla.Proin malesuada tellus ut ullamcorper accumsan.Donec semper justo vulputate neque tempus ultricies.Proin non aliquet ipsum.Praesent mauris sem, facilisis eu justo nec, euismod imperdiet tellus.Duis eget justo congue, lacinia orci sed, fermentum urna.Quisque sed massa faucibus, interdum dolor rhoncus, molestie erat.Proin suscipit ante non mauris volutpat egestas.Donec a ultrices ligula.Mauris in felis vel dui consectetur viverra.Nam vitae quam in arcu aliquam aliquam.Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Cras non velit nisl.Donec viverra, magna quis vestibulum volutpat, metus ante tincidunt augue, non porta nisi mi sit amet neque.Proin dapibus eros vitae nibh tincidunt, blandit rhoncus est porttitor.
</ion-content>
<ion-footer translucent>
<ion-toolbar>
<ion-title>Footer</ion-title>
</ion-toolbar>
</ion-footer>
`;
}
}
customElements.define('translucent-header-page', TranslucentHeaderPage);
</script>
</body>

View File

@@ -1,8 +1,16 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop, State, Watch, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { Color, Gesture, GestureDetail, KnobName, RangeChangeEventDetail, RangeValue, StyleEventDetail } from '../../interface';
import { clamp, debounceEvent, getAriaLabel, inheritAttributes, renderHiddenInput } from '../../utils/helpers';
import type {
Color,
Gesture,
GestureDetail,
KnobName,
RangeChangeEventDetail,
RangeValue,
StyleEventDetail,
} from '../../interface';
import { clamp, debounceEvent, getAriaLabel, inheritAriaAttributes, renderHiddenInput } from '../../utils/helpers';
import { createColorClasses, hostContext } from '../../utils/theme';
/**
@@ -205,7 +213,7 @@ export class Range implements ComponentInterface {
*/
this.rangeId = (this.el.hasAttribute('id')) ? this.el.getAttribute('id')! : `ion-r-${rangeIds++}`;
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
componentDidLoad() {

View File

@@ -30,15 +30,24 @@ const CHAIN_3: RouteChain = [
describe('matchesIDs', () => {
it('should match simple set of ids', () => {
const chain: RouteChain = CHAIN_1;
expect(matchesIDs(['2'], chain)).toBe(1);
expect(matchesIDs(['2', '1'], chain)).toBe(2);
expect(matchesIDs(['2', '1', '3'], chain)).toBe(3);
expect(matchesIDs(['2', '1', '3', '4'], chain)).toBe(4);
expect(matchesIDs(['2', '1', '3', '4', '5'], chain)).toBe(4);
expect(matchesIDs([{ id: '2' }], chain)).toBe(1);
expect(matchesIDs([{ id: '2' }, { id: '1' }], chain)).toBe(2);
expect(matchesIDs([{ id: '2' }, { id: '1' }, { id: '3' }], chain)).toBe(3);
expect(matchesIDs([{ id: '2' }, { id: '1' }, { id: '3' }, { id: '4' }], chain)).toBe(4);
expect(matchesIDs([{ id: '2' }, { id: '1' }, { id: '3' }, { id: '4' }, { id: '5' }], chain)).toBe(4);
expect(matchesIDs([], chain)).toBe(0);
expect(matchesIDs(['1'], chain)).toBe(0);
expect(matchesIDs([{ id: '1' }], chain)).toBe(0);
});
it('should match path with params', () => {
const ids = [{ id: 'my-page', params: { s1: 'a', s2: 'b' } }];
expect(matchesIDs(ids, [{ id: 'my-page', path: [''], params: {} }])).toBe(1);
expect(matchesIDs(ids, [{ id: 'my-page', path: [':s1'], params: {} }])).toBe(1);
expect(matchesIDs(ids, [{ id: 'my-page', path: [':s1', ':s2'], params: {} }])).toBe(3);
expect(matchesIDs(ids, [{ id: 'my-page', path: [':s1', ':s2', ':s3'], params: {} }])).toBe(1);
})
});
describe('matchesPath', () => {
@@ -227,7 +236,7 @@ describe('mergeParams', () => {
});
describe('RouterSegments', () => {
it ('should initialize with empty array', () => {
it('should initialize with empty array', () => {
const s = new RouterSegments([]);
expect(s.next()).toEqual('');
expect(s.next()).toEqual('');
@@ -236,7 +245,7 @@ describe('RouterSegments', () => {
expect(s.next()).toEqual('');
});
it ('should initialize with array', () => {
it('should initialize with array', () => {
const s = new RouterSegments(['', 'path', 'to', 'destination']);
expect(s.next()).toEqual('');
expect(s.next()).toEqual('path');

View File

@@ -32,16 +32,60 @@ export const findRouteRedirect = (path: string[], redirects: RouteRedirect[]) =>
return redirects.find(redirect => matchesRedirect(path, redirect));
};
export const matchesIDs = (ids: string[], chain: RouteChain): number => {
export const matchesIDs = (ids: Pick<RouteID, 'id' | 'params'>[], chain: RouteChain): number => {
const len = Math.min(ids.length, chain.length);
let i = 0;
for (; i < len; i++) {
if (ids[i].toLowerCase() !== chain[i].id) {
let score = 0;
for (let i = 0; i < len; i++) {
const routeId = ids[i];
const routeChain = chain[i];
// Skip results where the route id does not match the chain at the same index
if (routeId.id.toLowerCase() !== routeChain.id) {
break;
}
if (routeId.params) {
const routeIdParams = Object.keys(routeId.params);
/**
* Only compare routes with the chain that have the same number of parameters.
*/
if (routeIdParams.length === routeChain.path.length) {
/**
* Maps the route's params into a path based on the path variable names,
* to compare against the route chain format.
*
* Before:
* ```ts
* {
* params: {
* s1: 'a',
* s2: 'b'
* }
* }
* ```
*
* After:
* ```ts
* [':s1',':s2']
* ```
*/
const pathWithParams = routeIdParams.map(key => `:${key}`);
for (let j = 0; j < pathWithParams.length; j++) {
// Skip results where the path variable is not a match
if (pathWithParams[j].toLowerCase() !== routeChain.path[j]) {
break;
}
// Weight path matches for the same index higher.
score++;
}
}
}
// Weight id matches
score++;
}
return i;
};
return score;
}
export const matchesPath = (inputPath: string[], chain: RouteChain): RouteChain | null => {
const segments = new RouterSegments(inputPath);
@@ -90,16 +134,16 @@ export const matchesPath = (inputPath: string[], chain: RouteChain): RouteChain
// Merges the route parameter objects.
// Returns undefined when both parameters are undefined.
export const mergeParams = (a: {[key: string]: any} | undefined, b: {[key: string]: any} | undefined): {[key: string]: any} | undefined => {
export const mergeParams = (a: { [key: string]: any } | undefined, b: { [key: string]: any } | undefined): { [key: string]: any } | undefined => {
return a || b ? { ...a, ...b } : undefined;
};
export const routerIDsToChain = (ids: RouteID[], chains: RouteChain[]): RouteChain | null => {
let match: RouteChain | null = null;
let maxMatches = 0;
const plainIDs = ids.map(i => i.id);
for (const chain of chains) {
const score = matchesIDs(plainIDs, chain);
const score = matchesIDs(ids, chain);
if (score > maxMatches) {
match = chain;
maxMatches = score;

View File

@@ -0,0 +1,102 @@
export const IonicSlides = (opts: any) => {
const { swiper, extendParams } = opts;
const slidesParams: any = {
effect: undefined,
direction: 'horizontal',
initialSlide: 0,
loop: false,
parallax: false,
slidesPerView: 1,
spaceBetween: 0,
speed: 300,
slidesPerColumn: 1,
slidesPerColumnFill: 'column',
slidesPerGroup: 1,
centeredSlides: false,
slidesOffsetBefore: 0,
slidesOffsetAfter: 0,
touchEventsTarget: 'container',
autoplay: false,
freeMode: false,
freeModeMomentum: true,
freeModeMomentumRatio: 1,
freeModeMomentumBounce: true,
freeModeMomentumBounceRatio: 1,
freeModeMomentumVelocityRatio: 1,
freeModeSticky: false,
freeModeMinimumVelocity: 0.02,
autoHeight: false,
setWrapperSize: false,
zoom: {
maxRatio: 3,
minRatio: 1,
toggle: false,
},
touchRatio: 1,
touchAngle: 45,
simulateTouch: true,
touchStartPreventDefault: false,
shortSwipes: true,
longSwipes: true,
longSwipesRatio: 0.5,
longSwipesMs: 300,
followFinger: true,
threshold: 0,
touchMoveStopPropagation: true,
touchReleaseOnEdges: false,
iOSEdgeSwipeDetection: false,
iOSEdgeSwipeThreshold: 20,
resistance: true,
resistanceRatio: 0.85,
watchSlidesProgress: false,
watchSlidesVisibility: false,
preventClicks: true,
preventClicksPropagation: true,
slideToClickedSlide: false,
loopAdditionalSlides: 0,
noSwiping: true,
runCallbacksOnInit: true,
coverflowEffect: {
rotate: 50,
stretch: 0,
depth: 100,
modifier: 1,
slideShadows: true
},
flipEffect: {
slideShadows: true,
limitRotation: true
},
cubeEffect: {
slideShadows: true,
shadow: true,
shadowOffset: 20,
shadowScale: 0.94
},
fadeEffect: {
crossFade: false
},
a11y: {
prevSlideMessage: 'Previous slide',
nextSlideMessage: 'Next slide',
firstSlideMessage: 'This is the first slide',
lastSlideMessage: 'This is the last slide'
}
}
if (swiper.pagination) {
slidesParams.pagination = {
type: 'bullets',
clickable: false,
hideOnClick: false,
}
}
if (swiper.scrollbar) {
slidesParams.scrollbar = {
hide: true
}
}
extendParams(slidesParams);
}

View File

@@ -120,6 +120,8 @@ export const IonicSwiper = {
name: 'ionic',
on: {
afterInit(swiper: any) {
console.warn('[Deprecation Warning]: The IonicSwiper module has been deprecated in favor of the IonSlides module. This change was made to better support the Swiper 7 release. The IonicSwiper module will be removed in Ionic 7.0. See https://ionicframework.com/docs/api/slides#migration for revised migration steps.');
setupSwiperInIonic(swiper);
}
}

View File

@@ -1,4 +1,4 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, Watch, h } from '@stencil/core';
import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, Watch, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { componentOnReady } from '../../utils/helpers'
@@ -24,8 +24,6 @@ export class Slides implements ComponentInterface {
private mutationO?: MutationObserver;
private readySwiper!: (swiper: SwiperInterface) => void;
private swiper: Promise<SwiperInterface> = new Promise(resolve => { this.readySwiper = resolve; });
private syncSwiper?: SwiperInterface;
private didInit = false;
@Element() el!: HTMLIonSlidesElement;
@@ -141,8 +139,7 @@ export class Slides implements ComponentInterface {
}
connectedCallback() {
// tslint:disable-next-line: strict-type-predicates
if (typeof MutationObserver !== 'undefined') {
if (Build.isBrowser) {
const mut = this.mutationO = new MutationObserver(() => {
if (this.swiperReady) {
this.update();
@@ -154,10 +151,7 @@ export class Slides implements ComponentInterface {
});
componentOnReady(this.el, () => {
if (!this.didInit) {
this.didInit = true;
this.initSwiper();
}
})
}
}
@@ -167,23 +161,6 @@ export class Slides implements ComponentInterface {
this.mutationO.disconnect();
this.mutationO = undefined;
}
/**
* We need to synchronously destroy
* swiper otherwise it is possible
* that it will be left in a
* destroyed state if connectedCallback
* is called multiple times
*/
const swiper = this.syncSwiper;
if (swiper !== undefined) {
swiper.destroy(true, true);
this.swiper = new Promise(resolve => { this.readySwiper = resolve; });
this.swiperReady = false;
this.syncSwiper = undefined;
}
this.didInit = false;
}
/**
@@ -369,7 +346,6 @@ export class Slides implements ComponentInterface {
await waitForSlides(this.el);
const swiper = new Swiper(this.el, finalOptions);
this.swiperReady = true;
this.syncSwiper = swiper;
this.readySwiper(swiper);
}
@@ -483,6 +459,8 @@ export class Slides implements ComponentInterface {
init: () => {
setTimeout(() => {
this.ionSlidesDidLoad.emit();
// Forces the swiper instance to update after initializing.
this.update();
}, 20);
},
slideChangeTransitionStart: this.ionSlideWillChange.emit,

View File

@@ -1,8 +1,8 @@
import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, State, Watch, h, readTask } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { Color, StyleEventDetail, TextareaChangeEventDetail } from '../../interface';
import { debounceEvent, findItemLabel, inheritAttributes, raf } from '../../utils/helpers';
import type { Color, StyleEventDetail, TextareaChangeEventDetail } from '../../interface';
import { debounceEvent, findItemLabel, inheritAriaAttributes, inheritAttributes, raf } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';
/**
@@ -214,7 +214,10 @@ export class Textarea implements ComponentInterface {
}
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['title']);
this.inheritedAttributes = {
...inheritAriaAttributes(this.el),
...inheritAttributes(this.el, ['title']),
};
}
componentDidLoad() {

View File

@@ -11,15 +11,15 @@ This guide will go over the recommended virtual scrolling packages for each fram
## Angular
For virtual scrolling options in Ionic Angular, please see [Angular Virtual Scroll Guide](../angular/virtual-scroll).
For virtual scrolling options in Ionic Angular, please see [Angular Virtual Scroll Guide](../../angular/virtual-scroll).
## React
For virtual scrolling options in Ionic React, please see [React Virtual Scroll Guide](../react/virtual-scroll).
For virtual scrolling options in Ionic React, please see [React Virtual Scroll Guide](../../react/virtual-scroll).
## Vue
For virtual scrolling options in Ionic Vue, please see [Vue Virtual Scroll Guide](../vue/virtual-scroll).
For virtual scrolling options in Ionic Vue, please see [Vue Virtual Scroll Guide](../../vue/virtual-scroll).
------

View File

@@ -201,3 +201,10 @@ ion-card-header.ion-color .ion-inherit-color {
.md .menu-content-push {
box-shadow: $menu-md-box-shadow;
}
// Safari/iOS 15 changes the appearance of input[type="date"].
// For backwards compatibility from Ionic 5/Safari 14 designs,
// we override the appearance only when using within an ion-input.
ion-input input::-webkit-date-and-time-value {
text-align: start;
}

View File

@@ -4,7 +4,7 @@
// Slides
// --------------------------------------------------
.swiper-container {
.swiper {
// These values are the same for iOS and MD
// We just do not add a .md or .ios class beforehand
@@ -33,11 +33,11 @@
// Pagination Bullets
// --------------------------------------------------
.swiper-pagination-bullet {
.swiper .swiper-pagination-bullet {
background: var(--bullet-background);
}
.swiper-pagination-bullet-active {
.swiper .swiper-pagination-bullet-active {
background: var(--bullet-background-active);
}
@@ -45,36 +45,29 @@
// Pagination Progress Bar
// --------------------------------------------------
.swiper-pagination-progressbar {
.swiper .swiper-pagination-progressbar {
background: var(--progress-bar-background);
}
.swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
.swiper .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
background: var(--progress-bar-background-active);
}
// Scrollbar
// --------------------------------------------------
.swiper-scrollbar {
.swiper .swiper-scrollbar {
background: var(--scroll-bar-background);
}
.swiper-scrollbar-drag {
.swiper .swiper-scrollbar-drag {
background: var(--scroll-bar-background-active);
}
// Slide
// --------------------------------------------------
ion-slide {
display: block;
width: 100%;
height: 100%;
}
.slide-zoom {
.swiper .slide-zoom {
display: block;
width: 100%;
@@ -82,7 +75,7 @@ ion-slide {
text-align: center;
}
.swiper-slide {
.swiper .swiper-slide {
// Center slide text vertically
display: flex;
@@ -101,7 +94,7 @@ ion-slide {
box-sizing: border-box;
}
.swiper-slide img {
.swiper .swiper-slide img {
width: auto;
max-width: 100%;
height: auto;

View File

@@ -14,3 +14,4 @@ export { LIFECYCLE_WILL_ENTER, LIFECYCLE_DID_ENTER, LIFECYCLE_WILL_LEAVE, LIFECY
export { menuController } from './utils/menu-controller';
export { alertController, actionSheetController, modalController, loadingController, pickerController, popoverController, toastController } from './utils/overlays';
export { IonicSwiper } from './components/slides/IonicSwiper';
export { IonicSlides } from './components/slides/IonicSlides';

View File

@@ -51,6 +51,74 @@ export const inheritAttributes = (el: HTMLElement, attributes: string[] = []) =>
return attributeObject;
}
/**
* List of available ARIA attributes + `role`.
* Removed deprecated attributes.
* https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes
*/
const ariaAttributes = [
'role',
'aria-activedescendant',
'aria-atomic',
'aria-autocomplete',
'aria-braillelabel',
'aria-brailleroledescription',
'aria-busy',
'aria-checked',
'aria-colcount',
'aria-colindex',
'aria-colindextext',
'aria-colspan',
'aria-controls',
'aria-current',
'aria-describedby',
'aria-description',
'aria-details',
'aria-disabled',
'aria-errormessage',
'aria-expanded',
'aria-flowto',
'aria-haspopup',
'aria-hidden',
'aria-invalid',
'aria-keyshortcuts',
'aria-label',
'aria-labelledby',
'aria-level',
'aria-live',
'aria-multiline',
'aria-multiselectable',
'aria-orientation',
'aria-owns',
'aria-placeholder',
'aria-posinset',
'aria-pressed',
'aria-readonly',
'aria-relevant',
'aria-required',
'aria-roledescription',
'aria-rowcount',
'aria-rowindex',
'aria-rowindextext',
'aria-rowspan',
'aria-selected',
'aria-setsize',
'aria-sort',
'aria-valuemax',
'aria-valuemin',
'aria-valuenow',
'aria-valuetext',
];
/**
* Returns an array of aria attributes that should be copied from
* the shadow host element to a target within the light DOM.
* @param el The element that the attributes should be copied from.
*/
export const inheritAriaAttributes = (el: HTMLElement) => {
return inheritAttributes(el, ariaAttributes);
};
export const addEventListener = (el: any, eventName: string, callback: any, opts?: any) => {
if (typeof (window as any) !== 'undefined') {
const win = window as any;
@@ -164,8 +232,8 @@ export const getAriaLabel = (componentEl: HTMLElement, inputId: string): { label
labelText = label.textContent;
label.setAttribute('aria-hidden', 'true');
// if there is no label, check to see if the user has provided
// one by setting an id on the component and using the label element
// if there is no label, check to see if the user has provided
// one by setting an id on the component and using the label element
} else if (componentId.trim() !== '') {
label = document.querySelector(`label[for="${componentId}"]`);

View File

@@ -1,4 +1,4 @@
import { inheritAttributes } from '../helpers';
import { inheritAttributes, inheritAriaAttributes } from '../helpers';
describe('inheritAttributes()', () => {
it('should create an attribute inheritance object', () => {
@@ -37,3 +37,29 @@ describe('inheritAttributes()', () => {
});
});
});
describe('inheritAriaAttributes()', () => {
it('should inherit ARIA attributes defined on the HTML element', () => {
const el = document.createElement('div');
el.setAttribute('aria-label', 'myLabel');
el.setAttribute('aria-describedby', 'myDescription');
const attributeObject = inheritAriaAttributes(el);
expect(attributeObject).toEqual({
'aria-label': 'myLabel',
'aria-describedby': 'myDescription',
});
});
it('should inherit the role attribute defined on the HTML element', () => {
const el = document.createElement('div');
el.setAttribute('role', 'button');
const attributeObject = inheritAriaAttributes(el);
expect(attributeObject).toEqual({
role: 'button',
});
});
});

View File

@@ -107,7 +107,7 @@ export const config: Config = {
{
type: 'docs-vscode',
file: 'dist/html.html-data.json',
sourceCodeBaseUrl: 'https://github.com/ionic-team/ionic/tree/master/core/',
sourceCodeBaseUrl: 'https://github.com/ionic-team/ionic/tree/main/core/',
},
{
type: 'dist',

View File

@@ -4,4 +4,4 @@ This package publishes the JSON data used to build the [Ionic API documentation]
## License
* [MIT](https://raw.githubusercontent.com/ionic-team/ionic/master/LICENSE)
* [MIT](https://raw.githubusercontent.com/ionic-team/ionic/main/LICENSE)

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/docs",
"version": "5.8.2",
"version": "5.9.4",
"description": "Pre-packaged API documentation for the Ionic docs.",
"main": "core.json",
"types": "core.d.ts",

View File

@@ -6,7 +6,8 @@
"release.dev": "node .scripts/release-dev.js",
"release.prepare": "node .scripts/prepare.js",
"release": "node .scripts/release.js",
"changelog": "conventional-changelog -p angular -i ./CHANGELOG.md -k core -s"
"changelog": "conventional-changelog -p angular -i ./CHANGELOG.md -k core -s",
"commitizenBranches": "git-branch-is -q --not -r \"^(main|next|release-)\""
},
"devDependencies": {
"@commitlint/cli": "^13.1.0",
@@ -18,6 +19,7 @@
"cz-conventional-changelog": "^3.3.0",
"execa": "^0.10.0",
"fs-extra": "^7.0.0",
"git-branch-is": "^4.0.0",
"husky": "^4.3.8",
"inquirer": "^6.0.0",
"listr": "^0.14.0",
@@ -34,8 +36,8 @@
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
"prepare-commit-msg": "exec < /dev/tty && git cz --hook || true"
"commit-msg": "npm run commitizenBranches --silent && commitlint -E HUSKY_GIT_PARAMS || true",
"prepare-commit-msg": "npm run commitizenBranches --silent && exec < /dev/tty && git cz --hook || true"
}
}
}

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/angular-server",
"version": "5.8.2",
"version": "5.9.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular-server",
"version": "5.8.2",
"version": "5.9.4",
"license": "MIT",
"devDependencies": {
"@angular/animations": "8.2.13",
@@ -16,7 +16,7 @@
"@angular/core": "8.2.13",
"@angular/platform-browser": "8.2.13",
"@angular/platform-server": "8.2.13",
"@ionic/core": "5.8.1",
"@ionic/core": "5.9.3",
"ng-packagr": "5.7.1",
"tslint": "^5.12.1",
"tslint-ionic-rules": "0.0.21",
@@ -137,9 +137,9 @@
}
},
"node_modules/@ionic/core": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.8.1.tgz",
"integrity": "sha512-twAPHhrGMYvHW0NgebMBfN70xDUkFxW+Zcxeus7jrhuJh1uWp/3MwawLi4InCwpyuFQiBo8yAaqC1hB1wTZmLA==",
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.3.tgz",
"integrity": "sha512-WM50vVxAAw+MQYqWXKUK4usBgkr7iQ9UWSb6t59mG4ZSy/fPAb7ZIdAjxY0U5i1ykk6A7Ur4B9ZJMpC/a7nnug==",
"dev": true,
"dependencies": {
"@stencil/core": "^2.4.0",
@@ -5424,9 +5424,9 @@
}
},
"@ionic/core": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.8.1.tgz",
"integrity": "sha512-twAPHhrGMYvHW0NgebMBfN70xDUkFxW+Zcxeus7jrhuJh1uWp/3MwawLi4InCwpyuFQiBo8yAaqC1hB1wTZmLA==",
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.3.tgz",
"integrity": "sha512-WM50vVxAAw+MQYqWXKUK4usBgkr7iQ9UWSb6t59mG4ZSy/fPAb7ZIdAjxY0U5i1ykk6A7Ur4B9ZJMpC/a7nnug==",
"dev": true,
"requires": {
"@stencil/core": "^2.4.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular-server",
"version": "5.8.2",
"version": "5.9.4",
"description": "Angular SSR Module for Ionic",
"keywords": [
"ionic",
@@ -49,7 +49,7 @@
"@angular/core": "8.2.13",
"@angular/platform-browser": "8.2.13",
"@angular/platform-server": "8.2.13",
"@ionic/core": "5.8.2",
"@ionic/core": "5.9.4",
"ng-packagr": "5.7.1",
"tslint": "^5.12.1",
"tslint-ionic-rules": "0.0.21",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/react-router",
"version": "5.8.2",
"version": "5.9.4",
"description": "React Router wrapper for @ionic/react",
"keywords": [
"ionic",
@@ -27,7 +27,8 @@
"lint": "tslint --project .",
"lint.fix": "tslint --project . --fix",
"tsc": "tsc -p .",
"test.spec": "jest --ci"
"test.spec": "jest --ci",
"sync": "sh ./scripts/sync.sh"
},
"main": "dist/index.js",
"module": "dist/index.esm.js",
@@ -39,15 +40,14 @@
"tslib": "*"
},
"peerDependencies": {
"@ionic/react": "5.8.2",
"@ionic/react": "5.9.4",
"react": ">=16.8.6",
"react-dom": ">=16.8.6",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1"
},
"devDependencies": {
"@ionic/core": "5.8.2",
"@ionic/react": "5.8.2",
"@ionic/react": "5.9.4",
"@rollup/plugin-node-resolve": "^8.1.0",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",

View File

@@ -0,0 +1,15 @@
# Copy ionic react dist
rm -rf node_modules/@ionic/react/dist node_modules/@ionic/react/css
cp -a ../react/dist node_modules/@ionic/react/dist
cp -a ../react/css node_modules/@ionic/react/css
cp -a ../react/package.json node_modules/@ionic/react/package.json
# Copy core dist
rm -rf node_modules/@ionic/core/dist node_modules/@ionic/core/loader
cp -a ../../core/dist node_modules/@ionic/core/dist
cp -a ../../core/loader node_modules/@ionic/core/loader
cp -a ../../core/package.json node_modules/@ionic/core/package.json
# Copy ionicons
rm -rf node_modules/ionicons
cp -a ../../core/node_modules/ionicons node_modules/ionicons

View File

File diff suppressed because it is too large Load Diff

View File

@@ -3,97 +3,57 @@
"version": "0.0.1",
"private": true,
"dependencies": {
"@babel/core": "7.8.4",
"@capacitor/android": "^2.2.0",
"@capacitor/core": "1.5.2",
"@capacitor/ios": "^2.2.0",
"@ionic/react": "5.6.3",
"@ionic/react-router": "^5.6.3",
"@svgr/webpack": "4.3.3",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^8.0.3",
"@types/jest": "^24.0.25",
"@types/node": "^12.12.24",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.9.4",
"@capacitor/app": "1.0.3",
"@capacitor/core": "3.2.4",
"@capacitor/haptics": "1.1.0",
"@capacitor/keyboard": "1.1.0",
"@capacitor/status-bar": "1.0.3",
"@ionic/react": "^5.8.3",
"@ionic/react-router": "^5.8.3",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.6.3",
"@types/jest": "^26.0.20",
"@types/node": "^12.19.15",
"@types/react": "^16.14.3",
"@types/react-dom": "^16.9.10",
"@types/react-router": "^5.1.8",
"@types/react-router-dom": "^5.1.3",
"@typescript-eslint/eslint-plugin": "^2.10.0",
"@typescript-eslint/parser": "^2.10.0",
"babel-jest": "^24.9.0",
"babel-loader": "^8.1.0",
"babel-plugin-named-asset-import": "^0.3.6",
"babel-preset-react-app": "^9.1.1",
"camelcase": "^5.3.1",
"case-sensitive-paths-webpack-plugin": "2.3.0",
"concurrently": "^6.0.0",
"cpy-cli": "^3.1.1",
"css-loader": "3.4.2",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
"eslint": "^6.6.0",
"eslint-config-react-app": "^5.2.0",
"eslint-loader": "3.0.3",
"eslint-plugin-flowtype": "4.6.0",
"eslint-plugin-import": "2.20.0",
"eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-react": "7.18.0",
"eslint-plugin-react-hooks": "^1.6.1",
"file-loader": "4.3.0",
"fs-extra": "^8.1.0",
"html-webpack-plugin": "4.0.0-beta.11",
"identity-obj-proxy": "3.0.0",
"ionicons": "^5.0.0",
"jest": "24.9.0",
"jest-environment-jsdom-fourteen": "1.0.1",
"jest-resolve": "24.9.0",
"jest-watch-typeahead": "0.4.2",
"mini-css-extract-plugin": "0.9.0",
"npm-watch": "^0.6.0",
"optimize-css-assets-webpack-plugin": "5.0.3",
"pnp-webpack-plugin": "1.6.0",
"postcss-flexbugs-fixes": "4.1.0",
"postcss-loader": "3.0.0",
"postcss-normalize": "8.0.1",
"postcss-preset-env": "6.7.0",
"postcss-safe-parser": "4.0.1",
"react": "^16.12.0",
"react-app-polyfill": "^1.0.6",
"react-dev-utils": "^10.2.0",
"react-dom": "^16.12.0",
"@types/react-router-dom": "^5.1.7",
"ionicons": "^5.4.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"resolve": "1.15.0",
"resolve-url-loader": "3.1.1",
"sass-loader": "8.0.2",
"semver": "6.3.0",
"source-map-loader": "^0.2.4",
"style-loader": "0.23.1",
"terser-webpack-plugin": "2.3.4",
"ts-pnp": "1.1.5",
"typescript": "3.7.4",
"url-loader": "2.3.0",
"wait-on": "^5.3.0",
"webpack": "4.41.5",
"webpack-dev-server": "3.10.2",
"webpack-manifest-plugin": "2.2.0",
"workbox-webpack-plugin": "4.3.1"
},
"watch": {
"copyRouter": "../src/ReactRouter/*.*"
"react-scripts": "^4.0.3",
"typescript": "^4.1.3",
"web-vitals": "^0.2.4",
"workbox-background-sync": "^5.1.4",
"workbox-broadcast-update": "^5.1.4",
"workbox-cacheable-response": "^5.1.4",
"workbox-core": "^5.1.4",
"workbox-expiration": "^5.1.4",
"workbox-google-analytics": "^5.1.4",
"workbox-navigation-preload": "^5.1.4",
"workbox-precaching": "^5.1.4",
"workbox-range-requests": "^5.1.4",
"workbox-routing": "^5.1.4",
"workbox-strategies": "^5.1.4",
"workbox-streams": "^5.1.4"
},
"scripts": {
"copyRouter": "cpy ../src/ReactRouter ./src/ReactRouter",
"start": "concurrently \"npm-watch copyRouter\" \"node scripts/start.js --no-cache\"",
"start": "npm run sync && react-scripts start",
"start.ci": "npm run sync && BROWSER=none react-scripts start",
"build": "node scripts/build.js",
"test": "cypress open",
"cypress": "node_modules/.bin/cypress run --headless --browser chrome",
"e2e": "npm run copyRouter && concurrently \"node scripts/start.js --no-cache\" \"wait-on http-get://localhost:3000 && npm run cypress\" --kill-others --success first",
"e2e": "npm run sync && concurrently \"SKIP_PREFLIGHT_CHECK=true react-scripts start\" \"wait-on http-get://localhost:3000 && npm run cypress\" --kill-others --success first",
"sync": "sh ./scripts/sync.sh"
},
"eslintConfig": {
"extends": "react-app"
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
@@ -108,64 +68,11 @@
]
},
"devDependencies": {
"@capacitor/cli": "1.5.2",
"cypress": "^6.8.0",
"react-scripts": "3.4.1"
"@capacitor/cli": "3.2.4",
"concurrently": "^6.3.0",
"cpy-cli": "^3.1.1",
"cypress": "^8.5.0",
"wait-on": "^6.0.0"
},
"description": "An Ionic project",
"jest": {
"roots": [
"<rootDir>/src"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts"
],
"setupFiles": [
"react-app-polyfill/jsdom"
],
"setupFilesAfterEnv": [
"<rootDir>/src/setupTests.ts"
],
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
],
"testEnvironment": "jest-environment-jsdom-fourteen",
"transform": {
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
},
"transformIgnorePatterns": [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$"
],
"modulePaths": [],
"moduleNameMapper": {
"^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"web.js",
"js",
"web.ts",
"ts",
"web.tsx",
"tsx",
"json",
"web.jsx",
"jsx",
"node"
],
"watchPlugins": [
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname"
]
},
"babel": {
"presets": [
"react-app"
]
}
"description": "An Ionic project"
}

View File

@@ -1,198 +0,0 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', (err) => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const path = require('path');
const chalk = require('react-dev-utils/chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const configFactory = require('../config/webpack.config');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Generate configuration
const config = configFactory('production');
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
return measureFileSizesBeforeBuild(paths.appBuild);
})
.then((previousFileSizes) => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' + chalk.cyan('// eslint-disable-next-line') + ' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrlOrPath;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(appPackage, publicUrl, publicPath, buildFolder, useYarn);
},
(err) => {
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
if (tscCompileOnError) {
console.log(
chalk.yellow(
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
)
);
printBuildError(err);
} else {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
}
)
.catch((err) => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
// We used to support resolving modules according to `NODE_PATH`.
// This now has been deprecated in favor of jsconfig/tsconfig.json
// This lets you use absolute paths in imports inside large monorepos:
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
console.log();
}
console.log('Creating an optimized production build...');
const compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
if (err) {
if (!err.message) {
return reject(err);
}
let errMessage = err.message;
// Add additional information for postcss errors
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
errMessage += '\nCompileError: Begins at CSS selector ' + err['postcssNode'].selector;
}
messages = formatWebpackMessages({
errors: [errMessage],
warnings: [],
});
} else {
messages = formatWebpackMessages(
stats.toJson({ all: false, warnings: true, errors: true })
);
}
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join('\n\n')));
}
if (
process.env.CI &&
(typeof process.env.CI !== 'string' || process.env.CI.toLowerCase() !== 'false') &&
messages.warnings.length
) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
)
);
return reject(new Error(messages.warnings.join('\n\n')));
}
return resolve({
stats,
previousFileSizes,
warnings: messages.warnings,
});
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: (file) => file !== paths.appHtml,
});
}

View File

@@ -1,138 +0,0 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', (err) => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const fs = require('fs');
const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const {
choosePort,
createCompiler,
prepareProxy,
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Tools like Cloud9 rely on this.
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';
if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST)
)}`
)
);
console.log(`If this was unintentional, check that you haven't mistakenly set it in your shell.`);
console.log(`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`);
console.log();
}
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
return choosePort(HOST, DEFAULT_PORT);
})
.then((port) => {
if (port == null) {
// We have not found a port.
return;
}
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const useTypeScript = fs.existsSync(paths.appTsConfig);
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
const urls = prepareUrls(protocol, HOST, port, paths.publicUrlOrPath.slice(0, -1));
const devSocket = {
warnings: (warnings) => devServer.sockWrite(devServer.sockets, 'warnings', warnings),
errors: (errors) => devServer.sockWrite(devServer.sockets, 'errors', errors),
};
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({
appName,
config,
devSocket,
urls,
useYarn,
useTypeScript,
tscCompileOnError,
webpack,
});
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic, paths.publicUrlOrPath);
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig(proxyConfig, urls.lanUrlForConfig);
const devServer = new WebpackDevServer(compiler, serverConfig);
// Launch WebpackDevServer.
devServer.listen(port, HOST, (err) => {
if (err) {
return console.log(err);
}
if (isInteractive) {
clearConsole();
}
// We used to support resolving modules according to `NODE_PATH`.
// This now has been deprecated in favor of jsconfig/tsconfig.json
// This lets you use absolute paths in imports inside large monorepos:
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
console.log();
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function (sig) {
process.on(sig, function () {
devServer.close();
process.exit();
});
});
})
.catch((err) => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});

View File

@@ -1,51 +0,0 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'test';
process.env.NODE_ENV = 'test';
process.env.PUBLIC_URL = '';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', (err) => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const jest = require('jest');
const execSync = require('child_process').execSync;
let argv = process.argv.slice(2);
function isInGitRepository() {
try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
function isInMercurialRepository() {
try {
execSync('hg --cwd . root', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
// Watch unless on CI or explicitly running all tests
if (
!process.env.CI &&
argv.indexOf('--watchAll') === -1 &&
argv.indexOf('--watchAll=false') === -1
) {
// https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
argv.push(hasSourceControl ? '--watch' : '--watchAll');
}
jest.run(argv);

View File

@@ -1,6 +1,6 @@
import { IonApp } from '@ionic/react';
import React from 'react';
import { Route, Redirect, BrowserRouter, Link } from 'react-router-dom';
import { Route } from 'react-router-dom';
/* Core CSS required for Ionic components to work properly */
import '@ionic/react/css/core.css';
@@ -21,7 +21,7 @@ import '@ionic/react/css/text-transformation.css';
/* Theme variables */
import './theme/variables.css';
import Main from './pages/Main';
import { IonReactRouter } from './ReactRouter/IonReactRouter';
import { IonReactRouter } from '@ionic/react-router';
import DynamicRoutes from './pages/dynamic-routes/DynamicRoutes';
import Routing from './pages/routing/Routing';
import MultipleTabs from './pages/muiltiple-tabs/MultipleTabs';

View File

@@ -15,7 +15,7 @@ import {
IonButton,
} from '@ionic/react';
import { Route, Redirect } from 'react-router';
import { IonReactRouter } from '../../ReactRouter/IonReactRouter';
import { IonReactRouter } from '@ionic/react-router';
import { triangle, square } from 'ionicons/icons';
const DynamicTabs: React.FC = () => {

View File

@@ -8,9 +8,7 @@ import {
IonButtons,
IonBackButton,
IonLabel,
IonCard,
IonButton,
useIonViewWillEnter,
} from '@ionic/react';
import { useParams, useLocation } from 'react-router';

View File

@@ -7,10 +7,9 @@ import {
IonListHeader,
IonMenu,
IonMenuToggle,
IonNote,
} from '@ionic/react';
import React from 'react';
import { bookmarkOutline, heartOutline, heartSharp, mailOutline, mailSharp } from 'ionicons/icons';
import { heartOutline, heartSharp, mailOutline, mailSharp } from 'ionicons/icons';
interface MenuProps {}

View File

@@ -10,7 +10,6 @@ import {
useIonViewWillEnter,
IonButton,
} from '@ionic/react';
import { Route } from 'react-router';
interface OtherPageProps {}

View File

@@ -5,6 +5,7 @@ const RedirectRouting: React.FC = () => {
const ionRouterContext = useContext(IonRouterContext);
useEffect(() => {
ionRouterContext.push('/routing/tabs', 'none');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};

View File

@@ -1,10 +1,7 @@
import React from 'react';
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonRouterOutlet,
IonSplitPane,
} from '@ionic/react';

Some files were not shown because too many files have changed in this diff Show More