Compare commits
6 Commits
datetime-r
...
FW-119
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32b679dbe3 | ||
|
|
dcea071daa | ||
|
|
9cab6052fb | ||
|
|
379e804fbf | ||
|
|
07f4121658 | ||
|
|
bf3426dad7 |
23
.github/CODEOWNERS
vendored
@@ -13,7 +13,30 @@
|
||||
|
||||
* @ionic-team/framework
|
||||
|
||||
# Frameworks
|
||||
|
||||
## Angular
|
||||
|
||||
/packages/angular/ @sean-perkins @thetaPC
|
||||
/packages/angular-server @sean-perkins @thetaPC
|
||||
/packages/angular/test @thetaPC
|
||||
|
||||
## Vue
|
||||
|
||||
/packages/vue/ @thetaPC
|
||||
/packages/vue-router/ @liamdebeasi @thetaPC
|
||||
/packages/vue/test/ @thetaPC
|
||||
/packages/vue-router/__tests__ @thetaPC
|
||||
|
||||
# Components
|
||||
|
||||
/core/src/components/datetime/ @sean-perkins
|
||||
|
||||
/core/src/components/nav/ @sean-perkins
|
||||
/core/src/components/nav-link/ @sean-perkins
|
||||
|
||||
# Utilities
|
||||
|
||||
/core/src/utils/content/ @sean-perkins
|
||||
/core/src/utils/gesture/ @liamdebeasi
|
||||
/core/src/utils/input-shims/ @liamdebeasi
|
||||
|
||||
@@ -728,7 +728,7 @@ To work around this, you should set an RTL class on the host of your component a
|
||||
<Host
|
||||
class={{
|
||||
'my-cmp-rtl': document.dir === 'rtl'
|
||||
}}
|
||||
})
|
||||
>
|
||||
...
|
||||
</Host>
|
||||
24
docs/CONTRIBUTING.md → .github/CONTRIBUTING.md
vendored
@@ -1,11 +1,10 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for your interest in contributing to the Ionic Framework! 🎉
|
||||
Thanks for your interest in contributing to the Ionic Framework! :tada:
|
||||
|
||||
- [Contributing Etiquette](#contributing-etiquette)
|
||||
- [Creating an Issue](#creating-an-issue)
|
||||
* [Creating a Good Code Reproduction](#creating-a-good-code-reproduction)
|
||||
- [Using VS Code on Windows](#using-vs-code-on-windows)
|
||||
- [Creating a Pull Request](#creating-a-pull-request)
|
||||
* [Requirements](#requirements)
|
||||
* [Setup](#setup)
|
||||
@@ -82,19 +81,6 @@ Without a reliable code reproduction, it is unlikely we will be able to resolve
|
||||
* **No secret code needed:** Creating a minimal reproduction of the issue prevents you from having to publish any proprietary code used in your project.
|
||||
* **Get help fixing the issue:** If we can reliably reproduce an issue, there is a good chance we will be able to address it.
|
||||
|
||||
## Using VS Code on Windows
|
||||
|
||||
To contribute on Windows, do the following:
|
||||
|
||||
- Configure VS Code to read/save files using line breaks (LF) instead of carriage returns (CRLF). Set it globally by navigating to: Settings -> Text Editor -> Files -> Eol. Set to `\n`.
|
||||
|
||||
- You can optionally use the following settings in your `.vscode/settings.json`:
|
||||
```json
|
||||
{ "files.eol": "\n" }
|
||||
```
|
||||
|
||||
- Check that the Git setting `core.autocrlf` is set to `false`: run `git config -l | grep autocrlf`. Switch it to false using: `git config --global core.autocrlf false`.
|
||||
- If you've already cloned the `ionic-framework` repo, the files may already be cached as LF. To undo this, you need to clean the cache files of the repository. Run the following (make sure you stage or commit your changes first): `git rm --cached -r .` then `git reset --hard`.
|
||||
|
||||
## Creating a Pull Request
|
||||
|
||||
@@ -260,14 +246,6 @@ npm install file:/~/ionic-vue-router-7.0.1.tgz
|
||||
|
||||
#### Lint Changes
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you are using a Windows machine, you will need to configure your local development environment to use the correct line endings.
|
||||
> - Check that the Git setting `core.autocrlf` is set to `false`: run `git config -l | grep autocrlf`. Switch it to false using: `git config --global core.autocrlf false`.
|
||||
> - If you've already cloned the `ionic-docs` repo, the files may already be cached as LF. To undo this, you need to clean the cache files of the repository. Run the following (make sure you stage or commit your changes first): `git rm --cached -r .` then `git reset --hard`.
|
||||
|
||||
|
||||
|
||||
|
||||
1. Run `npm run lint` to lint the TypeScript and Sass.
|
||||
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.
|
||||
3. To lint and fix only TypeScript errors, run `npm run lint.ts` and `npm run lint.ts.fix`, respectively.
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -25,7 +25,7 @@ Issue number: resolves #
|
||||
If this introduces a breaking change:
|
||||
1. Describe the impact and migration path for existing applications below.
|
||||
2. Update the BREAKING.md file with the breaking change.
|
||||
3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information.
|
||||
3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer for more information.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
4
.github/workflows/assign-issues.yml
vendored
@@ -11,8 +11,8 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: 'Auto-assign issue'
|
||||
uses: pozil/auto-assign-issue@65947009a243e6b3993edeef4e64df3ca85d760c # v1.14.0
|
||||
uses: pozil/auto-assign-issue@edee9537367a8fbc625d27f9e10aa8bad47b8723 # v1.13.0
|
||||
with:
|
||||
assignees: liamdebeasi, sean-perkins, brandyscarney, thetaPC
|
||||
assignees: liamdebeasi, sean-perkins, brandyscarney, amandaejohnston, thetaPC
|
||||
numOfAssignee: 1
|
||||
allowSelfAssign: false
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
core/src/components/**/*/*.png
|
||||
52
CHANGELOG.md
@@ -3,58 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [8.0.0](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-rc.2...v8.0.0) (2024-04-17)
|
||||
|
||||
**Note:** Version bump only for package ionic-framework
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [8.0.0-rc.2](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-rc.1...v8.0.0-rc.2) (2024-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dark-palette:** improve base colors ([#29239](https://github.com/ionic-team/ionic-framework/issues/29239)) ([4698d22](https://github.com/ionic-team/ionic-framework/commit/4698d22413966b59f9fa65b4e2533695cf00ed70)), closes [#29219](https://github.com/ionic-team/ionic-framework/issues/29219)
|
||||
* **form-controls:** adjust flex properties inside of an item ([#29328](https://github.com/ionic-team/ionic-framework/issues/29328)) ([9b59138](https://github.com/ionic-team/ionic-framework/commit/9b59138011fd1e71def209ec3a43fb7f91b58949)), closes [#29319](https://github.com/ionic-team/ionic-framework/issues/29319)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.8.5](https://github.com/ionic-team/ionic-framework/compare/v7.8.4...v7.8.5) (2024-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** improve sheet modal scrolling and gesture behavior ([#29312](https://github.com/ionic-team/ionic-framework/issues/29312)) ([9738228](https://github.com/ionic-team/ionic-framework/commit/9738228bc05abe3e2012e57b0e6b85f0ec06f66b)), closes [#24583](https://github.com/ionic-team/ionic-framework/issues/24583)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [8.0.0-rc.1](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-rc.0...v8.0.0-rc.1) (2024-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** use clamp for font sizes on circle shape ([#29200](https://github.com/ionic-team/ionic-framework/issues/29200)) ([4d6edee](https://github.com/ionic-team/ionic-framework/commit/4d6edee89c7bb2cb669d67730d7ddf24c78b3cb1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.8.4](https://github.com/ionic-team/ionic-framework/compare/v7.8.3...v7.8.4) (2024-04-10)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **styles:** compress distributed global stylesheets ([#29275](https://github.com/ionic-team/ionic-framework/issues/29275)) ([b3cd49b](https://github.com/ionic-team/ionic-framework/commit/b3cd49bf2219aacffc1ac34acbae7c76ef243765))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.8.3](https://github.com/ionic-team/ionic-framework/compare/v7.8.2...v7.8.3) (2024-04-03)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<a href="https://github.com/ionic-team/ionic-framework/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Ionic Framework is released under the MIT license." />
|
||||
</a>
|
||||
<a href="https://github.com/ionic-team/ionic/blob/main/docs/CONTRIBUTING.md">
|
||||
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">
|
||||
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome!" />
|
||||
</a>
|
||||
<a href="https://twitter.com/Ionicframework">
|
||||
@@ -38,7 +38,7 @@
|
||||
Documentation
|
||||
</a>
|
||||
<span> · </span>
|
||||
<a href="https://github.com/ionic-team/ionic/blob/main/docs/CONTRIBUTING.md">Contribute</a>
|
||||
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">Contribute</a>
|
||||
<span> · </span>
|
||||
<a href="https://blog.ionicframework.com/">Blog</a>
|
||||
<br />
|
||||
@@ -88,7 +88,7 @@ The Ionic Conference App is a full featured Ionic app. It is the perfect startin
|
||||
### Contributing
|
||||
|
||||
Thanks for your interest in contributing! Read up on our guidelines for
|
||||
[contributing](https://github.com/ionic-team/ionic/blob/main/docs/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.
|
||||
|
||||
|
||||
@@ -3,58 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [8.0.0](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-rc.2...v8.0.0) (2024-04-17)
|
||||
|
||||
**Note:** Version bump only for package @ionic/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [8.0.0-rc.2](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-rc.1...v8.0.0-rc.2) (2024-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dark-palette:** improve base colors ([#29239](https://github.com/ionic-team/ionic-framework/issues/29239)) ([4698d22](https://github.com/ionic-team/ionic-framework/commit/4698d22413966b59f9fa65b4e2533695cf00ed70)), closes [#29219](https://github.com/ionic-team/ionic-framework/issues/29219)
|
||||
* **form-controls:** adjust flex properties inside of an item ([#29328](https://github.com/ionic-team/ionic-framework/issues/29328)) ([9b59138](https://github.com/ionic-team/ionic-framework/commit/9b59138011fd1e71def209ec3a43fb7f91b58949)), closes [#29319](https://github.com/ionic-team/ionic-framework/issues/29319)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.8.5](https://github.com/ionic-team/ionic-framework/compare/v7.8.4...v7.8.5) (2024-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** improve sheet modal scrolling and gesture behavior ([#29312](https://github.com/ionic-team/ionic-framework/issues/29312)) ([9738228](https://github.com/ionic-team/ionic-framework/commit/9738228bc05abe3e2012e57b0e6b85f0ec06f66b)), closes [#24583](https://github.com/ionic-team/ionic-framework/issues/24583)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [8.0.0-rc.1](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-rc.0...v8.0.0-rc.1) (2024-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** use clamp for font sizes on circle shape ([#29200](https://github.com/ionic-team/ionic-framework/issues/29200)) ([4d6edee](https://github.com/ionic-team/ionic-framework/commit/4d6edee89c7bb2cb669d67730d7ddf24c78b3cb1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.8.4](https://github.com/ionic-team/ionic-framework/compare/v7.8.3...v7.8.4) (2024-04-10)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **styles:** compress distributed global stylesheets ([#29275](https://github.com/ionic-team/ionic-framework/issues/29275)) ([b3cd49b](https://github.com/ionic-team/ionic-framework/commit/b3cd49bf2219aacffc1ac34acbae7c76ef243765))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.8.3](https://github.com/ionic-team/ionic-framework/compare/v7.8.2...v7.8.3) (2024-04-03)
|
||||
|
||||
|
||||
@@ -549,7 +549,7 @@ ion-infinite-scroll-content,prop,loadingText,IonicSafeString | string | undefine
|
||||
|
||||
ion-input,scoped
|
||||
ion-input,prop,autocapitalize,string,'off',false,false
|
||||
ion-input,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
|
||||
ion-input,prop,autocomplete,"name" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "email" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "url" | "photo",'off',false,false
|
||||
ion-input,prop,autocorrect,"off" | "on",'off',false,false
|
||||
ion-input,prop,autofocus,boolean,false,false,false
|
||||
ion-input,prop,clearInput,boolean,false,false,false
|
||||
@@ -1173,7 +1173,7 @@ ion-row,shadow
|
||||
ion-searchbar,scoped
|
||||
ion-searchbar,prop,animated,boolean,false,false,false
|
||||
ion-searchbar,prop,autocapitalize,string,'off',false,false
|
||||
ion-searchbar,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
|
||||
ion-searchbar,prop,autocomplete,"name" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "email" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "url" | "photo",'off',false,false
|
||||
ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
|
||||
ion-searchbar,prop,cancelButtonIcon,string,config.get('backButtonIcon', arrowBackSharp) as string,false,false
|
||||
ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false
|
||||
|
||||
731
core/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "8.0.0",
|
||||
"version": "8.0.0-rc.0",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -54,13 +54,12 @@
|
||||
"@types/node": "^14.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||
"@typescript-eslint/parser": "^6.7.2",
|
||||
"chalk": "^5.3.0",
|
||||
"clean-css-cli": "^5.6.1",
|
||||
"domino": "^2.1.6",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-custom-rules": "file:custom-rules",
|
||||
"execa": "^8.0.1",
|
||||
"execa": "^5.0.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-cli": "^29.7.0",
|
||||
@@ -78,7 +77,7 @@
|
||||
"build.docs.json": "stencil build --docs-json dist/docs.json",
|
||||
"clean": "node scripts/clean.js",
|
||||
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
|
||||
"css.sass": "sass --embed-sources --style compressed src/css:./css",
|
||||
"css.sass": "sass --embed-sources src/css:./css",
|
||||
"eslint": "eslint src",
|
||||
"lint": "npm run lint.ts && npm run lint.sass && npm run prettier -- --write --cache",
|
||||
"lint.fix": "npm run lint.ts.fix && npm run lint.sass.fix && npm run prettier -- --write --cache",
|
||||
@@ -97,9 +96,10 @@
|
||||
"test.treeshake": "node scripts/treeshaking.js dist/index.js",
|
||||
"validate": "npm run lint && npm run test && npm run build && npm run test.treeshake",
|
||||
"docker.build": "docker build -t ionic-playwright .",
|
||||
"test.e2e.docker": "npm run docker.build && node ./scripts/docker.mjs",
|
||||
"test.e2e.docker": "npm run docker.build && docker run -it --rm -e DISPLAY=$(cat docker-display.txt) -v $(cat docker-display-volume.txt) --ipc=host --mount=type=bind,source=./,target=/ionic ionic-playwright npm run test.e2e --",
|
||||
"test.e2e.docker.update-snapshots": "npm run test.e2e.docker -- --update-snapshots",
|
||||
"test.e2e.docker.ci": "npm run docker.build && CI=true node ./scripts/docker.mjs"
|
||||
"test.e2e.docker.ci": "npm run docker.build && docker run -e CI='true' --rm --ipc=host --mount=type=bind,source=./,target=/ionic ionic-playwright npm run test.e2e --",
|
||||
"test.report": "npx playwright show-report"
|
||||
},
|
||||
"author": "Ionic Team",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { execa } from 'execa';
|
||||
import * as fs from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const removeNewline = (string) => {
|
||||
return string.replace(/(\r\n|\n|\r)/gm, "");
|
||||
}
|
||||
|
||||
const readConfigFile = (file) => {
|
||||
if (fs.existsSync(file)) {
|
||||
return fs.readFileSync(file, { encoding: 'utf-8' });
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// These files are optional, so we don't want to error if they don't exist
|
||||
const display = removeNewline(readConfigFile('docker-display.txt'));
|
||||
const displayVolume = removeNewline(readConfigFile('docker-display-volume.txt'));
|
||||
|
||||
// Using --mount requires an absolute path which is what this gives us.
|
||||
const pwd = resolve('./');
|
||||
|
||||
/**
|
||||
* -it will let the user gracefully kill the process using Ctrl+C (or equivalent)
|
||||
* -e DISPLAY and -v handle configuration for headed mode
|
||||
* --ipc=host is recommended when using Chromium to avoid out of memory crashes: https://playwright.dev/docs/ci#docker
|
||||
* --init is recommended to avoid zombie processes: https://playwright.dev/docs/ci#docker
|
||||
* --mount allow us to mount the local Ionic project inside of the Docker container so devs do not need to re-build the project in Docker.
|
||||
*/
|
||||
const args = ['run', '--rm', '--init', `-e DISPLAY=${display}`, `-v ${displayVolume}`, '--ipc=host', `--mount=type=bind,source=${pwd},target=/ionic`, 'ionic-playwright', 'npm run test.e2e --', ...process.argv.slice(2)];
|
||||
|
||||
// Set the CI env variable so Playwright uses the CI config
|
||||
if (process.env.CI) {
|
||||
args.splice(1, 0, '-e CI=true');
|
||||
/**
|
||||
* Otherwise, we should let the session be interactive locally. This will
|
||||
* not work on CI which is why we do not apply it there.
|
||||
*/
|
||||
} else {
|
||||
args.splice(1, 0, '-it');
|
||||
}
|
||||
|
||||
/**
|
||||
* While these config files are optional to run the tests, they are required to run
|
||||
* the tests in headed mode. Add a warning if dev tries to run headed tests without
|
||||
* the correct config files.
|
||||
*/
|
||||
const requestHeaded = process.argv.find(arg => arg.includes('headed'));
|
||||
const hasHeadedConfigFiles = display && displayVolume;
|
||||
if (requestHeaded && !hasHeadedConfigFiles) {
|
||||
console.warn(chalk.yellow.bold('\n⚠️ You are running tests in headed mode, but one or more of your headed config files was not found.\nPlease ensure that both docker-display.txt and docker-display-volume.txt have been created in the correct location.\n'));
|
||||
}
|
||||
|
||||
execa('docker', args, { shell: true, stdio: 'inherit' });
|
||||
@@ -1,4 +1,44 @@
|
||||
|
||||
# Core Scripts
|
||||
## Build
|
||||
|
||||
This file has been moved to [/docs/core/testing/preview-changes.md](/docs/core/testing/preview-changes.md).
|
||||
### 1. Clone ionic
|
||||
|
||||
git@github.com:ionic-team/ionic.git
|
||||
cd ionic
|
||||
|
||||
### 2. Run `npm install`
|
||||
|
||||
cd core
|
||||
npm install
|
||||
|
||||
|
||||
Notice that `@ionic/core` lives in `core`.
|
||||
|
||||
### 3. Run `npm start`
|
||||
|
||||
Make sure you are inside the `core` directory.
|
||||
|
||||
npm start
|
||||
|
||||
With the `dev` command, Ionic components will be built with [Stencil](https://stenciljs.com/), changes to source files are watched, a local http server will startup, and http://localhost:3333/ will open in a browser.
|
||||
|
||||
### 4. Preview
|
||||
|
||||
Navigate to http://localhost:3333/src/components/. Each component has small e2e apps found in the `test` directory, for example: http://localhost:3333/src/components/button/test/basic
|
||||
|
||||
As changes are made in an editor to source files, the e2e app will live-reload.
|
||||
|
||||
## How to contribute
|
||||
|
||||
1. `npm start` allows you to modify the components and have live reloading, just like another ionic app.
|
||||
|
||||
2. When everything looks good, run `npm run validate` to verify the tests linter and production build passes.
|
||||
|
||||
|
||||
# Deploy
|
||||
|
||||
1. `npm run prepare.deploy`
|
||||
2. Review/update changelog
|
||||
3. Commit updates using the package name and version number as the commit message.
|
||||
4. `npm run deploy`
|
||||
5. :tada:
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
}
|
||||
|
||||
:host(.in-item) {
|
||||
flex: 1 1 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -847,23 +847,6 @@ export class Datetime implements ComponentInterface {
|
||||
this.maxParts = parseMaxParts(max, defaultParts);
|
||||
};
|
||||
|
||||
private centerCalendarGrid = (workingMonth: HTMLElement) => {
|
||||
const { el, calendarBodyRef } = this;
|
||||
|
||||
if (!calendarBodyRef) return;
|
||||
|
||||
const monthRight = workingMonth.offsetLeft + workingMonth.offsetWidth;
|
||||
const elRight = el.offsetLeft + el.offsetWidth;
|
||||
|
||||
if (monthRight > elRight) {
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth;
|
||||
console.log('LTR', workingMonth)
|
||||
} else {
|
||||
calendarBodyRef.scrollLeft = -workingMonth.clientWidth;
|
||||
console.log('RTL', workingMonth)
|
||||
}
|
||||
}
|
||||
|
||||
private initializeCalendarListener = () => {
|
||||
const calendarBodyRef = this.calendarBodyRef;
|
||||
if (!calendarBodyRef) {
|
||||
@@ -885,11 +868,11 @@ export class Datetime implements ComponentInterface {
|
||||
* the months in a row allows us to mostly sidestep
|
||||
* that issue.
|
||||
*/
|
||||
const months = calendarBodyRef.querySelectorAll<HTMLElement>('.calendar-month');
|
||||
const months = calendarBodyRef.querySelectorAll('.calendar-month');
|
||||
|
||||
const startMonth = months[0];
|
||||
const workingMonth = months[1];
|
||||
const endMonth = months[2];
|
||||
const startMonth = months[0] as HTMLElement;
|
||||
const workingMonth = months[1] as HTMLElement;
|
||||
const endMonth = months[2] as HTMLElement;
|
||||
const mode = getIonMode(this);
|
||||
const needsiOSRubberBandFix = mode === 'ios' && typeof navigator !== 'undefined' && navigator.maxTouchPoints > 1;
|
||||
|
||||
@@ -900,7 +883,7 @@ export class Datetime implements ComponentInterface {
|
||||
* if element is not in viewport. Use scrollLeft instead.
|
||||
*/
|
||||
writeTask(() => {
|
||||
this.centerCalendarGrid(workingMonth);
|
||||
calendarBodyRef.scrollLeft = startMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
|
||||
const getChangedMonth = (parts: DatetimeParts): DatetimeParts | undefined => {
|
||||
const box = calendarBodyRef.getBoundingClientRect();
|
||||
@@ -970,7 +953,6 @@ export class Datetime implements ComponentInterface {
|
||||
* then we can return early.
|
||||
*/
|
||||
const newDate = getChangedMonth(this.workingParts);
|
||||
console.log(newDate,this.workingParts)
|
||||
if (!newDate) return;
|
||||
|
||||
const { month, day, year } = newDate;
|
||||
@@ -1011,7 +993,7 @@ export class Datetime implements ComponentInterface {
|
||||
year,
|
||||
});
|
||||
|
||||
this.centerCalendarGrid(workingMonth);
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
calendarBodyRef.style.removeProperty('overflow');
|
||||
|
||||
if (this.resolveForceDateScrolling) {
|
||||
@@ -1203,7 +1185,7 @@ export class Datetime implements ComponentInterface {
|
||||
*/
|
||||
const hasCalendarGrid = !preferWheel && ['date-time', 'time-date', 'date'].includes(presentation);
|
||||
if (minParts !== undefined && hasCalendarGrid && calendarBodyRef) {
|
||||
const workingMonth = calendarBodyRef.querySelector<HTMLElement>('.calendar-month:nth-of-type(1)');
|
||||
const workingMonth = calendarBodyRef.querySelector('.calendar-month:nth-of-type(1)');
|
||||
/**
|
||||
* We need to make sure the datetime is not in the process
|
||||
* of scrolling to a new datetime value if the value
|
||||
@@ -1218,7 +1200,7 @@ export class Datetime implements ComponentInterface {
|
||||
* and the resolveForceDateScrolling promise never resolves.
|
||||
*/
|
||||
if (workingMonth && forceRenderDate === undefined) {
|
||||
this.centerCalendarGrid(workingMonth);
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1482,24 +1464,16 @@ export class Datetime implements ComponentInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextMonth = calendarBodyRef.querySelector<HTMLElement>('.calendar-month:last-of-type');
|
||||
const nextMonth = calendarBodyRef.querySelector('.calendar-month:last-of-type');
|
||||
if (!nextMonth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const left = nextMonth.offsetWidth * 2;
|
||||
|
||||
if (!calendarBodyRef) return;
|
||||
|
||||
console.log('yay',left)
|
||||
|
||||
const monthRight = nextMonth.offsetLeft + nextMonth.offsetWidth;
|
||||
const elRight = this.el.offsetLeft + this.el.offsetWidth;
|
||||
const direction = monthRight > elRight ? 1 : -1;
|
||||
const left = (nextMonth as HTMLElement).offsetWidth * 2;
|
||||
|
||||
calendarBodyRef.scrollTo({
|
||||
top: 0,
|
||||
left: left * direction,
|
||||
left: left * (isRTL(this.el) ? -1 : 1),
|
||||
behavior: 'smooth',
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<title>Datetime - Basic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../css/palettes/dark.class.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
@@ -50,6 +49,158 @@
|
||||
ion-datetime {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
body.dark {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66, 140, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80, 200, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47, 223, 117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255, 213, 52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255, 73, 97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244, 245, 248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152, 154, 162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34, 36, 40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body.dark {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0, 0, 0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios body.dark ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
--ion-item-background: var(--ion-color-step-150);
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body.dark {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18, 18, 18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -99,12 +250,173 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-datetime></ion-datetime>
|
||||
<ion-datetime dir="rtl"></ion-datetime>
|
||||
<div dir="rtl">
|
||||
<ion-datetime></ion-datetime>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Inline</h2>
|
||||
<ion-datetime value="2020-03-14T14:23:00.000Z" id="inline-datetime"></ion-datetime>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Inline - No Default Value</h2>
|
||||
<ion-datetime id="inline-datetime-no-value"></ion-datetime>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Popover</h2>
|
||||
<ion-button onclick="presentPopover(defaultPopover, event)">Present Popover</ion-button>
|
||||
<ion-popover class="datetime-popover" id="default-popover">
|
||||
<ion-datetime></ion-datetime>
|
||||
</ion-popover>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Modal</h2>
|
||||
<ion-button onclick="presentModal()">Present Modal</ion-button>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Inline - Custom</h2>
|
||||
<ion-datetime show-default-buttons="true" color="danger" id="custom-datetime" show-clear-button="true">
|
||||
<span slot="title">Select Date</span>
|
||||
</ion-datetime>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Popover - Custom</h2>
|
||||
<ion-button id="open-popover">Present Popover</ion-button>
|
||||
<ion-popover class="datetime-popover" trigger="open-popover" id="custom-popover">
|
||||
<ion-datetime>
|
||||
<span slot="title">My Custom Title</span>
|
||||
<ion-buttons slot="buttons">
|
||||
<ion-button color="danger">Destroy</ion-button>
|
||||
<ion-button color="primary">Confirm</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-datetime>
|
||||
</ion-popover>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Modal - Custom</h2>
|
||||
<ion-button id="open-modal">Present Modal</ion-button>
|
||||
<ion-modal trigger="open-modal" id="modal">
|
||||
<ion-datetime>
|
||||
<span slot="title">My Custom Title</span>
|
||||
<ion-buttons slot="buttons">
|
||||
<ion-button color="danger">Destroy</ion-button>
|
||||
<ion-button color="primary">Confirm</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-datetime>
|
||||
</ion-modal>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>formatOptions</h2>
|
||||
<ion-datetime value="2020-03-14T14:23:00.000Z" id="format-options-datetime">
|
||||
<span slot="title">Select Date</span>
|
||||
</ion-datetime>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
<script>
|
||||
const datetimes = document.querySelectorAll('ion-datetime');
|
||||
const color = document.querySelector('#color');
|
||||
const locale = document.querySelector('#locale');
|
||||
const datetime = document.querySelector('ion-datetime');
|
||||
const buttons = document.querySelectorAll('ion-button');
|
||||
const titleToggle = document.querySelector('#titleToggle');
|
||||
const buttonsToggle = document.querySelector('#buttonsToggle');
|
||||
|
||||
locale.addEventListener('ionBlur', (ev) => {
|
||||
const value = ev.target.value;
|
||||
datetimes.forEach((datetime) => {
|
||||
datetime.locale = !!value ? value : 'default';
|
||||
});
|
||||
});
|
||||
|
||||
const darkModeCheckbox = document.querySelector('ion-checkbox');
|
||||
darkModeCheckbox.addEventListener('ionChange', (ev) => {
|
||||
if (ev.detail.checked) {
|
||||
document.body.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
});
|
||||
color.addEventListener('ionChange', (ev) => {
|
||||
datetime.color = ev.target.value;
|
||||
buttons.forEach((button) => {
|
||||
button.color = ev.target.value;
|
||||
});
|
||||
});
|
||||
|
||||
titleToggle.addEventListener('ionChange', (ev) => {
|
||||
datetimes.forEach((datetime) => {
|
||||
datetime.showDefaultTitle = ev.detail.checked;
|
||||
});
|
||||
});
|
||||
buttonsToggle.addEventListener('ionChange', (ev) => {
|
||||
datetimes.forEach((datetime) => {
|
||||
datetime.showDefaultButtons = ev.detail.checked;
|
||||
});
|
||||
});
|
||||
|
||||
datetimes.forEach((datetime) => {
|
||||
datetime.addEventListener('ionFocus', () => {
|
||||
console.log('Listen ionFocus: fired');
|
||||
});
|
||||
|
||||
datetime.addEventListener('ionBlur', () => {
|
||||
console.log('Listen ionBlur: fired');
|
||||
});
|
||||
});
|
||||
|
||||
const defaultPopover = document.querySelector('ion-popover#default-popover');
|
||||
const customPopover = document.querySelector('ion-popover#custom-popover');
|
||||
const presentPopover = (popover, ev) => {
|
||||
popover.event = ev;
|
||||
popover.showBackdrop = false;
|
||||
popover.isOpen = true;
|
||||
|
||||
const dismiss = () => {
|
||||
popover.isOpen = false;
|
||||
popover.event = undefined;
|
||||
|
||||
popover.removeEventListener('didDismiss', dismiss);
|
||||
};
|
||||
|
||||
popover.addEventListener('didDismiss', dismiss);
|
||||
};
|
||||
|
||||
const presentModal = async () => {
|
||||
const modal = await createModal();
|
||||
|
||||
await modal.present();
|
||||
};
|
||||
|
||||
const createModal = () => {
|
||||
// create component to open
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = `
|
||||
<ion-datetime show-default-buttons="true" color="danger">
|
||||
<span slot="title">Select Date</span>
|
||||
</ion-datetime>
|
||||
`;
|
||||
|
||||
// present the modal
|
||||
const modalElement = Object.assign(document.createElement('ion-modal'), {
|
||||
component: element,
|
||||
});
|
||||
|
||||
const app = document.querySelector('ion-app');
|
||||
app.appendChild(modalElement);
|
||||
return modalElement;
|
||||
};
|
||||
|
||||
const formatOptions = document.querySelector('#format-options-datetime');
|
||||
formatOptions.formatOptions = {
|
||||
time: { hour: '2-digit', minute: '2-digit' },
|
||||
date: { day: '2-digit', month: 'long', era: 'short' },
|
||||
};
|
||||
</script>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,28 +4,19 @@ import { configs, test } from '@utils/test/playwright';
|
||||
/**
|
||||
* This behavior does not vary across directions
|
||||
*/
|
||||
configs({ directions: ['ltr'], palettes: ['light', 'dark'] }).forEach(({ title, screenshot, config }) => {
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('datetime: color'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<div id="container" style="width: 250px;">
|
||||
<ion-datetime
|
||||
color="danger"
|
||||
value="2022-05-03"
|
||||
show-default-title="true"
|
||||
show-default-buttons="true"
|
||||
></ion-datetime>
|
||||
</div>
|
||||
`,
|
||||
config
|
||||
);
|
||||
await page.goto('/src/components/datetime/test/color', config);
|
||||
|
||||
const container = page.locator('#container');
|
||||
const datetime = page.locator('#color-datetime');
|
||||
|
||||
await page.locator('.datetime-ready').waitFor();
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-color`));
|
||||
|
||||
await expect(container).toHaveScreenshot(screenshot(`datetime-color`));
|
||||
await page.evaluate(() => document.body.classList.toggle('dark'));
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-color-dark`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 18 KiB |
@@ -5,7 +5,6 @@
|
||||
<title>Datetime - Color</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../css/palettes/dark.class.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
@@ -40,6 +39,163 @@
|
||||
#color-name::first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body.dark {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66, 140, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80, 200, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47, 223, 117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255, 213, 52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255, 73, 97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244, 245, 248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152, 154, 162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34, 36, 40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body.dark {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0, 0, 0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios body.dark ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
--ion-item-background: var(--ion-color-step-150);
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body.dark {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18, 18, 18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -75,15 +231,15 @@
|
||||
const colorDatetime = document.querySelector('#color-datetime');
|
||||
const colorName = document.querySelector('#color-name');
|
||||
const colorSelect = document.querySelector('ion-select');
|
||||
const darkModeCheckbox = document.querySelector('ion-checkbox');
|
||||
const darkModeToggle = document.querySelector('ion-checkbox');
|
||||
|
||||
colorSelect.addEventListener('ionChange', (ev) => {
|
||||
colorDatetime.color = ev.detail.value;
|
||||
colorName.innerHTML = ev.detail.value;
|
||||
});
|
||||
|
||||
darkModeCheckbox.addEventListener('ionChange', (ev) => {
|
||||
document.documentElement.classList.toggle('ion-palette-dark');
|
||||
darkModeToggle.addEventListener('ionChange', (ev) => {
|
||||
document.body.classList.toggle('dark');
|
||||
});
|
||||
</script>
|
||||
</ion-app>
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -1,6 +1,5 @@
|
||||
import { isIonContent, findClosestIonContent } from '@utils/content';
|
||||
import { createGesture } from '@utils/gesture';
|
||||
import { clamp, raf, getElementRoot } from '@utils/helpers';
|
||||
import { clamp, raf } from '@utils/helpers';
|
||||
|
||||
import type { Animation } from '../../../interface';
|
||||
import type { GestureDetail } from '../../../utils/gesture';
|
||||
@@ -143,35 +142,22 @@ export const createSheetGesture = (
|
||||
|
||||
const canStart = (detail: GestureDetail) => {
|
||||
/**
|
||||
* If we are swiping on the content, swiping should only be possible if the content
|
||||
* is scrolled all the way to the top so that we do not interfere with scrolling.
|
||||
*
|
||||
* We cannot assume that the `ion-content` target will remain consistent between swipes.
|
||||
* For example, when using ion-nav within a modal it is possible to swipe, push a view,
|
||||
* and then swipe again. The target content will not be the same between swipes.
|
||||
* If the sheet is fully expanded and
|
||||
* the user is swiping on the content,
|
||||
* the gesture should not start to
|
||||
* allow for scrolling on the content.
|
||||
*/
|
||||
const contentEl = findClosestIonContent(detail.event.target! as HTMLElement);
|
||||
const content = (detail.event.target! as HTMLElement).closest('ion-content');
|
||||
currentBreakpoint = getCurrentBreakpoint();
|
||||
|
||||
if (currentBreakpoint === 1 && contentEl) {
|
||||
/**
|
||||
* The modal should never swipe to close on the content with a refresher.
|
||||
* Note 1: We cannot solve this by making this gesture have a higher priority than
|
||||
* the refresher gesture as the iOS native refresh gesture uses a scroll listener in
|
||||
* addition to a gesture.
|
||||
*
|
||||
* Note 2: Do not use getScrollElement here because we need this to be a synchronous
|
||||
* operation, and getScrollElement is asynchronous.
|
||||
*/
|
||||
const scrollEl = isIonContent(contentEl) ? getElementRoot(contentEl).querySelector('.inner-scroll') : contentEl;
|
||||
const hasRefresherInContent = !!contentEl.querySelector('ion-refresher');
|
||||
return !hasRefresherInContent && scrollEl!.scrollTop === 0;
|
||||
if (currentBreakpoint === 1 && content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const onStart = (detail: GestureDetail) => {
|
||||
const onStart = () => {
|
||||
/**
|
||||
* If canDismiss is anything other than `true`
|
||||
* then users should be able to swipe down
|
||||
@@ -187,10 +173,11 @@ export const createSheetGesture = (
|
||||
canDismissBlocksGesture = baseEl.canDismiss !== undefined && baseEl.canDismiss !== true && minBreakpoint === 0;
|
||||
|
||||
/**
|
||||
* If we are pulling down, then it is possible we are pulling on the content.
|
||||
* We do not want scrolling to happen at the same time as the gesture.
|
||||
* If swiping on the content
|
||||
* we should disable scrolling otherwise
|
||||
* the sheet will expand and the content will scroll.
|
||||
*/
|
||||
if (detail.deltaY > 0 && contentEl) {
|
||||
if (contentEl) {
|
||||
contentEl.scrollY = false;
|
||||
}
|
||||
|
||||
@@ -206,16 +193,6 @@ export const createSheetGesture = (
|
||||
};
|
||||
|
||||
const onMove = (detail: GestureDetail) => {
|
||||
/**
|
||||
* If we are pulling down, then it is possible we are pulling on the content.
|
||||
* We do not want scrolling to happen at the same time as the gesture.
|
||||
* This accounts for when the user scrolls down, scrolls all the way up, and then
|
||||
* pulls down again such that the modal should start to move.
|
||||
*/
|
||||
if (detail.deltaY > 0 && contentEl) {
|
||||
contentEl.scrollY = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the change in gesture position on the Y axis,
|
||||
* compute where the offset of the animation should be
|
||||
@@ -337,17 +314,6 @@ export const createSheetGesture = (
|
||||
onDismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the sheet is going to be fully expanded then we should enable
|
||||
* scrolling immediately. The sheet modal animation takes ~500ms to finish
|
||||
* so if we wait until then there is a visible delay for when scrolling is
|
||||
* re-enabled. Native iOS allows for scrolling on the sheet modal as soon
|
||||
* as the gesture is released, so we align with that.
|
||||
*/
|
||||
if (contentEl && snapToBreakpoint === breakpoints[breakpoints.length - 1]) {
|
||||
contentEl.scrollY = true;
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
animation
|
||||
.onFinish(
|
||||
@@ -368,6 +334,14 @@ export const createSheetGesture = (
|
||||
currentBreakpoint = snapToBreakpoint;
|
||||
onBreakpointChange(currentBreakpoint);
|
||||
|
||||
/**
|
||||
* If the sheet is fully expanded, we can safely
|
||||
* enable scrolling again.
|
||||
*/
|
||||
if (contentEl && currentBreakpoint === breakpoints[breakpoints.length - 1]) {
|
||||
contentEl.scrollY = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backdrop should become enabled
|
||||
* after the backdropBreakpoint value
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../css/palettes/dark.always.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>
|
||||
@@ -37,10 +36,167 @@
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body.dark {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66, 140, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80, 200, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47, 223, 117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255, 213, 52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255, 73, 97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244, 245, 248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152, 154, 162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34, 36, 40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body.dark {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0, 0, 0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios body.dark ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
--ion-item-background: var(--ion-color-step-150);
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body.dark {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18, 18, 18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="dark">
|
||||
<ion-app>
|
||||
<div class="ion-page">
|
||||
<ion-header>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.2 KiB |
@@ -58,8 +58,6 @@ input {
|
||||
}
|
||||
|
||||
:host(.in-item) {
|
||||
flex: 1 1 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
@@ -74,3 +75,52 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, screenshot, c
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('title: level 1 heading'), () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
/**
|
||||
* Level 1 headings must be inside of a landmark (ion-header)
|
||||
*/
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>My Title</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
test('should not have accessibility violations with multiple h1 elements on a hidden page', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<div class="ion-page ion-page-hidden">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>My Title</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
</div>
|
||||
<div class="ion-page">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>My Title</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
</div>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
147
core/src/components/title/test/title.spec.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Header } from '../../header/header';
|
||||
import { Toolbar } from '../../toolbar/toolbar';
|
||||
import { ToolbarTitle } from '../title';
|
||||
|
||||
describe('title: a11y', () => {
|
||||
it('should add heading level 1 attributes when inside of a landmark', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Header, Toolbar, ToolbarTitle],
|
||||
html: `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
});
|
||||
|
||||
const title = page.body.querySelector('ion-title')!;
|
||||
|
||||
expect(title.getAttribute('role')).toBe('heading');
|
||||
expect(title.getAttribute('aria-level')).toBe('1');
|
||||
});
|
||||
it('should not override a custom role', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Header, Toolbar, ToolbarTitle],
|
||||
html: `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title role="article">Title 1</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
});
|
||||
const title = page.body.querySelector('ion-title')!;
|
||||
|
||||
expect(title.getAttribute('role')).toBe('article');
|
||||
expect(title.hasAttribute('aria-level')).toBe(false);
|
||||
});
|
||||
it('should not add heading level 1 attributes when outside of a landmark', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Header, Toolbar, ToolbarTitle],
|
||||
html: `
|
||||
<ion-title>Title</ion-title>
|
||||
`,
|
||||
});
|
||||
|
||||
const title = page.body.querySelector('ion-title')!;
|
||||
|
||||
expect(title.hasAttribute('role')).toBe(false);
|
||||
expect(title.hasAttribute('aria-level')).toBe(false);
|
||||
});
|
||||
it('at most one ion-title should have level 1 attributes', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Header, Toolbar, ToolbarTitle],
|
||||
html: `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title 1</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title 2</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
});
|
||||
|
||||
const titles = page.body.querySelectorAll('ion-title');
|
||||
|
||||
expect(titles[0].getAttribute('role')).toBe('heading');
|
||||
expect(titles[0].getAttribute('aria-level')).toBe('1');
|
||||
|
||||
expect(titles[1].hasAttribute('role')).toBe(false);
|
||||
expect(titles[1].hasAttribute('aria-level')).toBe(false);
|
||||
});
|
||||
it('should not add level 1 attributes if other level 1 headings exist', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Header, Toolbar, ToolbarTitle],
|
||||
html: `
|
||||
<h1>Title</h1>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title 1</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
});
|
||||
|
||||
const title = page.body.querySelector('ion-title')!;
|
||||
|
||||
expect(title.hasAttribute('role')).toBe(false);
|
||||
expect(title.hasAttribute('aria-level')).toBe(false);
|
||||
});
|
||||
it('should not add level 1 attributes if other level 1 attributes exist', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Header, Toolbar, ToolbarTitle],
|
||||
html: `
|
||||
<div role="heading" aria-level="1">Title</div>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title 1</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
});
|
||||
|
||||
const title = page.body.querySelector('ion-title')!;
|
||||
|
||||
expect(title.hasAttribute('role')).toBe(false);
|
||||
expect(title.hasAttribute('aria-level')).toBe(false);
|
||||
});
|
||||
|
||||
it('should have level 1 attributes even if there is a level 1 heading on another page', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Header, Toolbar, ToolbarTitle],
|
||||
html: `
|
||||
<div class="ion-page">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title 1</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
</div>
|
||||
|
||||
<div class="ion-page">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title 2</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
const pages = page.body.querySelectorAll('.ion-page');
|
||||
|
||||
pages.forEach((page) => {
|
||||
const title = page.querySelector('ion-title')!;
|
||||
|
||||
expect(title.getAttribute('role')).toBe('heading');
|
||||
expect(title.getAttribute('aria-level')).toBe('1');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
||||
import { Component, Element, Event, Host, Prop, Watch, h } from '@stencil/core';
|
||||
import { doc } from '@utils/browser';
|
||||
import { createColorClasses } from '@utils/theme';
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
@@ -56,11 +57,37 @@ export class ToolbarTitle implements ComponentInterface {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { el } = this;
|
||||
const mode = getIonMode(this);
|
||||
const size = this.getSize();
|
||||
|
||||
/**
|
||||
* If there is already a level one heading
|
||||
* within the context of the page then
|
||||
* do not add another one.
|
||||
*/
|
||||
const root = el.closest('.ion-page') ?? doc?.body;
|
||||
const hasHeading = root?.querySelector('h1, [role="heading"][aria-level="1"]');
|
||||
const hasRole = el.hasAttribute('role');
|
||||
|
||||
/**
|
||||
* The first `ion-title` on the page is considered
|
||||
* the heading. This can be customized by setting
|
||||
* role="heading" aria-level="1" on another element
|
||||
* or by using h1.
|
||||
*
|
||||
* Level 1 headings must be contained inside of a landmark,
|
||||
* so we check for ion-header which is typically the landmark.
|
||||
*/
|
||||
const isHeading =
|
||||
hasRole === false &&
|
||||
hasHeading === null &&
|
||||
root?.querySelector('ion-title') === el &&
|
||||
el?.closest('ion-header[role]') !== null;
|
||||
return (
|
||||
<Host
|
||||
role={isHeading ? 'heading' : null}
|
||||
aria-level={isHeading ? '1' : null}
|
||||
class={createColorClasses(this.color, {
|
||||
[mode]: true,
|
||||
[`title-${size}`]: true,
|
||||
|
||||
@@ -8,11 +8,162 @@
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../css/palettes/dark.class.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>
|
||||
|
||||
<style>
|
||||
/*
|
||||
* Dark Colors
|
||||
* ------------------
|
||||
*/
|
||||
|
||||
body.dark {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66, 140, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80, 200, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47, 223, 117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255, 213, 52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255, 73, 97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244, 245, 248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152, 154, 162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34, 36, 40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------
|
||||
*/
|
||||
|
||||
.ios body.dark {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0, 0, 0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-toolbar-background: #0d0d0d;
|
||||
|
||||
--ion-item-background: #1c1c1c;
|
||||
--ion-item-background-activated: #313131;
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* ------------------------------
|
||||
*/
|
||||
|
||||
.md body.dark {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18, 18, 18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1a1b1e;
|
||||
}
|
||||
|
||||
/* Optional CSS, this is added for the flashing that happens when toggling between themes */
|
||||
ion-item {
|
||||
--transition: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -105,7 +256,11 @@
|
||||
<script>
|
||||
const darkModeCheckbox = document.querySelector('ion-checkbox');
|
||||
darkModeCheckbox.addEventListener('ionChange', (ev) => {
|
||||
document.documentElement.classList.toggle('ion-palette-dark');
|
||||
if (ev.detail.checked) {
|
||||
document.body.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,70 +1,49 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ palettes: ['light', 'dark'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('toggle: enable-on-off-labels'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, auto);
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="container">
|
||||
<ion-toggle enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
</div>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const container = page.locator('#container');
|
||||
|
||||
await expect(container).toHaveScreenshot(screenshot(`toggle-on-off-labels`));
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('toggle: enableOnOffLabels'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/toggle/test/enable-on-off-labels`, config);
|
||||
});
|
||||
|
||||
test('should not have visual regressions with color', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, auto);
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
</style>
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setIonViewport();
|
||||
|
||||
<div id="container">
|
||||
<ion-toggle color="secondary" enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle color="secondary" enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
<ion-toggle color="success" enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle color="success" enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
<ion-toggle color="danger" enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle color="danger" enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
<ion-toggle color="tertiary" enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle color="tertiary" enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
<ion-toggle color="light" enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle color="light" enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
<ion-toggle color="medium" enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle color="medium" enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
<ion-toggle color="dark" enable-on-off-labels="true">Unchecked</ion-toggle>
|
||||
<ion-toggle color="dark" enable-on-off-labels="true" checked>Checked</ion-toggle>
|
||||
</div>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const container = page.locator('#container');
|
||||
|
||||
await expect(container).toHaveScreenshot(screenshot(`toggle-on-off-labels-color`));
|
||||
await expect(page).toHaveScreenshot(screenshot(`toggle-on-off-labels-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior does not vary across directions
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('toggle: dark mode'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/toggle/test/enable-on-off-labels`, config);
|
||||
});
|
||||
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent');
|
||||
const ionPopoverDidDismiss = await page.spyOnEvent('ionPopoverDidDismiss');
|
||||
|
||||
await page.click('#popover-trigger');
|
||||
await ionPopoverDidPresent.next();
|
||||
|
||||
await page.click('#dark-mode');
|
||||
|
||||
await page.evaluate(() => {
|
||||
const popover = document.querySelector('ion-popover');
|
||||
return popover?.dismiss();
|
||||
});
|
||||
await ionPopoverDidDismiss.next();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(screenshot(`toggle-on-off-labels-dark-mode-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 55 KiB |