mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 04:14:21 +08:00
test(react): add react e2e app
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
version: 2.1
|
||||
orbs:
|
||||
cypress: cypress-io/cypress@1.27.0
|
||||
cypress: cypress-io/cypress@1.29.0
|
||||
|
||||
aliases:
|
||||
- &restore-cache
|
||||
@ -400,6 +400,33 @@ jobs:
|
||||
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/test-app npm install
|
||||
working_directory: /tmp/workspace/packages/react/test-app
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- packages/react/test-app
|
||||
|
||||
test-react-e2e:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run sync
|
||||
working_directory: /tmp/workspace/packages/react/test-app
|
||||
- run:
|
||||
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/react/test-app npm run e2e
|
||||
working_directory: /tmp/workspace/packages/react/test-app
|
||||
|
||||
install-react-router-test-app:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
@ -413,7 +440,7 @@ jobs:
|
||||
paths:
|
||||
- packages/react-router/test-app
|
||||
|
||||
test-react-e2e:
|
||||
test-react-router-e2e:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
@ -543,6 +570,10 @@ workflows:
|
||||
requires: [build-core]
|
||||
- test-react-e2e:
|
||||
requires: [install-react-test-app, build-react, build-react-router]
|
||||
- install-react-router-test-app:
|
||||
requires: [build-core]
|
||||
- test-react-router-e2e:
|
||||
requires: [install-react-router-test-app, build-react, build-react-router]
|
||||
- build-vue:
|
||||
requires: [build-core]
|
||||
- build-vue-router:
|
||||
|
40462
packages/react-router/test-app/package-lock.json
generated
40462
packages/react-router/test-app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,97 +3,60 @@
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"watch": {
|
||||
"copyRouter": "../src/ReactRouter/*.*"
|
||||
},
|
||||
"scripts": {
|
||||
"copyRouter": "cpy ../src/ReactRouter ./src/ReactRouter",
|
||||
"start": "concurrently \"npm-watch copyRouter\" \"node scripts/start.js --no-cache\"",
|
||||
"start": "concurrently \"npm-watch copyRouter\" \"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 copyRouter && 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 +71,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"
|
||||
}
|
||||
|
198
packages/react-router/test-app/scripts/build.js
vendored
198
packages/react-router/test-app/scripts/build.js
vendored
@ -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,
|
||||
});
|
||||
}
|
138
packages/react-router/test-app/scripts/start.js
vendored
138
packages/react-router/test-app/scripts/start.js
vendored
@ -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);
|
||||
});
|
51
packages/react-router/test-app/scripts/test.js
vendored
51
packages/react-router/test-app/scripts/test.js
vendored
@ -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);
|
@ -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';
|
||||
|
@ -8,9 +8,7 @@ import {
|
||||
IonButtons,
|
||||
IonBackButton,
|
||||
IonLabel,
|
||||
IonCard,
|
||||
IonButton,
|
||||
useIonViewWillEnter,
|
||||
} from '@ionic/react';
|
||||
import { useParams, useLocation } from 'react-router';
|
||||
|
||||
|
@ -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 {}
|
||||
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
useIonViewWillEnter,
|
||||
IonButton,
|
||||
} from '@ionic/react';
|
||||
import { Route } from 'react-router';
|
||||
|
||||
interface OtherPageProps {}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -1,10 +1,7 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonPage,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
IonRouterOutlet,
|
||||
IonSplitPane,
|
||||
} from '@ionic/react';
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
IonButtons,
|
||||
IonBackButton,
|
||||
IonLabel,
|
||||
IonCard,
|
||||
IonButton,
|
||||
} from '@ionic/react';
|
||||
import { useParams } from 'react-router';
|
||||
|
@ -9,13 +9,9 @@ import {
|
||||
IonItem,
|
||||
IonLabel,
|
||||
useIonViewWillEnter,
|
||||
useIonViewWillLeave,
|
||||
useIonViewDidEnter,
|
||||
IonButtons,
|
||||
IonMenuButton,
|
||||
IonInput,
|
||||
IonButton,
|
||||
useIonRouter,
|
||||
IonRouterContext,
|
||||
} from '@ionic/react';
|
||||
import './Tab1.css';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonHeader,
|
||||
|
@ -19,9 +19,9 @@ import {
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import { triangle, square } from 'ionicons/icons';
|
||||
|
||||
interface TabsContext {}
|
||||
interface TabsContextProps {}
|
||||
|
||||
const TabsContext: React.FC<TabsContext> = () => {
|
||||
const TabsContext: React.FC<TabsContextProps> = () => {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet id="tabs">
|
||||
|
@ -18,9 +18,9 @@ import {
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import { triangle, square } from 'ionicons/icons';
|
||||
|
||||
interface Tabs {}
|
||||
interface TabsProps {}
|
||||
|
||||
const Tabs: React.FC<Tabs> = () => {
|
||||
const Tabs: React.FC<TabsProps> = () => {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet id="tabs">
|
||||
|
@ -13,14 +13,13 @@ import {
|
||||
IonBackButton,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonButton,
|
||||
} from '@ionic/react';
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import { triangle, square } from 'ionicons/icons';
|
||||
|
||||
interface TabsSecondary {}
|
||||
interface TabsSecondaryProps {}
|
||||
|
||||
const TabsSecondary: React.FC<TabsSecondary> = () => {
|
||||
const TabsSecondary: React.FC<TabsSecondaryProps> = () => {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet id="tabs-secondary">
|
||||
|
@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
@ -13,7 +17,10 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react"
|
||||
"jsx": "react-jsx",
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
|
@ -78,6 +78,7 @@
|
||||
"<rootDir>/jest.setup.js"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"cypress",
|
||||
"node_modules",
|
||||
"dist-transpiled",
|
||||
"dist",
|
||||
|
30
packages/react/test-app/.gitignore
vendored
Normal file
30
packages/react/test-app/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
cypress/videos
|
10
packages/react/test-app/capacitor.config.ts
Normal file
10
packages/react/test-app/capacitor.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { CapacitorConfig } from '@capacitor/cli';
|
||||
|
||||
const config: CapacitorConfig = {
|
||||
appId: 'io.ionic.starter',
|
||||
appName: 'test-app',
|
||||
webDir: 'build',
|
||||
bundledWebRuntime: false
|
||||
};
|
||||
|
||||
export default config;
|
3
packages/react/test-app/cypress.json
Normal file
3
packages/react/test-app/cypress.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3000"
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
describe('IonActionSheet', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-components/actionsheet');
|
||||
});
|
||||
|
||||
it('display action sheet', () => {
|
||||
//show action sheet
|
||||
cy.get('ion-button').contains('Show ActionSheet').click();
|
||||
cy.get('ion-action-sheet').contains('Action Sheet');
|
||||
cy.get('ion-action-sheet').get('button').contains('Ok');
|
||||
cy.get('ion-action-sheet').get('button').contains('Cancel');
|
||||
|
||||
//click ok
|
||||
cy.get('ion-action-sheet').get('button:contains("Ok")').click();
|
||||
cy.get('div').contains('Ok clicked');
|
||||
cy.get('ion-action-sheet').should('not.exist');
|
||||
});
|
||||
|
||||
it('display action and call dismiss to close it', () => {
|
||||
//show action sheet
|
||||
cy.get('ion-button').contains('Show ActionSheet, hide after 250 ms').click();
|
||||
cy.get('ion-action-sheet').contains('Action Sheet');
|
||||
|
||||
//verify action sheet is gone
|
||||
cy.get('ion-action-sheet').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,27 @@
|
||||
describe('IonAlert', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-components/alert');
|
||||
});
|
||||
|
||||
it('display alert', () => {
|
||||
//show alert
|
||||
cy.get('ion-button').contains('Show Alert').click();
|
||||
cy.get('ion-alert').contains('Alert');
|
||||
cy.get('ion-alert').get('button').contains('Ok');
|
||||
cy.get('ion-alert').get('button').contains('Cancel');
|
||||
|
||||
//click ok
|
||||
cy.get('ion-alert').get('button:contains("Ok")').click();
|
||||
cy.get('div').contains('Ok clicked');
|
||||
cy.get('ion-alert').should('not.exist');
|
||||
});
|
||||
|
||||
it('display alert and call dismiss to close it', () => {
|
||||
//show alert
|
||||
cy.get('ion-button').contains('Show Alert, hide after 250 ms').click();
|
||||
cy.get('ion-alert').contains('Alert');
|
||||
|
||||
//verify alert is gone
|
||||
cy.get('ion-alert').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,23 @@
|
||||
describe('IonLoading', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-components/loading');
|
||||
});
|
||||
|
||||
it('display loading', () => {
|
||||
//show loading
|
||||
cy.get('ion-button').contains('Show Loading').click();
|
||||
cy.get('ion-loading').contains('Loading');
|
||||
|
||||
//loading goes away after 1s
|
||||
cy.get('ion-loading').should('not.exist');
|
||||
});
|
||||
|
||||
it('display loading and call dismiss to close it', () => {
|
||||
//show loading
|
||||
cy.get('ion-button').contains('Show Loading, hide after 250 ms').click();
|
||||
cy.get('ion-loading').contains('Loading');
|
||||
|
||||
//verify loading is gone
|
||||
cy.get('ion-loading').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
describe('IonModal', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-components/modal');
|
||||
});
|
||||
|
||||
it('display modal', () => {
|
||||
//show modal
|
||||
cy.get('ion-button').contains('Show Modal').click();
|
||||
cy.get('ion-modal ion-title').contains('My Modal');
|
||||
|
||||
//increment count
|
||||
cy.get('ion-button').contains('Increment Count').click();
|
||||
cy.get('ion-button').contains('Increment Count').click();
|
||||
cy.get('ion-modal').contains('Count in modal: 2');
|
||||
|
||||
//close modal
|
||||
cy.get('ion-button').contains('Close').click();
|
||||
cy.get('ion-modal').should('not.exist');
|
||||
|
||||
//verify count on main page was updated
|
||||
cy.contains('Count: 2');
|
||||
});
|
||||
|
||||
it('display modal and call dismiss to close it', () => {
|
||||
//show modal
|
||||
cy.get('ion-button').contains('Show Modal, hide after 250 ms').click();
|
||||
cy.get('ion-modal ion-title').contains('My Modal');
|
||||
|
||||
//verify modal is gone
|
||||
cy.get('ion-modal').should('not.exist');
|
||||
});
|
||||
|
||||
it('display modal with context', () => {
|
||||
//show modal
|
||||
cy.get('ion-button').contains('Show Modal with Context').click();
|
||||
|
||||
//verify context value is overriden value
|
||||
cy.get('ion-modal div').contains('overriden value')
|
||||
});
|
||||
});
|
@ -0,0 +1,26 @@
|
||||
describe('IonPicker', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-components/picker');
|
||||
});
|
||||
|
||||
it('display picker', () => {
|
||||
//show picker
|
||||
cy.get('ion-button').contains('Show Picker').click();
|
||||
cy.get('ion-picker').contains('Bird').click();
|
||||
cy.get('ion-picker').contains('Bike').click();
|
||||
cy.get('ion-picker button').contains('Confirm').click();
|
||||
cy.get('ion-picker').should('not.exist');
|
||||
|
||||
//confirm value
|
||||
cy.get('div').contains('Selected Value: bird, bike');
|
||||
});
|
||||
|
||||
it('display picker and call dismiss to close it', () => {
|
||||
//show picker
|
||||
cy.get('ion-button').contains('Show Picker, hide after 250 ms').click();
|
||||
cy.get('ion-picker').contains('Cat');
|
||||
|
||||
//verify picker is gone
|
||||
cy.get('ion-picker').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,25 @@
|
||||
describe('IonPopover', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-components/popover');
|
||||
});
|
||||
|
||||
it('display popover', () => {
|
||||
//show popover
|
||||
cy.get('ion-button').contains('Show Popover').click();
|
||||
cy.get('ion-popover ion-list-header').contains('Ionic');
|
||||
|
||||
//close popover
|
||||
cy.get('ion-item').contains('Close').click();
|
||||
cy.get('ion-popover').should('not.exist');
|
||||
});
|
||||
|
||||
it('display popover and call dismiss to close it', () => {
|
||||
//show popover
|
||||
cy.get('ion-button').contains('Show Popover, hide after 250 ms').click();
|
||||
cy.get('ion-popover ion-list-header').contains('Ionic');
|
||||
|
||||
//verify popover is gone
|
||||
cy.get('ion-popover').should('not.exist');
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,22 @@
|
||||
describe('IonToast', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-components/toast');
|
||||
});
|
||||
|
||||
it('display toast', () => {
|
||||
//show toast
|
||||
cy.get('ion-button').contains('Show Toast').click();
|
||||
cy.get('ion-toast');
|
||||
cy.get('ion-toast').shadow().contains('Hello from a toast!');
|
||||
cy.get('ion-toast').shadow().find('button').contains('hide').click();
|
||||
cy.get('ion-toast').should('not.exist');
|
||||
});
|
||||
|
||||
it('display toast and call dismiss to close it', () => {
|
||||
//show toast
|
||||
cy.get('ion-button').contains('Show Toast, call dismiss in 250 ms').click();
|
||||
cy.get('ion-toast');
|
||||
cy.get('ion-toast').shadow().contains('Hello from a toast!');
|
||||
cy.get('ion-toast').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
describe('useIonActionSheet', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-hooks/actionsheet');
|
||||
});
|
||||
|
||||
it('display action sheet using options', () => {
|
||||
//show action sheet
|
||||
cy.get('ion-button').contains('Show ActionSheet with options').click();
|
||||
cy.get('ion-action-sheet').contains('Action Sheet');
|
||||
cy.get('ion-action-sheet').get('button').contains('Ok');
|
||||
cy.get('ion-action-sheet').get('button').contains('Cancel');
|
||||
|
||||
//click ok
|
||||
cy.get('ion-action-sheet').get('button:contains("Ok")').click();
|
||||
cy.get('div').contains('Ok clicked');
|
||||
cy.get('ion-action-sheet').should('not.exist');
|
||||
});
|
||||
|
||||
it('display action sheet using params', () => {
|
||||
//show action sheet
|
||||
cy.get('ion-button').contains('Show ActionSheet with params').click();
|
||||
cy.get('ion-action-sheet').contains('Action Sheet');
|
||||
cy.get('ion-action-sheet').get('button').contains('Ok');
|
||||
cy.get('ion-action-sheet').get('button').contains('Cancel');
|
||||
|
||||
//click ok
|
||||
cy.get('ion-action-sheet').get('button:contains("Ok")').click();
|
||||
cy.get('div').contains('Ok clicked');
|
||||
cy.get('ion-action-sheet').should('not.exist');
|
||||
});
|
||||
|
||||
it('display action and call dismiss to close it', () => {
|
||||
//show action sheet
|
||||
cy.get('ion-button').contains('Show ActionSheet, hide after 250 ms').click();
|
||||
cy.get('ion-action-sheet').contains('Action Sheet');
|
||||
|
||||
//verify action sheet is gone
|
||||
cy.get('ion-action-sheet').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,39 @@
|
||||
describe('useIonAlert', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-hooks/alert');
|
||||
});
|
||||
|
||||
it('display alert using options', () => {
|
||||
//show alert
|
||||
cy.get('ion-button').contains('Show Alert with options').click();
|
||||
cy.get('ion-alert').contains('Alert');
|
||||
cy.get('ion-alert').get('button').contains('Ok');
|
||||
cy.get('ion-alert').get('button').contains('Cancel');
|
||||
|
||||
//click ok
|
||||
cy.get('ion-alert').get('button:contains("Ok")').click();
|
||||
cy.get('div').contains('Ok clicked');
|
||||
cy.get('ion-alert').should('not.exist');
|
||||
});
|
||||
|
||||
it('display alert using params', () => {
|
||||
//show alert
|
||||
cy.get('ion-button').contains('Show Alert with params').click();
|
||||
cy.get('ion-alert').contains('Hello!');
|
||||
cy.get('ion-alert').get('button').contains('Ok');
|
||||
|
||||
//click ok
|
||||
cy.get('ion-alert').get('button:contains("Ok")').click();
|
||||
cy.get('div').contains('Ok clicked');
|
||||
cy.get('ion-alert').should('not.exist');
|
||||
});
|
||||
|
||||
it('display alert and call dismiss to close it', () => {
|
||||
//show alert
|
||||
cy.get('ion-button').contains('Show Alert, hide after 250 ms').click();
|
||||
cy.get('ion-alert').contains('Hello!');
|
||||
|
||||
//verify alert is gone
|
||||
cy.get('ion-alert').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,32 @@
|
||||
describe('useIonLoading', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-hooks/loading');
|
||||
});
|
||||
|
||||
it('display loading using options', () => {
|
||||
//show loading
|
||||
cy.get('ion-button').contains('Show Loading with options').click();
|
||||
cy.get('ion-loading').contains('Loading');
|
||||
|
||||
//loading goes away after 1s
|
||||
cy.get('ion-loading').should('not.exist');
|
||||
});
|
||||
|
||||
it('display loading using params', () => {
|
||||
//show loading
|
||||
cy.get('ion-button').contains('Show Loading with params').click();
|
||||
cy.get('ion-loading').contains('Loading');
|
||||
|
||||
//loading goes away after 1s
|
||||
cy.get('ion-loading').should('not.exist');
|
||||
});
|
||||
|
||||
it('display loading and call dismiss to close it', () => {
|
||||
//show loading
|
||||
cy.get('ion-button').contains('Show Loading, hide after 250 ms').click();
|
||||
cy.get('ion-loading').contains('Loading');
|
||||
|
||||
//verify loading is gone
|
||||
cy.get('ion-loading').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
describe('useIonModal', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-hooks/modal');
|
||||
});
|
||||
|
||||
it('display modal using component param', () => {
|
||||
//show modal
|
||||
cy.get('ion-button').contains('Show Modal using component param').click();
|
||||
cy.get('ion-modal ion-title').contains('My Component Modal');
|
||||
|
||||
//increment count
|
||||
cy.get('ion-button').contains('Increment Count').click();
|
||||
cy.get('ion-button').contains('Increment Count').click();
|
||||
cy.get('ion-modal').contains('Count in modal: 2');
|
||||
|
||||
//close modal
|
||||
cy.get('ion-button').contains('Close').click();
|
||||
cy.get('ion-modal').should('not.exist');
|
||||
|
||||
//verify count on main page was updated
|
||||
cy.contains('Count: 2');
|
||||
});
|
||||
|
||||
it('display modal using element param', () => {
|
||||
//show modal
|
||||
cy.get('ion-button').contains('Show Modal using element param').click();
|
||||
cy.get('ion-modal ion-title').contains('My Element Modal');
|
||||
|
||||
//increment count
|
||||
cy.get('ion-button').contains('Increment Count').click();
|
||||
cy.get('ion-button').contains('Increment Count').click();
|
||||
cy.get('ion-modal').contains('Count in modal: 2');
|
||||
|
||||
//close modal
|
||||
cy.get('ion-button').contains('Close').click();
|
||||
cy.get('ion-modal').should('not.exist');
|
||||
|
||||
//verify count on main page was updated
|
||||
cy.contains('Count: 2');
|
||||
});
|
||||
|
||||
it('display modal and call dismiss to close it', () => {
|
||||
//show modal
|
||||
cy.get('ion-button').contains('Show Modal, hide after 250 ms').click();
|
||||
cy.get('ion-modal ion-title').contains('My Element Modal');
|
||||
|
||||
//verify modal is gone
|
||||
cy.get('ion-modal').should('not.exist');
|
||||
});
|
||||
|
||||
|
||||
// This test should pass in v6, skipping until merged with v6
|
||||
it.skip('display modal with context', () => {
|
||||
//show modal
|
||||
cy.get('ion-button').contains('Show Modal with Context').click();
|
||||
cy.get('ion-modal ion-title').contains('My Element Modal');
|
||||
|
||||
//verify context value is overriden value
|
||||
cy.get('div').contains('overriden value')
|
||||
});
|
||||
});
|
@ -0,0 +1,37 @@
|
||||
describe('useIonPicker', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-hooks/picker');
|
||||
});
|
||||
|
||||
it('display picker using options', () => {
|
||||
//show picker
|
||||
cy.get('ion-button').contains('Show Picker with options').click();
|
||||
cy.get('ion-picker').contains('Cat').click();
|
||||
cy.get('ion-picker button').contains('Confirm').click();
|
||||
cy.get('ion-picker').should('not.exist');
|
||||
|
||||
//confirm value
|
||||
cy.get('div').contains('Selected Value: cat');
|
||||
});
|
||||
|
||||
it('display picker using params', () => {
|
||||
//show picker
|
||||
cy.get('ion-button').contains('Show Picker with params').click();
|
||||
cy.get('ion-picker').contains('Bird').click();
|
||||
cy.get('ion-picker').contains('Bike').click();
|
||||
cy.get('ion-picker button').contains('Confirm').click();
|
||||
cy.get('ion-picker').should('not.exist');
|
||||
|
||||
//confirm value
|
||||
cy.get('div').contains('Selected Value: bird, bike');
|
||||
});
|
||||
|
||||
it('display picker and call dismiss to close it', () => {
|
||||
//show picker
|
||||
cy.get('ion-button').contains('Show Picker, hide after 250 ms').click();
|
||||
cy.get('ion-picker').contains('Cat');
|
||||
|
||||
//verify picker is gone
|
||||
cy.get('ion-picker').should('not.exist');
|
||||
});
|
||||
});
|
@ -0,0 +1,35 @@
|
||||
describe('useIonPopover', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-hooks/popover');
|
||||
});
|
||||
|
||||
it('display popover using component param', () => {
|
||||
//show popover
|
||||
cy.get('ion-button').contains('Show Popover with component param').click();
|
||||
cy.get('ion-popover ion-list-header').contains('Ionic');
|
||||
|
||||
//close popover
|
||||
cy.get('ion-item').contains('Close').click();
|
||||
cy.get('ion-popover').should('not.exist');
|
||||
});
|
||||
|
||||
it('display popover using element param', () => {
|
||||
//show popover
|
||||
cy.get('ion-button').contains('Show Popover with element param').click();
|
||||
cy.get('ion-popover ion-list-header').contains('Ionic');
|
||||
|
||||
//close popover
|
||||
cy.get('ion-item').contains('Close').click();
|
||||
cy.get('ion-popover').should('not.exist');
|
||||
});
|
||||
|
||||
it('display popover and call dismiss to close it', () => {
|
||||
//show popover
|
||||
cy.get('ion-button').contains('Show Popover, hide after 250 ms').click();
|
||||
cy.get('ion-popover ion-list-header').contains('Ionic');
|
||||
|
||||
//verify popover is gone
|
||||
cy.get('ion-popover').should('not.exist');
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,29 @@
|
||||
describe('useIonToast', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/overlay-hooks/toast');
|
||||
});
|
||||
|
||||
it('display toast using options', () => {
|
||||
//show toast
|
||||
cy.get('ion-button').contains('Show Toast with options').click();
|
||||
cy.get('ion-toast');
|
||||
cy.get('ion-toast').shadow().contains('toast from hook, click hide to dismiss');
|
||||
cy.get('ion-toast').shadow().find('button').contains('hide').click();
|
||||
cy.get('ion-toast').should('not.exist');
|
||||
});
|
||||
|
||||
it('display toast using params', () => {
|
||||
cy.get('ion-button').contains('Show Toast Hook with params, closes in 250 ms').click();
|
||||
cy.get('ion-toast');
|
||||
cy.get('ion-toast').shadow().contains('Hello from a toast!');
|
||||
cy.get('ion-toast').should('not.exist');
|
||||
});
|
||||
|
||||
it('display toast and call dismiss to close it', () => {
|
||||
//show toast
|
||||
cy.get('ion-button').contains('Show Toast Hook with params, call dismiss in 250 ms').click();
|
||||
cy.get('ion-toast');
|
||||
cy.get('ion-toast').shadow().contains('Hello from a toast!');
|
||||
cy.get('ion-toast').should('not.exist');
|
||||
});
|
||||
});
|
22
packages/react/test-app/cypress/plugins/index.js
Normal file
22
packages/react/test-app/cypress/plugins/index.js
Normal file
@ -0,0 +1,22 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
25
packages/react/test-app/cypress/support/commands.js
Normal file
25
packages/react/test-app/cypress/support/commands.js
Normal file
@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
20
packages/react/test-app/cypress/support/index.js
Normal file
20
packages/react/test-app/cypress/support/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
8
packages/react/test-app/cypress/tsconfig.json
Normal file
8
packages/react/test-app/cypress/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
7
packages/react/test-app/ionic.config.json
Normal file
7
packages/react/test-app/ionic.config.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "test-app",
|
||||
"integrations": {
|
||||
"capacitor": {}
|
||||
},
|
||||
"type": "react"
|
||||
}
|
40930
packages/react/test-app/package-lock.json
generated
Normal file
40930
packages/react/test-app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
77
packages/react/test-app/package.json
Normal file
77
packages/react/test-app/package.json
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "test-app",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@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.11",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"cypress": "^8.5.0",
|
||||
"ionicons": "^5.4.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"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": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"sync": "sh ./scripts/sync.sh",
|
||||
"cypress": "cypress run --headless --browser chrome",
|
||||
"e2e": "concurrently \"SKIP_PREFLIGHT_CHECK=true npm run start -b\" \"wait-on http-get://localhost:3000 && npm run cypress\" --kill-others --success first"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@capacitor/cli": "3.2.4",
|
||||
"concurrently": "^6.3.0",
|
||||
"wait-on": "^6.0.0"
|
||||
},
|
||||
"description": "An Ionic project"
|
||||
}
|
BIN
packages/react/test-app/public/assets/icon/favicon.png
Normal file
BIN
packages/react/test-app/public/assets/icon/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 930 B |
BIN
packages/react/test-app/public/assets/icon/icon.png
Normal file
BIN
packages/react/test-app/public/assets/icon/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
1
packages/react/test-app/public/assets/shapes.svg
Normal file
1
packages/react/test-app/public/assets/shapes.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
31
packages/react/test-app/public/index.html
Normal file
31
packages/react/test-app/public/index.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Ionic App</title>
|
||||
|
||||
<base href="/" />
|
||||
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="msapplication-tap-highlight" content="no" />
|
||||
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="%PUBLIC_URL%/assets/icon/favicon.png" />
|
||||
|
||||
<!-- add to homescreen for ios -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-title" content="Ionic App" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
21
packages/react/test-app/public/manifest.json
Normal file
21
packages/react/test-app/public/manifest.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"short_name": "Ionic App",
|
||||
"name": "My Ionic App",
|
||||
"icons": [
|
||||
{
|
||||
"src": "assets/icon/favicon.png",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "assets/icon/icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff"
|
||||
}
|
20
packages/react/test-app/scripts/sync.sh
Executable file
20
packages/react/test-app/scripts/sync.sh
Executable file
@ -0,0 +1,20 @@
|
||||
# Copy ionic react dist
|
||||
rm -rf node_modules/@ionic/react/dist node_modules/@ionic/react/css
|
||||
cp -a ../dist node_modules/@ionic/react/dist
|
||||
cp -a ../css node_modules/@ionic/react/css
|
||||
cp -a ../package.json node_modules/@ionic/react/package.json
|
||||
|
||||
# Copy ionic react router dist
|
||||
rm -rf node_modules/@ionic/react-router/dist
|
||||
cp -a ../../react-router/dist node_modules/@ionic/react-router/dist
|
||||
cp -a ../../react-router/package.json node_modules/@ionic/react-router/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
|
8
packages/react/test-app/src/App.test.tsx
Normal file
8
packages/react/test-app/src/App.test.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders without crashing', () => {
|
||||
const { baseElement } = render(<App />);
|
||||
expect(baseElement).toBeDefined();
|
||||
});
|
38
packages/react/test-app/src/App.tsx
Normal file
38
packages/react/test-app/src/App.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { IonApp } from '@ionic/react';
|
||||
import { IonReactRouter } from '@ionic/react-router';
|
||||
|
||||
/* Core CSS required for Ionic components to work properly */
|
||||
import '@ionic/react/css/core.css';
|
||||
|
||||
/* Basic CSS for apps built with Ionic */
|
||||
import '@ionic/react/css/normalize.css';
|
||||
import '@ionic/react/css/structure.css';
|
||||
import '@ionic/react/css/typography.css';
|
||||
|
||||
/* Optional CSS utils that can be commented out */
|
||||
import '@ionic/react/css/padding.css';
|
||||
import '@ionic/react/css/float-elements.css';
|
||||
import '@ionic/react/css/text-alignment.css';
|
||||
import '@ionic/react/css/text-transformation.css';
|
||||
import '@ionic/react/css/flex-utils.css';
|
||||
import '@ionic/react/css/display.css';
|
||||
|
||||
/* Theme variables */
|
||||
import './theme/variables.css';
|
||||
import Main from './pages/Main';
|
||||
import OverlayHooks from './pages/overlay-hooks/OverlayHooks';
|
||||
import OverlayComponents from './pages/overlay-components/OverlayComponents';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<IonApp>
|
||||
<IonReactRouter>
|
||||
<Route path="/" component={Main} />
|
||||
<Route path="/overlay-hooks" component={OverlayHooks} />
|
||||
<Route path="/overlay-components" component={OverlayComponents} />
|
||||
</IonReactRouter>
|
||||
</IonApp>
|
||||
);
|
||||
|
||||
export default App;
|
22
packages/react/test-app/src/index.tsx
Normal file
22
packages/react/test-app/src/index.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://cra.link/PWA
|
||||
serviceWorkerRegistration.unregister();
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
40
packages/react/test-app/src/pages/Main.tsx
Normal file
40
packages/react/test-app/src/pages/Main.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonLabel,
|
||||
IonPage,
|
||||
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
IonItem,
|
||||
IonList
|
||||
} from '@ionic/react';
|
||||
|
||||
interface MainProps {}
|
||||
|
||||
const Main: React.FC<MainProps> = () => {
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonTitle>Ionic React Test App</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent>
|
||||
<IonList>
|
||||
<IonItem routerLink="/overlay-hooks">
|
||||
<IonLabel>Overlay Hooks</IonLabel>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
<IonList>
|
||||
<IonItem routerLink="/overlay-components">
|
||||
<IonLabel>Overlay Components</IonLabel>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Main;
|
@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, IonActionSheet } from '@ionic/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
const ActionSheetComponent: React.FC = () => {
|
||||
const [message, setMessage] = useState('');
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonActionSheet
|
||||
isOpen={show}
|
||||
buttons={[
|
||||
{
|
||||
text: 'Ok',
|
||||
handler: () => {
|
||||
setMessage('Ok clicked');
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Cancel',
|
||||
handler: () => {
|
||||
setMessage('Cancel clicked');
|
||||
},
|
||||
},
|
||||
]}
|
||||
header="Action Sheet"
|
||||
onDidDismiss={() => setShow(false)}
|
||||
/>
|
||||
<IonButton expand="block" onClick={() => setShow(true)}>
|
||||
Show ActionSheet
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
setTimeout(() => setShow(false), 250);
|
||||
}}
|
||||
>
|
||||
Show ActionSheet, hide after 250 mss
|
||||
</IonButton>
|
||||
<div>{message}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionSheetComponent;
|
@ -0,0 +1,37 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, IonAlert } from '@ionic/react';
|
||||
|
||||
const AlertComponent: React.FC = () => {
|
||||
const [message, setMessage] = useState('');
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonAlert
|
||||
isOpen={show}
|
||||
cssClass="my-css"
|
||||
header="Alert"
|
||||
message="alert from hook"
|
||||
buttons={['Cancel', { text: 'Ok', handler: (d) => setMessage('Ok clicked') }]}
|
||||
onDidDismiss={() => setShow(false)}
|
||||
/>
|
||||
<IonButton expand="block" onClick={() => setShow(true)}>
|
||||
Show Alert
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
setTimeout(() => setShow(false), 250);
|
||||
}}
|
||||
>
|
||||
Show Alert, hide after 250 ms
|
||||
</IonButton>
|
||||
<div>{message}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlertComponent;
|
@ -0,0 +1,36 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, IonLoading } from '@ionic/react';
|
||||
|
||||
interface LoadingProps {}
|
||||
|
||||
const LoadingComponent: React.FC<LoadingProps> = () => {
|
||||
const [show, setShow] = useState(false);
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonLoading
|
||||
isOpen={show}
|
||||
duration={1000}
|
||||
message="Loading"
|
||||
onDidDismiss={() => setShow(false)}
|
||||
/>
|
||||
|
||||
<IonButton expand="block" onClick={() => setShow(true)}>
|
||||
Show Loading with options
|
||||
</IonButton>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
setTimeout(() => setShow(false), 250);
|
||||
}}
|
||||
>
|
||||
Show Loading, hide after 250 ms
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingComponent;
|
@ -0,0 +1,102 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonModal,
|
||||
IonHeader,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
} from '@ionic/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
const Body: React.FC<{
|
||||
count: number;
|
||||
onDismiss: () => void;
|
||||
onIncrement: () => void;
|
||||
}> = ({ count, onDismiss, onIncrement }) => (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonTitle>My Modal</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent>
|
||||
Count in modal: {count}
|
||||
<IonButton expand="block" onClick={() => onIncrement()}>
|
||||
Increment Count
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={() => onDismiss()}>
|
||||
Close
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
|
||||
const ModalWithContext: React.FC = () => {
|
||||
const ctx = useContext(MyContext);
|
||||
return <div>{ctx.value}</div>;
|
||||
};
|
||||
|
||||
const ModalComponent: React.FC = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
const [show, setShow] = useState(false);
|
||||
const [showContext, setShowContext] = useState(false);
|
||||
|
||||
const handleIncrement = useCallback(() => {
|
||||
setCount(count + 1);
|
||||
}, [count, setCount]);
|
||||
|
||||
return (
|
||||
<MyContext.Provider value={{ value: 'overriden value' }}>
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonModal isOpen={show} cssClass="my-class" onDidDismiss={() => setShow(false)}>
|
||||
<Body
|
||||
count={count}
|
||||
onDismiss={() => setShow(false)}
|
||||
onIncrement={handleIncrement}
|
||||
/>
|
||||
</IonModal>
|
||||
<IonModal isOpen={showContext} onDidDismiss={() => setShowContext(false)}>
|
||||
<ModalWithContext />
|
||||
</IonModal>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
}}
|
||||
>
|
||||
Show Modal
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
setTimeout(() => setShow(false), 250);
|
||||
}}
|
||||
>
|
||||
Show Modal, hide after 250 ms
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShowContext(true);
|
||||
}}
|
||||
>
|
||||
Show Modal with Context
|
||||
</IonButton>
|
||||
|
||||
<div>Count: {count}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</MyContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const MyContext = React.createContext({
|
||||
value: 'default value',
|
||||
});
|
||||
|
||||
export default ModalComponent;
|
@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import {
|
||||
addCircleOutline,
|
||||
alarm,
|
||||
alertCircle,
|
||||
logoGoogle,
|
||||
logoIonic,
|
||||
newspaper,
|
||||
star,
|
||||
} from 'ionicons/icons';
|
||||
import ActionSheetComponent from './ActionSheetComponent';
|
||||
import AlertComponent from './AlertComponent';
|
||||
import LoadingComponent from './LoadingComponent';
|
||||
import ModalComponent from './ModalComponent';
|
||||
import PickerComponent from './PickerComponent';
|
||||
import PopoverComponent from './PopoverComponent';
|
||||
import ToastComponent from './ToastComponent';
|
||||
|
||||
interface OverlayHooksProps {}
|
||||
|
||||
const OverlayHooks: React.FC<OverlayHooksProps> = () => {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>
|
||||
<Redirect from="/overlay-components" to="/overlay-components/actionsheet" exact />
|
||||
<Route path="/overlay-components/actionsheet" component={ActionSheetComponent} />
|
||||
<Route path="/overlay-components/alert" component={AlertComponent} />
|
||||
<Route path="/overlay-components/loading" component={LoadingComponent} />
|
||||
<Route path="/overlay-components/modal" component={ModalComponent} />
|
||||
<Route path="/overlay-components/picker" component={PickerComponent} />
|
||||
<Route path="/overlay-components/popover" component={PopoverComponent} />
|
||||
<Route path="/overlay-components/toast" component={ToastComponent} />
|
||||
</IonRouterOutlet>
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="actionsheet" href="/overlay-components/actionsheet">
|
||||
<IonIcon icon={newspaper} />
|
||||
<IonLabel>ActionSheet</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="alert" href="/overlay-components/alert">
|
||||
<IonIcon icon={alertCircle} />
|
||||
<IonLabel>Alert</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="loading" href="/overlay-components/loading">
|
||||
<IonIcon icon={addCircleOutline} />
|
||||
<IonLabel>Loading</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="modal" href="/overlay-components/modal">
|
||||
<IonIcon icon={star} />
|
||||
<IonLabel>Modal</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="picker" href="/overlay-components/picker">
|
||||
<IonIcon icon={logoIonic} />
|
||||
<IonLabel>Picker</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="popover" href="/overlay-components/popover">
|
||||
<IonIcon icon={logoGoogle} />
|
||||
<IonLabel>Popover</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="toast" href="/overlay-components/toast">
|
||||
<IonIcon icon={alarm} />
|
||||
<IonLabel>Toast</IonLabel>
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
</IonTabs>
|
||||
);
|
||||
};
|
||||
|
||||
export default OverlayHooks;
|
@ -0,0 +1,59 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, IonPicker } from '@ionic/react';
|
||||
|
||||
const PickerComponent: React.FC = () => {
|
||||
const [show, setShow] = useState(false);
|
||||
const [value, setValue] = useState('');
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonPicker
|
||||
isOpen={show}
|
||||
buttons={[
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(`${selected.animal.value}, ${selected.vehicle.value}`);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'vehicle',
|
||||
options: [
|
||||
{ text: 'Car', value: 'car' },
|
||||
{ text: 'Truck', value: 'truck' },
|
||||
{ text: 'Bike', value: 'bike' },
|
||||
],
|
||||
},
|
||||
]}
|
||||
onDidDismiss={() => setShow(false)}
|
||||
/>
|
||||
<IonButton expand="block" onClick={() => setShow(true)}>
|
||||
Show Picker
|
||||
</IonButton>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
setTimeout(() => setShow(false), 250);
|
||||
}}
|
||||
>
|
||||
Show Picker, hide after 250 ms
|
||||
</IonButton>
|
||||
{value && <div>Selected Value: {value}</div>}
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default PickerComponent;
|
@ -0,0 +1,78 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonPage,
|
||||
IonPopover,
|
||||
} from '@ionic/react';
|
||||
|
||||
const PopoverList: React.FC<{
|
||||
onHide: () => void;
|
||||
}> = ({ onHide }) => (
|
||||
<IonList>
|
||||
<IonListHeader>Ionic</IonListHeader>
|
||||
<IonItem button>Learn Ionic</IonItem>
|
||||
<IonItem button>Documentation</IonItem>
|
||||
<IonItem button>Showcase</IonItem>
|
||||
<IonItem button>GitHub Repo</IonItem>
|
||||
<IonItem lines="none" detail={false} button onClick={onHide}>
|
||||
Close
|
||||
</IonItem>
|
||||
</IonList>
|
||||
);
|
||||
|
||||
const PopoverComponent: React.FC = () => {
|
||||
const [popoverState, setShowPopover] = useState<{ showPopover: boolean; event?: Event }>({
|
||||
showPopover: false,
|
||||
event: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonPopover
|
||||
isOpen={popoverState.showPopover}
|
||||
event={popoverState.event}
|
||||
onDidDismiss={() => setShowPopover({ showPopover: false, event: undefined })}
|
||||
>
|
||||
<PopoverList onHide={() => setShowPopover({ showPopover: false, event: undefined })} />
|
||||
</IonPopover>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) =>
|
||||
setShowPopover({
|
||||
showPopover: true,
|
||||
event: e.nativeEvent,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Popover
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) => {
|
||||
setShowPopover({
|
||||
showPopover: true,
|
||||
event: e.nativeEvent,
|
||||
});
|
||||
setTimeout(
|
||||
() =>
|
||||
setShowPopover({
|
||||
showPopover: false,
|
||||
event: undefined,
|
||||
}),
|
||||
250
|
||||
);
|
||||
}}
|
||||
>
|
||||
Show Popover, hide after 250 ms
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopoverComponent;
|
@ -0,0 +1,34 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, IonToast } from '@ionic/react';
|
||||
|
||||
const ToastComponent: React.FC = () => {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonToast
|
||||
isOpen={show}
|
||||
buttons={[{ text: 'hide', handler: () => setShow(false) }]}
|
||||
message="Hello from a toast!"
|
||||
onDidDismiss={() => setShow(false)}
|
||||
/>
|
||||
<IonButton expand="block" onClick={() => setShow(true)}>
|
||||
Show Toast
|
||||
</IonButton>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
setTimeout(() => setShow(false), 250);
|
||||
}}
|
||||
>
|
||||
Show Toast, call dismiss in 250 ms
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToastComponent;
|
@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonActionSheet } from '@ionic/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
const ActionSheetHook: React.FC = () => {
|
||||
const [present, dismiss] = useIonActionSheet();
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [
|
||||
{
|
||||
text: 'Ok',
|
||||
handler: () => {
|
||||
setMessage('Ok clicked');
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Cancel',
|
||||
handler: () => {
|
||||
setMessage('Cancel clicked');
|
||||
},
|
||||
},
|
||||
],
|
||||
header: 'Action Sheet',
|
||||
})
|
||||
}
|
||||
>
|
||||
Show ActionSheet with options
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present(
|
||||
[
|
||||
{
|
||||
text: 'Ok',
|
||||
handler: () => {
|
||||
setMessage('Ok clicked');
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Cancel',
|
||||
handler: () => {
|
||||
setMessage('Cancel clicked');
|
||||
},
|
||||
},
|
||||
],
|
||||
'Action Sheet'
|
||||
)
|
||||
}
|
||||
>
|
||||
Show ActionSheet with params
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
|
||||
setTimeout(dismiss, 250);
|
||||
}}
|
||||
>
|
||||
Show ActionSheet, hide after 250 mss
|
||||
</IonButton>
|
||||
<div>{message}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionSheetHook;
|
@ -0,0 +1,47 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonAlert } from '@ionic/react';
|
||||
|
||||
const AlertHook: React.FC = () => {
|
||||
const [present, dismiss] = useIonAlert();
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
cssClass: 'my-css',
|
||||
header: 'Alert',
|
||||
message: 'alert from hook',
|
||||
buttons: ['Cancel', { text: 'Ok', handler: (d) => setMessage('Ok clicked') }],
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Alert with options
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present('Hello!', [{ text: 'Ok', handler: (d) => setMessage('Ok clicked') }])
|
||||
}
|
||||
>
|
||||
Show Alert with params
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present('Hello!', [{ text: 'Ok', handler: (d) => setMessage('Ok clicked') }]);
|
||||
setTimeout(dismiss, 250);
|
||||
}}
|
||||
>
|
||||
Show Alert, hide after 250 ms
|
||||
</IonButton>
|
||||
<div>{message}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlertHook;
|
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
|
||||
|
||||
interface LoadingProps {}
|
||||
|
||||
const LoadingHook: React.FC<LoadingProps> = () => {
|
||||
const [present, dismiss] = useIonLoading();
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
duration: 1000,
|
||||
message: 'Loading',
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Loading with options
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={() => present('Loading', 1000, 'dots')}>
|
||||
Show Loading with params
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present('Loading', 10000, 'dots');
|
||||
setTimeout(dismiss, 250);
|
||||
}}
|
||||
>
|
||||
Show Loading, hide after 250 ms
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingHook;
|
140
packages/react/test-app/src/pages/overlay-hooks/ModalHook.tsx
Normal file
140
packages/react/test-app/src/pages/overlay-hooks/ModalHook.tsx
Normal file
@ -0,0 +1,140 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonPage,
|
||||
useIonModal,
|
||||
IonHeader,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
} from '@ionic/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
const Body: React.FC<{
|
||||
type: string;
|
||||
count: number;
|
||||
onDismiss: () => void;
|
||||
onIncrement: () => void;
|
||||
}> = ({ count, onDismiss, onIncrement, type }) => (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonTitle>My {type} Modal</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent>
|
||||
Count in modal: {count}
|
||||
<IonButton expand="block" onClick={() => onIncrement()}>
|
||||
Increment Count
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={() => onDismiss()}>
|
||||
Close
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
|
||||
const ModalWithContext: React.FC = () => {
|
||||
const ctx = useContext(MyContext);
|
||||
return <div>{ctx.value}</div>;
|
||||
};
|
||||
|
||||
const ModalHook: React.FC = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const handleIncrement = useCallback(() => {
|
||||
setCount(count + 1);
|
||||
}, [count, setCount]);
|
||||
|
||||
const handleDismissWithComponent = useCallback(() => {
|
||||
dismissWithComponent();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleDismissWithElement = useCallback(() => {
|
||||
dismissWithElement();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* First parameter to useIonModal is the component to show, second is props
|
||||
*/
|
||||
const [presentWithComponent, dismissWithComponent] = useIonModal(Body, {
|
||||
count: count,
|
||||
onIncrement: handleIncrement,
|
||||
onDismiss: handleDismissWithComponent,
|
||||
type: 'Component',
|
||||
});
|
||||
|
||||
/**
|
||||
* First parameter to useIonModal is the element to show
|
||||
*/
|
||||
const [presentWithElement, dismissWithElement] = useIonModal(
|
||||
<Body
|
||||
count={count}
|
||||
onDismiss={handleDismissWithElement}
|
||||
onIncrement={handleIncrement}
|
||||
type="Element"
|
||||
/>
|
||||
);
|
||||
|
||||
const [presentModalWithContext] = useIonModal(ModalWithContext);
|
||||
|
||||
return (
|
||||
<MyContext.Provider value={{ value: 'overriden value' }}>
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
presentWithComponent({
|
||||
cssClass: 'my-class',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Modal using component param
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
presentWithElement({
|
||||
cssClass: 'my-class',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Modal using element param
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
presentWithElement({
|
||||
cssClass: 'my-class',
|
||||
});
|
||||
setTimeout(dismissWithElement, 250);
|
||||
}}
|
||||
>
|
||||
Show Modal, hide after 250 ms
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
presentModalWithContext({
|
||||
cssClass: 'my-class',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Modal with Context
|
||||
</IonButton>
|
||||
|
||||
<div>Count: {count}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</MyContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const MyContext = React.createContext({
|
||||
value: 'default value',
|
||||
});
|
||||
|
||||
export default ModalHook;
|
@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import ActionSheetHook from './ActionSheetHook';
|
||||
import {
|
||||
addCircleOutline,
|
||||
alarm,
|
||||
alertCircle,
|
||||
logoGoogle,
|
||||
logoIonic,
|
||||
newspaper,
|
||||
star,
|
||||
} from 'ionicons/icons';
|
||||
import AlertHook from './AlertHook';
|
||||
import LoadingHook from './LoadingHook';
|
||||
import ModalHook from './ModalHook';
|
||||
import PickerHook from './PickerHook';
|
||||
import PopoverHook from './PopoverHook';
|
||||
import ToastHook from './ToastHook';
|
||||
|
||||
interface OverlayHooksProps {}
|
||||
|
||||
const OverlayHooks: React.FC<OverlayHooksProps> = () => {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>
|
||||
<Redirect from="/overlay-hooks" to="/overlay-hooks/actionsheet" exact />
|
||||
<Route path="/overlay-hooks/actionsheet" component={ActionSheetHook} />
|
||||
<Route path="/overlay-hooks/alert" component={AlertHook} />
|
||||
<Route path="/overlay-hooks/loading" component={LoadingHook} />
|
||||
<Route path="/overlay-hooks/modal" component={ModalHook} />
|
||||
<Route path="/overlay-hooks/picker" component={PickerHook} />
|
||||
<Route path="/overlay-hooks/popover" component={PopoverHook} />
|
||||
<Route path="/overlay-hooks/toast" component={ToastHook} />
|
||||
</IonRouterOutlet>
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="actionsheet" href="/overlay-hooks/actionsheet">
|
||||
<IonIcon icon={newspaper} />
|
||||
<IonLabel>ActionSheet</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="alert" href="/overlay-hooks/alert">
|
||||
<IonIcon icon={alertCircle} />
|
||||
<IonLabel>Alert</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="loading" href="/overlay-hooks/loading">
|
||||
<IonIcon icon={addCircleOutline} />
|
||||
<IonLabel>Loading</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="modal" href="/overlay-hooks/modal">
|
||||
<IonIcon icon={star} />
|
||||
<IonLabel>Modal</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="picker" href="/overlay-hooks/picker">
|
||||
<IonIcon icon={logoIonic} />
|
||||
<IonLabel>Picker</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="popover" href="/overlay-hooks/popover">
|
||||
<IonIcon icon={logoGoogle} />
|
||||
<IonLabel>Popover</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="toast" href="/overlay-hooks/toast">
|
||||
<IonIcon icon={alarm} />
|
||||
<IonLabel>Toast</IonLabel>
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
</IonTabs>
|
||||
);
|
||||
};
|
||||
|
||||
export default OverlayHooks;
|
@ -0,0 +1,97 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonPicker } from '@ionic/react';
|
||||
|
||||
const PickerHook: React.FC = () => {
|
||||
const [present, dismiss] = useIonPicker();
|
||||
const [value, setValue] = useState('');
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
console.log(`${selected.animal.value} picked`);
|
||||
setValue(selected.animal.value);
|
||||
},
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Picker with options
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present(
|
||||
[
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'vehicle',
|
||||
options: [
|
||||
{ text: 'Car', value: 'car' },
|
||||
{ text: 'Truck', value: 'truck' },
|
||||
{ text: 'Bike', value: 'bike' },
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(`${selected.animal.value}, ${selected.vehicle.value}`);
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
}
|
||||
>
|
||||
Show Picker with params
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present([
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
setTimeout(dismiss, 250);
|
||||
}}
|
||||
>
|
||||
Show Picker, hide after 250 ms
|
||||
</IonButton>
|
||||
{value && <div>Selected Value: {value}</div>}
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default PickerHook;
|
@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonPage,
|
||||
useIonPopover,
|
||||
} from '@ionic/react';
|
||||
|
||||
const PopoverList: React.FC<{
|
||||
onHide: () => void;
|
||||
}> = ({ onHide }) => (
|
||||
<IonList>
|
||||
<IonListHeader>Ionic</IonListHeader>
|
||||
<IonItem button>Learn Ionic</IonItem>
|
||||
<IonItem button>Documentation</IonItem>
|
||||
<IonItem button>Showcase</IonItem>
|
||||
<IonItem button>GitHub Repo</IonItem>
|
||||
<IonItem lines="none" detail={false} button onClick={onHide}>
|
||||
Close
|
||||
</IonItem>
|
||||
</IonList>
|
||||
);
|
||||
|
||||
const PopoverHook: React.FC = () => {
|
||||
const [presentWithComponent, dismissWithComponent] = useIonPopover(PopoverList, {
|
||||
onHide: () => dismissWithComponent(),
|
||||
});
|
||||
const [presentWithElement, dismissWithElement] = useIonPopover(
|
||||
<PopoverList onHide={() => dismissWithElement()} />
|
||||
);
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) =>
|
||||
presentWithComponent({
|
||||
event: e.nativeEvent,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Popover with component param
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) =>
|
||||
presentWithElement({
|
||||
event: e.nativeEvent,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Popover with element param
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) => {
|
||||
presentWithComponent({
|
||||
event: e.nativeEvent,
|
||||
});
|
||||
setTimeout(dismissWithComponent, 250);
|
||||
}}
|
||||
>
|
||||
Show Popover, hide after 250 ms
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopoverHook;
|
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonToast } from '@ionic/react';
|
||||
|
||||
const ToastHook: React.FC = () => {
|
||||
const [present, dismiss] = useIonToast();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [{ text: 'hide', handler: () => dismiss() }],
|
||||
message: 'toast from hook, click hide to dismiss',
|
||||
onDidDismiss: () => console.log('dismissed'),
|
||||
onWillDismiss: () => console.log('will dismiss'),
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Toast with options
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={() => present('Hello from a toast!', 250)}>
|
||||
Show Toast Hook with params, closes in 250 ms
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present('Hello from a toast!');
|
||||
setTimeout(dismiss, 250);
|
||||
}}
|
||||
>
|
||||
Show Toast Hook with params, call dismiss in 250 ms
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToastHook;
|
1
packages/react/test-app/src/react-app-env.d.ts
vendored
Normal file
1
packages/react/test-app/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
15
packages/react/test-app/src/reportWebVitals.ts
Normal file
15
packages/react/test-app/src/reportWebVitals.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { ReportHandler } from 'web-vitals';
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
80
packages/react/test-app/src/service-worker.ts
Normal file
80
packages/react/test-app/src/service-worker.ts
Normal file
@ -0,0 +1,80 @@
|
||||
/// <reference lib="webworker" />
|
||||
/* eslint-disable no-restricted-globals */
|
||||
|
||||
// This service worker can be customized!
|
||||
// See https://developers.google.com/web/tools/workbox/modules
|
||||
// for the list of available Workbox modules, or add any other
|
||||
// code you'd like.
|
||||
// You can also remove this file if you'd prefer not to use a
|
||||
// service worker, and the Workbox build step will be skipped.
|
||||
|
||||
import { clientsClaim } from 'workbox-core';
|
||||
import { ExpirationPlugin } from 'workbox-expiration';
|
||||
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
|
||||
import { registerRoute } from 'workbox-routing';
|
||||
import { StaleWhileRevalidate } from 'workbox-strategies';
|
||||
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
clientsClaim();
|
||||
|
||||
// Precache all of the assets generated by your build process.
|
||||
// Their URLs are injected into the manifest variable below.
|
||||
// This variable must be present somewhere in your service worker file,
|
||||
// even if you decide not to use precaching. See https://cra.link/PWA
|
||||
precacheAndRoute(self.__WB_MANIFEST);
|
||||
|
||||
// Set up App Shell-style routing, so that all navigation requests
|
||||
// are fulfilled with your index.html shell. Learn more at
|
||||
// https://developers.google.com/web/fundamentals/architecture/app-shell
|
||||
const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$');
|
||||
registerRoute(
|
||||
// Return false to exempt requests from being fulfilled by index.html.
|
||||
({ request, url }: { request: Request; url: URL }) => {
|
||||
// If this isn't a navigation, skip.
|
||||
if (request.mode !== 'navigate') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a URL that starts with /_, skip.
|
||||
if (url.pathname.startsWith('/_')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this looks like a URL for a resource, because it contains
|
||||
// a file extension, skip.
|
||||
if (url.pathname.match(fileExtensionRegexp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true to signal that we want to use the handler.
|
||||
return true;
|
||||
},
|
||||
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
|
||||
);
|
||||
|
||||
// An example runtime caching route for requests that aren't handled by the
|
||||
// precache, in this case same-origin .png requests like those from in public/
|
||||
registerRoute(
|
||||
// Add in any other file extensions or routing criteria as needed.
|
||||
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'),
|
||||
// Customize this strategy as needed, e.g., by changing to CacheFirst.
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: 'images',
|
||||
plugins: [
|
||||
// Ensure that once this runtime cache reaches a maximum size the
|
||||
// least-recently used images are removed.
|
||||
new ExpirationPlugin({ maxEntries: 50 }),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
// This allows the web app to trigger skipWaiting via
|
||||
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
|
||||
self.addEventListener('message', (event) => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
||||
// Any other custom service worker logic can go here.
|
142
packages/react/test-app/src/serviceWorkerRegistration.ts
Normal file
142
packages/react/test-app/src/serviceWorkerRegistration.ts
Normal file
@ -0,0 +1,142 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://cra.link/PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
|
||||
);
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
|
||||
export function register(config?: Config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://cra.link/PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then((registration) => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://cra.link/PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl, {
|
||||
headers: { 'Service-Worker': 'script' },
|
||||
})
|
||||
.then((response) => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('No internet connection found. App is running in offline mode.');
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then((registration) => {
|
||||
registration.unregister();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error.message);
|
||||
});
|
||||
}
|
||||
}
|
14
packages/react/test-app/src/setupTests.ts
Normal file
14
packages/react/test-app/src/setupTests.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
|
||||
// Mock matchmedia
|
||||
window.matchMedia = window.matchMedia || function() {
|
||||
return {
|
||||
matches: false,
|
||||
addListener: function() {},
|
||||
removeListener: function() {}
|
||||
};
|
||||
};
|
236
packages/react/test-app/src/theme/variables.css
Normal file
236
packages/react/test-app/src/theme/variables.css
Normal file
@ -0,0 +1,236 @@
|
||||
/* Ionic Variables and Theming. For more info, please see:
|
||||
http://ionicframework.com/docs/theming/ */
|
||||
|
||||
/** Ionic CSS Variables **/
|
||||
:root {
|
||||
/** primary **/
|
||||
--ion-color-primary: #3880ff;
|
||||
--ion-color-primary-rgb: 56, 128, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3171e0;
|
||||
--ion-color-primary-tint: #4c8dff;
|
||||
|
||||
/** secondary **/
|
||||
--ion-color-secondary: #3dc2ff;
|
||||
--ion-color-secondary-rgb: 61, 194, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #36abe0;
|
||||
--ion-color-secondary-tint: #50c8ff;
|
||||
|
||||
/** tertiary **/
|
||||
--ion-color-tertiary: #5260ff;
|
||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #4854e0;
|
||||
--ion-color-tertiary-tint: #6370ff;
|
||||
|
||||
/** success **/
|
||||
--ion-color-success: #2dd36f;
|
||||
--ion-color-success-rgb: 45, 211, 111;
|
||||
--ion-color-success-contrast: #ffffff;
|
||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-success-shade: #28ba62;
|
||||
--ion-color-success-tint: #42d77d;
|
||||
|
||||
/** warning **/
|
||||
--ion-color-warning: #ffc409;
|
||||
--ion-color-warning-rgb: 255, 196, 9;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0ac08;
|
||||
--ion-color-warning-tint: #ffca22;
|
||||
|
||||
/** danger **/
|
||||
--ion-color-danger: #eb445a;
|
||||
--ion-color-danger-rgb: 235, 68, 90;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #cf3c4f;
|
||||
--ion-color-danger-tint: #ed576b;
|
||||
|
||||
/** dark **/
|
||||
--ion-color-dark: #222428;
|
||||
--ion-color-dark-rgb: 34, 36, 40;
|
||||
--ion-color-dark-contrast: #ffffff;
|
||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-dark-shade: #1e2023;
|
||||
--ion-color-dark-tint: #383a3e;
|
||||
|
||||
/** medium **/
|
||||
--ion-color-medium: #92949c;
|
||||
--ion-color-medium-rgb: 146, 148, 156;
|
||||
--ion-color-medium-contrast: #ffffff;
|
||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-medium-shade: #808289;
|
||||
--ion-color-medium-tint: #9d9fa6;
|
||||
|
||||
/** light **/
|
||||
--ion-color-light: #f4f5f8;
|
||||
--ion-color-light-rgb: 244, 245, 248;
|
||||
--ion-color-light-contrast: #000000;
|
||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-light-shade: #d7d8da;
|
||||
--ion-color-light-tint: #f5f6f9;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66,140,255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255,255,255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80,200,255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255,255,255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106,100,255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255,255,255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47,223,117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0,0,0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255,213,52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0,0,0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255,73,97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255,255,255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244,245,248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0,0,0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152,154,162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0,0,0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34,36,40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255,255,255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0,0,0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18,18,18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
}
|
26
packages/react/test-app/tsconfig.json
Normal file
26
packages/react/test-app/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user