mirror of
https://github.com/apache/superset.git
synced 2026-06-29 19:35:33 +00:00
Compare commits
160 Commits
docs/testi
...
ts-migrate
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
297515db62 | ||
|
|
0cecb061f0 | ||
|
|
1f2c20fb5b | ||
|
|
5c4fcdb867 | ||
|
|
3880cd5642 | ||
|
|
062b7937f5 | ||
|
|
6bfbbbf89e | ||
|
|
ef8ba1cbeb | ||
|
|
926d2bf031 | ||
|
|
bd77f82cc9 | ||
|
|
9c7b676bfc | ||
|
|
28db9ad7fc | ||
|
|
58435e3e28 | ||
|
|
8e48fdbd6f | ||
|
|
17ef5b67d1 | ||
|
|
c279d08d5e | ||
|
|
2979c30703 | ||
|
|
20a17be0f3 | ||
|
|
4070dba438 | ||
|
|
0af5770a49 | ||
|
|
33f2ffd2a9 | ||
|
|
fcea7e4af9 | ||
|
|
7258dc9ea0 | ||
|
|
14645a2dfa | ||
|
|
6fe8a54b6f | ||
|
|
b9710f947c | ||
|
|
399788442a | ||
|
|
b03c425393 | ||
|
|
e862b5cddd | ||
|
|
5a67713f3f | ||
|
|
f00adac73e | ||
|
|
1c28138938 | ||
|
|
cf68c879e2 | ||
|
|
5b991800c3 | ||
|
|
7472714ce7 | ||
|
|
67aa991099 | ||
|
|
5267ec2028 | ||
|
|
3983ee0c2f | ||
|
|
863a0bea5c | ||
|
|
d635c2a9ab | ||
|
|
2c038f5bd6 | ||
|
|
fc031ca35b | ||
|
|
89424894f3 | ||
|
|
9da62ed63a | ||
|
|
49bcf79f71 | ||
|
|
e529d84e34 | ||
|
|
f580da88ca | ||
|
|
b040d52c2d | ||
|
|
a7b7e6319c | ||
|
|
c217f56aea | ||
|
|
d43657ed90 | ||
|
|
6c01173c21 | ||
|
|
4709eb0153 | ||
|
|
51f719f8d4 | ||
|
|
50535a92a0 | ||
|
|
3fe5db1e72 | ||
|
|
08c9d28545 | ||
|
|
86d5537a7f | ||
|
|
18c3f0adb6 | ||
|
|
5e8cd7a6ee | ||
|
|
dcea6c09ca | ||
|
|
dff7c1b50d | ||
|
|
4f93c2d2e7 | ||
|
|
334aa1a672 | ||
|
|
2667a14678 | ||
|
|
29ba5adf21 | ||
|
|
f18455cc2d | ||
|
|
4123d25873 | ||
|
|
662f33b7de | ||
|
|
c14dcecd8f | ||
|
|
6a4730bbbe | ||
|
|
739caa19cb | ||
|
|
8bb02e2958 | ||
|
|
7c2fd55104 | ||
|
|
6c8e72b889 | ||
|
|
3b198ab656 | ||
|
|
c993abe58c | ||
|
|
a9bc4655a4 | ||
|
|
b835478514 | ||
|
|
3950cf065e | ||
|
|
c7d2881d04 | ||
|
|
33febb669e | ||
|
|
6254db34cd | ||
|
|
e6df194201 | ||
|
|
2580a8ba78 | ||
|
|
6b58ef155e | ||
|
|
6f73e58b25 | ||
|
|
bc85a118ba | ||
|
|
70a5925b03 | ||
|
|
d266835820 | ||
|
|
952658ee63 | ||
|
|
27d723fba1 | ||
|
|
971715931b | ||
|
|
e3342bb731 | ||
|
|
506c8387fc | ||
|
|
9dedb588ba | ||
|
|
8b69958f19 | ||
|
|
1dd8a76113 | ||
|
|
cde1da6285 | ||
|
|
3665ebcb4b | ||
|
|
c31d70dd12 | ||
|
|
f217865435 | ||
|
|
501874980e | ||
|
|
e7b2b586b6 | ||
|
|
6a15aaf562 | ||
|
|
f5b680699f | ||
|
|
3c289a927d | ||
|
|
5805f242d0 | ||
|
|
4f0a4454ec | ||
|
|
d752b0f06a | ||
|
|
06d737ec9f | ||
|
|
04729794c8 | ||
|
|
68ea9ac4d0 | ||
|
|
1afa4971d1 | ||
|
|
5418f09864 | ||
|
|
aabeefb761 | ||
|
|
023c7da07b | ||
|
|
7af32d4c70 | ||
|
|
58724b1c5c | ||
|
|
f9494128bc | ||
|
|
cebff5e726 | ||
|
|
bcb6da18ef | ||
|
|
344c8f5c37 | ||
|
|
d3f450fca0 | ||
|
|
11a29b1610 | ||
|
|
54d67b679b | ||
|
|
64c480a8f1 | ||
|
|
cf6816064d | ||
|
|
59e402ac68 | ||
|
|
5042248ed7 | ||
|
|
56e3d165dd | ||
|
|
8ce144983d | ||
|
|
4afbfd11e0 | ||
|
|
31eb10590e | ||
|
|
358633e98d | ||
|
|
b7bc1113ac | ||
|
|
11bc4965e3 | ||
|
|
1ca0f34210 | ||
|
|
9cb6c3b039 | ||
|
|
b9be692e55 | ||
|
|
e0d86df5a5 | ||
|
|
2f80ebb3e8 | ||
|
|
83f47d3ca1 | ||
|
|
48df49d89c | ||
|
|
f16600ee86 | ||
|
|
7dbe05f6d8 | ||
|
|
2d461deb68 | ||
|
|
dbc7db981c | ||
|
|
5faf0189e8 | ||
|
|
4b55a928c9 | ||
|
|
d15c6d361b | ||
|
|
6b5d53ad39 | ||
|
|
b575aa6aac | ||
|
|
d131c29f3b | ||
|
|
22cbec1d95 | ||
|
|
b2b7b899a3 | ||
|
|
ac81eefe3f | ||
|
|
8d361205f6 | ||
|
|
352aa36823 | ||
|
|
336763f0c9 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
|||||||
docker/**/*.sh text eol=lf
|
docker/**/*.sh text eol=lf
|
||||||
*.svg binary
|
*.svg binary
|
||||||
*.ipynb binary
|
*.ipynb binary
|
||||||
|
*.geojson binary
|
||||||
|
|||||||
3
.github/workflows/docker.yml
vendored
3
.github/workflows/docker.yml
vendored
@@ -111,6 +111,9 @@ jobs:
|
|||||||
docker compose up superset-init --exit-code-from superset-init
|
docker compose up superset-init --exit-code-from superset-init
|
||||||
|
|
||||||
docker-compose-image-tag:
|
docker-compose-image-tag:
|
||||||
|
# Run this job only on pushes to master (not for PRs)
|
||||||
|
# goal is to check that building the latest image works, not required for all PR pushes
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||||
|
|||||||
8
.github/workflows/pre-commit.yml
vendored
8
.github/workflows/pre-commit.yml
vendored
@@ -53,6 +53,14 @@ jobs:
|
|||||||
cd docs
|
cd docs
|
||||||
yarn install --immutable
|
yarn install --immutable
|
||||||
|
|
||||||
|
- name: Cache pre-commit environments
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pre-commit
|
||||||
|
key: pre-commit-v2-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
pre-commit-v2-${{ runner.os }}-py${{ matrix.python-version }}-
|
||||||
|
|
||||||
- name: pre-commit
|
- name: pre-commit
|
||||||
run: |
|
run: |
|
||||||
set +e # Don't exit immediately on failure
|
set +e # Don't exit immediately on failure
|
||||||
|
|||||||
10
.github/workflows/superset-e2e.yml
vendored
10
.github/workflows/superset-e2e.yml
vendored
@@ -73,6 +73,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||||
- name: Checkout using ref (workflow_dispatch)
|
- name: Checkout using ref (workflow_dispatch)
|
||||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -137,9 +138,16 @@ jobs:
|
|||||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||||
with:
|
with:
|
||||||
run: cypress-run-all ${{ env.USE_DASHBOARD }} ${{ matrix.app_root }}
|
run: cypress-run-all ${{ env.USE_DASHBOARD }} ${{ matrix.app_root }}
|
||||||
|
- name: Set safe app root
|
||||||
|
if: failure()
|
||||||
|
id: set-safe-app-root
|
||||||
|
run: |
|
||||||
|
APP_ROOT="${{ matrix.app_root }}"
|
||||||
|
SAFE_APP_ROOT=${APP_ROOT//\//_}
|
||||||
|
echo "safe_app_root=$SAFE_APP_ROOT" >> $GITHUB_OUTPUT
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/superset-frontend/cypress-base/cypress/screenshots
|
path: ${{ github.workspace }}/superset-frontend/cypress-base/cypress/screenshots
|
||||||
name: cypress-artifact-${{ github.run_id }}-${{ github.job }}-${{ matrix.browser }}-${{ matrix.parallel_id }}
|
name: cypress-artifact-${{ github.run_id }}-${{ github.job }}-${{ matrix.browser }}-${{ matrix.parallel_id }}--${{ steps.set-safe-app-root.outputs.safe_app_root }}
|
||||||
|
|||||||
27
.github/workflows/superset-frontend.yml
vendored
27
.github/workflows/superset-frontend.yml
vendored
@@ -26,6 +26,8 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check for File Changes
|
- name: Check for File Changes
|
||||||
id: check
|
id: check
|
||||||
@@ -39,6 +41,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
|
echo "git rev-parse --short HEAD"
|
||||||
|
git rev-parse --short HEAD
|
||||||
|
echo "git show -s --format=raw HEAD"
|
||||||
|
git show -s --format=raw HEAD
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
-t $TAG \
|
-t $TAG \
|
||||||
--cache-from=type=registry,ref=apache/superset-cache:3.10-slim-bookworm \
|
--cache-from=type=registry,ref=apache/superset-cache:3.10-slim-bookworm \
|
||||||
@@ -115,24 +121,6 @@ jobs:
|
|||||||
files: merged-output/coverage-summary.json
|
files: merged-output/coverage-summary.json
|
||||||
slug: apache/superset
|
slug: apache/superset
|
||||||
|
|
||||||
core-cover:
|
|
||||||
needs: frontend-build
|
|
||||||
if: needs.frontend-build.outputs.should-run == 'true'
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- name: Download Docker Image Artifact
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: docker-image
|
|
||||||
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < docker-image.tar.gz
|
|
||||||
|
|
||||||
- name: superset-ui/core coverage
|
|
||||||
run: |
|
|
||||||
docker run --rm $TAG bash -c \
|
|
||||||
"npm run core:cover"
|
|
||||||
|
|
||||||
lint-frontend:
|
lint-frontend:
|
||||||
needs: frontend-build
|
needs: frontend-build
|
||||||
if: needs.frontend-build.outputs.should-run == 'true'
|
if: needs.frontend-build.outputs.should-run == 'true'
|
||||||
@@ -144,7 +132,8 @@ jobs:
|
|||||||
name: docker-image
|
name: docker-image
|
||||||
|
|
||||||
- name: Load Docker Image
|
- name: Load Docker Image
|
||||||
run: docker load < docker-image.tar.gz
|
run: |
|
||||||
|
docker load < docker-image.tar.gz
|
||||||
|
|
||||||
- name: eslint
|
- name: eslint
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ repos:
|
|||||||
- id: prettier
|
- id: prettier
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- prettier@3.5.3
|
- prettier@3.5.3
|
||||||
args: ["--ignore-path=./superset-frontend/.prettierignore"]
|
args: ["--ignore-path=./superset-frontend/.prettierignore", "--exclude", "site-packages"]
|
||||||
files: "superset-frontend"
|
files: "superset-frontend"
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ assists people when migrating to a new version.
|
|||||||
- [33116](https://github.com/apache/superset/pull/33116) In Echarts Series charts (e.g. Line, Area, Bar, etc.) charts, the `x_axis_sort_series` and `x_axis_sort_series_ascending` form data items have been renamed with `x_axis_sort` and `x_axis_sort_asc`.
|
- [33116](https://github.com/apache/superset/pull/33116) In Echarts Series charts (e.g. Line, Area, Bar, etc.) charts, the `x_axis_sort_series` and `x_axis_sort_series_ascending` form data items have been renamed with `x_axis_sort` and `x_axis_sort_asc`.
|
||||||
There's a migration added that can potentially affect a significant number of existing charts.
|
There's a migration added that can potentially affect a significant number of existing charts.
|
||||||
- [32317](https://github.com/apache/superset/pull/32317) The horizontal filter bar feature is now out of testing/beta development and its feature flag `HORIZONTAL_FILTER_BAR` has been removed.
|
- [32317](https://github.com/apache/superset/pull/32317) The horizontal filter bar feature is now out of testing/beta development and its feature flag `HORIZONTAL_FILTER_BAR` has been removed.
|
||||||
|
- [31590](https://github.com/apache/superset/pull/31590) Marks the begining of intricate work around supporting dynamic Theming, and breaks support for [THEME_OVERRIDES](https://github.com/apache/superset/blob/732de4ac7fae88e29b7f123b6cbb2d7cd411b0e4/superset/config.py#L671) in favor of a new theming system based on AntD V5. Likely this will be in disrepair until settling over the 5.x lifecycle.
|
||||||
- [31976](https://github.com/apache/superset/pull/31976) Removed the `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag. The previous value of the feature flag was `True` and now the feature is permanently removed.
|
- [31976](https://github.com/apache/superset/pull/31976) Removed the `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag. The previous value of the feature flag was `True` and now the feature is permanently removed.
|
||||||
- [31959](https://github.com/apache/superset/pull/32000) Removes CSV_UPLOAD_MAX_SIZE config, use your web server to control file upload size.
|
- [31959](https://github.com/apache/superset/pull/32000) Removes CSV_UPLOAD_MAX_SIZE config, use your web server to control file upload size.
|
||||||
- [31959](https://github.com/apache/superset/pull/31959) Removes the following endpoints from data uploads: `/api/v1/database/<id>/<file type>_upload` and `/api/v1/database/<file type>_metadata`, in favour of new one (Details on the PR). And simplifies permissions.
|
- [31959](https://github.com/apache/superset/pull/31959) Removes the following endpoints from data uploads: `/api/v1/database/<id>/<file type>_upload` and `/api/v1/database/<file type>_metadata`, in favour of new one (Details on the PR). And simplifies permissions.
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export type EmbeddedDashboard = {
|
|||||||
callbackFn: ObserveDataMaskCallbackFn,
|
callbackFn: ObserveDataMaskCallbackFn,
|
||||||
) => void;
|
) => void;
|
||||||
getDataMask: () => Record<string, any>;
|
getDataMask: () => Record<string, any>;
|
||||||
|
setThemeConfig: (themeConfig: Record<string, any>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -245,6 +246,18 @@ export async function embedDashboard({
|
|||||||
ourPort.start();
|
ourPort.start();
|
||||||
ourPort.defineMethod('observeDataMask', callbackFn);
|
ourPort.defineMethod('observeDataMask', callbackFn);
|
||||||
};
|
};
|
||||||
|
// TODO: Add proper types once theming branch is merged
|
||||||
|
const setThemeConfig = async (themeConfig: Record<string, any>): Promise<void> => {
|
||||||
|
try {
|
||||||
|
ourPort.emit('setThemeConfig', { themeConfig });
|
||||||
|
log('Theme config sent successfully (or at least message dispatched)');
|
||||||
|
} catch (error) {
|
||||||
|
log(
|
||||||
|
'Error sending theme config. Ensure the iframe side implements the "setThemeConfig" method.',
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getScrollSize,
|
getScrollSize,
|
||||||
@@ -253,5 +266,6 @@ export async function embedDashboard({
|
|||||||
getActiveTabs,
|
getActiveTabs,
|
||||||
observeDataMask,
|
observeDataMask,
|
||||||
getDataMask,
|
getDataMask,
|
||||||
|
setThemeConfig
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
coverage/**
|
coverage/**
|
||||||
dist/*
|
dist/*
|
||||||
src/assets/images/*
|
src/assets/images/*
|
||||||
src/assets/stylesheets/*
|
|
||||||
node_modules/*
|
node_modules/*
|
||||||
node_modules*/*
|
node_modules*/*
|
||||||
vendor/*
|
vendor/*
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const restrictedImportsRules = {
|
|||||||
message: 'Lodash Memoize is unsafe! Please use memoize-one instead',
|
message: 'Lodash Memoize is unsafe! Please use memoize-one instead',
|
||||||
},
|
},
|
||||||
'no-testing-library-react': {
|
'no-testing-library-react': {
|
||||||
name: '@testing-library/react',
|
name: '@superset-ui/core/spec',
|
||||||
message: 'Please use spec/helpers/testing-library instead',
|
message: 'Please use spec/helpers/testing-library instead',
|
||||||
},
|
},
|
||||||
'no-testing-library-react-dom-utils': {
|
'no-testing-library-react-dom-utils': {
|
||||||
@@ -63,10 +63,6 @@ const restrictedImportsRules = {
|
|||||||
name: 'antd',
|
name: 'antd',
|
||||||
message: 'Please import Ant components from the index of src/components',
|
message: 'Please import Ant components from the index of src/components',
|
||||||
},
|
},
|
||||||
'no-antd-v5': {
|
|
||||||
name: 'antd-v5',
|
|
||||||
message: 'Please import Ant v5 components from the index of src/components',
|
|
||||||
},
|
|
||||||
'no-superset-theme': {
|
'no-superset-theme': {
|
||||||
name: '@superset-ui/core',
|
name: '@superset-ui/core',
|
||||||
importNames: ['supersetTheme'],
|
importNames: ['supersetTheme'],
|
||||||
@@ -101,6 +97,15 @@ module.exports = {
|
|||||||
// resolve modules from `/superset_frontend/node_modules` and `/superset_frontend`
|
// resolve modules from `/superset_frontend/node_modules` and `/superset_frontend`
|
||||||
moduleDirectory: ['node_modules', '.'],
|
moduleDirectory: ['node_modules', '.'],
|
||||||
},
|
},
|
||||||
|
typescript: {
|
||||||
|
alwaysTryTypes: true,
|
||||||
|
project: [
|
||||||
|
'./tsconfig.json',
|
||||||
|
'./packages/superset-ui-core/tsconfig.json',
|
||||||
|
'./packages/superset-ui-chart-controls/',
|
||||||
|
'./plugins/*/tsconfig.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// only allow import from top level of module
|
// only allow import from top level of module
|
||||||
'import/core-modules': importCoreModules,
|
'import/core-modules': importCoreModules,
|
||||||
@@ -239,6 +244,14 @@ module.exports = {
|
|||||||
'ImportNamespaceSpecifier[parent.source.value!=/^(\\.|src)/]',
|
'ImportNamespaceSpecifier[parent.source.value!=/^(\\.|src)/]',
|
||||||
message: 'Wildcard imports are not allowed',
|
message: 'Wildcard imports are not allowed',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
selector: "TSTypeReference[typeName.name='$TSFixMe']",
|
||||||
|
message: 'Fix this $TSFixMe type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: "TSTypeReference[typeName.name='$TSFixMeFunction']",
|
||||||
|
message: 'Fix this $TSFixMeFunction type',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'no-restricted-imports': [
|
'no-restricted-imports': [
|
||||||
'error',
|
'error',
|
||||||
@@ -295,9 +308,9 @@ module.exports = {
|
|||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
paths: Object.values(restrictedImportsRules).filter(
|
paths: Object.values(restrictedImportsRules).filter(
|
||||||
r => r.name !== 'antd-v5',
|
r => r.name !== 'antd',
|
||||||
),
|
),
|
||||||
patterns: ['antd/*'],
|
patterns: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -330,7 +343,9 @@ module.exports = {
|
|||||||
rules: {
|
rules: {
|
||||||
'import/no-extraneous-dependencies': [
|
'import/no-extraneous-dependencies': [
|
||||||
'error',
|
'error',
|
||||||
{ devDependencies: true },
|
{
|
||||||
|
devDependencies: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'no-only-tests/no-only-tests': 'error',
|
'no-only-tests/no-only-tests': 'error',
|
||||||
'max-classes-per-file': 0,
|
'max-classes-per-file': 0,
|
||||||
@@ -373,7 +388,7 @@ module.exports = {
|
|||||||
'fixtures.*',
|
'fixtures.*',
|
||||||
'cypress-base/cypress/**/*',
|
'cypress-base/cypress/**/*',
|
||||||
'Stories.tsx',
|
'Stories.tsx',
|
||||||
'packages/superset-ui-core/src/style/index.tsx',
|
'packages/superset-ui-core/src/theme/index.tsx',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'theme-colors/no-literal-colors': 0,
|
'theme-colors/no-literal-colors': 0,
|
||||||
|
|||||||
@@ -17,31 +17,75 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { withJsx } from '@mihkeleidast/storybook-addon-source';
|
import { withJsx } from '@mihkeleidast/storybook-addon-source';
|
||||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
import { themeObject, css, exampleThemes } from '@superset-ui/core';
|
||||||
import { AntdThemeProvider } from '../src/components/AntdThemeProvider';
|
|
||||||
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
|
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import reducerIndex from 'spec/helpers/reducerIndex';
|
import reducerIndex from 'spec/helpers/reducerIndex';
|
||||||
import { GlobalStyles } from '../src/GlobalStyles';
|
import { Global } from '@emotion/react';
|
||||||
|
import { App, Layout, Space, Content } from 'antd';
|
||||||
|
|
||||||
import 'src/theme.ts';
|
import 'src/theme.ts';
|
||||||
import './storybook.css';
|
import './storybook.css';
|
||||||
|
|
||||||
|
export const GlobalStylesOverrides = () => (
|
||||||
|
<Global
|
||||||
|
styles={css`
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#storybook-root {
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
min-height: 100vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-app {
|
||||||
|
min-height: 100vh !important;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
combineReducers(reducerIndex),
|
combineReducers(reducerIndex),
|
||||||
{},
|
{},
|
||||||
compose(applyMiddleware(thunk)),
|
compose(applyMiddleware(thunk)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const themeDecorator = Story => (
|
export const globalTypes = {
|
||||||
<ThemeProvider theme={supersetTheme}>
|
theme: {
|
||||||
<AntdThemeProvider>
|
name: 'Theme',
|
||||||
<GlobalStyles />
|
description: 'Global theme for components',
|
||||||
<Story />
|
defaultValue: 'superset',
|
||||||
</AntdThemeProvider>
|
toolbar: {
|
||||||
</ThemeProvider>
|
icon: 'paintbrush',
|
||||||
);
|
items: Object.keys(exampleThemes),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const themeDecorator = (Story, context) => {
|
||||||
|
const themeKey = context.globals.theme || 'superset';
|
||||||
|
themeObject.setConfig(exampleThemes[themeKey]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<themeObject.SupersetThemeProvider>
|
||||||
|
<App>
|
||||||
|
<GlobalStylesOverrides />
|
||||||
|
<Layout
|
||||||
|
style={{
|
||||||
|
minHeight: '100vh',
|
||||||
|
width: '100%',
|
||||||
|
padding: 24,
|
||||||
|
backgroundColor: themeObject.theme.colorBgBase,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Story {...context} />
|
||||||
|
</Layout>
|
||||||
|
</App>
|
||||||
|
</themeObject.SupersetThemeProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const providerDecorator = Story => (
|
const providerDecorator = Story => (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
@@ -81,5 +125,5 @@ export const parameters = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
controls: { expanded: true, sort: 'alpha' },
|
controls: { expanded: true, sort: 'alpha', disableSaveFromUI: true },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
body {
|
body {
|
||||||
background: transparent;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import { LOGIN } from 'cypress/utils/urls';
|
||||||
|
|
||||||
|
function interceptLogin() {
|
||||||
|
cy.intercept('POST', '/login/').as('login');
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Login view', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit(LOGIN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load login page', () => {
|
||||||
|
cy.getBySel('login-form').should('be.visible');
|
||||||
|
cy.getBySel('username-input').should('be.visible');
|
||||||
|
cy.getBySel('password-input').should('be.visible');
|
||||||
|
cy.getBySel('login-button').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should redirect to login with incorrect username and password', () => {
|
||||||
|
interceptLogin();
|
||||||
|
cy.getBySel('login-form').should('be.visible');
|
||||||
|
cy.getBySel('username-input').type('admin');
|
||||||
|
cy.getBySel('password-input').type('wrongpassword');
|
||||||
|
cy.getBySel('login-button').click();
|
||||||
|
cy.wait('@login');
|
||||||
|
cy.url().should('include', LOGIN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should login with correct username and password', () => {
|
||||||
|
interceptLogin();
|
||||||
|
cy.getBySel('login-form').should('be.visible');
|
||||||
|
cy.getBySel('username-input').type('admin');
|
||||||
|
cy.getBySel('password-input').type('general');
|
||||||
|
cy.getBySel('login-button').click();
|
||||||
|
cy.wait('@login');
|
||||||
|
cy.getCookies().should('have.length', 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { REGISTER } from 'cypress/utils/urls';
|
||||||
|
|
||||||
|
describe('Register view', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit(REGISTER);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load register page', () => {
|
||||||
|
cy.getBySel('register-form').should('be.visible');
|
||||||
|
cy.getBySel('username-input').should('be.visible');
|
||||||
|
cy.getBySel('first-name-input').should('be.visible');
|
||||||
|
cy.getBySel('last-name-input').should('be.visible');
|
||||||
|
cy.getBySel('email-input').should('be.visible');
|
||||||
|
cy.getBySel('password-input').should('be.visible');
|
||||||
|
cy.getBySel('confirm-password-input').should('be.visible');
|
||||||
|
cy.getBySel('register-button').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -35,12 +35,12 @@ function orderAlphabetical() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openProperties() {
|
function openProperties() {
|
||||||
cy.get('[aria-label="more"]').eq(1).click();
|
cy.get('[aria-label="more"]').eq(0).click();
|
||||||
cy.getBySel('chart-list-edit-option').click();
|
cy.getBySel('chart-list-edit-option').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openMenu() {
|
function openMenu() {
|
||||||
cy.get('[aria-label="more"]').eq(1).click();
|
cy.get('[aria-label="more"]').eq(0).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete() {
|
function confirmDelete() {
|
||||||
@@ -81,12 +81,13 @@ describe('Charts list', () => {
|
|||||||
cy.wait('@get');
|
cy.wait('@get');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.only('should show the newly added dashboards in a tooltip', () => {
|
it('should show the newly added dashboards in a tooltip', () => {
|
||||||
interceptDashboardGet();
|
interceptDashboardGet();
|
||||||
visitSampleChartFromList('1 - Sample chart');
|
visitSampleChartFromList('1 - Sample chart');
|
||||||
saveChartToDashboard('1 - Sample dashboard');
|
saveChartToDashboard('1 - Sample chart', '1 - Sample dashboard');
|
||||||
saveChartToDashboard('2 - Sample dashboard');
|
saveChartToDashboard('1 - Sample chart', '2 - Sample dashboard');
|
||||||
saveChartToDashboard('3 - Sample dashboard');
|
saveChartToDashboard('1 - Sample chart', '3 - Sample dashboard');
|
||||||
|
saveChartToDashboard('1 - Sample chart', '4 - Sample dashboard');
|
||||||
visitChartList();
|
visitChartList();
|
||||||
|
|
||||||
cy.getBySel('count-crosslinks').should('be.visible');
|
cy.getBySel('count-crosslinks').should('be.visible');
|
||||||
@@ -95,8 +96,6 @@ describe('Charts list', () => {
|
|||||||
|
|
||||||
describe('list mode', () => {
|
describe('list mode', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.createSampleDashboards([0, 1, 2, 3]);
|
|
||||||
cy.createSampleCharts([0]);
|
|
||||||
visitChartList();
|
visitChartList();
|
||||||
setGridMode('list');
|
setGridMode('list');
|
||||||
});
|
});
|
||||||
@@ -112,18 +111,10 @@ describe('Charts list', () => {
|
|||||||
cy.getBySel('sort-header').eq(7).contains('Actions');
|
cy.getBySel('sort-header').eq(7).contains('Actions');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sort correctly in list mode', () => {
|
|
||||||
cy.getBySel('sort-header').eq(1).click();
|
|
||||||
cy.getBySel('table-row').first().contains('Area Chart');
|
|
||||||
cy.getBySel('sort-header').eq(1).click();
|
|
||||||
cy.getBySel('table-row').first().contains("World's Population");
|
|
||||||
cy.getBySel('sort-header').eq(1).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bulk select in list mode', () => {
|
it('should bulk select in list mode', () => {
|
||||||
toggleBulkSelect();
|
toggleBulkSelect();
|
||||||
cy.get('#header-toggle-all').click();
|
cy.get('[aria-label="Select all"]').click();
|
||||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 26);
|
cy.get('input[type="checkbox"]:checked').should('have.length', 26);
|
||||||
cy.getBySel('bulk-select-copy').contains('25 Selected');
|
cy.getBySel('bulk-select-copy').contains('25 Selected');
|
||||||
cy.getBySel('bulk-select-action')
|
cy.getBySel('bulk-select-action')
|
||||||
.should('have.length', 2)
|
.should('have.length', 2)
|
||||||
@@ -132,7 +123,7 @@ describe('Charts list', () => {
|
|||||||
expect($btns).to.contain('Export');
|
expect($btns).to.contain('Export');
|
||||||
});
|
});
|
||||||
cy.getBySel('bulk-select-deselect-all').click();
|
cy.getBySel('bulk-select-deselect-all').click();
|
||||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
|
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
|
||||||
cy.getBySel('bulk-select-copy').contains('0 Selected');
|
cy.getBySel('bulk-select-copy').contains('0 Selected');
|
||||||
cy.getBySel('bulk-select-action').should('not.exist');
|
cy.getBySel('bulk-select-action').should('not.exist');
|
||||||
});
|
});
|
||||||
@@ -164,11 +155,6 @@ describe('Charts list', () => {
|
|||||||
cy.getBySel('bulk-select-action').should('not.exist');
|
cy.getBySel('bulk-select-action').should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sort in card mode', () => {
|
|
||||||
orderAlphabetical();
|
|
||||||
cy.getBySel('styled-card').first().contains('% Rural');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should preserve other filters when sorting', () => {
|
it('should preserve other filters when sorting', () => {
|
||||||
cy.getBySel('styled-card').should('have.length', 25);
|
cy.getBySel('styled-card').should('have.length', 25);
|
||||||
setFilter('Type', 'Big Number');
|
setFilter('Type', 'Big Number');
|
||||||
@@ -179,40 +165,12 @@ describe('Charts list', () => {
|
|||||||
|
|
||||||
describe('common actions', () => {
|
describe('common actions', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.createSampleCharts([0, 1, 2, 3]);
|
|
||||||
visitChartList();
|
visitChartList();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow to favorite/unfavorite', () => {
|
|
||||||
cy.intercept({ url: `**/api/v1/chart/*/favorites/`, method: 'POST' }).as(
|
|
||||||
'select',
|
|
||||||
);
|
|
||||||
cy.intercept({
|
|
||||||
url: `**/api/v1/chart/*/favorites/`,
|
|
||||||
method: 'DELETE',
|
|
||||||
}).as('unselect');
|
|
||||||
|
|
||||||
setGridMode('card');
|
|
||||||
orderAlphabetical();
|
|
||||||
|
|
||||||
cy.getBySel('styled-card').first().contains('% Rural');
|
|
||||||
cy.getBySel('styled-card')
|
|
||||||
.first()
|
|
||||||
.find("[aria-label='favorite-unselected']")
|
|
||||||
.click();
|
|
||||||
cy.wait('@select');
|
|
||||||
cy.getBySel('styled-card')
|
|
||||||
.first()
|
|
||||||
.find("[aria-label='favorite-selected']")
|
|
||||||
.click();
|
|
||||||
cy.wait('@unselect');
|
|
||||||
cy.getBySel('styled-card')
|
|
||||||
.first()
|
|
||||||
.find("[aria-label='favorite-selected']")
|
|
||||||
.should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bulk delete correctly', () => {
|
it('should bulk delete correctly', () => {
|
||||||
|
cy.createSampleCharts([0, 1, 2, 3]);
|
||||||
|
|
||||||
interceptBulkDelete();
|
interceptBulkDelete();
|
||||||
toggleBulkSelect();
|
toggleBulkSelect();
|
||||||
|
|
||||||
@@ -220,9 +178,10 @@ describe('Charts list', () => {
|
|||||||
setGridMode('card');
|
setGridMode('card');
|
||||||
orderAlphabetical();
|
orderAlphabetical();
|
||||||
|
|
||||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart').click();
|
cy.getBySel('skeleton-card').should('not.exist');
|
||||||
cy.getBySel('styled-card').eq(2).contains('2 - Sample chart').click();
|
cy.getBySel('styled-card').contains('1 - Sample chart').click();
|
||||||
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
cy.getBySel('styled-card').contains('2 - Sample chart').click();
|
||||||
|
cy.getBySel('bulk-select-action').contains('Delete').click();
|
||||||
confirmDelete();
|
confirmDelete();
|
||||||
cy.wait('@bulkDelete');
|
cy.wait('@bulkDelete');
|
||||||
cy.getBySel('styled-card')
|
cy.getBySel('styled-card')
|
||||||
@@ -234,56 +193,71 @@ describe('Charts list', () => {
|
|||||||
|
|
||||||
// bulk deletes in list-view
|
// bulk deletes in list-view
|
||||||
setGridMode('list');
|
setGridMode('list');
|
||||||
cy.getBySel('table-row').eq(1).contains('3 - Sample chart');
|
cy.get('.loading').should('not.exist');
|
||||||
cy.getBySel('table-row').eq(2).contains('4 - Sample chart');
|
cy.getBySel('table-row').contains('3 - Sample chart').should('exist');
|
||||||
|
cy.getBySel('table-row').contains('4 - Sample chart').should('exist');
|
||||||
|
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(0).click();
|
||||||
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
|
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
|
||||||
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(2).click();
|
|
||||||
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
||||||
confirmDelete();
|
confirmDelete();
|
||||||
cy.wait('@bulkDelete');
|
cy.wait('@bulkDelete');
|
||||||
cy.getBySel('table-row').eq(1).should('not.contain', '3 - Sample chart');
|
cy.get('.loading').should('exist');
|
||||||
cy.getBySel('table-row').eq(2).should('not.contain', '4 - Sample chart');
|
cy.get('.loading').should('not.exist');
|
||||||
|
cy.getBySel('table-row').eq(0).should('not.contain', '3 - Sample chart');
|
||||||
|
cy.getBySel('table-row').eq(1).should('not.contain', '4 - Sample chart');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete correctly', () => {
|
it('should delete correctly in card mode', () => {
|
||||||
|
cy.createSampleCharts([0, 1]);
|
||||||
interceptDelete();
|
interceptDelete();
|
||||||
|
|
||||||
// deletes in card-view
|
// deletes in card-view
|
||||||
setGridMode('card');
|
setGridMode('card');
|
||||||
orderAlphabetical();
|
orderAlphabetical();
|
||||||
|
|
||||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart');
|
cy.getBySel('styled-card').contains('1 - Sample chart');
|
||||||
openMenu();
|
openMenu();
|
||||||
cy.getBySel('chart-list-delete-option').click();
|
cy.getBySel('chart-list-delete-option').click();
|
||||||
confirmDelete();
|
confirmDelete();
|
||||||
cy.wait('@delete');
|
cy.wait('@delete');
|
||||||
cy.getBySel('styled-card')
|
cy.getBySel('styled-card')
|
||||||
.eq(1)
|
.contains('1 - Sample chart')
|
||||||
.should('not.contain', '1 - Sample chart');
|
.should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
// deletes in list-view
|
it('should delete correctly in list mode', () => {
|
||||||
setGridMode('list');
|
cy.createSampleCharts([2, 3]);
|
||||||
cy.getBySel('table-row').eq(1).contains('2 - Sample chart');
|
interceptDelete();
|
||||||
cy.getBySel('delete').eq(1).click();
|
cy.getBySel('sort-header').contains('Name').click();
|
||||||
|
|
||||||
|
// Modal closes immediatly without this
|
||||||
|
cy.wait(2000);
|
||||||
|
|
||||||
|
cy.getBySel('table-row').eq(0).contains('3 - Sample chart');
|
||||||
|
cy.getBySel('delete').eq(0).click();
|
||||||
confirmDelete();
|
confirmDelete();
|
||||||
cy.wait('@delete');
|
cy.wait('@delete');
|
||||||
cy.getBySel('table-row').eq(1).should('not.contain', '2 - Sample chart');
|
cy.get('.loading').should('exist');
|
||||||
|
cy.get('.loading').should('not.exist');
|
||||||
|
cy.getBySel('table-row').eq(0).should('not.contain', '3 - Sample chart');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should edit correctly', () => {
|
it('should edit correctly', () => {
|
||||||
|
cy.createSampleCharts([0]);
|
||||||
interceptUpdate();
|
interceptUpdate();
|
||||||
|
|
||||||
// edits in card-view
|
// edits in card-view
|
||||||
setGridMode('card');
|
setGridMode('card');
|
||||||
orderAlphabetical();
|
orderAlphabetical();
|
||||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart');
|
cy.getBySel('skeleton-card').should('not.exist');
|
||||||
|
cy.getBySel('styled-card').eq(0).contains('1 - Sample chart');
|
||||||
|
|
||||||
// change title
|
// change title
|
||||||
openProperties();
|
openProperties();
|
||||||
cy.getBySel('properties-modal-name-input').type(' | EDITED');
|
cy.getBySel('properties-modal-name-input').type(' | EDITED');
|
||||||
cy.get('button:contains("Save")').click();
|
cy.get('button:contains("Save")').click();
|
||||||
cy.wait('@update');
|
cy.wait('@update');
|
||||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart | EDITED');
|
cy.getBySel('styled-card').eq(0).contains('1 - Sample chart | EDITED');
|
||||||
|
|
||||||
// edits in list-view
|
// edits in list-view
|
||||||
setGridMode('list');
|
setGridMode('list');
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ describe.skip('Dashboard top-level controls', () => {
|
|||||||
// Solution: pause the network before clicking, assert, then unpause network.
|
// Solution: pause the network before clicking, assert, then unpause network.
|
||||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||||
'have.class',
|
'have.class',
|
||||||
'antd5-dropdown-menu-item-disabled',
|
'ant-dropdown-menu-item-disabled',
|
||||||
);
|
);
|
||||||
waitForChartLoad(mapSpec);
|
waitForChartLoad(mapSpec);
|
||||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||||
'not.have.class',
|
'not.have.class',
|
||||||
'antd5-dropdown-menu-item-disabled',
|
'ant-dropdown-menu-item-disabled',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -65,7 +65,7 @@ describe.skip('Dashboard top-level controls', () => {
|
|||||||
cy.get('[aria-label="ellipsis"]').click();
|
cy.get('[aria-label="ellipsis"]').click();
|
||||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||||
'not.have.class',
|
'not.have.class',
|
||||||
'antd5-dropdown-menu-item-disabled',
|
'ant-dropdown-menu-item-disabled',
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.get('[data-test="refresh-dashboard-menu-item"]').click({
|
cy.get('[data-test="refresh-dashboard-menu-item"]').click({
|
||||||
@@ -73,7 +73,7 @@ describe.skip('Dashboard top-level controls', () => {
|
|||||||
});
|
});
|
||||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||||
'have.class',
|
'have.class',
|
||||||
'antd5-dropdown-menu-item-disabled',
|
'ant-dropdown-menu-item-disabled',
|
||||||
);
|
);
|
||||||
|
|
||||||
// wait all charts force refreshed.
|
// wait all charts force refreshed.
|
||||||
@@ -94,7 +94,7 @@ describe.skip('Dashboard top-level controls', () => {
|
|||||||
cy.get('[aria-label="ellipsis"]').click();
|
cy.get('[aria-label="ellipsis"]').click();
|
||||||
cy.get('[data-test="refresh-dashboard-menu-item"]').and(
|
cy.get('[data-test="refresh-dashboard-menu-item"]').and(
|
||||||
'not.have.class',
|
'not.have.class',
|
||||||
'antd5-dropdown-menu-item-disabled',
|
'ant-dropdown-menu-item-disabled',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,6 +62,6 @@ describe('Dashboard actions', () => {
|
|||||||
// Verify the color of the outlined star (gray)
|
// Verify the color of the outlined star (gray)
|
||||||
cy.get('@starIconOutlinedAfter')
|
cy.get('@starIconOutlinedAfter')
|
||||||
.should('have.css', 'color')
|
.should('have.css', 'color')
|
||||||
.and('eq', 'rgb(178, 178, 178)');
|
.and('eq', 'rgb(133, 133, 133)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,14 +54,14 @@ const drillBy = (targetDrillByColumn: string, isLegacy = false) => {
|
|||||||
interceptV1ChartData();
|
interceptV1ChartData();
|
||||||
}
|
}
|
||||||
|
|
||||||
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
|
cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.find("[role='menu'] [role='menuitem']")
|
.find("[role='menu'] [role='menuitem']")
|
||||||
.contains(/^Drill by$/)
|
.contains(/^Drill by$/)
|
||||||
.trigger('mouseover', { force: true });
|
.trigger('mouseover', { force: true });
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
'.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
|
'.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
|
||||||
)
|
)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.find('[role="menuitem"]')
|
.find('[role="menuitem"]')
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ function openModalFromMenu(chartType: string) {
|
|||||||
cy.get(
|
cy.get(
|
||||||
`[data-test-viz-type='${chartType}'] [aria-label='More Options']`,
|
`[data-test-viz-type='${chartType}'] [aria-label='More Options']`,
|
||||||
).click();
|
).click();
|
||||||
cy.get('.antd5-dropdown')
|
cy.get('.ant-dropdown')
|
||||||
.not('.antd5-dropdown-hidden')
|
.not('.ant-dropdown-hidden')
|
||||||
.find("[role='menu'] [role='menuitem']")
|
.find("[role='menu'] [role='menuitem']")
|
||||||
.eq(5)
|
.eq(5)
|
||||||
.should('contain', 'Drill to detail')
|
.should('contain', 'Drill to detail')
|
||||||
@@ -46,8 +46,8 @@ function openModalFromMenu(chartType: string) {
|
|||||||
function drillToDetail(targetMenuItem: string) {
|
function drillToDetail(targetMenuItem: string) {
|
||||||
interceptSamples();
|
interceptSamples();
|
||||||
|
|
||||||
cy.get('.antd5-dropdown')
|
cy.get('.ant-dropdown')
|
||||||
.not('.antd5-dropdown-hidden')
|
.not('.ant-dropdown-hidden')
|
||||||
.first()
|
.first()
|
||||||
.find("[role='menu'] [role='menuitem']")
|
.find("[role='menu'] [role='menuitem']")
|
||||||
.contains(new RegExp(`^${targetMenuItem}$`))
|
.contains(new RegExp(`^${targetMenuItem}$`))
|
||||||
@@ -61,14 +61,14 @@ function drillToDetail(targetMenuItem: string) {
|
|||||||
const drillToDetailBy = (targetDrill: string) => {
|
const drillToDetailBy = (targetDrill: string) => {
|
||||||
interceptSamples();
|
interceptSamples();
|
||||||
|
|
||||||
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
|
cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.find("[role='menu'] [role='menuitem']")
|
.find("[role='menu'] [role='menuitem']")
|
||||||
.contains(/^Drill to detail by$/)
|
.contains(/^Drill to detail by$/)
|
||||||
.trigger('mouseover', { force: true });
|
.trigger('mouseover', { force: true });
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
'.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
|
'.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
|
||||||
)
|
)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.find('[role="menuitem"]')
|
.find('[role="menuitem"]')
|
||||||
@@ -121,7 +121,10 @@ function testTimeChart(vizType: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Drill to detail modal', () => {
|
// TODO fix this test, it has issues with autoscrolling and the locked title
|
||||||
|
// flakes intricately when the righClick is obstructed by the title.
|
||||||
|
// Tried many option around scrollIntoView, force, etc. but no luck.
|
||||||
|
describe.skip('Drill to detail modal', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
closeModal();
|
closeModal();
|
||||||
});
|
});
|
||||||
@@ -463,7 +466,7 @@ describe('Drill to detail modal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// close the filter and test that data was reloaded
|
// close the filter and test that data was reloaded
|
||||||
cy.getBySel('filter-col').find("[aria-label='close']").click();
|
cy.getBySel('filter-col').find("[aria-label='Close']").click();
|
||||||
cy.wait('@samples');
|
cy.wait('@samples');
|
||||||
cy.getBySel('row-count-label').should('contain', '75.7k rows');
|
cy.getBySel('row-count-label').should('contain', '75.7k rows');
|
||||||
cy.get('.ant-pagination-item-active').should('contain', '1');
|
cy.get('.ant-pagination-item-active').should('contain', '1');
|
||||||
|
|||||||
@@ -17,7 +17,12 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { SAMPLE_DASHBOARD_1, TABBED_DASHBOARD } from 'cypress/utils/urls';
|
import { SAMPLE_DASHBOARD_1, TABBED_DASHBOARD } from 'cypress/utils/urls';
|
||||||
import { drag, resize, waitForChartLoad } from 'cypress/utils';
|
import {
|
||||||
|
drag,
|
||||||
|
resize,
|
||||||
|
setSelectSearchInput,
|
||||||
|
waitForChartLoad,
|
||||||
|
} from 'cypress/utils';
|
||||||
import { edit } from 'brace';
|
import { edit } from 'brace';
|
||||||
import {
|
import {
|
||||||
interceptExploreUpdate,
|
interceptExploreUpdate,
|
||||||
@@ -34,21 +39,12 @@ function editDashboard() {
|
|||||||
cy.getBySel('edit-dashboard-button').click();
|
cy.getBySel('edit-dashboard-button').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
cy.getBySel('properties-modal-cancel-button').click({ force: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
function openProperties() {
|
function openProperties() {
|
||||||
cy.get('body').then($body => {
|
cy.getBySel('actions-trigger').click({ force: true });
|
||||||
if ($body.find('[data-test="properties-modal-cancel-button"]').length) {
|
cy.getBySel('header-actions-menu')
|
||||||
closeModal();
|
.contains('Edit properties')
|
||||||
}
|
.click({ force: true });
|
||||||
cy.getBySel('actions-trigger').click({ force: true });
|
cy.get('.ant-modal-body').should('be.visible');
|
||||||
cy.getBySel('header-actions-menu')
|
|
||||||
.contains('Edit properties')
|
|
||||||
.click({ force: true });
|
|
||||||
cy.get('.antd5-modal-body').should('be.visible');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertMetadata(text: string) {
|
function assertMetadata(text: string) {
|
||||||
@@ -65,7 +61,7 @@ function assertMetadata(text: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openAdvancedProperties() {
|
function openAdvancedProperties() {
|
||||||
cy.get('.antd5-modal-body')
|
cy.get('.ant-modal-body')
|
||||||
.contains('Advanced')
|
.contains('Advanced')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
@@ -150,12 +146,10 @@ function selectColorScheme(
|
|||||||
target = 'dashboard-edit-properties-form',
|
target = 'dashboard-edit-properties-form',
|
||||||
) {
|
) {
|
||||||
cy.get(`[data-test="${target}"] input[aria-label="Select color scheme"]`)
|
cy.get(`[data-test="${target}"] input[aria-label="Select color scheme"]`)
|
||||||
.first()
|
.should('exist')
|
||||||
.then($input => {
|
.then($input => {
|
||||||
cy.wrap($input).click({ force: true });
|
setSelectSearchInput($input, color.slice(0, 5));
|
||||||
cy.wrap($input).type(color.slice(0, 5), { force: true });
|
|
||||||
});
|
});
|
||||||
cy.getBySel(color).click({ force: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAndGo(dashboard = 'Tabbed Dashboard') {
|
function saveAndGo(dashboard = 'Tabbed Dashboard') {
|
||||||
@@ -1095,7 +1089,7 @@ describe('Dashboard edit', () => {
|
|||||||
cy.allowConsoleErrors(['Error: A valid color scheme is required']);
|
cy.allowConsoleErrors(['Error: A valid color scheme is required']);
|
||||||
writeMetadata('{"color_scheme":"wrongcolorscheme"}');
|
writeMetadata('{"color_scheme":"wrongcolorscheme"}');
|
||||||
applyChanges();
|
applyChanges();
|
||||||
cy.get('.antd5-modal-body')
|
cy.get('.ant-modal-body')
|
||||||
.contains('A valid color scheme is required')
|
.contains('A valid color scheme is required')
|
||||||
.should('be.visible');
|
.should('be.visible');
|
||||||
});
|
});
|
||||||
@@ -1130,7 +1124,7 @@ describe('Dashboard edit', () => {
|
|||||||
|
|
||||||
it('should filter charts', () => {
|
it('should filter charts', () => {
|
||||||
interceptCharts();
|
interceptCharts();
|
||||||
cy.get('[role="checkbox"]').click();
|
cy.get('input[type="checkbox"]').click();
|
||||||
cy.getBySel('dashboard-charts-filter-search-input').type('Unicode');
|
cy.getBySel('dashboard-charts-filter-search-input').type('Unicode');
|
||||||
cy.wait('@filtering');
|
cy.wait('@filtering');
|
||||||
cy.getBySel('chart-card')
|
cy.getBySel('chart-card')
|
||||||
@@ -1140,8 +1134,8 @@ describe('Dashboard edit', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// TODO fix this test! This was the #1 flaky test as of 4/21/23 according to cypress dashboard.
|
// TODO fix this test! This was the #1 flaky test as of 4/21/23 according to cypress dashboard.
|
||||||
xit('should disable the Save button when undoing', () => {
|
it.skip('should disable the Save button when undoing', () => {
|
||||||
cy.get('[role="checkbox"]').click();
|
cy.get('input[type="checkbox"]').click();
|
||||||
dragComponent('Unicode Cloud', 'card-title', false);
|
dragComponent('Unicode Cloud', 'card-title', false);
|
||||||
cy.getBySel('header-save-button').should('be.enabled');
|
cy.getBySel('header-save-button').should('be.enabled');
|
||||||
discardChanges();
|
discardChanges();
|
||||||
@@ -1155,13 +1149,13 @@ describe('Dashboard edit', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add charts', () => {
|
it('should add charts', () => {
|
||||||
cy.get('[role="checkbox"]').click();
|
cy.get('input[type="checkbox"]').click();
|
||||||
dragComponent();
|
dragComponent();
|
||||||
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
|
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should remove added charts', () => {
|
it.skip('should remove added charts', () => {
|
||||||
cy.get('[role="checkbox"]').click();
|
cy.get('input[type="checkbox"]').click();
|
||||||
dragComponent('Unicode Cloud');
|
dragComponent('Unicode Cloud');
|
||||||
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
|
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
|
||||||
cy.getBySel('dashboard-delete-component-button').click();
|
cy.getBySel('dashboard-delete-component-button').click();
|
||||||
@@ -1204,7 +1198,7 @@ describe('Dashboard edit', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should save', () => {
|
it('should save', () => {
|
||||||
cy.get('[role="checkbox"]').click();
|
cy.get('input[type="checkbox"]').click();
|
||||||
dragComponent();
|
dragComponent();
|
||||||
cy.getBySel('header-save-button').should('be.enabled');
|
cy.getBySel('header-save-button').should('be.enabled');
|
||||||
saveChanges();
|
saveChanges();
|
||||||
|
|||||||
@@ -57,16 +57,16 @@ function setFilterBarOrientation(orientation: 'vertical' | 'horizontal') {
|
|||||||
.trigger('mouseover');
|
.trigger('mouseover');
|
||||||
|
|
||||||
if (orientation === 'vertical') {
|
if (orientation === 'vertical') {
|
||||||
cy.get('.antd5-dropdown-menu-item-selected')
|
cy.get('.ant-dropdown-menu-item-selected')
|
||||||
.contains('Horizontal (Top)')
|
.contains('Horizontal (Top)')
|
||||||
.should('exist');
|
.should('exist');
|
||||||
cy.get('.antd5-dropdown-menu-item').contains('Vertical (Left)').click();
|
cy.get('.ant-dropdown-menu-item').contains('Vertical (Left)').click();
|
||||||
cy.getBySel('dashboard-filters-panel').should('exist');
|
cy.getBySel('dashboard-filters-panel').should('exist');
|
||||||
} else {
|
} else {
|
||||||
cy.get('.antd5-dropdown-menu-item-selected')
|
cy.get('.ant-dropdown-menu-item-selected')
|
||||||
.contains('Vertical (Left)')
|
.contains('Vertical (Left)')
|
||||||
.should('exist');
|
.should('exist');
|
||||||
cy.get('.antd5-dropdown-menu-item').contains('Horizontal (Top)').click();
|
cy.get('.ant-dropdown-menu-item').contains('Horizontal (Top)').click();
|
||||||
cy.getBySel('loading-indicator').should('exist');
|
cy.getBySel('loading-indicator').should('exist');
|
||||||
cy.getBySel('filter-bar').should('exist');
|
cy.getBySel('filter-bar').should('exist');
|
||||||
cy.getBySel('dashboard-filters-panel').should('not.exist');
|
cy.getBySel('dashboard-filters-panel').should('not.exist');
|
||||||
@@ -138,7 +138,7 @@ describe('Horizontal FilterBar', () => {
|
|||||||
cy.getBySel('dropdown-container-btn').should('not.exist');
|
cy.getBySel('dropdown-container-btn').should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show "more filters" and scroll', () => {
|
it.only('should show "more filters" and scroll', () => {
|
||||||
prepareDashboardFilters([
|
prepareDashboardFilters([
|
||||||
{ name: 'test_1', column: 'country_name', datasetId: 2 },
|
{ name: 'test_1', column: 'country_name', datasetId: 2 },
|
||||||
{ name: 'test_2', column: 'country_code', datasetId: 2 },
|
{ name: 'test_2', column: 'country_code', datasetId: 2 },
|
||||||
@@ -157,11 +157,11 @@ describe('Horizontal FilterBar', () => {
|
|||||||
cy.get('.filter-item-wrapper').should('have.length', 3);
|
cy.get('.filter-item-wrapper').should('have.length', 3);
|
||||||
openMoreFilters();
|
openMoreFilters();
|
||||||
cy.getBySel('form-item-value').should('have.length', 12);
|
cy.getBySel('form-item-value').should('have.length', 12);
|
||||||
cy.getBySel('filter-control-name').contains('test_10').should('be.visible');
|
cy.getBySel('filter-control-name').contains('test_3').should('be.visible');
|
||||||
cy.getBySel('filter-control-name')
|
cy.getBySel('filter-control-name')
|
||||||
.contains('test_12')
|
.contains('test_12')
|
||||||
.should('not.be.visible');
|
.should('not.be.visible');
|
||||||
cy.get('.antd5-popover-inner').scrollTo('bottom');
|
cy.getBySel('filter-control-name').contains('test_12').scrollIntoView();
|
||||||
cy.getBySel('filter-control-name').contains('test_12').should('be.visible');
|
cy.getBySel('filter-control-name').contains('test_12').should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ describe('Horizontal FilterBar', () => {
|
|||||||
applyNativeFilterValueWithIndex(8, testItems.filterDefaultValue);
|
applyNativeFilterValueWithIndex(8, testItems.filterDefaultValue);
|
||||||
cy.get(nativeFilters.applyFilter).click({ force: true });
|
cy.get(nativeFilters.applyFilter).click({ force: true });
|
||||||
cy.wait('@chart');
|
cy.wait('@chart');
|
||||||
cy.get('.antd5-scroll-number.antd5-badge-count').should(
|
cy.get('.ant-scroll-number.ant-badge-count').should(
|
||||||
'have.attr',
|
'have.attr',
|
||||||
'title',
|
'title',
|
||||||
'1',
|
'1',
|
||||||
|
|||||||
@@ -199,14 +199,16 @@ describe('Native filters', () => {
|
|||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('[data-test="range-filter-from-input"]').type('{selectall}5');
|
cy.get('[data-test="range-filter-from-input"]').type('{selectall}40');
|
||||||
|
|
||||||
cy.get('[data-test="range-filter-to-input"]')
|
cy.get('[data-test="range-filter-to-input"]')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('[data-test="range-filter-to-input"]').type('{selectall}50');
|
cy.get('[data-test="range-filter-to-input"]').type('{selectall}50');
|
||||||
cy.get(nativeFilters.applyFilter).click();
|
cy.get(nativeFilters.applyFilter).click({
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Assert that the URL contains 'native_filters'
|
// Assert that the URL contains 'native_filters'
|
||||||
cy.url().then(u => {
|
cy.url().then(u => {
|
||||||
@@ -215,7 +217,7 @@ describe('Native filters', () => {
|
|||||||
|
|
||||||
cy.get('[data-test="range-filter-from-input"]')
|
cy.get('[data-test="range-filter-from-input"]')
|
||||||
.invoke('val')
|
.invoke('val')
|
||||||
.should('equal', '5');
|
.should('equal', '40');
|
||||||
|
|
||||||
// Assert that the "To" input has the correct value
|
// Assert that the "To" input has the correct value
|
||||||
cy.get('[data-test="range-filter-to-input"]')
|
cy.get('[data-test="range-filter-to-input"]')
|
||||||
|
|||||||
@@ -293,7 +293,11 @@ describe('Native filters', () => {
|
|||||||
|
|
||||||
it('Verify setting options and tooltips for value filter', () => {
|
it('Verify setting options and tooltips for value filter', () => {
|
||||||
enterNativeFilterEditModal(false);
|
enterNativeFilterEditModal(false);
|
||||||
cy.contains('Filter value is required').should('be.visible').click();
|
cy.contains('Filter value is required').scrollIntoView();
|
||||||
|
|
||||||
|
cy.contains('Filter value is required').should('be.visible').click({
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
checkNativeFilterTooltip(0, nativeFilterTooltips.preFilter);
|
checkNativeFilterTooltip(0, nativeFilterTooltips.preFilter);
|
||||||
checkNativeFilterTooltip(1, nativeFilterTooltips.defaultValue);
|
checkNativeFilterTooltip(1, nativeFilterTooltips.defaultValue);
|
||||||
cy.get(nativeFilters.modal.container).should('be.visible');
|
cy.get(nativeFilters.modal.container).should('be.visible');
|
||||||
|
|||||||
@@ -18,7 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { dashboardView, nativeFilters } from 'cypress/support/directories';
|
import { dashboardView, nativeFilters } from 'cypress/support/directories';
|
||||||
import { ChartSpec, waitForChartLoad } from 'cypress/utils';
|
import {
|
||||||
|
ChartSpec,
|
||||||
|
setSelectSearchInput,
|
||||||
|
waitForChartLoad,
|
||||||
|
} from 'cypress/utils';
|
||||||
|
|
||||||
export const WORLD_HEALTH_CHARTS = [
|
export const WORLD_HEALTH_CHARTS = [
|
||||||
{ name: '% Rural', viz: 'world_map' },
|
{ name: '% Rural', viz: 'world_map' },
|
||||||
@@ -264,10 +268,11 @@ export function fillNativeFilterForm(
|
|||||||
dataset?: string,
|
dataset?: string,
|
||||||
filterColumn?: string,
|
filterColumn?: string,
|
||||||
) {
|
) {
|
||||||
cy.get(nativeFilters.filtersPanel.filterTypeInput)
|
cy.get(nativeFilters.filtersPanel.filterTypeInput).within(() => {
|
||||||
.find(nativeFilters.filtersPanel.filterTypeItem)
|
cy.get('input').then($input => {
|
||||||
.click({ multiple: true, force: true });
|
setSelectSearchInput($input, type);
|
||||||
cy.get(`[label="${type}"]`).click({ multiple: true, force: true });
|
});
|
||||||
|
});
|
||||||
cy.get(nativeFilters.modal.container)
|
cy.get(nativeFilters.modal.container)
|
||||||
.find(nativeFilters.filtersPanel.filterName)
|
.find(nativeFilters.filtersPanel.filterName)
|
||||||
.last()
|
.last()
|
||||||
@@ -280,31 +285,23 @@ export function fillNativeFilterForm(
|
|||||||
.find(nativeFilters.filtersPanel.filterName)
|
.find(nativeFilters.filtersPanel.filterName)
|
||||||
.last()
|
.last()
|
||||||
.type(name, { scrollBehavior: false, force: true });
|
.type(name, { scrollBehavior: false, force: true });
|
||||||
|
|
||||||
if (dataset) {
|
if (dataset) {
|
||||||
cy.get(nativeFilters.modal.container)
|
cy.get('div[aria-label="Dataset"]').within(() => {
|
||||||
.find(nativeFilters.filtersPanel.datasetName)
|
cy.get('input').then($input => {
|
||||||
.last()
|
setSelectSearchInput($input, dataset, true);
|
||||||
.click({ force: true, scrollBehavior: false });
|
});
|
||||||
cy.get(nativeFilters.modal.container)
|
});
|
||||||
.find(nativeFilters.filtersPanel.datasetName)
|
|
||||||
.type(`${dataset}`, { scrollBehavior: false });
|
|
||||||
cy.get(nativeFilters.silentLoading).should('not.exist');
|
cy.get(nativeFilters.silentLoading).should('not.exist');
|
||||||
cy.get(`[label="${dataset}"]`).click({ multiple: true, force: true });
|
|
||||||
}
|
}
|
||||||
cy.get(nativeFilters.silentLoading).should('not.exist');
|
|
||||||
if (filterColumn) {
|
if (filterColumn) {
|
||||||
cy.get(nativeFilters.filtersPanel.filterInfoInput)
|
cy.get('div[aria-label="Column select"]').within(() => {
|
||||||
.last()
|
cy.get('input').then($input => {
|
||||||
.click({ force: true });
|
setSelectSearchInput($input, filterColumn, true);
|
||||||
cy.get(nativeFilters.filtersPanel.filterInfoInput)
|
});
|
||||||
.last()
|
});
|
||||||
.type(filterColumn);
|
|
||||||
cy.get(nativeFilters.filtersPanel.inputDropdown)
|
|
||||||
.should('be.visible', { timeout: 20000 })
|
|
||||||
.last()
|
|
||||||
.click();
|
|
||||||
}
|
}
|
||||||
cy.get(nativeFilters.silentLoading).should('not.exist');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ************************************************************************
|
/** ************************************************************************
|
||||||
@@ -442,6 +439,9 @@ export function checkNativeFilterTooltip(index: number, value: string) {
|
|||||||
.eq(index)
|
.eq(index)
|
||||||
.trigger('mouseover');
|
.trigger('mouseover');
|
||||||
cy.contains(`${value}`);
|
cy.contains(`${value}`);
|
||||||
|
cy.get(nativeFilters.filterConfigurationSections.infoTooltip)
|
||||||
|
.eq(index)
|
||||||
|
.trigger('mouseout');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ************************************************************************
|
/** ************************************************************************
|
||||||
@@ -455,20 +455,20 @@ export function applyAdvancedTimeRangeFilterOnDashboard(
|
|||||||
startRange?: string,
|
startRange?: string,
|
||||||
endRange?: string,
|
endRange?: string,
|
||||||
) {
|
) {
|
||||||
cy.get('.control-label').contains('RANGE TYPE').should('be.visible');
|
cy.get('.control-label').contains('Range type').should('be.visible');
|
||||||
cy.get('.antd5-popover-content .ant-select-selector')
|
cy.get('.ant-popover-content .ant-select-selector')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
cy.get(`[label="Advanced"]`).should('be.visible').click();
|
cy.get(`[label="Advanced"]`).should('be.visible').click();
|
||||||
cy.get('.section-title').contains('Advanced Time Range').should('be.visible');
|
cy.get('.section-title').contains('Advanced Time Range').should('be.visible');
|
||||||
if (startRange) {
|
if (startRange) {
|
||||||
cy.get('.antd5-popover-inner-content')
|
cy.get('.ant-popover-inner-content')
|
||||||
.find('[class^=ant-input]')
|
.find('[class^=ant-input]')
|
||||||
.first()
|
.first()
|
||||||
.type(`${startRange}`);
|
.type(`${startRange}`);
|
||||||
}
|
}
|
||||||
if (endRange) {
|
if (endRange) {
|
||||||
cy.get('.antd5-popover-inner-content')
|
cy.get('.ant-popover-inner-content')
|
||||||
.find('[class^=ant-input]')
|
.find('[class^=ant-input]')
|
||||||
.last()
|
.last()
|
||||||
.type(`${endRange}`);
|
.type(`${endRange}`);
|
||||||
|
|||||||
@@ -79,16 +79,20 @@ describe('Dashboards list', () => {
|
|||||||
|
|
||||||
it('should sort correctly in list mode', () => {
|
it('should sort correctly in list mode', () => {
|
||||||
cy.getBySel('sort-header').eq(1).click();
|
cy.getBySel('sort-header').eq(1).click();
|
||||||
|
cy.getBySel('loading-indicator').should('not.exist');
|
||||||
cy.getBySel('table-row').first().contains('Supported Charts Dashboard');
|
cy.getBySel('table-row').first().contains('Supported Charts Dashboard');
|
||||||
cy.getBySel('sort-header').eq(1).click();
|
cy.getBySel('sort-header').eq(1).click();
|
||||||
|
cy.getBySel('loading-indicator').should('not.exist');
|
||||||
cy.getBySel('table-row').first().contains("World Bank's Data");
|
cy.getBySel('table-row').first().contains("World Bank's Data");
|
||||||
cy.getBySel('sort-header').eq(1).click();
|
cy.getBySel('sort-header').eq(1).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should bulk select in list mode', () => {
|
it('should bulk select in list mode', () => {
|
||||||
toggleBulkSelect();
|
toggleBulkSelect();
|
||||||
cy.get('#header-toggle-all').click();
|
cy.get('[aria-label="Select all"]').click();
|
||||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 6);
|
cy.get('.ant-checkbox-input')
|
||||||
|
.should('be.checked')
|
||||||
|
.should('have.length', 6);
|
||||||
cy.getBySel('bulk-select-copy').contains('5 Selected');
|
cy.getBySel('bulk-select-copy').contains('5 Selected');
|
||||||
cy.getBySel('bulk-select-action')
|
cy.getBySel('bulk-select-action')
|
||||||
.should('have.length', 2)
|
.should('have.length', 2)
|
||||||
@@ -97,7 +101,7 @@ describe('Dashboards list', () => {
|
|||||||
expect($btns).to.contain('Export');
|
expect($btns).to.contain('Export');
|
||||||
});
|
});
|
||||||
cy.getBySel('bulk-select-deselect-all').click();
|
cy.getBySel('bulk-select-deselect-all').click();
|
||||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
|
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
|
||||||
cy.getBySel('bulk-select-copy').contains('0 Selected');
|
cy.getBySel('bulk-select-copy').contains('0 Selected');
|
||||||
cy.getBySel('bulk-select-action').should('not.exist');
|
cy.getBySel('bulk-select-action').should('not.exist');
|
||||||
});
|
});
|
||||||
@@ -195,6 +199,8 @@ describe('Dashboards list', () => {
|
|||||||
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
|
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
|
||||||
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
||||||
confirmDelete(true);
|
confirmDelete(true);
|
||||||
|
cy.getBySel('loading-indicator').should('exist');
|
||||||
|
cy.getBySel('loading-indicator').should('not.exist');
|
||||||
cy.getBySel('table-row')
|
cy.getBySel('table-row')
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.should('not.contain', '3 - Sample dashboard');
|
.should('not.contain', '3 - Sample dashboard');
|
||||||
|
|||||||
@@ -30,36 +30,36 @@ import {
|
|||||||
const SAMPLE_DASHBOARDS_INDEXES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
const SAMPLE_DASHBOARDS_INDEXES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
|
||||||
function openDashboardsAddedTo() {
|
function openDashboardsAddedTo() {
|
||||||
cy.getBySel('actions-trigger').click();
|
cy.getBySel('actions-trigger').should('be.visible').click();
|
||||||
cy.get('.antd5-dropdown-menu-submenu-title')
|
cy.get('.ant-dropdown-menu-submenu-title')
|
||||||
.contains('On dashboards')
|
.contains('On dashboards')
|
||||||
.trigger('mouseover', { force: true });
|
.trigger('mouseover', { force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeDashboardsAddedTo() {
|
function closeDashboardsAddedTo() {
|
||||||
cy.get('.antd5-dropdown-menu-submenu-title')
|
cy.get('.ant-dropdown-menu-submenu-title')
|
||||||
.contains('On dashboards')
|
.contains('On dashboards')
|
||||||
.trigger('mouseout', { force: true });
|
.trigger('mouseout', { force: true });
|
||||||
cy.getBySel('actions-trigger').click();
|
cy.getBySel('actions-trigger').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyDashboardsSubmenuItem(dashboardName) {
|
function verifyDashboardsSubmenuItem(dashboardName) {
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup').contains(dashboardName);
|
cy.get('.ant-dropdown-menu-submenu-popup').contains(dashboardName);
|
||||||
closeDashboardsAddedTo();
|
closeDashboardsAddedTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyDashboardSearch() {
|
function verifyDashboardSearch() {
|
||||||
openDashboardsAddedTo();
|
openDashboardsAddedTo();
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
|
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup')
|
cy.get('.ant-dropdown-menu-submenu-popup')
|
||||||
.find('input[placeholder="Search"]')
|
.find('input[placeholder="Search"]')
|
||||||
.type('1');
|
.type('1');
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
|
cy.get('.ant-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup')
|
cy.get('.ant-dropdown-menu-submenu-popup')
|
||||||
.find('input[placeholder="Search"]')
|
.find('input[placeholder="Search"]')
|
||||||
.type('Blahblah');
|
.type('Blahblah');
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup').contains('No results found');
|
cy.get('.ant-dropdown-menu-submenu-popup').contains('No results found');
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup')
|
cy.get('.ant-dropdown-menu-submenu-popup')
|
||||||
.find('[aria-label="close-circle"]')
|
.find('[aria-label="close-circle"]')
|
||||||
.click();
|
.click();
|
||||||
closeDashboardsAddedTo();
|
closeDashboardsAddedTo();
|
||||||
@@ -68,8 +68,8 @@ function verifyDashboardSearch() {
|
|||||||
function verifyDashboardLink() {
|
function verifyDashboardLink() {
|
||||||
interceptDashboardGet();
|
interceptDashboardGet();
|
||||||
openDashboardsAddedTo();
|
openDashboardsAddedTo();
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
|
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
|
||||||
cy.get('.antd5-dropdown-menu-submenu-popup a')
|
cy.get('.ant-dropdown-menu-submenu-popup a')
|
||||||
.first()
|
.first()
|
||||||
.invoke('removeAttr', 'target')
|
.invoke('removeAttr', 'target')
|
||||||
.click();
|
.click();
|
||||||
@@ -80,8 +80,8 @@ function verifyMetabar(text) {
|
|||||||
cy.getBySel('metadata-bar').contains(text);
|
cy.getBySel('metadata-bar').contains(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAndVerifyDashboard(number) {
|
function saveAndVerifyDashboard(chartName, number) {
|
||||||
saveChartToDashboard(`${number} - Sample dashboard`);
|
saveChartToDashboard(chartName, `${number} - Sample dashboard`);
|
||||||
verifyMetabar(
|
verifyMetabar(
|
||||||
number > 1 ? `Added to ${number} dashboards` : 'Added to 1 dashboard',
|
number > 1 ? `Added to ${number} dashboards` : 'Added to 1 dashboard',
|
||||||
);
|
);
|
||||||
@@ -106,17 +106,17 @@ describe('Cross-referenced dashboards', () => {
|
|||||||
openDashboardsAddedTo();
|
openDashboardsAddedTo();
|
||||||
verifyDashboardsSubmenuItem('None');
|
verifyDashboardsSubmenuItem('None');
|
||||||
|
|
||||||
saveAndVerifyDashboard('1');
|
saveAndVerifyDashboard('1 - Sample chart', '1');
|
||||||
saveAndVerifyDashboard('2');
|
saveAndVerifyDashboard('1 - Sample chart', '2');
|
||||||
saveAndVerifyDashboard('3');
|
saveAndVerifyDashboard('1 - Sample chart', '3');
|
||||||
saveAndVerifyDashboard('4');
|
saveAndVerifyDashboard('1 - Sample chart', '4');
|
||||||
saveAndVerifyDashboard('5');
|
saveAndVerifyDashboard('1 - Sample chart', '5');
|
||||||
saveAndVerifyDashboard('6');
|
saveAndVerifyDashboard('1 - Sample chart', '6');
|
||||||
saveAndVerifyDashboard('7');
|
saveAndVerifyDashboard('1 - Sample chart', '7');
|
||||||
saveAndVerifyDashboard('8');
|
saveAndVerifyDashboard('1 - Sample chart', '8');
|
||||||
saveAndVerifyDashboard('9');
|
saveAndVerifyDashboard('1 - Sample chart', '9');
|
||||||
saveAndVerifyDashboard('10');
|
saveAndVerifyDashboard('1 - Sample chart', '10');
|
||||||
saveAndVerifyDashboard('11');
|
saveAndVerifyDashboard('1 - Sample chart', '11');
|
||||||
|
|
||||||
verifyDashboardSearch();
|
verifyDashboardSearch();
|
||||||
verifyDashboardLink();
|
verifyDashboardLink();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
// ***********************************************
|
// ***********************************************
|
||||||
// Tests for setting controls in the UI
|
// Tests for setting controls in the UI
|
||||||
// ***********************************************
|
// ***********************************************
|
||||||
import { interceptChart } from 'cypress/utils';
|
import { interceptChart, setSelectSearchInput } from 'cypress/utils';
|
||||||
|
|
||||||
describe('Datasource control', () => {
|
describe('Datasource control', () => {
|
||||||
const newMetricName = `abc${Date.now()}`;
|
const newMetricName = `abc${Date.now()}`;
|
||||||
@@ -40,44 +40,44 @@ describe('Datasource control', () => {
|
|||||||
// create new metric
|
// create new metric
|
||||||
cy.get('[data-test="crud-add-table-item"]', { timeout: 10000 }).click();
|
cy.get('[data-test="crud-add-table-item"]', { timeout: 10000 }).click();
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
cy.get(
|
cy.get('.ant-table-body [data-test="textarea-editable-title-input"]')
|
||||||
'[data-test="table-content-rows"] [data-test="editable-title-input"]',
|
|
||||||
)
|
|
||||||
.first()
|
.first()
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get(
|
cy.get('.ant-table-body [data-test="textarea-editable-title-input"]')
|
||||||
'[data-test="table-content-rows"] [data-test="editable-title-input"]',
|
|
||||||
)
|
|
||||||
.first()
|
.first()
|
||||||
.focus();
|
.focus();
|
||||||
cy.focused().clear({ force: true });
|
cy.focused().clear({ force: true });
|
||||||
cy.focused().type(`${newMetricName}{enter}`, { force: true });
|
cy.focused().type(`${newMetricName}{enter}`, { force: true });
|
||||||
|
|
||||||
cy.get('[data-test="datasource-modal-save"]').click();
|
cy.get('[data-test="datasource-modal-save"]').click();
|
||||||
cy.get('.antd5-modal-confirm-btns button').contains('OK').click();
|
cy.get('.ant-modal-confirm-btns button').contains('OK').click();
|
||||||
// select new metric
|
// select new metric
|
||||||
cy.get('[data-test=metrics]')
|
cy.get('[data-test=metrics]')
|
||||||
.contains('Drop columns/metrics here or click')
|
.contains('Drop columns/metrics here or click')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('input[aria-label="Select saved metrics"]').type(
|
cy.get('input[aria-label="Select saved metrics"]')
|
||||||
`${newMetricName}{enter}`,
|
.should('exist')
|
||||||
);
|
.then($input => {
|
||||||
|
setSelectSearchInput($input, newMetricName);
|
||||||
|
});
|
||||||
|
|
||||||
// delete metric
|
// delete metric
|
||||||
cy.get('[data-test="datasource-menu-trigger"]').click();
|
cy.get('[data-test="datasource-menu-trigger"]').click();
|
||||||
cy.get('[data-test="edit-dataset"]').click();
|
cy.get('[data-test="edit-dataset"]').click();
|
||||||
cy.get('.antd5-modal-content').within(() => {
|
cy.get('.ant-modal-content').within(() => {
|
||||||
cy.get('[data-test="collection-tab-Metrics"]')
|
cy.get('[data-test="collection-tab-Metrics"]')
|
||||||
.contains('Metrics')
|
.contains('Metrics')
|
||||||
.click();
|
.click();
|
||||||
});
|
});
|
||||||
cy.get(`input[value="${newMetricName}"]`)
|
cy.get(`[data-test="textarea-editable-title-input"]`)
|
||||||
|
.contains(newMetricName)
|
||||||
.closest('tr')
|
.closest('tr')
|
||||||
.find('[data-test="crud-delete-icon"]')
|
.find('[data-test="crud-delete-icon"]')
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-test="datasource-modal-save"]').click();
|
cy.get('[data-test="datasource-modal-save"]').click();
|
||||||
cy.get('.antd5-modal-confirm-btns button').contains('OK').click();
|
cy.get('.ant-modal-confirm-btns button').contains('OK').click();
|
||||||
cy.get('[data-test="metrics"]').contains(newMetricName).should('not.exist');
|
cy.get('[data-test="metrics"]').contains(newMetricName).should('not.exist');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -91,7 +91,7 @@ describe('Color scheme control', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should show color options with and without tooltips', () => {
|
it('should show color options with and without tooltips', () => {
|
||||||
cy.get('#controlSections-tab-display').click();
|
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||||
cy.get('.ant-select-selection-item .color-scheme-label').contains(
|
cy.get('.ant-select-selection-item .color-scheme-label').contains(
|
||||||
'Superset Colors',
|
'Superset Colors',
|
||||||
);
|
);
|
||||||
@@ -102,10 +102,19 @@ describe('Color scheme control', () => {
|
|||||||
cy.get('.color-scheme-tooltip').contains('Superset Colors');
|
cy.get('.color-scheme-tooltip').contains('Superset Colors');
|
||||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||||
|
|
||||||
|
cy.get('.color-scheme-label')
|
||||||
|
.contains('Superset Colors')
|
||||||
|
.trigger('mouseover');
|
||||||
|
|
||||||
|
cy.get('.color-scheme-label')
|
||||||
|
.contains('Superset Colors')
|
||||||
|
.trigger('mouseout');
|
||||||
|
|
||||||
cy.focused().type('lyftColors');
|
cy.focused().type('lyftColors');
|
||||||
cy.getBySel('lyftColors').should('exist');
|
cy.getBySel('lyftColors').should('exist');
|
||||||
cy.getBySel('lyftColors').trigger('mouseover');
|
cy.getBySel('lyftColors').trigger('mouseover', { force: true });
|
||||||
cy.get('.color-scheme-tooltip').should('not.exist');
|
cy.get('.color-scheme-tooltip').should('not.be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('VizType control', () => {
|
describe('VizType control', () => {
|
||||||
@@ -120,7 +129,7 @@ describe('VizType control', () => {
|
|||||||
|
|
||||||
cy.contains('View all charts').click();
|
cy.contains('View all charts').click();
|
||||||
|
|
||||||
cy.get('.antd5-modal-content').within(() => {
|
cy.get('.ant-modal-content').within(() => {
|
||||||
cy.get('button').contains('KPI').click(); // change categories
|
cy.get('button').contains('KPI').click(); // change categories
|
||||||
cy.get('[role="button"]').contains('Big Number').click();
|
cy.get('[role="button"]').contains('Big Number').click();
|
||||||
cy.get('button').contains('Select').click();
|
cy.get('button').contains('Select').click();
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ describe('Test explore links', () => {
|
|||||||
cy.wait('@chartData').then(() => {
|
cy.wait('@chartData').then(() => {
|
||||||
cy.get('code');
|
cy.get('code');
|
||||||
});
|
});
|
||||||
cy.get('.antd5-modal-content').within(() => {
|
cy.get('.ant-modal-content').within(() => {
|
||||||
cy.get('button.antd5-modal-close').first().click({ force: true });
|
cy.get('button.ant-modal-close').first().click({ force: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ describe('Test explore links', () => {
|
|||||||
.find('input[aria-label="Select a dashboard"]')
|
.find('input[aria-label="Select a dashboard"]')
|
||||||
.type(`${dashboardTitle}`, { force: true });
|
.type(`${dashboardTitle}`, { force: true });
|
||||||
|
|
||||||
cy.get(`.ant-select-item[label="${dashboardTitle}"]`).click({
|
cy.get(`.ant-select-item[title="${dashboardTitle}"]`).click({
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ describe('Test explore links', () => {
|
|||||||
.find('input[aria-label="Select a dashboard"]')
|
.find('input[aria-label="Select a dashboard"]')
|
||||||
.type(`${dashboardTitle}{enter}`, { force: true });
|
.type(`${dashboardTitle}{enter}`, { force: true });
|
||||||
|
|
||||||
cy.get(`.ant-select-item[label="${dashboardTitle}"]`).click({
|
cy.get(`.ant-select-item[title="${dashboardTitle}"]`).click({
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export function setFilter(filter: string, option: string) {
|
|||||||
cy.wait('@filtering');
|
cy.wait('@filtering');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveChartToDashboard(dashboardName: string) {
|
export function saveChartToDashboard(chartName: string, dashboardName: string) {
|
||||||
interceptDashboardGet();
|
interceptDashboardGet();
|
||||||
interceptUpdate();
|
interceptUpdate();
|
||||||
interceptExploreGet();
|
interceptExploreGet();
|
||||||
@@ -75,23 +75,30 @@ export function saveChartToDashboard(dashboardName: string) {
|
|||||||
cy.getBySel('query-save-button')
|
cy.getBySel('query-save-button')
|
||||||
.should('be.enabled')
|
.should('be.enabled')
|
||||||
.should('not.be.disabled')
|
.should('not.be.disabled')
|
||||||
.click();
|
.click({ force: true });
|
||||||
cy.getBySelLike('chart-modal').should('be.visible');
|
|
||||||
cy.get(
|
|
||||||
'[data-test="save-chart-modal-select-dashboard-form"] [aria-label="Select a dashboard"]',
|
|
||||||
)
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
cy.get(
|
|
||||||
'.ant-select-selection-search-input[aria-label="Select a dashboard"]',
|
|
||||||
).type(dashboardName, { force: true });
|
|
||||||
cy.get(`.ant-select-item-option[title="${dashboardName}"]`).click();
|
|
||||||
cy.getBySel('btn-modal-save').click();
|
|
||||||
|
|
||||||
cy.wait('@update');
|
cy.getBySel('save-modal-body')
|
||||||
|
.should('be.visible')
|
||||||
|
.then($modal => {
|
||||||
|
cy.wait(500);
|
||||||
|
cy.wrap($modal)
|
||||||
|
.find(
|
||||||
|
'.ant-select-selection-search-input[aria-label="Select a dashboard"]',
|
||||||
|
)
|
||||||
|
.type(dashboardName, { force: true });
|
||||||
|
cy.wrap($modal)
|
||||||
|
.find(`.ant-select-item-option[title="${dashboardName}"]`)
|
||||||
|
.click();
|
||||||
|
cy.getBySel('btn-modal-save').click();
|
||||||
|
cy.wait('@update');
|
||||||
|
});
|
||||||
|
cy.getBySel('save-modal-body').should('not.exist');
|
||||||
|
cy.getBySel('query-save-button').should('be.disabled');
|
||||||
cy.wait('@get');
|
cy.wait('@get');
|
||||||
cy.wait('@getExplore');
|
cy.wait('@getExplore');
|
||||||
cy.contains(`was added to dashboard [${dashboardName}]`);
|
cy.contains(`was added to dashboard [${dashboardName}]`);
|
||||||
|
cy.contains(`Chart [${chartName}] has been overwritten`);
|
||||||
|
cy.getBySel('query-save-button').should('be.enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function visitSampleChartFromList(chartName: string) {
|
export function visitSampleChartFromList(chartName: string) {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ describe('Visualization > Box Plot', () => {
|
|||||||
it('should allow type to search color schemes', () => {
|
it('should allow type to search color schemes', () => {
|
||||||
verify(BOX_PLOT_FORM_DATA);
|
verify(BOX_PLOT_FORM_DATA);
|
||||||
|
|
||||||
cy.get('#controlSections-tab-display').click();
|
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||||
cy.focused().type('supersetColors{enter}');
|
cy.focused().type('supersetColors{enter}');
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ describe('Visualization > Compare', () => {
|
|||||||
it('should allow type to search color schemes and apply the scheme', () => {
|
it('should allow type to search color schemes and apply the scheme', () => {
|
||||||
verify(COMPARE_FORM_DATA);
|
verify(COMPARE_FORM_DATA);
|
||||||
|
|
||||||
cy.get('#controlSections-tab-display').click();
|
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||||
cy.focused().type('supersetColors{enter}');
|
cy.focused().type('supersetColors{enter}');
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ describe('Download Chart > Bar chart', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
cy.visitChartByParams(formData);
|
cy.visitChartByParams(formData);
|
||||||
cy.get('.header-with-actions .antd5-dropdown-trigger').click();
|
cy.get('.header-with-actions .ant-dropdown-trigger').click();
|
||||||
cy.get(':nth-child(3) > .antd5-dropdown-menu-submenu-title').click();
|
cy.get(':nth-child(3) > .ant-dropdown-menu-submenu-title').click();
|
||||||
cy.get(
|
cy.get(
|
||||||
'.antd5-dropdown-menu-submenu > .antd5-dropdown-menu li:nth-child(3)',
|
'.ant-dropdown-menu-submenu > .ant-dropdown-menu li:nth-child(3)',
|
||||||
).click();
|
).click();
|
||||||
cy.verifyDownload('.jpg', {
|
cy.verifyDownload('.jpg', {
|
||||||
contains: true,
|
contains: true,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ describe('Visualization > Gauge', () => {
|
|||||||
it('should allow type to search color schemes', () => {
|
it('should allow type to search color schemes', () => {
|
||||||
verify(GAUGE_FORM_DATA);
|
verify(GAUGE_FORM_DATA);
|
||||||
|
|
||||||
cy.get('#controlSections-tab-display').click();
|
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||||
cy.focused().type('bnbColors{enter}');
|
cy.focused().type('bnbColors{enter}');
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ describe('Visualization > Graph', () => {
|
|||||||
it('should allow type to search color schemes', () => {
|
it('should allow type to search color schemes', () => {
|
||||||
verify(GRAPH_FORM_DATA);
|
verify(GRAPH_FORM_DATA);
|
||||||
|
|
||||||
cy.get('#controlSections-tab-display').click();
|
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||||
cy.focused().type('bnbColors{enter}');
|
cy.focused().type('bnbColors{enter}');
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ describe('Visualization > Pie', () => {
|
|||||||
it('should allow type to search color schemes', () => {
|
it('should allow type to search color schemes', () => {
|
||||||
verify(PIE_FORM_DATA);
|
verify(PIE_FORM_DATA);
|
||||||
|
|
||||||
cy.get('#controlSections-tab-display').click();
|
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||||
cy.focused().type('supersetColors{enter}');
|
cy.focused().type('supersetColors{enter}');
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ describe('Visualization > Sunburst', () => {
|
|||||||
it('should allow type to search color schemes', () => {
|
it('should allow type to search color schemes', () => {
|
||||||
verify(SUNBURST_FORM_DATA);
|
verify(SUNBURST_FORM_DATA);
|
||||||
|
|
||||||
cy.get('#controlSections-tab-display').click();
|
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||||
cy.focused().type('supersetColors{enter}');
|
cy.focused().type('supersetColors{enter}');
|
||||||
|
|||||||
@@ -193,7 +193,9 @@ describe('Visualization > Table', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// should display in raw records mode
|
// should display in raw records mode
|
||||||
cy.get('div[data-test="query_mode"] .btn.active').contains('Raw records');
|
cy.get(
|
||||||
|
'div[data-test="query_mode"] .ant-radio-button-wrapper-checked',
|
||||||
|
).contains('Raw records');
|
||||||
cy.get('div[data-test="all_columns"]').should('be.visible');
|
cy.get('div[data-test="all_columns"]').should('be.visible');
|
||||||
cy.get('div[data-test="groupby"]').should('not.exist');
|
cy.get('div[data-test="groupby"]').should('not.exist');
|
||||||
|
|
||||||
@@ -201,8 +203,12 @@ describe('Visualization > Table', () => {
|
|||||||
cy.get('[data-test="row-count-label"]').contains('100 rows');
|
cy.get('[data-test="row-count-label"]').contains('100 rows');
|
||||||
|
|
||||||
// should allow switch back to aggregate mode
|
// should allow switch back to aggregate mode
|
||||||
cy.get('div[data-test="query_mode"] .btn').contains('Aggregate').click();
|
cy.get('div[data-test="query_mode"] .ant-radio-button-wrapper')
|
||||||
cy.get('div[data-test="query_mode"] .btn.active').contains('Aggregate');
|
.contains('Aggregate')
|
||||||
|
.click();
|
||||||
|
cy.get(
|
||||||
|
'div[data-test="query_mode"] .ant-radio-button-wrapper-checked',
|
||||||
|
).contains('Aggregate');
|
||||||
cy.get('div[data-test="all_columns"]').should('not.exist');
|
cy.get('div[data-test="all_columns"]').should('not.exist');
|
||||||
cy.get('div[data-test="groupby"]').should('be.visible');
|
cy.get('div[data-test="groupby"]').should('be.visible');
|
||||||
});
|
});
|
||||||
@@ -254,6 +260,8 @@ describe('Visualization > Table', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Test row limit with server pagination toggle', () => {
|
it('Test row limit with server pagination toggle', () => {
|
||||||
|
const serverPaginationSelector =
|
||||||
|
'[data-test="server_pagination-header"] div.pull-left [type="checkbox"]';
|
||||||
cy.visitChartByParams({
|
cy.visitChartByParams({
|
||||||
...VIZ_DEFAULTS,
|
...VIZ_DEFAULTS,
|
||||||
metrics: ['count'],
|
metrics: ['count'],
|
||||||
@@ -261,7 +269,7 @@ describe('Visualization > Table', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Enable server pagination
|
// Enable server pagination
|
||||||
cy.get('[data-test="server_pagination-header"] div.pull-left').click();
|
cy.get(serverPaginationSelector).click();
|
||||||
|
|
||||||
// Click row limit control and select high value (200k)
|
// Click row limit control and select high value (200k)
|
||||||
cy.get('div[aria-label="Row limit"]').click();
|
cy.get('div[aria-label="Row limit"]').click();
|
||||||
@@ -275,7 +283,7 @@ describe('Visualization > Table', () => {
|
|||||||
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
||||||
|
|
||||||
// Disable server pagination
|
// Disable server pagination
|
||||||
cy.get('[data-test="server_pagination-header"] div.pull-left').click();
|
cy.get(serverPaginationSelector).click();
|
||||||
|
|
||||||
// Verify error tooltip appears
|
// Verify error tooltip appears
|
||||||
cy.get('[data-test="error-tooltip"]').should('be.visible');
|
cy.get('[data-test="error-tooltip"]').should('be.visible');
|
||||||
@@ -284,17 +292,17 @@ describe('Visualization > Table', () => {
|
|||||||
cy.get('[data-test="error-tooltip"]').trigger('mouseover');
|
cy.get('[data-test="error-tooltip"]').trigger('mouseover');
|
||||||
|
|
||||||
// Verify tooltip content
|
// Verify tooltip content
|
||||||
cy.get('.antd5-tooltip-inner').should('be.visible');
|
cy.get('.ant-tooltip-inner').should('be.visible');
|
||||||
cy.get('.antd5-tooltip-inner').should(
|
cy.get('.ant-tooltip-inner').should(
|
||||||
'contain',
|
'contain',
|
||||||
'Server pagination needs to be enabled for values over',
|
'Server pagination needs to be enabled for values over',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Hide the tooltip by adding display:none style
|
// Hide the tooltip by adding display:none style
|
||||||
cy.get('.antd5-tooltip').invoke('attr', 'style', 'display: none');
|
cy.get('.ant-tooltip').invoke('attr', 'style', 'display: none');
|
||||||
|
|
||||||
// Enable server pagination again
|
// Enable server pagination again
|
||||||
cy.get('[data-test="server_pagination-header"] div.pull-left').click();
|
cy.get(serverPaginationSelector).click();
|
||||||
|
|
||||||
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
||||||
|
|
||||||
@@ -319,11 +327,11 @@ describe('Visualization > Table', () => {
|
|||||||
.trigger('mouseover');
|
.trigger('mouseover');
|
||||||
|
|
||||||
// Wait for tooltip content and verify
|
// Wait for tooltip content and verify
|
||||||
cy.get('.antd5-tooltip-inner').should('exist');
|
cy.get('.ant-tooltip-inner').should('exist');
|
||||||
cy.get('.antd5-tooltip-inner').should('be.visible');
|
cy.get('.ant-tooltip-inner').should('be.visible');
|
||||||
|
|
||||||
// Verify tooltip content separately
|
// Verify tooltip content separately
|
||||||
cy.get('.antd5-tooltip-inner').should('contain', 'Value cannot exceed');
|
cy.get('.ant-tooltip-inner').should('contain', 'Value cannot exceed');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test sorting with server pagination enabled', () => {
|
it('Test sorting with server pagination enabled', () => {
|
||||||
@@ -393,12 +401,12 @@ describe('Visualization > Table', () => {
|
|||||||
|
|
||||||
cy.wait('@chartData');
|
cy.wait('@chartData');
|
||||||
|
|
||||||
// Basic search test
|
const searchInputSelector = '.dt-global-filter input';
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').should(
|
|
||||||
'be.visible',
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('John');
|
// Basic search test
|
||||||
|
cy.get(searchInputSelector).should('be.visible');
|
||||||
|
|
||||||
|
cy.get(searchInputSelector).type('John');
|
||||||
|
|
||||||
cy.wait('@chartData');
|
cy.wait('@chartData');
|
||||||
|
|
||||||
@@ -407,11 +415,11 @@ describe('Visualization > Table', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Clear and test case-insensitive search
|
// Clear and test case-insensitive search
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
cy.get(searchInputSelector).clear();
|
||||||
|
|
||||||
cy.wait('@chartData');
|
cy.wait('@chartData');
|
||||||
|
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('mary');
|
cy.get(searchInputSelector).type('mary');
|
||||||
|
|
||||||
cy.wait('@chartData');
|
cy.wait('@chartData');
|
||||||
|
|
||||||
@@ -420,9 +428,9 @@ describe('Visualization > Table', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test special characters
|
// Test special characters
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
cy.get(searchInputSelector).clear();
|
||||||
|
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('Nicole');
|
cy.get(searchInputSelector).type('Nicole');
|
||||||
|
|
||||||
cy.wait('@chartData');
|
cy.wait('@chartData');
|
||||||
|
|
||||||
@@ -431,9 +439,9 @@ describe('Visualization > Table', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test no results
|
// Test no results
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
cy.get(searchInputSelector).clear();
|
||||||
|
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('XYZ123');
|
cy.get(searchInputSelector).type('XYZ123');
|
||||||
|
|
||||||
cy.wait('@chartData');
|
cy.wait('@chartData');
|
||||||
|
|
||||||
@@ -450,9 +458,9 @@ describe('Visualization > Table', () => {
|
|||||||
|
|
||||||
cy.get('.ant-select-item-option').contains('state').click();
|
cy.get('.ant-select-item-option').contains('state').click();
|
||||||
|
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
cy.get(searchInputSelector).clear();
|
||||||
|
|
||||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('CA');
|
cy.get(searchInputSelector).type('CA');
|
||||||
|
|
||||||
cy.wait('@chartData');
|
cy.wait('@chartData');
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe('SqlLab query tabs', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const tablistSelector = '[data-test="sql-editor-tabs"] > [role="tablist"]';
|
const tablistSelector = '[data-test="sql-editor-tabs"] > [role="tablist"]';
|
||||||
const tabSelector = `${tablistSelector} [role="tab"]`;
|
const tabSelector = `${tablistSelector} [role="tab"]:not([type="button"])`;
|
||||||
|
|
||||||
it('allows you to create and close a tab', () => {
|
it('allows you to create and close a tab', () => {
|
||||||
cy.get(tabSelector).then(tabs => {
|
cy.get(tabSelector).then(tabs => {
|
||||||
@@ -80,9 +80,9 @@ describe('SqlLab query tabs', () => {
|
|||||||
// configure some editor settings
|
// configure some editor settings
|
||||||
cy.get(editorInput).type('some random query string', { force: true });
|
cy.get(editorInput).type('some random query string', { force: true });
|
||||||
cy.get(queryLimitSelector).parent().click({ force: true });
|
cy.get(queryLimitSelector).parent().click({ force: true });
|
||||||
cy.get('.antd5-dropdown-menu')
|
cy.get('.ant-dropdown-menu')
|
||||||
.last()
|
.last()
|
||||||
.find('.antd5-dropdown-menu-item')
|
.find('.ant-dropdown-menu-item')
|
||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
|
|
||||||
|
|||||||
@@ -25,16 +25,16 @@ export function dataTestChartName(chartName: string): string {
|
|||||||
|
|
||||||
export const pageHeader = {
|
export const pageHeader = {
|
||||||
logo: '.navbar-brand > img',
|
logo: '.navbar-brand > img',
|
||||||
headerNavigationItem: '.antd5-menu-submenu-title',
|
headerNavigationItem: '.ant-menu-submenu-title',
|
||||||
headerNavigationDropdown: "[aria-label='triangle-down']",
|
headerNavigationDropdown: "[aria-label='triangle-down']",
|
||||||
headerNavigationItemMenu: '.antd5-menu-item-group-list',
|
headerNavigationItemMenu: '.ant-menu-item-group-list',
|
||||||
plusIcon: ':nth-child(2) > .antd5-menu-submenu-title',
|
plusIcon: ':nth-child(2) > .ant-menu-submenu-title',
|
||||||
plusIconMenuOptions: {
|
plusIconMenuOptions: {
|
||||||
sqlQueryOption: dataTestLocator('menu-item-SQL query'),
|
sqlQueryOption: dataTestLocator('menu-item-SQL query'),
|
||||||
chartOption: dataTestLocator('menu-item-Chart'),
|
chartOption: dataTestLocator('menu-item-Chart'),
|
||||||
dashboardOption: dataTestLocator('menu-item-Dashboard'),
|
dashboardOption: dataTestLocator('menu-item-Dashboard'),
|
||||||
},
|
},
|
||||||
plusMenu: '.antd5-menu-submenu-popup',
|
plusMenu: '.ant-menu-submenu-popup',
|
||||||
barButtons: '[role="presentation"]',
|
barButtons: '[role="presentation"]',
|
||||||
sqlLabMenu: '[id="item_3$Menu"]',
|
sqlLabMenu: '[id="item_3$Menu"]',
|
||||||
dataMenu: '[id="item_4$Menu"]',
|
dataMenu: '[id="item_4$Menu"]',
|
||||||
@@ -48,12 +48,12 @@ export const profile = {
|
|||||||
favoritesSpace: '#rc-tabs-0-panel-2',
|
favoritesSpace: '#rc-tabs-0-panel-2',
|
||||||
};
|
};
|
||||||
export const securityAccess = {
|
export const securityAccess = {
|
||||||
rolesBubble: '.antd5-badge-count',
|
rolesBubble: '.ant-badge-count',
|
||||||
};
|
};
|
||||||
export const homePage = {
|
export const homePage = {
|
||||||
homeSection: {
|
homeSection: {
|
||||||
sectionArea: '.ant-collapse-content-box',
|
sectionArea: '.ant-collapse-content-box',
|
||||||
sectionElement: '.antd5-card-meta-title',
|
sectionElement: '.ant-card-meta-title',
|
||||||
},
|
},
|
||||||
sections: {
|
sections: {
|
||||||
expandedSection: '.ant-collapse-item-active',
|
expandedSection: '.ant-collapse-item-active',
|
||||||
@@ -94,19 +94,19 @@ export const databasesPage = {
|
|||||||
dbDropdown: '[class="ant-select-selection-search-input"]',
|
dbDropdown: '[class="ant-select-selection-search-input"]',
|
||||||
dbDropdownMenu: '.rc-virtual-list-holder-inner',
|
dbDropdownMenu: '.rc-virtual-list-holder-inner',
|
||||||
dbDropdownMenuItem: '[class="ant-select-item-option-content"]',
|
dbDropdownMenuItem: '[class="ant-select-item-option-content"]',
|
||||||
infoAlert: '.antd5-alert',
|
infoAlert: '.ant-alert',
|
||||||
serviceAccountInput: '[name="credentials_info"]',
|
serviceAccountInput: '[name="credentials_info"]',
|
||||||
connectionStep: {
|
connectionStep: {
|
||||||
modal: '.antd5-modal-content',
|
modal: '.ant-modal-content',
|
||||||
modalBody: '.antd5-modal-body',
|
modalBody: '.ant-modal-body',
|
||||||
stepTitle: '.css-7x6kk > h4',
|
stepTitle: '.css-7x6kk > h4',
|
||||||
helperBottom: '.helper-bottom',
|
helperBottom: '.helper-bottom',
|
||||||
postgresDatabase: '[name="database"]',
|
postgresDatabase: '[name="database"]',
|
||||||
dbInput: '[name="database_name"]',
|
dbInput: '[name="database_name"]',
|
||||||
alertMessage: '.antd5-alert-message',
|
alertMessage: '.ant-alert-message',
|
||||||
errorField: '[role="alert"]',
|
errorField: '[role="alert"]',
|
||||||
uploadJson: '[title="Upload JSON file"]',
|
uploadJson: '[title="Upload JSON file"]',
|
||||||
chooseFile: '[class="antd5-btn input-upload-btn"]',
|
chooseFile: '[class="ant-btn input-upload-btn"]',
|
||||||
additionalParameters: '[name="query_input"]',
|
additionalParameters: '[name="query_input"]',
|
||||||
sqlAlchemyUriInput: dataTestLocator('sqlalchemy-uri-input'),
|
sqlAlchemyUriInput: dataTestLocator('sqlalchemy-uri-input'),
|
||||||
advancedTab: '#rc-tabs-0-tab-2',
|
advancedTab: '#rc-tabs-0-tab-2',
|
||||||
@@ -140,7 +140,7 @@ export const sqlLabView = {
|
|||||||
tabsNavList: "[class='ant-tabs-nav-list']",
|
tabsNavList: "[class='ant-tabs-nav-list']",
|
||||||
tab: "[class='ant-tabs-tab-btn']",
|
tab: "[class='ant-tabs-tab-btn']",
|
||||||
addTabButton: dataTestLocator('add-tab-icon'),
|
addTabButton: dataTestLocator('add-tab-icon'),
|
||||||
tooltip: '.antd5-tooltip-content',
|
tooltip: '.ant-tooltip-content',
|
||||||
tabName: '.css-1suejie',
|
tabName: '.css-1suejie',
|
||||||
schemaInput: '[data-test=DatabaseSelector] > :nth-child(2)',
|
schemaInput: '[data-test=DatabaseSelector] > :nth-child(2)',
|
||||||
loadingIndicator: '.Select__loading-indicator',
|
loadingIndicator: '.Select__loading-indicator',
|
||||||
@@ -148,9 +148,9 @@ export const sqlLabView = {
|
|||||||
examplesMenuItem: '[title="examples"]',
|
examplesMenuItem: '[title="examples"]',
|
||||||
tableInput: ':nth-child(4) > .select > :nth-child(1)',
|
tableInput: ':nth-child(4) > .select > :nth-child(1)',
|
||||||
sqlEditor: '#brace-editor textarea',
|
sqlEditor: '#brace-editor textarea',
|
||||||
saveAsButton: '.SaveQuery > .antd5-btn',
|
saveAsButton: '.SaveQuery > .ant-btn',
|
||||||
saveAsModal: {
|
saveAsModal: {
|
||||||
footer: '.antd5-modal-footer',
|
footer: '.ant-modal-footer',
|
||||||
queryNameInput: 'input[class^="ant-input"]',
|
queryNameInput: 'input[class^="ant-input"]',
|
||||||
},
|
},
|
||||||
sqlToolbar: {
|
sqlToolbar: {
|
||||||
@@ -158,15 +158,15 @@ export const sqlLabView = {
|
|||||||
runButton: '.css-d3dxop',
|
runButton: '.css-d3dxop',
|
||||||
},
|
},
|
||||||
rowsLimit: {
|
rowsLimit: {
|
||||||
dropdown: '.antd5-dropdown-menu',
|
dropdown: '.ant-dropdown-menu',
|
||||||
limitButton: '.antd5-dropdown-menu-item',
|
limitButton: '.ant-dropdown-menu-item',
|
||||||
limitButtonText: '.css-151uxnz',
|
limitButtonText: '.css-151uxnz',
|
||||||
limitTextWithValue: '[class="antd5-dropdown-trigger"]',
|
limitTextWithValue: '[class="ant-dropdown-trigger"]',
|
||||||
},
|
},
|
||||||
renderedTableHeader: '.ReactVirtualized__Table__headerRow',
|
renderedTableHeader: '.ReactVirtualized__Table__headerRow',
|
||||||
renderedTableRow: '.ReactVirtualized__Table__row',
|
renderedTableRow: '.ReactVirtualized__Table__row',
|
||||||
errorBody: '.error-body',
|
errorBody: '.error-body',
|
||||||
alertMessage: '.antd5-alert-message',
|
alertMessage: '.ant-alert-message',
|
||||||
historyTable: {
|
historyTable: {
|
||||||
header: '[role=columnheader]',
|
header: '[role=columnheader]',
|
||||||
table: '.QueryTable',
|
table: '.QueryTable',
|
||||||
@@ -195,16 +195,16 @@ export const savedQuery = {
|
|||||||
export const annotationLayersView = {
|
export const annotationLayersView = {
|
||||||
emptyDescription: {
|
emptyDescription: {
|
||||||
description: '.ant-empty-description',
|
description: '.ant-empty-description',
|
||||||
addAnnotationLayerButton: '.ant-empty-footer > .antd5-btn',
|
addAnnotationLayerButton: '.ant-empty-footer > .ant-btn',
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
content: {
|
content: {
|
||||||
content: '.antd5-modal-body',
|
content: '.ant-modal-body',
|
||||||
title: '.antd5-modal-body > :nth-child(2) > input',
|
title: '.ant-modal-body > :nth-child(2) > input',
|
||||||
description: "[name='descr']",
|
description: "[name='descr']",
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
footer: '.antd5-modal-footer',
|
footer: '.ant-modal-footer',
|
||||||
addButton: dataTestLocator('modal-confirm-button'),
|
addButton: dataTestLocator('modal-confirm-button'),
|
||||||
cancelButton: dataTestLocator('modal-cancel-button'),
|
cancelButton: dataTestLocator('modal-cancel-button'),
|
||||||
},
|
},
|
||||||
@@ -216,7 +216,7 @@ export const datasetsList = {
|
|||||||
newDatasetModal: {
|
newDatasetModal: {
|
||||||
inputField: '[class="section"]',
|
inputField: '[class="section"]',
|
||||||
addButton: dataTestLocator('modal-confirm-button'),
|
addButton: dataTestLocator('modal-confirm-button'),
|
||||||
body: '.antd5-modal-body',
|
body: '.ant-modal-body',
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
tableRow: {
|
tableRow: {
|
||||||
@@ -261,7 +261,7 @@ export const datasetsList = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
deleteDatasetModal: {
|
deleteDatasetModal: {
|
||||||
modal: '.antd5-modal-content',
|
modal: '.ant-modal-content',
|
||||||
deleteInput: dataTestLocator('delete-modal-input'),
|
deleteInput: dataTestLocator('delete-modal-input'),
|
||||||
deleteButton: dataTestLocator('modal-confirm-button'),
|
deleteButton: dataTestLocator('modal-confirm-button'),
|
||||||
text: '.css-kxmt87',
|
text: '.css-kxmt87',
|
||||||
@@ -275,8 +275,8 @@ export const chartListView = {
|
|||||||
bulkSelect: dataTestLocator('bulk-select'),
|
bulkSelect: dataTestLocator('bulk-select'),
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
cardView: '[aria-label="appstore"]',
|
cardView: '[aria-label="card-view"]',
|
||||||
listView: '[aria-label="unordered-list"]',
|
listView: '[aria-label="list-view"]',
|
||||||
sort: '[class="ant-select-selection-search-input"][aria-label="Sort"]',
|
sort: '[class="ant-select-selection-search-input"][aria-label="Sort"]',
|
||||||
sortRecentlyModifiedMenuOption: '[label="Recently modified"]',
|
sortRecentlyModifiedMenuOption: '[label="Recently modified"]',
|
||||||
sortAlphabeticalMenuOption: '[label="Alphabetical"]',
|
sortAlphabeticalMenuOption: '[label="Alphabetical"]',
|
||||||
@@ -284,7 +284,7 @@ export const chartListView = {
|
|||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
card: dataTestLocator('styled-card'),
|
card: dataTestLocator('styled-card'),
|
||||||
cardCover: '[class="antd5-card-cover"]',
|
cardCover: '[class="ant-card-cover"]',
|
||||||
cardImage: '[class="gradient-container"]',
|
cardImage: '[class="gradient-container"]',
|
||||||
starIcon: dataTestLocator('fave-unfave-icon'),
|
starIcon: dataTestLocator('fave-unfave-icon'),
|
||||||
},
|
},
|
||||||
@@ -294,8 +294,8 @@ export const chartListView = {
|
|||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
bulkSelect: {
|
bulkSelect: {
|
||||||
checkboxOff: '[aria-label="checkbox-off"]',
|
checkboxOff: 'input[type="checkbox"]:checked',
|
||||||
checkboxOn: '[aria-label="checkbox-on"]',
|
checkboxOn: 'input[type="checkbox"]:not(:checked)',
|
||||||
action: dataTestLocator('bulk-select-action'),
|
action: dataTestLocator('bulk-select-action'),
|
||||||
},
|
},
|
||||||
tableList: dataTestLocator('listview-table'),
|
tableList: dataTestLocator('listview-table'),
|
||||||
@@ -316,14 +316,14 @@ export const chartListView = {
|
|||||||
};
|
};
|
||||||
export const nativeFilters = {
|
export const nativeFilters = {
|
||||||
modal: {
|
modal: {
|
||||||
container: '.antd5-modal',
|
container: '.ant-modal',
|
||||||
footer: '.antd5-modal-footer',
|
footer: '.ant-modal-footer',
|
||||||
saveButton: dataTestLocator('native-filter-modal-save-button'),
|
saveButton: dataTestLocator('native-filter-modal-save-button'),
|
||||||
cancelButton: dataTestLocator('native-filter-modal-cancel-button'),
|
cancelButton: dataTestLocator('native-filter-modal-cancel-button'),
|
||||||
confirmCancelButton: dataTestLocator(
|
confirmCancelButton: dataTestLocator(
|
||||||
'native-filter-modal-confirm-cancel-button',
|
'native-filter-modal-confirm-cancel-button',
|
||||||
),
|
),
|
||||||
alertXUnsavedFilters: '.antd5-alert-message',
|
alertXUnsavedFilters: '.ant-alert-message',
|
||||||
tabsList: {
|
tabsList: {
|
||||||
filterItemsContainer: dataTestLocator('filter-title-container'),
|
filterItemsContainer: dataTestLocator('filter-title-container'),
|
||||||
tabsContainer: '[class="ant-tabs-nav-list"]',
|
tabsContainer: '[class="ant-tabs-nav-list"]',
|
||||||
@@ -398,7 +398,7 @@ export const dashboardListView = {
|
|||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
card: dataTestLocator('styled-card'),
|
card: dataTestLocator('styled-card'),
|
||||||
cardCover: '[class="antd5-card-cover"]',
|
cardCover: '[class="ant-card-cover"]',
|
||||||
cardImage: '[class="gradient-container"]',
|
cardImage: '[class="gradient-container"]',
|
||||||
selectedStarIcon: "[aria-label='star']",
|
selectedStarIcon: "[aria-label='star']",
|
||||||
unselectedStarIcon: "[aria-label='star']",
|
unselectedStarIcon: "[aria-label='star']",
|
||||||
@@ -432,7 +432,7 @@ export const dashboardListView = {
|
|||||||
newDashboardButton: '.css-yff34v',
|
newDashboardButton: '.css-yff34v',
|
||||||
},
|
},
|
||||||
importModal: {
|
importModal: {
|
||||||
selectFileButton: '.ant-upload > .antd5-btn > span',
|
selectFileButton: '.ant-upload > .ant-btn > span',
|
||||||
importButton: dataTestLocator('modal-confirm-button'),
|
importButton: dataTestLocator('modal-confirm-button'),
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
@@ -474,15 +474,15 @@ export const exploreView = {
|
|||||||
},
|
},
|
||||||
chartAreaItem: '.nv-legend-text',
|
chartAreaItem: '.nv-legend-text',
|
||||||
viewQueryModal: {
|
viewQueryModal: {
|
||||||
container: '.antd5-modal-content',
|
container: '.ant-modal-content',
|
||||||
closeButton: 'button.antd5-modal-close',
|
closeButton: 'button.ant-modal-close',
|
||||||
},
|
},
|
||||||
embedCodeModal: {
|
embedCodeModal: {
|
||||||
container: dataTestLocator('embed-code-popover'),
|
container: dataTestLocator('embed-code-popover'),
|
||||||
textfield: dataTestLocator('embed-code-textarea'),
|
textfield: dataTestLocator('embed-code-textarea'),
|
||||||
},
|
},
|
||||||
saveModal: {
|
saveModal: {
|
||||||
modal: '.antd5-modal-content',
|
modal: '.ant-modal-content',
|
||||||
chartNameInput: dataTestLocator('new-chart-name'),
|
chartNameInput: dataTestLocator('new-chart-name'),
|
||||||
dashboardNameInput: '.ant-select-selection-search-input',
|
dashboardNameInput: '.ant-select-selection-search-input',
|
||||||
addToDashboardInput: dataTestLocator(
|
addToDashboardInput: dataTestLocator(
|
||||||
@@ -553,7 +553,7 @@ export const exploreView = {
|
|||||||
timeSection: {
|
timeSection: {
|
||||||
timeRangeFilter: dataTestLocator('time-range-trigger'),
|
timeRangeFilter: dataTestLocator('time-range-trigger'),
|
||||||
timeRangeFilterModal: {
|
timeRangeFilterModal: {
|
||||||
container: '.antd5-popover-content',
|
container: '.ant-popover-content',
|
||||||
footer: '.footer',
|
footer: '.footer',
|
||||||
cancelButton: dataTestLocator('cancel-button'),
|
cancelButton: dataTestLocator('cancel-button'),
|
||||||
configureLastTimeRange: {
|
configureLastTimeRange: {
|
||||||
@@ -578,15 +578,15 @@ export const exploreView = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
editDatasetModal: {
|
editDatasetModal: {
|
||||||
container: '.antd5-modal-content',
|
container: '.ant-modal-content',
|
||||||
datasetTabsContainer: dataTestLocator('edit-dataset-tabs'),
|
datasetTabsContainer: dataTestLocator('edit-dataset-tabs'),
|
||||||
saveButton: dataTestLocator('datasource-modal-save'),
|
saveButton: dataTestLocator('datasource-modal-save'),
|
||||||
metricsTab: {
|
metricsTab: {
|
||||||
addItem: dataTestLocator('crud-add-table-item'),
|
addItem: dataTestLocator('crud-add-table-item'),
|
||||||
rowsContainer: dataTestLocator('table-content-rows'),
|
rowsContainer: '.ant-table-body',
|
||||||
},
|
},
|
||||||
confirmModal: {
|
confirmModal: {
|
||||||
okButton: '.antd5-modal-confirm-btns .antd5-btn-primary',
|
okButton: '.ant-modal-confirm-btns .ant-btn-primary',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
visualizationTypeModal: {
|
visualizationTypeModal: {
|
||||||
@@ -617,12 +617,12 @@ export const dashboardView = {
|
|||||||
closeButton: dataTestLocator('close-button'),
|
closeButton: dataTestLocator('close-button'),
|
||||||
},
|
},
|
||||||
saveModal: {
|
saveModal: {
|
||||||
modal: '.antd5-modal-content',
|
modal: '.ant-modal-content',
|
||||||
dashboardNameInput: '.ant-input',
|
dashboardNameInput: '.ant-input',
|
||||||
saveButton: dataTestLocator('modal-save-dashboard-button'),
|
saveButton: dataTestLocator('modal-save-dashboard-button'),
|
||||||
},
|
},
|
||||||
dashboardProperties: {
|
dashboardProperties: {
|
||||||
modal: '.antd5-modal-content',
|
modal: '.ant-modal-content',
|
||||||
dashboardTitleInput: dataTestLocator('dashboard-title-input'),
|
dashboardTitleInput: dataTestLocator('dashboard-title-input'),
|
||||||
modalButton: '[type="button"]',
|
modalButton: '[type="button"]',
|
||||||
},
|
},
|
||||||
@@ -631,7 +631,7 @@ export const dashboardView = {
|
|||||||
refreshChart: dataTestLocator('refresh-chart-menu-item'),
|
refreshChart: dataTestLocator('refresh-chart-menu-item'),
|
||||||
},
|
},
|
||||||
threeDotsMenuIcon:
|
threeDotsMenuIcon:
|
||||||
'.header-with-actions .right-button-panel .antd5-dropdown-trigger',
|
'.header-with-actions .right-button-panel .ant-dropdown-trigger',
|
||||||
threeDotsMenuDropdown: dataTestLocator('header-actions-menu'),
|
threeDotsMenuDropdown: dataTestLocator('header-actions-menu'),
|
||||||
refreshDashboard: dataTestLocator('refresh-dashboard-menu-item'),
|
refreshDashboard: dataTestLocator('refresh-dashboard-menu-item'),
|
||||||
saveAsMenuOption: dataTestLocator('save-as-menu-item'),
|
saveAsMenuOption: dataTestLocator('save-as-menu-item'),
|
||||||
|
|||||||
@@ -69,7 +69,21 @@ Cypress.Commands.add('loadDashboardFixtures', () =>
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const PATHS_TO_SKIP_LOGIN = ['login', 'register'];
|
||||||
|
|
||||||
|
const skipLogin = () => {
|
||||||
|
for (const path of PATHS_TO_SKIP_LOGIN) {
|
||||||
|
if (Cypress.currentTest.title.toLowerCase().includes(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
|
if (skipLogin()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
cy.login();
|
cy.login();
|
||||||
Cypress.Cookies.defaults({ preserve: 'session' });
|
Cypress.Cookies.defaults({ preserve: 'session' });
|
||||||
cy.loadChartFixtures();
|
cy.loadChartFixtures();
|
||||||
@@ -77,6 +91,9 @@ before(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
if (skipLogin()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
cy.cleanDashboards();
|
cy.cleanDashboards();
|
||||||
cy.cleanCharts();
|
cy.cleanCharts();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -143,3 +143,29 @@ export function resize(selector: string) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setSelectSearchInput = (
|
||||||
|
$input: any,
|
||||||
|
value: string,
|
||||||
|
async = false,
|
||||||
|
) => {
|
||||||
|
// Ant Design 5 Select crashes Chromium with type/click events when showSearch is true.
|
||||||
|
// This copies the value directly to the input element as a workaround.
|
||||||
|
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||||
|
window.HTMLInputElement.prototype,
|
||||||
|
'value',
|
||||||
|
)?.set;
|
||||||
|
nativeInputValueSetter?.call($input[0], value);
|
||||||
|
|
||||||
|
// Trigger the input and change events
|
||||||
|
if (async) {
|
||||||
|
$input[0].dispatchEvent(new Event('mousedown', { bubbles: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
$input[0].dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
$input[0].dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
|
||||||
|
cy.get('.ant-select-item-option-content').should('exist').first().click({
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -28,3 +28,5 @@ export const DATABASE_LIST = '/databaseview/list';
|
|||||||
export const DATASET_LIST_PATH = 'tablemodelview/list';
|
export const DATASET_LIST_PATH = 'tablemodelview/list';
|
||||||
export const ALERT_LIST = '/alert/list/';
|
export const ALERT_LIST = '/alert/list/';
|
||||||
export const REPORT_LIST = '/report/list/';
|
export const REPORT_LIST = '/report/list/';
|
||||||
|
export const LOGIN = '/login/';
|
||||||
|
export const REGISTER = '/register/';
|
||||||
|
|||||||
@@ -16,10 +16,8 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// timezone for unit tests
|
// timezone for unit tests
|
||||||
process.env.TZ = 'America/New_York';
|
process.env.TZ = 'America/New_York';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
testRegex:
|
testRegex:
|
||||||
'\\/superset-frontend\\/(spec|src|plugins|packages|tools)\\/.*(_spec|\\.test)\\.[jt]sx?$',
|
'\\/superset-frontend\\/(spec|src|plugins|packages|tools)\\/.*(_spec|\\.test)\\.[jt]sx?$',
|
||||||
@@ -30,7 +28,9 @@ module.exports = {
|
|||||||
'^src/(.*)$': '<rootDir>/src/$1',
|
'^src/(.*)$': '<rootDir>/src/$1',
|
||||||
'^spec/(.*)$': '<rootDir>/spec/$1',
|
'^spec/(.*)$': '<rootDir>/spec/$1',
|
||||||
// mapping plugins of superset-ui to source code
|
// mapping plugins of superset-ui to source code
|
||||||
'@superset-ui/(.*)$': '<rootDir>/node_modules/@superset-ui/$1/src',
|
'^@superset-ui/([^/]+)/(.*)$':
|
||||||
|
'<rootDir>/node_modules/@superset-ui/$1/src/$2',
|
||||||
|
'^@superset-ui/([^/]+)$': '<rootDir>/node_modules/@superset-ui/$1/src',
|
||||||
},
|
},
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
modulePathIgnorePatterns: ['<rootDir>/packages/generator-superset'],
|
modulePathIgnorePatterns: ['<rootDir>/packages/generator-superset'],
|
||||||
@@ -55,7 +55,7 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
coverageReporters: ['lcov', 'json-summary', 'html', 'text'],
|
coverageReporters: ['lcov', 'json-summary', 'html', 'text'],
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!d3-(interpolate|color|time)|remark-gfm|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|jest-enzyme)',
|
'node_modules/(?!d3-(interpolate|color|time)|remark-gfm|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|jest-enzyme|geostyler|geostyler-.*)',
|
||||||
],
|
],
|
||||||
preset: 'ts-jest',
|
preset: 'ts-jest',
|
||||||
transform: {
|
transform: {
|
||||||
|
|||||||
7336
superset-frontend/package-lock.json
generated
7336
superset-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,7 @@
|
|||||||
"src/setup/*"
|
"src/setup/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"_prettier": "prettier './({src,spec,cypress-base,plugins,packages,.storybook}/**/*{.js,.jsx,.ts,.tsx,.css,.less,.scss,.sass}|package.json)'",
|
"_prettier": "prettier './({src,spec,cypress-base,plugins,packages,.storybook}/**/*{.js,.jsx,.ts,.tsx,.css,.scss,.sass}|package.json)'",
|
||||||
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --color --mode production",
|
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --color --mode production",
|
||||||
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --color",
|
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --color",
|
||||||
"build-instrumented": "cross-env NODE_ENV=production BABEL_ENV=instrumented webpack --mode=production --color",
|
"build-instrumented": "cross-env NODE_ENV=production BABEL_ENV=instrumented webpack --mode=production --color",
|
||||||
@@ -70,7 +70,10 @@
|
|||||||
"storybook": "cross-env NODE_ENV=development BABEL_ENV=development storybook dev -p 6006",
|
"storybook": "cross-env NODE_ENV=development BABEL_ENV=development storybook dev -p 6006",
|
||||||
"tdd": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --watch",
|
"tdd": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --watch",
|
||||||
"test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80% --silent",
|
"test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80% --silent",
|
||||||
|
"test-loud": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80%",
|
||||||
"type": "tsc --noEmit",
|
"type": "tsc --noEmit",
|
||||||
|
"tsc": "tsc ",
|
||||||
|
"ts-migrate-all": "ts-migrate rename . && ts-migrate migrate --aliases tsfixme . && ts-migrate reignore .",
|
||||||
"update-maps": "jupyter nbconvert --to notebook --execute --inplace 'plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb' -Xfrozen_modules=off",
|
"update-maps": "jupyter nbconvert --to notebook --execute --inplace 'plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb' -Xfrozen_modules=off",
|
||||||
"validate-release": "../RELEASING/validate_this_release.sh"
|
"validate-release": "../RELEASING/validate_this_release.sh"
|
||||||
},
|
},
|
||||||
@@ -81,12 +84,9 @@
|
|||||||
"last 3 edge versions"
|
"last 3 edge versions"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.2.6",
|
|
||||||
"@emotion/cache": "^11.4.0",
|
"@emotion/cache": "^11.4.0",
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.3.0",
|
"@emotion/styled": "^11.3.0",
|
||||||
"@fontsource/fira-code": "^5.0.18",
|
|
||||||
"@fontsource/inter": "^5.0.20",
|
|
||||||
"@reduxjs/toolkit": "^1.9.3",
|
"@reduxjs/toolkit": "^1.9.3",
|
||||||
"@rjsf/core": "^5.21.1",
|
"@rjsf/core": "^5.21.1",
|
||||||
"@rjsf/utils": "^5.24.3",
|
"@rjsf/utils": "^5.24.3",
|
||||||
@@ -115,6 +115,7 @@
|
|||||||
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
||||||
"@types/d3-format": "^3.0.1",
|
"@types/d3-format": "^3.0.1",
|
||||||
"@types/d3-time-format": "^4.0.3",
|
"@types/d3-time-format": "^4.0.3",
|
||||||
|
"@types/react-google-recaptcha": "^2.1.9",
|
||||||
"@visx/axis": "^3.8.0",
|
"@visx/axis": "^3.8.0",
|
||||||
"@visx/grid": "^3.5.0",
|
"@visx/grid": "^3.5.0",
|
||||||
"@visx/responsive": "^3.0.0",
|
"@visx/responsive": "^3.0.0",
|
||||||
@@ -122,22 +123,16 @@
|
|||||||
"@visx/tooltip": "^3.0.0",
|
"@visx/tooltip": "^3.0.0",
|
||||||
"@visx/xychart": "^3.5.1",
|
"@visx/xychart": "^3.5.1",
|
||||||
"abortcontroller-polyfill": "^1.7.8",
|
"abortcontroller-polyfill": "^1.7.8",
|
||||||
"ace-builds": "^1.41.0",
|
|
||||||
"ag-grid-community": "33.1.1",
|
"ag-grid-community": "33.1.1",
|
||||||
"ag-grid-react": "33.1.1",
|
"ag-grid-react": "33.1.1",
|
||||||
"antd": "4.10.3",
|
"antd": "^5.24.6",
|
||||||
"antd-v5": "npm:antd@^5.18.0",
|
|
||||||
"bootstrap": "^3.4.1",
|
|
||||||
"brace": "^0.11.1",
|
|
||||||
"chrono-node": "^2.7.8",
|
"chrono-node": "^2.7.8",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"core-js": "^3.38.1",
|
|
||||||
"d3-color": "^3.1.0",
|
"d3-color": "^3.1.0",
|
||||||
"d3-scale": "^2.1.2",
|
"d3-scale": "^2.1.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"dom-to-image-more": "^3.2.0",
|
"dom-to-image-more": "^3.2.0",
|
||||||
"dom-to-pdf": "^0.3.2",
|
"dom-to-pdf": "^0.3.2",
|
||||||
"dompurify": "^3.2.4",
|
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"emotion-rgba": "0.0.12",
|
"emotion-rgba": "0.0.12",
|
||||||
"eslint-plugin-i18n-strings": "file:eslint-rules/eslint-plugin-i18n-strings",
|
"eslint-plugin-i18n-strings": "file:eslint-rules/eslint-plugin-i18n-strings",
|
||||||
@@ -145,12 +140,14 @@
|
|||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"geolib": "^2.0.24",
|
"geolib": "^2.0.24",
|
||||||
"geostyler": "^12.0.2",
|
"geostyler": "^14.1.3",
|
||||||
"geostyler-data": "^1.0.0",
|
"geostyler-data": "^1.0.0",
|
||||||
"geostyler-openlayers-parser": "^4.3.0",
|
"geostyler-openlayers-parser": "^4.3.0",
|
||||||
|
"geostyler-qgis-parser": "^2.0.0",
|
||||||
"geostyler-style": "^7.5.0",
|
"geostyler-style": "^7.5.0",
|
||||||
"geostyler-wfs-parser": "^2.0.3",
|
"geostyler-wfs-parser": "^2.0.3",
|
||||||
"googleapis": "^130.0.0",
|
"googleapis": "^130.0.0",
|
||||||
|
"html-webpack-plugin": "^5.6.3",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"interweave": "^13.1.0",
|
"interweave": "^13.1.0",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
@@ -174,17 +171,15 @@
|
|||||||
"rc-trigger": "^5.3.4",
|
"rc-trigger": "^5.3.4",
|
||||||
"re-resizable": "^6.10.1",
|
"re-resizable": "^6.10.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-ace": "^10.1.0",
|
|
||||||
"react-checkbox-tree": "^1.8.0",
|
"react-checkbox-tree": "^1.8.0",
|
||||||
"react-color": "^2.13.8",
|
"react-color": "^2.13.8",
|
||||||
"react-diff-viewer-continued": "^3.4.0",
|
"react-diff-viewer-continued": "^3.4.0",
|
||||||
"react-dnd": "^11.1.3",
|
"react-dnd": "^11.1.3",
|
||||||
"react-dnd-html5-backend": "^11.1.3",
|
"react-dnd-html5-backend": "^11.1.3",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-draggable": "^4.4.6",
|
"react-google-recaptcha": "^3.1.0",
|
||||||
"react-hot-loader": "^4.13.1",
|
"react-hot-loader": "^4.13.1",
|
||||||
"react-intersection-observer": "^9.16.0",
|
"react-intersection-observer": "^9.16.0",
|
||||||
"react-js-cron": "^2.1.2",
|
|
||||||
"react-json-tree": "^0.17.0",
|
"react-json-tree": "^0.17.0",
|
||||||
"react-lines-ellipsis": "^0.15.4",
|
"react-lines-ellipsis": "^0.15.4",
|
||||||
"react-loadable": "^5.5.0",
|
"react-loadable": "^5.5.0",
|
||||||
@@ -198,14 +193,12 @@
|
|||||||
"react-syntax-highlighter": "^15.4.5",
|
"react-syntax-highlighter": "^15.4.5",
|
||||||
"react-table": "^7.8.0",
|
"react-table": "^7.8.0",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"react-ultimate-pagination": "^1.3.2",
|
|
||||||
"react-virtualized-auto-sizer": "^1.0.25",
|
"react-virtualized-auto-sizer": "^1.0.25",
|
||||||
"react-window": "^1.8.10",
|
"react-window": "^1.8.10",
|
||||||
"redux": "^4.2.1",
|
"redux": "^4.2.1",
|
||||||
"redux-localstorage": "^0.4.1",
|
"redux-localstorage": "^0.4.1",
|
||||||
"redux-thunk": "^2.1.0",
|
"redux-thunk": "^2.1.0",
|
||||||
"redux-undo": "^1.0.0-beta9-9-7",
|
"redux-undo": "^1.0.0-beta9-9-7",
|
||||||
"regenerator-runtime": "^0.14.1",
|
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"rison": "^0.1.1",
|
"rison": "^0.1.1",
|
||||||
"scroll-into-view-if-needed": "^3.1.0",
|
"scroll-into-view-if-needed": "^3.1.0",
|
||||||
@@ -263,12 +256,12 @@
|
|||||||
"@types/dom-to-image": "^2.6.7",
|
"@types/dom-to-image": "^2.6.7",
|
||||||
"@types/enzyme": "^3.10.18",
|
"@types/enzyme": "^3.10.18",
|
||||||
"@types/fetch-mock": "^7.3.2",
|
"@types/fetch-mock": "^7.3.2",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/jquery": "^3.5.8",
|
|
||||||
"@types/js-levenshtein": "^1.1.3",
|
"@types/js-levenshtein": "^1.1.3",
|
||||||
"@types/json-bigint": "^1.0.4",
|
"@types/json-bigint": "^1.0.4",
|
||||||
"@types/math-expression-evaluator": "^1.3.3",
|
"@types/math-expression-evaluator": "^1.3.3",
|
||||||
"@types/mousetrap": "^1.6.15",
|
"@types/mousetrap": "^1.6.15",
|
||||||
|
"@types/node": "^22.12.0",
|
||||||
"@types/react": "^17.0.83",
|
"@types/react": "^17.0.83",
|
||||||
"@types/react-dom": "^17.0.26",
|
"@types/react-dom": "^17.0.26",
|
||||||
"@types/react-gravatar": "^2.6.14",
|
"@types/react-gravatar": "^2.6.14",
|
||||||
@@ -278,7 +271,6 @@
|
|||||||
"@types/react-resizable": "^3.0.8",
|
"@types/react-resizable": "^3.0.8",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@types/react-syntax-highlighter": "^15.5.13",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
"@types/react-table": "^7.7.20",
|
|
||||||
"@types/react-transition-group": "^4.4.12",
|
"@types/react-transition-group": "^4.4.12",
|
||||||
"@types/react-ultimate-pagination": "^1.2.4",
|
"@types/react-ultimate-pagination": "^1.2.4",
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.4",
|
"@types/react-virtualized-auto-sizer": "^1.0.4",
|
||||||
@@ -287,6 +279,7 @@
|
|||||||
"@types/redux-mock-store": "^1.0.6",
|
"@types/redux-mock-store": "^1.0.6",
|
||||||
"@types/rison": "0.1.0",
|
"@types/rison": "0.1.0",
|
||||||
"@types/sinon": "^17.0.3",
|
"@types/sinon": "^17.0.3",
|
||||||
|
"@types/testing-library__jest-dom": "^5.14.9",
|
||||||
"@types/tinycolor2": "^1.4.3",
|
"@types/tinycolor2": "^1.4.3",
|
||||||
"@types/yargs": "12 - 18",
|
"@types/yargs": "12 - 18",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
@@ -305,9 +298,12 @@
|
|||||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
"css-minimizer-webpack-plugin": "^7.0.2",
|
||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"enzyme-matchers": "^7.1.2",
|
"enzyme-matchers": "^7.1.2",
|
||||||
|
"esbuild": "^0.20.0",
|
||||||
|
"esbuild-loader": "^4.2.2",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-prettier": "^7.2.0",
|
"eslint-config-prettier": "^7.2.0",
|
||||||
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
"eslint-import-resolver-typescript": "^3.7.0",
|
"eslint-import-resolver-typescript": "^3.7.0",
|
||||||
"eslint-plugin-cypress": "^3.6.0",
|
"eslint-plugin-cypress": "^3.6.0",
|
||||||
"eslint-plugin-file-progress": "^1.5.0",
|
"eslint-plugin-file-progress": "^1.5.0",
|
||||||
@@ -331,13 +327,13 @@
|
|||||||
"html-webpack-plugin": "^5.6.3",
|
"html-webpack-plugin": "^5.6.3",
|
||||||
"imports-loader": "^5.0.0",
|
"imports-loader": "^5.0.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"jest-environment-enzyme": "^7.1.2",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-enzyme": "^7.1.2",
|
||||||
"jest-html-reporter": "^3.10.2",
|
"jest-html-reporter": "^3.10.2",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
"lerna": "^8.2.1",
|
"lerna": "^8.2.1",
|
||||||
"less": "^4.2.0",
|
|
||||||
"less-loader": "^12.2.0",
|
|
||||||
"mini-css-extract-plugin": "^2.9.0",
|
"mini-css-extract-plugin": "^2.9.0",
|
||||||
"open-cli": "^8.0.0",
|
"open-cli": "^8.0.0",
|
||||||
"po2json": "^0.4.5",
|
"po2json": "^0.4.5",
|
||||||
@@ -355,7 +351,10 @@
|
|||||||
"thread-loader": "^4.0.4",
|
"thread-loader": "^4.0.4",
|
||||||
"ts-jest": "^29.2.5",
|
"ts-jest": "^29.2.5",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
|
"ts-migrate": "^0.1.35",
|
||||||
|
"ts-migrate-plugins": "^0.1.35",
|
||||||
"tscw-config": "^1.1.2",
|
"tscw-config": "^1.1.2",
|
||||||
|
"tsx": "^4.19.2",
|
||||||
"typescript": "5.1.6",
|
"typescript": "5.1.6",
|
||||||
"vm-browserify": "^1.1.2",
|
"vm-browserify": "^1.1.2",
|
||||||
"webpack": "^5.98.0",
|
"webpack": "^5.98.0",
|
||||||
@@ -366,6 +365,12 @@
|
|||||||
"webpack-sources": "^3.2.3",
|
"webpack-sources": "^3.2.3",
|
||||||
"webpack-visualizer-plugin2": "^1.1.0"
|
"webpack-visualizer-plugin2": "^1.1.0"
|
||||||
},
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"ace-builds": "^1.41.0",
|
||||||
|
"core-js": "^3.38.1",
|
||||||
|
"react-ace": "^10.1.0",
|
||||||
|
"regenerator-runtime": "^0.14.1"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.16.0",
|
"node": "^20.16.0",
|
||||||
"npm": "^10.8.1"
|
"npm": "^10.8.1"
|
||||||
@@ -375,7 +380,8 @@
|
|||||||
"d3-color": "^3.1.0",
|
"d3-color": "^3.1.0",
|
||||||
"puppeteer": "^22.4.1",
|
"puppeteer": "^22.4.1",
|
||||||
"underscore": "^1.13.7",
|
"underscore": "^1.13.7",
|
||||||
"jspdf": "^3.0.1"
|
"jspdf": "^3.0.1",
|
||||||
|
"nwsapi": "^2.2.13"
|
||||||
},
|
},
|
||||||
"readme": "ERROR: No README data found!",
|
"readme": "ERROR: No README data found!",
|
||||||
"scarfSettings": {
|
"scarfSettings": {
|
||||||
|
|||||||
@@ -4194,7 +4194,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"antd": "^4.9.4",
|
"antd": "^5.24.6",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-dom": "^16.13.1"
|
"react-dom": "^16.13.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,29 +25,14 @@ import { <%= packageLabel %>Props, <%= packageLabel %>StylesProps } from './type
|
|||||||
|
|
||||||
// Theming variables are provided for your use via a ThemeProvider
|
// Theming variables are provided for your use via a ThemeProvider
|
||||||
// imported from @superset-ui/core. For variables available, please visit
|
// imported from @superset-ui/core. For variables available, please visit
|
||||||
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/style/index.ts
|
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/theme/index.ts
|
||||||
|
|
||||||
const Styles = styled.div<<%= packageLabel %>StylesProps>`
|
const Styles = styled.div<<%= packageLabel %>StylesProps>`
|
||||||
background-color: ${({ theme }) => theme.colors.secondary.light2};
|
background-color: ${({ theme }) => theme.colors.primary.light2};
|
||||||
padding: ${({ theme }) => theme.gridUnit * 4}px;
|
padding: ${({ theme }) => theme.sizeUnit * 4}px;
|
||||||
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
|
border-radius: ${({ theme }) => theme.borderRadius}px;
|
||||||
height: ${({ height }) => height}px;
|
height: ${({ height }) => height}px;
|
||||||
width: ${({ width }) => width}px;
|
width: ${({ width }) => width}px;
|
||||||
|
|
||||||
h3 {
|
|
||||||
/* You can use your props to control CSS! */
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: ${({ theme }) => theme.gridUnit * 3}px;
|
|
||||||
font-size: ${({ theme, headerFontSize }) =>
|
|
||||||
theme.typography.sizes[headerFontSize]}px;
|
|
||||||
font-weight: ${({ theme, boldText }) =>
|
|
||||||
theme.typography.weights[boldText ? 'bold' : 'normal']};
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
height: ${({ theme, headerFontSize, height }) =>
|
|
||||||
height - theme.gridUnit * 12 - theme.typography.sizes[headerFontSize]}px;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
"@testing-library/react-hooks": "*",
|
"@testing-library/react-hooks": "*",
|
||||||
"@testing-library/user-event": "*",
|
"@testing-library/user-event": "*",
|
||||||
"ace-builds": "^1.4.14",
|
"ace-builds": "^1.4.14",
|
||||||
"antd": "4.10.3",
|
|
||||||
"brace": "^0.11.1",
|
"brace": "^0.11.1",
|
||||||
"memoize-one": "^5.1.1",
|
"memoize-one": "^5.1.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
import { kebabCase } from 'lodash';
|
import { kebabCase } from 'lodash';
|
||||||
import { t, useTheme, styled } from '@superset-ui/core';
|
import { t, useTheme, styled } from '@superset-ui/core';
|
||||||
import Tooltip from './Tooltip';
|
import { Tooltip } from '@superset-ui/core/components';
|
||||||
|
|
||||||
interface CertifiedIconWithTooltipProps {
|
interface CertifiedIconWithTooltipProps {
|
||||||
certifiedBy?: string | null;
|
certifiedBy?: string | null;
|
||||||
@@ -27,7 +27,7 @@ interface CertifiedIconWithTooltipProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const StyledDiv = styled.div`
|
const StyledDiv = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
|
margin-bottom: ${({ theme }) => theme.sizeUnit * 2}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function CertifiedIconWithTooltip({
|
function CertifiedIconWithTooltip({
|
||||||
@@ -58,7 +58,7 @@ function CertifiedIconWithTooltip({
|
|||||||
>
|
>
|
||||||
<g>
|
<g>
|
||||||
<path
|
<path
|
||||||
fill={theme.colors.primary.base}
|
fill={theme.colorPrimary}
|
||||||
d="M23,12l-2.44-2.79l0.34-3.69l-3.61-0.82L15.4,1.5L12,2.96L8.6,1.5L6.71,4.69L3.1,5.5L3.44,9.2L1,12l2.44,2.79l-0.34,3.7 l3.61,0.82L8.6,22.5l3.4-1.47l3.4,1.46l1.89-3.19l3.61-0.82l-0.34-3.69L23,12z M9.38,16.01L7,13.61c-0.39-0.39-0.39-1.02,0-1.41 l0.07-0.07c0.39-0.39,1.03-0.39,1.42,0l1.61,1.62l5.15-5.16c0.39-0.39,1.03-0.39,1.42,0l0.07,0.07c0.39,0.39,0.39,1.02,0,1.41 l-5.92,5.94C10.41,16.4,9.78,16.4,9.38,16.01z"
|
d="M23,12l-2.44-2.79l0.34-3.69l-3.61-0.82L15.4,1.5L12,2.96L8.6,1.5L6.71,4.69L3.1,5.5L3.44,9.2L1,12l2.44,2.79l-0.34,3.7 l3.61,0.82L8.6,22.5l3.4-1.47l3.4,1.46l1.89-3.19l3.61-0.82l-0.34-3.69L23,12z M9.38,16.01L7,13.61c-0.39-0.39-0.39-1.02,0-1.41 l0.07-0.07c0.39-0.39,1.03-0.39,1.42,0l1.61,1.62l5.15-5.16c0.39-0.39,1.03-0.39,1.42,0l0.07,0.07c0.39,0.39,0.39,1.02,0,1.41 l-5.92,5.94C10.41,16.4,9.78,16.4,9.38,16.01z"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
|
|||||||
@@ -17,8 +17,12 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
|
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
|
||||||
import { css, SafeMarkdown, styled, SupersetTheme } from '@superset-ui/core';
|
import { css, styled, SupersetTheme } from '@superset-ui/core';
|
||||||
import { Tooltip } from './Tooltip';
|
import {
|
||||||
|
SafeMarkdown,
|
||||||
|
Tooltip,
|
||||||
|
InfoTooltip,
|
||||||
|
} from '@superset-ui/core/components';
|
||||||
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
||||||
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
||||||
import { ColumnMeta } from '../types';
|
import { ColumnMeta } from '../types';
|
||||||
@@ -28,7 +32,6 @@ import {
|
|||||||
getColumnTypeTooltipNode,
|
getColumnTypeTooltipNode,
|
||||||
} from './labelUtils';
|
} from './labelUtils';
|
||||||
import { SQLPopover } from './SQLPopover';
|
import { SQLPopover } from './SQLPopover';
|
||||||
import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
|
|
||||||
|
|
||||||
export type ColumnOptionProps = {
|
export type ColumnOptionProps = {
|
||||||
column: ColumnMeta;
|
column: ColumnMeta;
|
||||||
@@ -40,7 +43,7 @@ const StyleOverrides = styled.span`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
svg {
|
svg {
|
||||||
margin-right: ${({ theme }) => theme.gridUnit}px;
|
margin-right: ${({ theme }) => theme.sizeUnit}px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -82,7 +85,7 @@ export function ColumnOption({
|
|||||||
<span
|
<span
|
||||||
className="option-label column-option-label"
|
className="option-label column-option-label"
|
||||||
css={(theme: SupersetTheme) => css`
|
css={(theme: SupersetTheme) => css`
|
||||||
margin-right: ${theme.gridUnit}px;
|
margin-right: ${theme.sizeUnit}px;
|
||||||
`}
|
`}
|
||||||
ref={labelRef}
|
ref={labelRef}
|
||||||
>
|
>
|
||||||
@@ -98,15 +101,13 @@ export function ColumnOption({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{warningMarkdown && (
|
{warningMarkdown && (
|
||||||
<InfoTooltipWithTrigger
|
<InfoTooltip
|
||||||
className="text-warning"
|
type="warning"
|
||||||
icon="warning"
|
|
||||||
tooltip={<SafeMarkdown source={warningMarkdown} />}
|
tooltip={<SafeMarkdown source={warningMarkdown} />}
|
||||||
label={`warn-${column.column_name}`}
|
label={`warn-${column.column_name}`}
|
||||||
iconsStyle={{ marginLeft: 0 }}
|
iconStyle={{ marginLeft: 0 }}
|
||||||
{...(column.error_text && {
|
{...(column.error_text && {
|
||||||
className: 'text-danger',
|
type: 'error',
|
||||||
icon: 'exclamation-circle',
|
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ const TypeIconWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: ${theme.gridUnit * 6}px;
|
width: ${theme.sizeUnit * 6}px;
|
||||||
height: ${theme.gridUnit * 6}px;
|
height: ${theme.sizeUnit * 6}px;
|
||||||
margin-right: ${theme.gridUnit}px;
|
margin-right: ${theme.sizeUnit}px;
|
||||||
|
|
||||||
&& svg {
|
&& svg {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { t } from '@superset-ui/core';
|
import { t, css } from '@superset-ui/core';
|
||||||
import { InfoTooltipWithTrigger } from './InfoTooltipWithTrigger';
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
import { Tooltip } from './Tooltip';
|
import { InfoTooltip, Tooltip } from '@superset-ui/core/components';
|
||||||
|
|
||||||
type ValidationError = string;
|
type ValidationError = string;
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export function ControlHeader({
|
|||||||
<span>
|
<span>
|
||||||
{description && (
|
{description && (
|
||||||
<span>
|
<span>
|
||||||
<InfoTooltipWithTrigger
|
<InfoTooltip
|
||||||
label={t('description')}
|
label={t('description')}
|
||||||
tooltip={description}
|
tooltip={description}
|
||||||
placement="top"
|
placement="top"
|
||||||
@@ -70,11 +70,11 @@ export function ControlHeader({
|
|||||||
)}
|
)}
|
||||||
{renderTrigger && (
|
{renderTrigger && (
|
||||||
<span>
|
<span>
|
||||||
<InfoTooltipWithTrigger
|
<InfoTooltip
|
||||||
label={t('bolt')}
|
label={t('bolt')}
|
||||||
tooltip={t('Changing this control takes effect instantly')}
|
tooltip={t('Changing this control takes effect instantly')}
|
||||||
placement="top"
|
placement="top"
|
||||||
icon="bolt"
|
type="notice"
|
||||||
/>{' '}
|
/>{' '}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -88,6 +88,7 @@ export function ControlHeader({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const labelClass = validationErrors.length > 0 ? 'text-danger' : '';
|
const labelClass = validationErrors.length > 0 ? 'text-danger' : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ControlHeader" data-test={`${name}-header`}>
|
<div className="ControlHeader" data-test={`${name}-header`}>
|
||||||
<div className="pull-left">
|
<div className="pull-left">
|
||||||
@@ -98,25 +99,30 @@ export function ControlHeader({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={labelClass}
|
className={labelClass}
|
||||||
style={{ cursor: onClick ? 'pointer' : '' }}
|
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
{warning && (
|
{warning && (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip id="error-tooltip" placement="top" title={warning}>
|
<Tooltip id="error-tooltip" placement="top" title={warning}>
|
||||||
{/* TODO: Remove fa-icon */}
|
<InfoCircleOutlined
|
||||||
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
|
css={theme => css`
|
||||||
<i className="fa fa-exclamation-circle text-warning" />
|
font-size: ${theme.sizeUnit * 3}px;
|
||||||
|
color: ${theme.colorError};
|
||||||
|
`}
|
||||||
|
/>
|
||||||
</Tooltip>{' '}
|
</Tooltip>{' '}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{danger && (
|
{danger && (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip id="error-tooltip" placement="top" title={danger}>
|
<Tooltip id="error-tooltip" placement="top" title={danger}>
|
||||||
{/* TODO: Remove fa-icon */}
|
<InfoCircleOutlined
|
||||||
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
|
css={theme => css`
|
||||||
<i className="fa fa-exclamation-circle text-danger" />
|
font-size: ${theme.sizeUnit * 3}px;
|
||||||
|
color: ${theme.colorError};
|
||||||
|
`}
|
||||||
|
/>{' '}
|
||||||
</Tooltip>{' '}
|
</Tooltip>{' '}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -127,18 +133,17 @@ export function ControlHeader({
|
|||||||
placement="top"
|
placement="top"
|
||||||
title={validationErrors.join(' ')}
|
title={validationErrors.join(' ')}
|
||||||
>
|
>
|
||||||
{/* TODO: Remove fa-icon */}
|
<InfoCircleOutlined
|
||||||
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
|
css={theme => css`
|
||||||
<i className="fa fa-exclamation-circle text-danger" />
|
font-size: ${theme.sizeUnit * 3}px;
|
||||||
|
color: ${theme.colorError};
|
||||||
|
`}
|
||||||
|
/>{' '}
|
||||||
</Tooltip>{' '}
|
</Tooltip>{' '}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{renderOptionalIcons()}
|
{renderOptionalIcons()}
|
||||||
{required && (
|
{required && <strong> *</strong>}
|
||||||
<span className="text-danger m-l-4">
|
|
||||||
<strong>*</strong>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{rightNode && <div className="pull-right">{rightNode}</div>}
|
{rightNode && <div className="pull-right">{rightNode}</div>}
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ import { styled, css } from '@superset-ui/core';
|
|||||||
|
|
||||||
export const ControlSubSectionHeader = styled.div`
|
export const ControlSubSectionHeader = styled.div`
|
||||||
${({ theme }) => css`
|
${({ theme }) => css`
|
||||||
font-weight: ${theme.typography.weights.bold};
|
font-weight: ${theme.fontWeightStrong};
|
||||||
font-size: ${theme.typography.sizes.s};
|
margin-bottom: ${theme.sizeUnit}px;
|
||||||
margin-bottom: ${theme.gridUnit}px;
|
font-size: ${theme.fontSizeSM}px;
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
export default ControlSubSectionHeader;
|
|
||||||
|
|||||||
@@ -17,5 +17,4 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { Dropdown } from 'antd';
|
export { Dropdown, type DropdownProps } from '@superset-ui/core/components';
|
||||||
export type { DropDownProps } from 'antd/lib/dropdown';
|
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
import { CSSProperties } from 'react';
|
|
||||||
import { kebabCase } from 'lodash';
|
|
||||||
import { t } from '@superset-ui/core';
|
|
||||||
import { Tooltip, TooltipProps, TooltipPlacement } from './Tooltip';
|
|
||||||
|
|
||||||
export interface InfoTooltipWithTriggerProps {
|
|
||||||
label?: string;
|
|
||||||
tooltip?: TooltipProps['title'];
|
|
||||||
icon?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
placement?: TooltipPlacement;
|
|
||||||
bsStyle?: string;
|
|
||||||
className?: string;
|
|
||||||
iconsStyle?: CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InfoTooltipWithTrigger({
|
|
||||||
label,
|
|
||||||
tooltip,
|
|
||||||
bsStyle,
|
|
||||||
onClick,
|
|
||||||
icon = 'info-circle',
|
|
||||||
className = 'text-muted',
|
|
||||||
placement = 'right',
|
|
||||||
iconsStyle = {},
|
|
||||||
}: InfoTooltipWithTriggerProps) {
|
|
||||||
const iconClass = `fa fa-${icon} ${className} ${
|
|
||||||
bsStyle ? `text-${bsStyle}` : ''
|
|
||||||
}`;
|
|
||||||
const iconEl = (
|
|
||||||
<i
|
|
||||||
role="button"
|
|
||||||
aria-label={t('Show info tooltip')}
|
|
||||||
tabIndex={0}
|
|
||||||
className={iconClass}
|
|
||||||
style={{ cursor: onClick ? 'pointer' : undefined, ...iconsStyle }}
|
|
||||||
onClick={onClick}
|
|
||||||
onKeyPress={
|
|
||||||
onClick &&
|
|
||||||
(event => {
|
|
||||||
if (event.key === 'Enter' || event.key === ' ') {
|
|
||||||
onClick();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
if (!tooltip) {
|
|
||||||
return iconEl;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
id={`${kebabCase(label)}-tooltip`}
|
|
||||||
title={tooltip}
|
|
||||||
placement={placement}
|
|
||||||
>
|
|
||||||
{iconEl}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default InfoTooltipWithTrigger;
|
|
||||||
@@ -17,5 +17,4 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { Menu } from 'antd';
|
export { Menu, type MenuProps } from '@superset-ui/core/components';
|
||||||
export type { MenuProps } from 'antd/lib/menu';
|
|
||||||
|
|||||||
@@ -18,17 +18,16 @@
|
|||||||
*/
|
*/
|
||||||
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
|
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
|
||||||
|
|
||||||
|
import { css, styled, Metric, SupersetTheme } from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
styled,
|
|
||||||
Metric,
|
|
||||||
SafeMarkdown,
|
SafeMarkdown,
|
||||||
SupersetTheme,
|
Typography,
|
||||||
} from '@superset-ui/core';
|
// TODO: somehow doesn't work with our main Tooltip (?)
|
||||||
import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
|
RawAntdTooltip as Tooltip,
|
||||||
|
InfoTooltip,
|
||||||
|
} from '@superset-ui/core/components';
|
||||||
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
||||||
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
||||||
import Tooltip from './Tooltip';
|
|
||||||
import { getMetricTooltipNode } from './labelUtils';
|
import { getMetricTooltipNode } from './labelUtils';
|
||||||
import { SQLPopover } from './SQLPopover';
|
import { SQLPopover } from './SQLPopover';
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ const FlexRowContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
> svg {
|
> svg {
|
||||||
margin-right: ${({ theme }) => theme.gridUnit}px;
|
margin-right: ${({ theme }) => theme.sizeUnit}px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -61,23 +60,26 @@ export function MetricOption({
|
|||||||
url = '',
|
url = '',
|
||||||
}: MetricOptionProps) {
|
}: MetricOptionProps) {
|
||||||
const verbose = metric.verbose_name || metric.metric_name || metric.label;
|
const verbose = metric.verbose_name || metric.metric_name || metric.label;
|
||||||
const link = url ? (
|
|
||||||
<a href={url} target={openInNewWindow ? '_blank' : ''} rel="noreferrer">
|
|
||||||
{verbose}
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
verbose
|
|
||||||
);
|
|
||||||
|
|
||||||
const label = (
|
const label = (
|
||||||
<span
|
<span
|
||||||
className="option-label metric-option-label"
|
className="option-label metric-option-label"
|
||||||
css={(theme: SupersetTheme) => css`
|
css={(theme: SupersetTheme) => css`
|
||||||
margin-right: ${theme.gridUnit}px;
|
margin-right: ${theme.sizeUnit}px;
|
||||||
`}
|
`}
|
||||||
ref={labelRef}
|
ref={labelRef}
|
||||||
>
|
>
|
||||||
{link}
|
{url ? (
|
||||||
|
<Typography.Link
|
||||||
|
href={url}
|
||||||
|
target={openInNewWindow ? '_blank' : ''}
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{verbose}
|
||||||
|
</Typography.Link>
|
||||||
|
) : (
|
||||||
|
verbose
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -111,15 +113,13 @@ export function MetricOption({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{warningMarkdown && (
|
{warningMarkdown && (
|
||||||
<InfoTooltipWithTrigger
|
<InfoTooltip
|
||||||
className="text-warning"
|
type="warning"
|
||||||
icon="warning"
|
|
||||||
tooltip={<SafeMarkdown source={warningMarkdown} />}
|
tooltip={<SafeMarkdown source={warningMarkdown} />}
|
||||||
label={`warn-${metric.metric_name}`}
|
label={`warn-${metric.metric_name}`}
|
||||||
iconsStyle={{ marginLeft: 0 }}
|
iconStyle={{ marginLeft: 0 }}
|
||||||
{...(metric.error_text && {
|
{...(metric.error_text && {
|
||||||
className: 'text-danger',
|
type: 'error',
|
||||||
icon: 'exclamation-circle',
|
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -17,19 +17,18 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Popover } from 'antd-v5';
|
import { Popover, type PopoverProps } from '@superset-ui/core/components';
|
||||||
import type ReactAce from 'react-ace';
|
import type ReactAce from 'react-ace';
|
||||||
import type { PopoverProps } from 'antd-v5/lib/popover';
|
|
||||||
import { CalculatorOutlined } from '@ant-design/icons';
|
import { CalculatorOutlined } from '@ant-design/icons';
|
||||||
import { css, styled, useTheme, t } from '@superset-ui/core';
|
import { css, styled, useTheme, t } from '@superset-ui/core';
|
||||||
|
|
||||||
const StyledCalculatorIcon = styled(CalculatorOutlined)`
|
const StyledCalculatorIcon = styled(CalculatorOutlined)`
|
||||||
${({ theme }) => css`
|
${({ theme }) => css`
|
||||||
color: ${theme.colors.grayscale.base};
|
color: ${theme.colors.grayscale.base};
|
||||||
font-size: ${theme.typography.sizes.s}px;
|
font-size: ${theme.fontSizeSM}px;
|
||||||
& svg {
|
& svg {
|
||||||
margin-left: ${theme.gridUnit}px;
|
margin-left: ${theme.sizeUnit}px;
|
||||||
margin-right: ${theme.gridUnit}px;
|
margin-right: ${theme.sizeUnit}px;
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@@ -65,9 +64,9 @@ export const SQLPopover = (props: PopoverProps & { sqlExpression: string }) => {
|
|||||||
readOnly
|
readOnly
|
||||||
wrapEnabled
|
wrapEnabled
|
||||||
style={{
|
style={{
|
||||||
border: `1px solid ${theme.colors.grayscale.light2}`,
|
border: `1px solid ${theme.colorBorder}`,
|
||||||
background: theme.colors.secondary.light5,
|
background: theme.colorPrimaryBg,
|
||||||
maxWidth: theme.gridUnit * 100,
|
maxWidth: theme.sizeUnit * 100,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { useState, ReactNode } from 'react';
|
import { useState, ReactNode } from 'react';
|
||||||
import AntdSelect, { SelectProps as AntdSelectProps } from 'antd/lib/select';
|
import {
|
||||||
|
RawAntdSelect as AntdSelect,
|
||||||
|
type RawAntdSelectProps as AntdSelectProps,
|
||||||
|
} from '@superset-ui/core/components';
|
||||||
|
|
||||||
export const { Option }: any = AntdSelect;
|
export const { Option }: any = AntdSelect;
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ export type SelectProps<VT> = Omit<AntdSelectProps<VT>, 'options'> & {
|
|||||||
export default function Select<VT extends string | number>({
|
export default function Select<VT extends string | number>({
|
||||||
creatable,
|
creatable,
|
||||||
onSearch,
|
onSearch,
|
||||||
dropdownMatchSelectWidth = false,
|
popupMatchSelectWidth = false,
|
||||||
minWidth = '100%',
|
minWidth = '100%',
|
||||||
showSearch: showSearch_ = true,
|
showSearch: showSearch_ = true,
|
||||||
onChange,
|
onChange,
|
||||||
@@ -73,7 +76,7 @@ export default function Select<VT extends string | number>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AntdSelect<VT>
|
<AntdSelect<VT>
|
||||||
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
|
popupMatchSelectWidth={popupMatchSelectWidth}
|
||||||
showSearch={showSearch}
|
showSearch={showSearch}
|
||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useTheme } from '@superset-ui/core';
|
|
||||||
import { Tooltip as BaseTooltip } from 'antd-v5';
|
|
||||||
import {
|
|
||||||
TooltipProps as BaseTooltipProps,
|
|
||||||
TooltipPlacement as BaseTooltipPlacement,
|
|
||||||
} from 'antd-v5/lib/tooltip';
|
|
||||||
|
|
||||||
export type TooltipProps = BaseTooltipProps;
|
|
||||||
export type TooltipPlacement = BaseTooltipPlacement;
|
|
||||||
|
|
||||||
export const Tooltip = ({
|
|
||||||
overlayStyle = {},
|
|
||||||
color,
|
|
||||||
...props
|
|
||||||
}: BaseTooltipProps) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const defaultColor = `${theme.colors.grayscale.dark2}e6`;
|
|
||||||
return (
|
|
||||||
<BaseTooltip
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
fontSize: theme.typography.sizes.s,
|
|
||||||
lineHeight: '1.6',
|
|
||||||
maxWidth: theme.gridUnit * 62,
|
|
||||||
minWidth: theme.gridUnit * 30,
|
|
||||||
...overlayStyle,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
// make the tooltip display closer to the label
|
|
||||||
align={{ offset: [0, 1] }}
|
|
||||||
color={defaultColor || color}
|
|
||||||
trigger="hover"
|
|
||||||
placement="bottom"
|
|
||||||
// don't allow hovering over the tooltip
|
|
||||||
mouseLeaveDelay={0}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Tooltip;
|
|
||||||
@@ -29,18 +29,18 @@ const TooltipSectionWrapper = styled.div`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
font-size: ${theme.typography.sizes.s}px;
|
font-size: ${theme.fontSizeSM}px;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
|
|
||||||
&:not(:last-of-type) {
|
&:not(:last-of-type) {
|
||||||
margin-bottom: ${theme.gridUnit * 2}px;
|
margin-bottom: ${theme.sizeUnit * 2}px;
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TooltipSectionLabel = styled.span`
|
const TooltipSectionLabel = styled.span`
|
||||||
${({ theme }) => css`
|
${({ theme }) => css`
|
||||||
font-weight: ${theme.typography.weights.bold};
|
font-weight: ${theme.fontWeightStrong};
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -25,14 +25,12 @@ export * from './operators';
|
|||||||
// can't do `export * as sections from './sections'`, babel-transformer will fail
|
// can't do `export * as sections from './sections'`, babel-transformer will fail
|
||||||
export const sections = sectionsModule;
|
export const sections = sectionsModule;
|
||||||
|
|
||||||
export * from './components/InfoTooltipWithTrigger';
|
|
||||||
export * from './components/ColumnOption';
|
export * from './components/ColumnOption';
|
||||||
export * from './components/ColumnTypeLabel/ColumnTypeLabel';
|
export * from './components/ColumnTypeLabel/ColumnTypeLabel';
|
||||||
export * from './components/ControlSubSectionHeader';
|
export * from './components/ControlSubSectionHeader';
|
||||||
export * from './components/Dropdown';
|
export * from './components/Dropdown';
|
||||||
export * from './components/Menu';
|
export * from './components/Menu';
|
||||||
export * from './components/MetricOption';
|
export * from './components/MetricOption';
|
||||||
export * from './components/Tooltip';
|
|
||||||
export * from './components/ControlHeader';
|
export * from './components/ControlHeader';
|
||||||
|
|
||||||
export * from './shared-controls';
|
export * from './shared-controls';
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { JsonValue, useTheme } from '@superset-ui/core';
|
import { JsonValue } from '@superset-ui/core';
|
||||||
|
import { Radio } from '@superset-ui/core/components';
|
||||||
import { ControlHeader } from '../../components/ControlHeader';
|
import { ControlHeader } from '../../components/ControlHeader';
|
||||||
|
|
||||||
// [value, label]
|
// [value, label]
|
||||||
@@ -42,51 +43,19 @@ export default function RadioButtonControl({
|
|||||||
...props
|
...props
|
||||||
}: RadioButtonControlProps) {
|
}: RadioButtonControlProps) {
|
||||||
const currentValue = initialValue || options[0][0];
|
const currentValue = initialValue || options[0][0];
|
||||||
const theme = useTheme();
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div>
|
||||||
css={{
|
|
||||||
'.btn svg': {
|
|
||||||
position: 'relative',
|
|
||||||
top: '0.2em',
|
|
||||||
},
|
|
||||||
'.btn:focus': {
|
|
||||||
outline: 'none',
|
|
||||||
},
|
|
||||||
'.control-label': {
|
|
||||||
color: theme.colors.grayscale.base,
|
|
||||||
marginBottom: theme.gridUnit,
|
|
||||||
},
|
|
||||||
'.control-label + .btn-group': {
|
|
||||||
marginTop: '1px',
|
|
||||||
},
|
|
||||||
'.btn-group .btn-default': {
|
|
||||||
color: theme.colors.grayscale.dark1,
|
|
||||||
},
|
|
||||||
'.btn-group .btn.active': {
|
|
||||||
background: theme.colors.grayscale.light4,
|
|
||||||
fontWeight: theme.typography.weights.bold,
|
|
||||||
boxShadow: 'none',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ControlHeader {...props} />
|
<ControlHeader {...props} />
|
||||||
<div className="btn-group btn-group-sm">
|
<Radio.Group
|
||||||
|
value={currentValue}
|
||||||
|
onChange={e => onChange(e.target.value)}
|
||||||
|
>
|
||||||
{options.map(([val, label]) => (
|
{options.map(([val, label]) => (
|
||||||
<button
|
<Radio.Button key={JSON.stringify(val)} value={val}>
|
||||||
key={JSON.stringify(val)}
|
|
||||||
type="button"
|
|
||||||
className={`btn btn-default ${
|
|
||||||
val === currentValue ? 'active' : ''
|
|
||||||
}`}
|
|
||||||
onClick={() => {
|
|
||||||
onChange(val);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</button>
|
</Radio.Button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Radio.Group>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ export interface FilterOption<T extends SelectOption> {
|
|||||||
data: T;
|
data: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ref: superset-frontend/src/components/Select/SupersetStyledSelect.tsx
|
// Ref: superset-frontend/@superset-ui/core/components/Select/SupersetStyledSelect.tsx
|
||||||
export interface SelectControlConfig<
|
export interface SelectControlConfig<
|
||||||
O extends SelectOption = SelectOption,
|
O extends SelectOption = SelectOption,
|
||||||
T extends SelectControlType = SelectControlType,
|
T extends SelectControlType = SelectControlType,
|
||||||
@@ -371,7 +371,9 @@ export type CustomControlItem = {
|
|||||||
export const isCustomControlItem = (obj: unknown): obj is CustomControlItem =>
|
export const isCustomControlItem = (obj: unknown): obj is CustomControlItem =>
|
||||||
typeof obj === 'object' &&
|
typeof obj === 'object' &&
|
||||||
obj !== null &&
|
obj !== null &&
|
||||||
|
// @ts-expect-error TS(2339): Property 'name' does not exist on type 'object'.
|
||||||
typeof ('name' in obj && obj.name) === 'string' &&
|
typeof ('name' in obj && obj.name) === 'string' &&
|
||||||
|
// @ts-expect-error TS(2339): Property 'config' does not exist on type 'object'.
|
||||||
typeof ('config' in obj && obj.config) === 'object' &&
|
typeof ('config' in obj && obj.config) === 'object' &&
|
||||||
(obj as CustomControlItem).config !== null;
|
(obj as CustomControlItem).config !== null;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@superset-ui/core/spec';
|
||||||
import {
|
import {
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
supersetTheme,
|
supersetTheme,
|
||||||
@@ -26,17 +26,21 @@ import {
|
|||||||
|
|
||||||
import { ColumnOption, ColumnOptionProps } from '../../src';
|
import { ColumnOption, ColumnOptionProps } from '../../src';
|
||||||
|
|
||||||
jest.mock('../../src/components/SQLPopover', () => ({
|
jest.mock('@superset-ui/chart-controls/components/SQLPopover', () => ({
|
||||||
SQLPopover: () => <div data-test="mock-sql-popover" />,
|
SQLPopover: () => <div data-test="mock-sql-popover" />,
|
||||||
}));
|
}));
|
||||||
jest.mock('../../src/components/ColumnTypeLabel/ColumnTypeLabel', () => ({
|
jest.mock(
|
||||||
ColumnTypeLabel: ({ type }: { type: string }) => (
|
'@superset-ui/chart-controls/components/ColumnTypeLabel/ColumnTypeLabel',
|
||||||
<div data-test="mock-column-type-label">{type}</div>
|
() => ({
|
||||||
),
|
ColumnTypeLabel: ({ type }: { type: string }) => (
|
||||||
|
<div data-test="mock-column-type-label">{type}</div>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('@superset-ui/core/components/InfoTooltip', () => ({
|
||||||
|
InfoTooltip: () => <div data-test="mock-tooltip" />,
|
||||||
}));
|
}));
|
||||||
jest.mock('../../src/components/InfoTooltipWithTrigger', () => () => (
|
|
||||||
<div data-test="mock-info-tooltip-with-trigger" />
|
|
||||||
));
|
|
||||||
|
|
||||||
const defaultProps: ColumnOptionProps = {
|
const defaultProps: ColumnOptionProps = {
|
||||||
column: {
|
column: {
|
||||||
@@ -114,11 +118,11 @@ test('dttm column has correct column label if showType is true', () => {
|
|||||||
String(GenericDataType.Temporal),
|
String(GenericDataType.Temporal),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
test('doesnt show InfoTooltipWithTrigger when no warning', () => {
|
test('doesnt show InfoTooltip when no warning', () => {
|
||||||
const { queryByText } = setup();
|
const { queryByText } = setup();
|
||||||
expect(queryByText('mock-info-tooltip-with-trigger')).not.toBeInTheDocument();
|
expect(queryByText('mock-tooltip')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
test('shows a warning with InfoTooltipWithTrigger when it contains warning', () => {
|
test('shows a warning with InfoTooltip when it contains warning', () => {
|
||||||
const { getByTestId } = setup({
|
const { getByTestId } = setup({
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
column: {
|
column: {
|
||||||
@@ -126,5 +130,5 @@ test('shows a warning with InfoTooltipWithTrigger when it contains warning', ()
|
|||||||
warning_text: 'This is a warning',
|
warning_text: 'This is a warning',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(getByTestId('mock-info-tooltip-with-trigger')).toBeInTheDocument();
|
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { isValidElement } from 'react';
|
import { isValidElement } from 'react';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@superset-ui/core/spec';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { GenericDataType } from '@superset-ui/core';
|
import { GenericDataType } from '@superset-ui/core';
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { fireEvent, render } from '@testing-library/react';
|
import { fireEvent, render } from '@superset-ui/core/spec';
|
||||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||||
import { InfoTooltipWithTrigger, InfoTooltipWithTriggerProps } from '../../src';
|
import { InfoTooltip, InfoTooltipProps } from '@superset-ui/core/components';
|
||||||
|
|
||||||
jest.mock('../../src/components/Tooltip', () => ({
|
jest.mock('@superset-ui/core/components/Tooltip', () => ({
|
||||||
Tooltip: ({ children }: { children: React.ReactNode }) => (
|
Tooltip: ({ children }: { children: React.ReactNode }) => (
|
||||||
<div data-test="mock-tooltip">{children}</div>
|
<div data-test="mock-tooltip">{children}</div>
|
||||||
),
|
),
|
||||||
@@ -29,10 +29,10 @@ jest.mock('../../src/components/Tooltip', () => ({
|
|||||||
|
|
||||||
const defaultProps = {};
|
const defaultProps = {};
|
||||||
|
|
||||||
const setup = (props: Partial<InfoTooltipWithTriggerProps> = {}) =>
|
const setup = (props: Partial<InfoTooltipProps> = {}) =>
|
||||||
render(
|
render(
|
||||||
<ThemeProvider theme={supersetTheme}>
|
<ThemeProvider theme={supersetTheme}>
|
||||||
<InfoTooltipWithTrigger {...defaultProps} {...props} />
|
<InfoTooltip {...defaultProps} {...props} />
|
||||||
</ThemeProvider>,
|
</ThemeProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -44,31 +44,29 @@ test('renders a tooltip', () => {
|
|||||||
expect(getAllByTestId('mock-tooltip').length).toEqual(1);
|
expect(getAllByTestId('mock-tooltip').length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders an info icon', () => {
|
test('responds to keydown events', () => {
|
||||||
const { container } = setup();
|
|
||||||
expect(container.getElementsByClassName('fa-info-circle')).toHaveLength(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('responds to keypresses', () => {
|
|
||||||
const clickHandler = jest.fn();
|
const clickHandler = jest.fn();
|
||||||
const { getByRole } = setup({
|
const { getByRole } = setup({
|
||||||
label: 'test',
|
label: 'test',
|
||||||
tooltip: 'this is a test',
|
tooltip: 'this is a test',
|
||||||
onClick: clickHandler,
|
onClick: clickHandler,
|
||||||
});
|
});
|
||||||
fireEvent.keyPress(getByRole('button'), {
|
|
||||||
|
fireEvent.keyDown(getByRole('button'), {
|
||||||
key: 'Tab',
|
key: 'Tab',
|
||||||
code: 9,
|
code: 9,
|
||||||
charCode: 9,
|
charCode: 9,
|
||||||
});
|
});
|
||||||
expect(clickHandler).toHaveBeenCalledTimes(0);
|
expect(clickHandler).toHaveBeenCalledTimes(0);
|
||||||
fireEvent.keyPress(getByRole('button'), {
|
|
||||||
|
fireEvent.keyDown(getByRole('button'), {
|
||||||
key: 'Enter',
|
key: 'Enter',
|
||||||
code: 13,
|
code: 13,
|
||||||
charCode: 13,
|
charCode: 13,
|
||||||
});
|
});
|
||||||
expect(clickHandler).toHaveBeenCalledTimes(1);
|
expect(clickHandler).toHaveBeenCalledTimes(1);
|
||||||
fireEvent.keyPress(getByRole('button'), {
|
|
||||||
|
fireEvent.keyDown(getByRole('button'), {
|
||||||
key: ' ',
|
key: ' ',
|
||||||
code: 32,
|
code: 32,
|
||||||
charCode: 32,
|
charCode: 32,
|
||||||
@@ -76,9 +74,47 @@ test('responds to keypresses', () => {
|
|||||||
expect(clickHandler).toHaveBeenCalledTimes(2);
|
expect(clickHandler).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('has a bsStyle', () => {
|
test('finds the info circle icon inside info variant', () => {
|
||||||
const { container } = setup({
|
const { container } = setup({
|
||||||
bsStyle: 'something',
|
type: 'info',
|
||||||
});
|
});
|
||||||
expect(container.getElementsByClassName('text-something')).toHaveLength(1);
|
|
||||||
|
const iconSpan = container.querySelector('svg[data-icon="info-circle"]');
|
||||||
|
expect(iconSpan).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('finds the warning icon inside warning variant', () => {
|
||||||
|
const { container } = setup({
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconSpan = container.querySelector('svg[data-icon="warning"]');
|
||||||
|
expect(iconSpan).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('finds the close circle icon inside error variant', () => {
|
||||||
|
const { container } = setup({
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconSpan = container.querySelector('svg[data-icon="close-circle"]');
|
||||||
|
expect(iconSpan).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('finds the question circle icon inside question variant', () => {
|
||||||
|
const { container } = setup({
|
||||||
|
type: 'question',
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconSpan = container.querySelector('svg[data-icon="question-circle"]');
|
||||||
|
expect(iconSpan).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('finds the thunderbolt icon inside notice variant', () => {
|
||||||
|
const { container } = setup({
|
||||||
|
type: 'notice',
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconSpan = container.querySelector('svg[data-icon="thunderbolt"]');
|
||||||
|
expect(iconSpan).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,24 +17,31 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@superset-ui/core/spec';
|
||||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||||
import { MetricOption, MetricOptionProps } from '../../src';
|
import {
|
||||||
|
MetricOption,
|
||||||
|
MetricOptionProps,
|
||||||
|
} from '../../src/components/MetricOption';
|
||||||
|
|
||||||
jest.mock('../../src/components/InfoTooltipWithTrigger', () => () => (
|
jest.mock('@superset-ui/core/components/InfoTooltip', () => ({
|
||||||
<div data-test="mock-info-tooltip-with-trigger" />
|
InfoTooltip: () => <div data-test="mock-tooltip" />,
|
||||||
));
|
|
||||||
jest.mock('../../src/components/ColumnTypeLabel/ColumnTypeLabel', () => ({
|
|
||||||
ColumnTypeLabel: () => <div data-test="mock-column-type-label" />,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
'../../src/components/Tooltip',
|
'@superset-ui/chart-controls/components/ColumnTypeLabel/ColumnTypeLabel',
|
||||||
|
() => ({
|
||||||
|
ColumnTypeLabel: () => <div data-test="mock-column-type-label" />,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
'@superset-ui/core/components/Tooltip',
|
||||||
() =>
|
() =>
|
||||||
({ children }: { children: React.ReactNode }) => (
|
({ children }: { children: React.ReactNode }) => (
|
||||||
<div data-test="mock-tooltip">{children}</div>
|
<div data-test="mock-tooltip">{children}</div>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
jest.mock('../../src/components/SQLPopover', () => ({
|
jest.mock('@superset-ui/chart-controls/components/SQLPopover', () => ({
|
||||||
SQLPopover: () => <div data-test="mock-sql-popover" />,
|
SQLPopover: () => <div data-test="mock-sql-popover" />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -65,9 +72,9 @@ test('shows a label with verbose_name', () => {
|
|||||||
expect(lbl).toHaveLength(1);
|
expect(lbl).toHaveLength(1);
|
||||||
expect(`${lbl[0].textContent}`).toEqual(defaultProps.metric.verbose_name);
|
expect(`${lbl[0].textContent}`).toEqual(defaultProps.metric.verbose_name);
|
||||||
});
|
});
|
||||||
test('shows a InfoTooltipWithTrigger', () => {
|
test('shows a InfoTooltip', () => {
|
||||||
const { getByTestId } = setup();
|
const { getByTestId } = setup();
|
||||||
expect(getByTestId('mock-info-tooltip-with-trigger')).toBeInTheDocument();
|
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
test('shows SQL Popover trigger', () => {
|
test('shows SQL Popover trigger', () => {
|
||||||
const { getByTestId } = setup();
|
const { getByTestId } = setup();
|
||||||
@@ -82,14 +89,14 @@ test('shows a label with metric_name when no verbose_name', () => {
|
|||||||
});
|
});
|
||||||
expect(getByText(defaultProps.metric.metric_name)).toBeInTheDocument();
|
expect(getByText(defaultProps.metric.metric_name)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
test('doesnt show InfoTooltipWithTrigger when no warning', () => {
|
test('doesnt show InfoTooltip when no warning', () => {
|
||||||
const { queryByText } = setup({
|
const { queryByText } = setup({
|
||||||
metric: {
|
metric: {
|
||||||
...defaultProps.metric,
|
...defaultProps.metric,
|
||||||
warning_text: '',
|
warning_text: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(queryByText('mock-info-tooltip-with-trigger')).not.toBeInTheDocument();
|
expect(queryByText('mock-tooltip')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
test('sets target="_blank" when openInNewWindow is true', () => {
|
test('sets target="_blank" when openInNewWindow is true', () => {
|
||||||
const { getByRole } = setup({
|
const { getByRole } = setup({
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { ReactElement } from 'react';
|
import { ReactElement } from 'react';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@superset-ui/core/spec';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ test('returns empty array if timeseries_limit_metric is an empty array', () => {
|
|||||||
expect(
|
expect(
|
||||||
extractExtraMetrics({
|
extractExtraMetrics({
|
||||||
...baseFormData,
|
...baseFormData,
|
||||||
// @ts-ignore
|
// @ts-expect-error TS(2322): Type 'never[]' is not assignable to type 'QueryFor... Remove this comment to see the full error message
|
||||||
timeseries_limit_metric: [],
|
timeseries_limit_metric: [],
|
||||||
}),
|
}),
|
||||||
).toEqual([]);
|
).toEqual([]);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ describe('defineSavedMetrics', () => {
|
|||||||
uuid: '1',
|
uuid: '1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
// @ts-ignore
|
// @ts-expect-error TS(2322): Type 'undefined' is not assignable to type 'Metric... Remove this comment to see the full error message
|
||||||
expect(defineSavedMetrics({ ...dataset, metrics: undefined })).toEqual([]);
|
expect(defineSavedMetrics({ ...dataset, metrics: undefined })).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ describe('getColorFunction()', () => {
|
|||||||
it('getColorFunction unsupported operator', () => {
|
it('getColorFunction unsupported operator', () => {
|
||||||
const colorFunction = getColorFunction(
|
const colorFunction = getColorFunction(
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// @ts-expect-error TS(2322): Type '"unsupported operator"' is not assignable to... Remove this comment to see the full error message
|
||||||
operator: 'unsupported operator',
|
operator: 'unsupported operator',
|
||||||
targetValue: 50,
|
targetValue: 50,
|
||||||
colorScheme: '#FF0000',
|
colorScheme: '#FF0000',
|
||||||
|
|||||||
68
superset-frontend/packages/superset-ui-core/.eslintrc
Normal file
68
superset-frontend/packages/superset-ui-core/.eslintrc
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"plugins": ["jest", "jest-dom", "no-only-tests", "testing-library"],
|
||||||
|
"env": {
|
||||||
|
"jest/globals": true
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"jest": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:jest/recommended",
|
||||||
|
"plugin:jest-dom/recommended",
|
||||||
|
"plugin:testing-library/react"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"**/*.stories.*",
|
||||||
|
"**/*.overview.*",
|
||||||
|
"**/fixtures.*"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"import/no-extraneous-dependencies": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }],
|
||||||
|
"jest/consistent-test-it": "error",
|
||||||
|
"no-only-tests/no-only-tests": "error",
|
||||||
|
"prefer-promise-reject-errors": 0,
|
||||||
|
|
||||||
|
"testing-library/no-node-access": "off",
|
||||||
|
"testing-library/prefer-screen-queries": "off",
|
||||||
|
"testing-library/no-container": "off",
|
||||||
|
"testing-library/await-async-queries": "off",
|
||||||
|
"testing-library/await-async-utils": "off",
|
||||||
|
"testing-library/no-await-sync-events": "off",
|
||||||
|
"testing-library/no-render-in-lifecycle": "off",
|
||||||
|
"testing-library/no-unnecessary-act": "off",
|
||||||
|
"testing-library/no-wait-for-multiple-assertions": "off",
|
||||||
|
"testing-library/await-async-events": "off",
|
||||||
|
"testing-library/no-wait-for-side-effects": "off",
|
||||||
|
"testing-library/prefer-presence-queries": "off",
|
||||||
|
"testing-library/render-result-naming-convention": "off",
|
||||||
|
"testing-library/prefer-find-by": "off",
|
||||||
|
"testing-library/no-manual-cleanup": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { SVGProps, forwardRef } from 'react';
|
||||||
|
|
||||||
|
const SvgrMock = forwardRef<SVGSVGElement, SVGProps<SVGSVGElement>>(
|
||||||
|
(props, ref) => <svg ref={ref} {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
SvgrMock.displayName = 'SvgrMock';
|
||||||
|
|
||||||
|
export const ReactComponent = SvgrMock;
|
||||||
|
export default SvgrMock;
|
||||||
@@ -24,21 +24,37 @@
|
|||||||
"lib"
|
"lib"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.2.6",
|
||||||
"@babel/runtime": "^7.25.6",
|
"@babel/runtime": "^7.25.6",
|
||||||
|
"@fontsource/fira-code": "^5.0.18",
|
||||||
|
"@fontsource/inter": "^5.0.20",
|
||||||
"@types/json-bigint": "^1.0.4",
|
"@types/json-bigint": "^1.0.4",
|
||||||
|
"ace-builds": "^1.41.0",
|
||||||
|
"brace": "^0.11.1",
|
||||||
|
"classnames": "^2.2.5",
|
||||||
"csstype": "^3.1.3",
|
"csstype": "^3.1.3",
|
||||||
|
"core-js": "^3.38.1",
|
||||||
"d3-format": "^1.3.2",
|
"d3-format": "^1.3.2",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"d3-interpolate": "^3.0.1",
|
"d3-interpolate": "^3.0.1",
|
||||||
"d3-scale": "^3.0.0",
|
"d3-scale": "^3.0.0",
|
||||||
"d3-time": "^3.1.0",
|
"d3-time": "^3.1.0",
|
||||||
"d3-time-format": "^4.1.0",
|
"d3-time-format": "^4.1.0",
|
||||||
|
"dompurify": "^3.2.4",
|
||||||
"fetch-retry": "^6.0.0",
|
"fetch-retry": "^6.0.0",
|
||||||
"jed": "^1.1.1",
|
"jed": "^1.1.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"math-expression-evaluator": "^2.0.6",
|
"math-expression-evaluator": "^2.0.6",
|
||||||
"pretty-ms": "^9.2.0",
|
"pretty-ms": "^9.2.0",
|
||||||
|
"re-resizable": "^6.10.1",
|
||||||
|
"react-ace": "^10.1.0",
|
||||||
|
"react-js-cron": "^2.1.2",
|
||||||
|
"react-draggable": "^4.4.6",
|
||||||
|
"react-resize-detector": "^7.1.2",
|
||||||
|
"react-ultimate-pagination": "^1.3.2",
|
||||||
"react-error-boundary": "^5.0.0",
|
"react-error-boundary": "^5.0.0",
|
||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
|
"regenerator-runtime": "^0.14.1",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"rehype-sanitize": "^6.0.0",
|
"rehype-sanitize": "^6.0.0",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
@@ -56,8 +72,10 @@
|
|||||||
"@types/d3-scale": "^2.1.1",
|
"@types/d3-scale": "^2.1.1",
|
||||||
"@types/d3-time": "^3.0.4",
|
"@types/d3-time": "^3.0.4",
|
||||||
"@types/d3-time-format": "^4.0.3",
|
"@types/d3-time-format": "^4.0.3",
|
||||||
|
"@types/react-table": "^7.7.20",
|
||||||
"@types/enzyme": "^3.10.18",
|
"@types/enzyme": "^3.10.18",
|
||||||
"@types/fetch-mock": "^7.3.8",
|
"@types/fetch-mock": "^7.3.8",
|
||||||
|
"@types/jquery": "^3.5.8",
|
||||||
"@types/lodash": "^4.17.16",
|
"@types/lodash": "^4.17.16",
|
||||||
"@types/math-expression-evaluator": "^1.3.3",
|
"@types/math-expression-evaluator": "^1.3.3",
|
||||||
"@types/node": "^22.10.3",
|
"@types/node": "^22.10.3",
|
||||||
@@ -70,6 +88,7 @@
|
|||||||
"timezone-mock": "1.3.6"
|
"timezone-mock": "1.3.6"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"antd": "^5.24.6",
|
||||||
"@emotion/cache": "^11.4.0",
|
"@emotion/cache": "^11.4.0",
|
||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
"@emotion/styled": "^11.3.0",
|
"@emotion/styled": "^11.3.0",
|
||||||
@@ -80,6 +99,7 @@
|
|||||||
"@testing-library/user-event": "*",
|
"@testing-library/user-event": "*",
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-loadable": "*",
|
"@types/react-loadable": "*",
|
||||||
|
"@types/react-window": "^1.8.8",
|
||||||
"@types/tinycolor2": "*",
|
"@types/tinycolor2": "*",
|
||||||
"nanoid": "^5.0.9",
|
"nanoid": "^5.0.9",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
@@ -88,5 +108,27 @@
|
|||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./esm/index.js",
|
||||||
|
"require": "./lib/index.js",
|
||||||
|
"types": "./lib/index.d.ts"
|
||||||
|
},
|
||||||
|
"./components/*": {
|
||||||
|
"import": "./esm/components/*/index.js",
|
||||||
|
"require": "./lib/components/*/index.js",
|
||||||
|
"types": "./lib/components/*/index.d.ts"
|
||||||
|
},
|
||||||
|
"./components": {
|
||||||
|
"import": "./esm/components/index.js",
|
||||||
|
"require": "./lib/components/index.js",
|
||||||
|
"types": "./lib/components/index.d.ts"
|
||||||
|
},
|
||||||
|
"./utils/*": {
|
||||||
|
"import": "./esm/utils/*.js",
|
||||||
|
"require": "./lib/utils/*.js",
|
||||||
|
"types": "./lib/utils/*.d.ts"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
import { CSSProperties, ReactNode } from 'react';
|
||||||
import { CSSProperties, PureComponent, ReactNode } from 'react';
|
import { Table, type TableColumnsType } from 'antd';
|
||||||
|
|
||||||
interface TooltipRowData {
|
interface TooltipRowData {
|
||||||
key: string | number;
|
key: string | number;
|
||||||
@@ -27,43 +27,52 @@ interface TooltipRowData {
|
|||||||
valueStyle?: CSSProperties;
|
valueStyle?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps = {
|
interface TooltipTableProps {
|
||||||
className: '',
|
|
||||||
data: [] as TooltipRowData[],
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
className?: string;
|
className?: string;
|
||||||
data: TooltipRowData[];
|
data: TooltipRowData[];
|
||||||
} & Readonly<typeof defaultProps>;
|
}
|
||||||
|
|
||||||
const VALUE_CELL_STYLE: CSSProperties = { paddingLeft: 8, textAlign: 'right' };
|
const VALUE_CELL_STYLE: CSSProperties = { paddingLeft: 8, textAlign: 'right' };
|
||||||
|
|
||||||
export default class TooltipTable extends PureComponent<Props, {}> {
|
const TooltipTable = ({ className = '', data }: TooltipTableProps) => {
|
||||||
static defaultProps = defaultProps;
|
const columns: TableColumnsType<TooltipRowData> = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'keyColumn',
|
||||||
|
key: 'keyColumn',
|
||||||
|
render: (text, record) => (
|
||||||
|
<div style={record.keyStyle}>{record.keyColumn ?? record.key}</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: 'valueColumn',
|
||||||
|
key: 'valueColumn',
|
||||||
|
align: 'right',
|
||||||
|
render: (text, record) => (
|
||||||
|
<div
|
||||||
|
style={
|
||||||
|
record.valueStyle
|
||||||
|
? { ...VALUE_CELL_STYLE, ...record.valueStyle }
|
||||||
|
: VALUE_CELL_STYLE
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{record.valueColumn}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { className, data } = this.props;
|
<Table
|
||||||
|
className={className}
|
||||||
|
columns={columns}
|
||||||
|
dataSource={data}
|
||||||
|
pagination={false}
|
||||||
|
showHeader={false}
|
||||||
|
bordered={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
export default TooltipTable;
|
||||||
<table className={className}>
|
|
||||||
<tbody>
|
|
||||||
{data.map(({ key, keyColumn, keyStyle, valueColumn, valueStyle }) => (
|
|
||||||
<tr key={key}>
|
|
||||||
<td style={keyStyle}>{keyColumn ?? key}</td>
|
|
||||||
<td
|
|
||||||
style={
|
|
||||||
valueStyle
|
|
||||||
? { ...VALUE_CELL_STYLE, ...valueStyle }
|
|
||||||
: VALUE_CELL_STYLE
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{valueColumn}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { t } from '@superset-ui/core';
|
import { t } from '@superset-ui/core';
|
||||||
import { SupersetTheme } from '../../style';
|
import { SupersetTheme } from '../..';
|
||||||
import { FallbackPropsWithDimension } from './SuperChart';
|
import { FallbackPropsWithDimension } from './SuperChart';
|
||||||
|
|
||||||
export type Props = FallbackPropsWithDimension;
|
export type Props = FallbackPropsWithDimension;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CSSProperties } from 'react';
|
import { CSSProperties } from 'react';
|
||||||
import { css, styled } from '../../style';
|
import { css, styled } from '../../theme';
|
||||||
import { t } from '../../translation';
|
import { t } from '../../translation';
|
||||||
|
|
||||||
const MESSAGE_STYLES: CSSProperties = { maxWidth: 800 };
|
const MESSAGE_STYLES: CSSProperties = { maxWidth: 800 };
|
||||||
@@ -36,16 +36,16 @@ const Container = styled.div<{
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
height: ${height}px;
|
height: ${height}px;
|
||||||
width: ${width}px;
|
width: ${width}px;
|
||||||
padding: ${theme.gridUnit * 4}px;
|
padding: ${theme.sizeUnit * 4}px;
|
||||||
|
|
||||||
& .no-results-title {
|
& .no-results-title {
|
||||||
font-size: ${theme.typography.sizes.l}px;
|
font-size: ${theme.fontSizeLG}px;
|
||||||
font-weight: ${theme.typography.weights.bold};
|
font-weight: ${theme.fontWeightStrong};
|
||||||
padding-bottom: ${theme.gridUnit * 2};
|
padding-bottom: ${theme.sizeUnit * 2};
|
||||||
}
|
}
|
||||||
|
|
||||||
& .no-results-body {
|
& .no-results-body {
|
||||||
font-size: ${theme.typography.sizes.m}px;
|
font-size: ${theme.fontSize}px;
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax -- whole React import is required for `reactify.test.tsx` Jest test passing.
|
// eslint-disable-next-line no-restricted-syntax -- whole React import is required for `reactify.test.tsx` Jest test passing.
|
||||||
import React, { Component, ComponentClass, WeakValidationMap } from 'react';
|
import { Component, ComponentClass, WeakValidationMap } from 'react';
|
||||||
|
|
||||||
// TODO: Note that id and className can collide between Props and ReactifyProps
|
// TODO: Note that id and className can collide between Props and ReactifyProps
|
||||||
// leading to (likely) unexpected behaviors. We should either require Props to not
|
// leading to (likely) unexpected behaviors. We should either require Props to not
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
SetDataMaskHook,
|
SetDataMaskHook,
|
||||||
} from '../types/Base';
|
} from '../types/Base';
|
||||||
import { QueryData, DataRecordFilters } from '..';
|
import { QueryData, DataRecordFilters } from '..';
|
||||||
import { SupersetTheme } from '../../style';
|
import { SupersetTheme } from '../../theme';
|
||||||
|
|
||||||
// TODO: more specific typing for these fields of ChartProps
|
// TODO: more specific typing for these fields of ChartProps
|
||||||
type AnnotationData = PlainObject;
|
type AnnotationData = PlainObject;
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import Alert, { AlertProps } from './index';
|
import { Alert } from '.';
|
||||||
|
import type { AlertProps } from './types';
|
||||||
|
|
||||||
type AlertType = Required<Pick<AlertProps, 'type'>>;
|
type AlertType = Required<Pick<AlertProps, 'type'>>;
|
||||||
type AlertTypeValue = AlertType['type'];
|
type AlertTypeValue = AlertType['type'];
|
||||||
@@ -30,7 +31,7 @@ const bigText =
|
|||||||
'purus convallis placerat in at nunc. Nulla nec viverra augue.';
|
'purus convallis placerat in at nunc. Nulla nec viverra augue.';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Alert',
|
title: 'Components/Alert',
|
||||||
component: Alert,
|
component: Alert,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,15 +16,10 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { PropsWithChildren } from 'react';
|
import { Alert as AntdAlert } from 'antd';
|
||||||
import { Alert as AntdAlert } from 'antd-v5';
|
import type { AlertProps } from './types';
|
||||||
import { AlertProps as AntdAlertProps } from 'antd-v5/lib/alert';
|
|
||||||
|
|
||||||
export type AlertProps = PropsWithChildren<
|
export const Alert = (props: AlertProps) => {
|
||||||
Omit<AntdAlertProps, 'children'> & { roomBelow?: boolean }
|
|
||||||
>;
|
|
||||||
|
|
||||||
export default function Alert(props: AlertProps) {
|
|
||||||
const {
|
const {
|
||||||
type = 'info',
|
type = 'info',
|
||||||
description,
|
description,
|
||||||
@@ -46,4 +41,6 @@ export default function Alert(props: AlertProps) {
|
|||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export type { AlertProps };
|
||||||
@@ -16,12 +16,9 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import type { AlertProps as AntdAlertProps } from 'antd/es/alert';
|
||||||
|
|
||||||
import { Divider as AntdDivider } from 'antd-v5';
|
export type AlertProps = PropsWithChildren<
|
||||||
import type { DividerProps } from 'antd-v5/es/divider';
|
Omit<AntdAlertProps, 'children'> & { roomBelow?: boolean }
|
||||||
|
>;
|
||||||
export function Divider(props: DividerProps) {
|
|
||||||
return <AntdDivider {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { DividerProps };
|
|
||||||
@@ -16,8 +16,12 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { SwitchProps } from 'antd-v5/lib/switch';
|
|
||||||
import { Switch as AntdSwitch } from 'antd-v5';
|
|
||||||
|
|
||||||
export const Switch = (props: SwitchProps) => <AntdSwitch {...props} />;
|
import { ConfigProvider, type ConfigProviderProps } from 'antd';
|
||||||
export type { SwitchProps };
|
|
||||||
|
export const AntdThemeProvider = ({
|
||||||
|
children,
|
||||||
|
...rest
|
||||||
|
}: ConfigProviderProps) => (
|
||||||
|
<ConfigProvider {...rest}>{children}</ConfigProvider>
|
||||||
|
);
|
||||||
@@ -24,9 +24,10 @@ import {
|
|||||||
CssEditor,
|
CssEditor,
|
||||||
JsonEditor,
|
JsonEditor,
|
||||||
ConfigEditor,
|
ConfigEditor,
|
||||||
AsyncAceEditorOptions,
|
|
||||||
} from '.';
|
} from '.';
|
||||||
|
|
||||||
|
import type { AsyncAceEditorOptions } from './types';
|
||||||
|
|
||||||
type EditorType =
|
type EditorType =
|
||||||
| 'sql'
|
| 'sql'
|
||||||
| 'full-sql'
|
| 'full-sql'
|
||||||
@@ -47,7 +48,7 @@ const editorTypes: EditorType[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'AsyncAceEditor',
|
title: 'Components/AsyncAceEditor',
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseEditorType = (editorType: EditorType) => {
|
const parseEditorType = (editorType: EditorType) => {
|
||||||
@@ -16,8 +16,9 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
import { render, screen, waitFor } from '@superset-ui/core/spec';
|
||||||
import AsyncAceEditor, {
|
import {
|
||||||
|
AsyncAceEditor,
|
||||||
SQLEditor,
|
SQLEditor,
|
||||||
FullSQLEditor,
|
FullSQLEditor,
|
||||||
MarkdownEditor,
|
MarkdownEditor,
|
||||||
@@ -25,9 +26,9 @@ import AsyncAceEditor, {
|
|||||||
CssEditor,
|
CssEditor,
|
||||||
JsonEditor,
|
JsonEditor,
|
||||||
ConfigEditor,
|
ConfigEditor,
|
||||||
AceModule,
|
} from '.';
|
||||||
AsyncAceEditorOptions,
|
|
||||||
} from 'src/components/AsyncAceEditor';
|
import type { AceModule, AsyncAceEditorOptions } from './types';
|
||||||
|
|
||||||
const selector = '[id="ace-editor"]';
|
const selector = '[id="ace-editor"]';
|
||||||
|
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { forwardRef, useEffect, ComponentType } from 'react';
|
import { forwardRef, useEffect, useCallback, ComponentType } from 'react';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Editor as OrigEditor,
|
Editor as OrigEditor,
|
||||||
@@ -27,10 +27,10 @@ import type {
|
|||||||
import type AceEditor from 'react-ace';
|
import type AceEditor from 'react-ace';
|
||||||
import type { IAceEditorProps } from 'react-ace';
|
import type { IAceEditorProps } from 'react-ace';
|
||||||
|
|
||||||
import AsyncEsmComponent, {
|
import {
|
||||||
|
AsyncEsmComponent,
|
||||||
PlaceholderProps,
|
PlaceholderProps,
|
||||||
} from 'src/components/AsyncEsmComponent';
|
} from '@superset-ui/core/components/AsyncEsmComponent';
|
||||||
import useEffectEvent from 'src/hooks/useEffectEvent';
|
|
||||||
import { useTheme, css } from '@superset-ui/core';
|
import { useTheme, css } from '@superset-ui/core';
|
||||||
import { Global } from '@emotion/react';
|
import { Global } from '@emotion/react';
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ export interface AceCompleterKeyword extends AceCompleterKeywordData {
|
|||||||
* Async loaders to import brace modules. Must manually create call `import(...)`
|
* Async loaders to import brace modules. Must manually create call `import(...)`
|
||||||
* promises because webpack can only analyze async imports statically.
|
* promises because webpack can only analyze async imports statically.
|
||||||
*/
|
*/
|
||||||
const aceModuleLoaders = {
|
export const aceModuleLoaders = {
|
||||||
'mode/sql': () => import('brace/mode/sql'),
|
'mode/sql': () => import('brace/mode/sql'),
|
||||||
'mode/markdown': () => import('brace/mode/markdown'),
|
'mode/markdown': () => import('brace/mode/markdown'),
|
||||||
'mode/css': () => import('brace/mode/css'),
|
'mode/css': () => import('brace/mode/css'),
|
||||||
@@ -102,7 +102,7 @@ export type AsyncAceEditorOptions = {
|
|||||||
/**
|
/**
|
||||||
* Get an async AceEditor with automatical loading of specified ace modules.
|
* Get an async AceEditor with automatical loading of specified ace modules.
|
||||||
*/
|
*/
|
||||||
export default function AsyncAceEditor(
|
export function AsyncAceEditor(
|
||||||
aceModules: AceModule[],
|
aceModules: AceModule[],
|
||||||
{
|
{
|
||||||
defaultMode,
|
defaultMode,
|
||||||
@@ -155,9 +155,10 @@ export default function AsyncAceEditor(
|
|||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const supersetTheme = useTheme();
|
const token = useTheme();
|
||||||
const langTools = acequire('ace/ext/language_tools');
|
const langTools = acequire('ace/ext/language_tools');
|
||||||
const setCompleters = useEffectEvent(
|
|
||||||
|
const setCompleters = useCallback(
|
||||||
(keywords: AceCompleterKeyword[]) => {
|
(keywords: AceCompleterKeyword[]) => {
|
||||||
const completer = {
|
const completer = {
|
||||||
getCompletions: (
|
getCompletions: (
|
||||||
@@ -180,7 +181,9 @@ export default function AsyncAceEditor(
|
|||||||
};
|
};
|
||||||
langTools.setCompleters([completer]);
|
langTools.setCompleters([completer]);
|
||||||
},
|
},
|
||||||
|
[langTools, mode],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (keywords) {
|
if (keywords) {
|
||||||
setCompleters(keywords);
|
setCompleters(keywords);
|
||||||
@@ -192,36 +195,148 @@ export default function AsyncAceEditor(
|
|||||||
<Global
|
<Global
|
||||||
key="ace-tooltip-global"
|
key="ace-tooltip-global"
|
||||||
styles={css`
|
styles={css`
|
||||||
|
.ace_editor {
|
||||||
|
border: 1px solid ${token.colorBorder} !important;
|
||||||
|
background-color: ${token.colorBgContainer} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Basic editor styles with dark mode support */
|
||||||
|
.ace_editor.ace-github,
|
||||||
|
.ace_editor.ace-textmate {
|
||||||
|
background-color: ${token.colorBgContainer} !important;
|
||||||
|
color: ${token.colorText} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust gutter colors */
|
||||||
|
.ace_editor .ace_gutter {
|
||||||
|
background-color: ${token.colorBgElevated} !important;
|
||||||
|
color: ${token.colorTextSecondary} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust selection color */
|
||||||
|
.ace_editor .ace_selection {
|
||||||
|
background-color: ${token.colorPrimaryBgHover} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Improve active line highlighting */
|
||||||
|
.ace_editor .ace_active-line {
|
||||||
|
background-color: ${token.colorPrimaryBg} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix indent guides and print margin (80 chars line) */
|
||||||
|
.ace_editor .ace_indent-guide,
|
||||||
|
.ace_editor .ace_print-margin {
|
||||||
|
background-color: ${token.colorSplit} !important;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust cursor color */
|
||||||
|
.ace_editor .ace_cursor {
|
||||||
|
color: ${token.colorPrimaryText} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Syntax highlighting using semantic color tokens */
|
||||||
|
.ace_editor .ace_keyword {
|
||||||
|
color: ${token.colorPrimaryText} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_editor .ace_string {
|
||||||
|
color: ${token.colorSuccessText} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_editor .ace_constant {
|
||||||
|
color: ${token.colorWarningActive} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_editor .ace_function {
|
||||||
|
color: ${token.colorInfoText} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_editor .ace_comment {
|
||||||
|
color: ${token.colorTextTertiary} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_editor .ace_variable {
|
||||||
|
color: ${token.colorTextSecondary} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust tooltip styles */
|
||||||
.ace_tooltip {
|
.ace_tooltip {
|
||||||
all: unset;
|
margin-left: ${token.margin}px;
|
||||||
position: fixed;
|
padding: 0px;
|
||||||
z-index: 9999;
|
background-color: ${token.colorBgElevated} !important;
|
||||||
background: ${supersetTheme.colors.grayscale.light5};
|
color: ${token.colorText} !important;
|
||||||
border: 1px solid ${supersetTheme.colors.grayscale.light1};
|
border: 1px solid ${token.colorBorderSecondary};
|
||||||
padding: ${supersetTheme.gridUnit}px
|
box-shadow: ${token.boxShadow};
|
||||||
${supersetTheme.gridUnit * 2}px;
|
border-radius: ${token.borderRadius}px;
|
||||||
line-height: 1.4;
|
|
||||||
max-width: 400px;
|
|
||||||
min-width: 200px;
|
|
||||||
pointer-events: auto;
|
|
||||||
font-size: ${supersetTheme.typography.sizes.m}px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& .tooltip-detail {
|
& .tooltip-detail {
|
||||||
|
background-color: ${token.colorBgContainer};
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
min-width: ${token.sizeXXL * 5}px;
|
||||||
|
max-width: ${token.sizeXXL * 10}px;
|
||||||
|
|
||||||
|
& .tooltip-detail-head {
|
||||||
|
background-color: ${token.colorBgElevated};
|
||||||
|
color: ${token.colorText};
|
||||||
|
display: flex;
|
||||||
|
column-gap: ${token.padding}px;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
& .tooltip-detail-title {
|
& .tooltip-detail-title {
|
||||||
font-weight: bold;
|
display: flex;
|
||||||
font-size: ${supersetTheme.typography.sizes.m}px;
|
column-gap: ${token.padding}px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .tooltip-detail-body {
|
& .tooltip-detail-body {
|
||||||
font-size: ${supersetTheme.typography.sizes.s}px;
|
word-break: break-word;
|
||||||
padding: ${supersetTheme.gridUnit}px;
|
color: ${token.colorTextSecondary};
|
||||||
}
|
}
|
||||||
|
|
||||||
& .tooltip-detail-head,
|
& .tooltip-detail-head,
|
||||||
& .tooltip-detail-body {
|
& .tooltip-detail-body {
|
||||||
|
padding: ${token.padding}px ${token.paddingLG}px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .tooltip-detail-footer {
|
& .tooltip-detail-footer {
|
||||||
font-size: ${supersetTheme.typography.sizes.s}px;
|
border-top: 1px ${token.colorSplit} solid;
|
||||||
|
padding: 0 ${token.paddingLG}px;
|
||||||
|
color: ${token.colorTextTertiary};
|
||||||
|
font-size: ${token.fontSizeSM}px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .tooltip-detail-meta {
|
||||||
|
& > .ant-tag {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust the searchbox to match theme */
|
||||||
|
.ace_search {
|
||||||
|
background-color: ${token.colorBgContainer} !important;
|
||||||
|
color: ${token.colorText} !important;
|
||||||
|
border: 1px solid ${token.colorBorder} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_search_field {
|
||||||
|
background-color: ${token.colorBgContainer} !important;
|
||||||
|
color: ${token.colorText} !important;
|
||||||
|
border: 1px solid ${token.colorBorder} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_button {
|
||||||
|
background-color: ${token.colorBgElevated} !important;
|
||||||
|
color: ${token.colorText} !important;
|
||||||
|
border: 1px solid ${token.colorBorder} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_button:hover {
|
||||||
|
background-color: ${token.colorPrimaryBg} !important;
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import type { Editor as OrigEditor, TextMode as OrigTextMode } from 'brace';
|
||||||
|
import type { ComponentType } from 'react';
|
||||||
|
import type { IAceEditorProps } from 'react-ace';
|
||||||
|
import type { PlaceholderProps } from '../AsyncEsmComponent/types';
|
||||||
|
import { aceModuleLoaders } from '.';
|
||||||
|
|
||||||
|
export interface AceCompleterKeywordData {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
score: number;
|
||||||
|
meta: string;
|
||||||
|
docText?: string;
|
||||||
|
docHTML?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TextMode = OrigTextMode & { $id: string };
|
||||||
|
|
||||||
|
export interface AceCompleter {
|
||||||
|
insertMatch: (
|
||||||
|
data?: Editor | { value: string } | string,
|
||||||
|
options?: AceCompleterKeywordData,
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Editor = OrigEditor & {
|
||||||
|
completer: AceCompleter;
|
||||||
|
completers: AceCompleter[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface AceCompleterKeyword extends AceCompleterKeywordData {
|
||||||
|
completer?: AceCompleter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AceModule = keyof typeof aceModuleLoaders;
|
||||||
|
|
||||||
|
export type AsyncAceEditorProps = IAceEditorProps & {
|
||||||
|
keywords?: AceCompleterKeyword[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AceEditorMode = 'sql';
|
||||||
|
export type AceEditorTheme = 'textmate' | 'github';
|
||||||
|
export type AsyncAceEditorOptions = {
|
||||||
|
defaultMode?: AceEditorMode;
|
||||||
|
defaultTheme?: AceEditorTheme;
|
||||||
|
defaultTabSize?: number;
|
||||||
|
fontFamily?: string;
|
||||||
|
placeholder?: ComponentType<
|
||||||
|
PlaceholderProps & Partial<IAceEditorProps>
|
||||||
|
> | null;
|
||||||
|
};
|
||||||
@@ -16,10 +16,11 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import AsyncEsmComponent, { PlaceholderProps } from '.';
|
import { AsyncEsmComponent } from '.';
|
||||||
|
import type { PlaceholderProps } from './types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'AsyncEsmComponent',
|
title: 'Components/AsyncEsmComponent',
|
||||||
};
|
};
|
||||||
|
|
||||||
const Placeholder = () => <span>Loading...</span>;
|
const Placeholder = () => <span>Loading...</span>;
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { render, screen } from 'spec/helpers/testing-library';
|
import { render, screen } from '@superset-ui/core/spec';
|
||||||
import AsyncEsmComponent from 'src/components/AsyncEsmComponent';
|
import { AsyncEsmComponent } from '.';
|
||||||
|
|
||||||
const Placeholder = () => <span>Loading...</span>;
|
const Placeholder = () => <span>Loading...</span>;
|
||||||
|
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
CSSProperties,
|
|
||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
RefObject,
|
RefObject,
|
||||||
@@ -28,16 +27,8 @@ import {
|
|||||||
RefAttributes,
|
RefAttributes,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import Loading from '../Loading';
|
import { Loading } from '../Loading';
|
||||||
|
import type { PlaceholderProps } from './types';
|
||||||
export type PlaceholderProps = {
|
|
||||||
showLoadingForImport?: boolean;
|
|
||||||
width?: string | number;
|
|
||||||
height?: string | number;
|
|
||||||
placeholderStyle?: CSSProperties;
|
|
||||||
} & {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
function DefaultPlaceholder({
|
function DefaultPlaceholder({
|
||||||
width,
|
width,
|
||||||
@@ -62,7 +53,7 @@ function DefaultPlaceholder({
|
|||||||
* Asynchronously import an ES module as a React component, render a placeholder
|
* Asynchronously import an ES module as a React component, render a placeholder
|
||||||
* first (if provided) and re-render once import is complete.
|
* first (if provided) and re-render once import is complete.
|
||||||
*/
|
*/
|
||||||
export default function AsyncEsmComponent<
|
export function AsyncEsmComponent<
|
||||||
P = PlaceholderProps,
|
P = PlaceholderProps,
|
||||||
M = ComponentType<P> | { default: ComponentType<P> },
|
M = ComponentType<P> | { default: ComponentType<P> },
|
||||||
>(
|
>(
|
||||||
@@ -128,6 +119,7 @@ export default function AsyncEsmComponent<
|
|||||||
const Component = component || placeholder;
|
const Component = component || placeholder;
|
||||||
return Component ? (
|
return Component ? (
|
||||||
// placeholder does not get the ref
|
// placeholder does not get the ref
|
||||||
|
// @ts-expect-error TS(2322): Type '{ ref: RefObject<ComponentType<FullProps>> |... Remove this comment to see the full error message
|
||||||
<Component ref={Component === component ? ref : null} {...props} />
|
<Component ref={Component === component ? ref : null} {...props} />
|
||||||
) : null;
|
) : null;
|
||||||
});
|
});
|
||||||
@@ -138,3 +130,5 @@ export default function AsyncEsmComponent<
|
|||||||
preload: typeof waitForPromise;
|
preload: typeof waitForPromise;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type { PlaceholderProps };
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import type { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
export type PlaceholderProps = {
|
||||||
|
showLoadingForImport?: boolean;
|
||||||
|
width?: string | number;
|
||||||
|
height?: string | number;
|
||||||
|
placeholderStyle?: CSSProperties;
|
||||||
|
} & {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import { useState } from 'react';
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { AutoComplete } from '.';
|
||||||
|
import type { AutoCompleteProps } from './types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/AutoComplete',
|
||||||
|
component: AutoComplete,
|
||||||
|
argTypes: {
|
||||||
|
style: {
|
||||||
|
control: 'object',
|
||||||
|
description: 'Custom styles for AutoComplete',
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'Placeholder text for AutoComplete',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'Selected option',
|
||||||
|
table: {
|
||||||
|
type: { summary: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Disable the AutoComplete',
|
||||||
|
defaultValue: false,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'false' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
popupMatchSelectWidth: {
|
||||||
|
control: 'number',
|
||||||
|
description: 'Width of the dropdown',
|
||||||
|
defaultValue: 252,
|
||||||
|
},
|
||||||
|
allowClear: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Show clear button',
|
||||||
|
defaultValue: false,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'false' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
autoFocus: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'If get focus when component mounted',
|
||||||
|
defaultValue: false,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'false' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
backfill: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'If backfill selected item the input when using keyboard',
|
||||||
|
defaultValue: false,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'false' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
popupClassName: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'The className of dropdown menu',
|
||||||
|
},
|
||||||
|
filterOption: {
|
||||||
|
control: 'boolean',
|
||||||
|
description:
|
||||||
|
'If true, filters options by input. If a function, filters options using `inputValue` and `option`. Returns true to include the option, false to exclude it.',
|
||||||
|
defaultValue: true,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean | function(inputValue, option)' },
|
||||||
|
defaultValue: { summary: 'true' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notFoundContent: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'Specify content to show when no result matches.',
|
||||||
|
defaultValue: undefined,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'ReactNode' },
|
||||||
|
defaultValue: { summary: '-' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
open: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Controlled open state of dropdown',
|
||||||
|
defaultValue: undefined,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
control: 'select',
|
||||||
|
options: [undefined, 'error', 'warning'],
|
||||||
|
description: 'Set validation status',
|
||||||
|
defaultValue: undefined,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: 'select',
|
||||||
|
options: [undefined, 'large', 'middle', 'small'],
|
||||||
|
description: 'The size of the input box',
|
||||||
|
defaultValue: undefined,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
control: 'select',
|
||||||
|
options: ['outlined', 'borderless', 'filled'],
|
||||||
|
description: 'Variants of input',
|
||||||
|
defaultValue: 'outlined',
|
||||||
|
},
|
||||||
|
virtual: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Disable virtual scroll when set to false',
|
||||||
|
defaultValue: true,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'true' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// reference of additional props
|
||||||
|
defaultValue: {
|
||||||
|
control: false,
|
||||||
|
description: 'Initial selected option from the `options` array.',
|
||||||
|
table: {
|
||||||
|
type: { summary: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultOpen: {
|
||||||
|
control: false,
|
||||||
|
description: 'Initial open state of dropdown',
|
||||||
|
defaultValue: undefined,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'false' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultActiveFirstOption: {
|
||||||
|
control: false,
|
||||||
|
description: 'Whether active first option by default',
|
||||||
|
defaultValue: undefined,
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'false' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dropdownRender: {
|
||||||
|
control: false,
|
||||||
|
description:
|
||||||
|
'Custom render function for dropdown content. `(menus: ReactNode) => ReactNode`',
|
||||||
|
table: {
|
||||||
|
type: { summary: '(menus: ReactNode) => ReactNode' },
|
||||||
|
defaultValue: { summary: '-' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
control: false,
|
||||||
|
description:
|
||||||
|
'Select options. Will get better performance than using JSX elements.',
|
||||||
|
table: {
|
||||||
|
type: { summary: '{ label: string, value: string }[]' },
|
||||||
|
defaultValue: { summary: '-' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
control: false,
|
||||||
|
description:
|
||||||
|
'Can be used in two ways:\n' +
|
||||||
|
'1. Customize input element (e.g., `<Input />`, `<TextArea />`).\n' +
|
||||||
|
'2. Provide data source for auto-complete (`React.ReactElement<OptionProps>` or an array of such elements).',
|
||||||
|
table: {
|
||||||
|
type: {
|
||||||
|
summary:
|
||||||
|
'React.ReactElement<InputProps> | React.ReactElement<OptionProps> | React.ReactElement<OptionProps>[]',
|
||||||
|
},
|
||||||
|
defaultValue: { summary: '-' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getPopupContainer: {
|
||||||
|
control: false,
|
||||||
|
description:
|
||||||
|
'Parent node of the dropdown. Defaults to `body`. If you encounter positioning issues during scrolling, try setting it to the scrollable area and positioning it relative to that.',
|
||||||
|
defaultValue: () => document.body,
|
||||||
|
table: {
|
||||||
|
type: { summary: '(triggerNode) => HTMLElement' },
|
||||||
|
defaultValue: { summary: '() => document.body' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: 'AutoComplete component for search functionality.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Meta<typeof AutoComplete>;
|
||||||
|
|
||||||
|
const getRandomInt = (max: number, min = 0) =>
|
||||||
|
Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
|
||||||
|
const searchResult = (query: string) =>
|
||||||
|
Array.from({ length: getRandomInt(5) }).map((_, idx) => {
|
||||||
|
const category = `${query}${idx}`;
|
||||||
|
return {
|
||||||
|
value: category,
|
||||||
|
label: (
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<span>
|
||||||
|
Found {query} on{' '}
|
||||||
|
<a
|
||||||
|
href={`https://github.com/apache/superset?q=${query}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span>{getRandomInt(200, 100)} results</span>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const AutoCompleteWithOptions = (args: AutoCompleteProps) => {
|
||||||
|
const [options, setOptions] = useState<AutoCompleteProps['options']>([]);
|
||||||
|
|
||||||
|
const handleSearch = (value: string) => {
|
||||||
|
setOptions(value ? searchResult(value) : []);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <AutoComplete {...args} options={options} onSearch={handleSearch} />;
|
||||||
|
};
|
||||||
|
type Story = StoryObj<typeof AutoComplete>;
|
||||||
|
|
||||||
|
export const AutoCompleteStory: Story = {
|
||||||
|
args: {
|
||||||
|
style: { width: 300 },
|
||||||
|
placeholder: 'Type to search...',
|
||||||
|
},
|
||||||
|
render: (args: AutoCompleteProps) => (
|
||||||
|
<div style={{ margin: '20px' }}>
|
||||||
|
<AutoCompleteWithOptions {...args} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { render, screen, userEvent, waitFor } from '@superset-ui/core/spec';
|
||||||
|
import { Input } from '../Input';
|
||||||
|
import { AutoComplete } from '.';
|
||||||
|
|
||||||
|
const searchResult = (query: string): Array<{ value: string; label: string }> =>
|
||||||
|
Array.from({ length: 3 }).map((_, idx) => ({
|
||||||
|
value: `${query}${idx}`,
|
||||||
|
label: `${query} result ${idx}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const AutoCompleteTest = () => {
|
||||||
|
const [options, setOptions] = useState<{ value: string; label: string }[]>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearch = (value: string) => {
|
||||||
|
setOptions(value ? searchResult(value) : []);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AutoComplete options={options} onSearch={handleSearch}>
|
||||||
|
<Input placeholder="Type to search..." />
|
||||||
|
</AutoComplete>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('AutoComplete Component', () => {
|
||||||
|
it('renders input field', () => {
|
||||||
|
render(<AutoCompleteTest />);
|
||||||
|
expect(
|
||||||
|
screen.getByPlaceholderText('Type to search...'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows options when user types', async () => {
|
||||||
|
render(<AutoCompleteTest />);
|
||||||
|
const input = screen.getByPlaceholderText('Type to search...');
|
||||||
|
await userEvent.type(input, 'test');
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('test result 0')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('test result 1')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('test result 2')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('selecting an option updates input value', async () => {
|
||||||
|
render(<AutoCompleteTest />);
|
||||||
|
const input = screen.getByPlaceholderText('Type to search...');
|
||||||
|
await userEvent.type(input, 'test');
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('test result 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByText('test result 0'));
|
||||||
|
expect(input).toHaveValue('test0');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
export { AutoComplete } from 'antd';
|
||||||
|
export type { AutoCompleteProps } from './types';
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
export type { AutoCompleteProps } from 'antd/es/auto-complete';
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user