mirror of
https://github.com/apache/superset.git
synced 2026-05-05 07:54:17 +00:00
Compare commits
27 Commits
fix/check-
...
react18-ti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65aea59df1 | ||
|
|
4bb6c8e932 | ||
|
|
41a22d7918 | ||
|
|
28239c18d4 | ||
|
|
6205afbaa0 | ||
|
|
dc1c0f6ba1 | ||
|
|
ad73395c89 | ||
|
|
867e173427 | ||
|
|
c90c8612ad | ||
|
|
b14cca15f6 | ||
|
|
9d4384e49e | ||
|
|
d8dd2d99b3 | ||
|
|
dbe26d81ce | ||
|
|
98eaaaa6d6 | ||
|
|
cb74438865 | ||
|
|
e77fb5e3fc | ||
|
|
1ac113fd44 | ||
|
|
6bfdee98cd | ||
|
|
de45f3a928 | ||
|
|
2ec53c0694 | ||
|
|
d23b0cad92 | ||
|
|
e585406fff | ||
|
|
957b298ae1 | ||
|
|
f29d82b3b1 | ||
|
|
3f550f166f | ||
|
|
86eb6176d1 | ||
|
|
4244ae87bf |
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -36,6 +36,10 @@
|
||||
**/*.geojson @villebro @rusackas
|
||||
/superset-frontend/plugins/legacy-plugin-chart-country-map/ @villebro @rusackas
|
||||
|
||||
# Notify translation maintainers of changes to translations
|
||||
|
||||
/superset/translations/ @sfirke
|
||||
|
||||
# Notify PMC members of changes to extension-related files
|
||||
|
||||
/docs/developer_portal/extensions/ @michael-s-molina @villebro @rusackas
|
||||
|
||||
4
.github/workflows/ephemeral-env.yml
vendored
4
.github/workflows/ephemeral-env.yml
vendored
@@ -265,7 +265,7 @@ jobs:
|
||||
|
||||
- name: Fill in the new image ID in the Amazon ECS task definition
|
||||
id: task-def
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@77954e213ba1f9f9cb016b86a1d4f6fcdea0d57e # v1
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@6853cfae8c3a7d978fbf68b5a55453395541dfbb # v1
|
||||
with:
|
||||
task-definition: .github/workflows/ecs-task-definition.json
|
||||
container-name: superset-ci
|
||||
@@ -300,7 +300,7 @@ jobs:
|
||||
--tags key=pr,value=$PR_NUMBER key=github_user,value=${{ github.actor }}
|
||||
- name: Deploy Amazon ECS task definition
|
||||
id: deploy-task
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@fc8fc60f3a60ffd500fcb13b209c59d221ac8c8c # v2
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@a310a830f5c14e583e35d84e4e1ec7dd177c3c9c # v2
|
||||
with:
|
||||
task-definition: ${{ steps.task-def.outputs.task-definition }}
|
||||
service: pr-${{ github.event.inputs.issue_number || github.event.pull_request.number }}-service
|
||||
|
||||
143
docs/docs/using-superset/handlebars-chart.mdx
Normal file
143
docs/docs/using-superset/handlebars-chart.mdx
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
title: Handlebars Chart
|
||||
hide_title: true
|
||||
sidebar_position: 10
|
||||
version: 1
|
||||
---
|
||||
|
||||
## Handlebars Chart
|
||||
|
||||
The Handlebars chart lets you render query results using a custom [Handlebars](https://handlebarsjs.com/) template. This gives you full control over how your data is displayed — from simple tables to rich HTML layouts.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
In the chart editor, write a Handlebars template in the **Template** field. Your query results are available as `data`, an array of row objects.
|
||||
|
||||
```handlebars
|
||||
{{#each data}}
|
||||
<p>{{this.name}}: {{this.value}}</p>
|
||||
{{/each}}
|
||||
```
|
||||
|
||||
### Built-in Helpers
|
||||
|
||||
Superset registers several custom helpers on top of the standard Handlebars built-ins.
|
||||
|
||||
#### `dateFormat`
|
||||
|
||||
Formats a date value using [Day.js](https://day.js.org/) format strings.
|
||||
|
||||
```handlebars
|
||||
{{dateFormat my_date format="MMMM YYYY"}}
|
||||
```
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `format` | `YYYY-MM-DD` | A Day.js-compatible format string |
|
||||
|
||||
---
|
||||
|
||||
#### `stringify`
|
||||
|
||||
Converts an object to a JSON string, or any other value to its string representation.
|
||||
|
||||
```handlebars
|
||||
{{stringify myObj}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### `formatNumber`
|
||||
|
||||
Formats a number using locale-aware formatting.
|
||||
|
||||
```handlebars
|
||||
{{formatNumber myNumber "en-US"}}
|
||||
```
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `locale` | `en-US` | A BCP 47 language tag |
|
||||
|
||||
---
|
||||
|
||||
#### `parseJson`
|
||||
|
||||
Parses a JSON string into an object that can be used in your template.
|
||||
|
||||
```handlebars
|
||||
{{parseJson myJsonString}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### `groupBy`
|
||||
|
||||
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by).
|
||||
|
||||
```handlebars
|
||||
{{#groupBy data "department"}}
|
||||
<h3>{{value}}</h3>
|
||||
{{#each items}}
|
||||
<p>{{this.name}}</p>
|
||||
{{/each}}
|
||||
{{/groupBy}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Helpers from just-handlebars-helpers
|
||||
|
||||
Superset also registers all helpers from the [just-handlebars-helpers](https://github.com/leapfrogtechnology/just-handlebars-helpers) library. These include a wide range of comparison, math, string, and conditional helpers. Commonly used ones include:
|
||||
|
||||
#### Comparison
|
||||
|
||||
| Helper | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `eq` | Strict equality | `{{#if (eq status "active")}}` |
|
||||
| `eqw` | Weak equality | `{{#if (eqw count "5")}}` |
|
||||
| `neq` | Strict inequality | `{{#if (neq role "admin")}}` |
|
||||
| `lt` | Less than | `{{#if (lt score 50)}}` |
|
||||
| `lte` | Less than or equal | `{{#if (lte score 100)}}` |
|
||||
| `gt` | Greater than | `{{#if (gt price 0)}}` |
|
||||
| `gte` | Greater than or equal | `{{#if (gte age 18)}}` |
|
||||
|
||||
#### Logical
|
||||
|
||||
| Helper | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `and` | Logical AND | `{{#if (and isActive isVerified)}}` |
|
||||
| `or` | Logical OR | `{{#if (or isAdmin isMod)}}` |
|
||||
| `not` | Logical NOT | `{{#if (not isDisabled)}}` |
|
||||
| `ifx` | Inline conditional | `{{ifx isActive "Yes" "No"}}` |
|
||||
| `coalesce` | Returns first non-falsy value | `{{coalesce nickname name "Anonymous"}}` |
|
||||
|
||||
#### String
|
||||
|
||||
| Helper | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `capitalize` | Capitalizes first letter | `{{capitalize name}}` |
|
||||
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
|
||||
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
|
||||
| `truncate` | Truncates a string | `{{truncate description 100}}` |
|
||||
| `contains` | Checks if string contains substring | `{{#if (contains tag "urgent")}}` |
|
||||
|
||||
#### Math
|
||||
|
||||
| Helper | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `add` | Addition | `{{add a b}}` |
|
||||
| `subtract` | Subtraction | `{{subtract total discount}}` |
|
||||
| `multiply` | Multiplication | `{{multiply price quantity}}` |
|
||||
| `divide` | Division | `{{divide total count}}` |
|
||||
| `ceil` | Ceiling | `{{ceil value}}` |
|
||||
| `floor` | Floor | `{{floor value}}` |
|
||||
| `round` | Round | `{{round value}}` |
|
||||
|
||||
For the full list of available helpers, see the [just-handlebars-helpers documentation](https://github.com/leapfrogtechnology/just-handlebars-helpers).
|
||||
|
||||
### Tips
|
||||
|
||||
- Use raw blocks to escape Handlebars syntax if you need to display double curly braces literally.
|
||||
- Comparison helpers like `eq` must be wrapped in a subexpression when used with `#if`: `{{#if (eq myVal "foo")}}`.
|
||||
- HTML output is sanitized by default based on your Superset configuration (`HTML_SANITIZATION`).
|
||||
@@ -41,12 +41,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^6.2.2",
|
||||
"@docusaurus/core": "^3.10.0",
|
||||
"@docusaurus/faster": "^3.10.0",
|
||||
"@docusaurus/plugin-client-redirects": "^3.10.0",
|
||||
"@docusaurus/preset-classic": "3.10.0",
|
||||
"@docusaurus/theme-live-codeblock": "^3.10.0",
|
||||
"@docusaurus/theme-mermaid": "^3.10.0",
|
||||
"@docusaurus/core": "^3.10.1",
|
||||
"@docusaurus/faster": "^3.10.1",
|
||||
"@docusaurus/plugin-client-redirects": "^3.10.1",
|
||||
"@docusaurus/preset-classic": "3.10.1",
|
||||
"@docusaurus/theme-live-codeblock": "^3.10.1",
|
||||
"@docusaurus/theme-mermaid": "^3.10.1",
|
||||
"@emotion/core": "^11.0.0",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
@@ -92,8 +92,8 @@
|
||||
"unist-util-visit": "^5.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.10.0",
|
||||
"@docusaurus/tsconfig": "^3.10.0",
|
||||
"@docusaurus/module-type-aliases": "^3.10.1",
|
||||
"@docusaurus/tsconfig": "^3.10.1",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/react": "^19.1.8",
|
||||
@@ -124,8 +124,7 @@
|
||||
"resolutions": {
|
||||
"react-redux": "^9.2.0",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"webpackbar": "^7.0.0"
|
||||
"baseline-browser-mapping": "^2.9.19"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
|
||||
}
|
||||
|
||||
508
docs/yarn.lock
508
docs/yarn.lock
@@ -1570,10 +1570,10 @@
|
||||
"@docsearch/core" "4.6.2"
|
||||
"@docsearch/css" "4.6.2"
|
||||
|
||||
"@docusaurus/babel@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.10.0.tgz#819819f107233dfcf50b59cd51158f23fb04878a"
|
||||
integrity sha512-mqCJhCZNZUDg0zgDEaPTM4DnRsisa24HdqTy/qn/MQlbwhTb4WVaZg6ZyX6yIVKqTz8fS1hBMgM+98z+BeJJDg==
|
||||
"@docusaurus/babel@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.10.1.tgz#2f714f682117658ba43d308e9b35b6a73a105227"
|
||||
integrity sha512-DZzFO1K3v/GoEt1fx1DiYHF4en+PuhtQf1AkQJa5zu3CoeKSpr5cpQRUlz3jr0m44wyzmSXu9bVpfir+N4+8bg==
|
||||
dependencies:
|
||||
"@babel/core" "^7.25.9"
|
||||
"@babel/generator" "^7.25.9"
|
||||
@@ -1584,23 +1584,23 @@
|
||||
"@babel/preset-typescript" "^7.25.9"
|
||||
"@babel/runtime" "^7.25.9"
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
fs-extra "^11.1.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/bundler@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.10.0.tgz#878c4c46bfa3434671ea37a43da184238a6aae26"
|
||||
integrity sha512-iONUGZGgp+lAkw/cJZH6irONcF4p8+278IsdRlq8lYhxGjkoNUs0w7F4gVXBYSNChq5KG5/JleTSsdJySShxow==
|
||||
"@docusaurus/bundler@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.10.1.tgz#82fa5079f3787a67502e25f82d37d05ec5de0cc3"
|
||||
integrity sha512-HIqQPvbqnnQRe4NsBd1774KRarjXqS6wHsWELtyuSs1gCfvixJO2jUGH/OEBtr1Gvzpw+ze5CjGMvSJ8UE1KUw==
|
||||
dependencies:
|
||||
"@babel/core" "^7.25.9"
|
||||
"@docusaurus/babel" "3.10.0"
|
||||
"@docusaurus/cssnano-preset" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/babel" "3.10.1"
|
||||
"@docusaurus/cssnano-preset" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
babel-loader "^9.2.1"
|
||||
clean-css "^5.3.3"
|
||||
copy-webpack-plugin "^11.0.0"
|
||||
@@ -1618,20 +1618,20 @@
|
||||
tslib "^2.6.0"
|
||||
url-loader "^4.1.1"
|
||||
webpack "^5.95.0"
|
||||
webpackbar "^6.0.1"
|
||||
webpackbar "^7.0.0"
|
||||
|
||||
"@docusaurus/core@3.10.0", "@docusaurus/core@^3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.10.0.tgz#642e71a0209d62c3f5ef275ed9d74a881f40df39"
|
||||
integrity sha512-mgLdQsO8xppnQZc3LPi+Mf+PkPeyxJeIx11AXAq/14fsaMefInQiMEZUUmrc7J+956G/f7MwE7tn8KZgi3iRcA==
|
||||
"@docusaurus/core@3.10.1", "@docusaurus/core@^3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.10.1.tgz#3f8bdb97451b4df14f2a3b39ab0186366fbf8fbe"
|
||||
integrity sha512-3pf2fXXw0eVk8WnC3T4LIigRDupcpvngpKo9Vy7mYyBhuddc0klDUuZAIfzMoK6z05pdlk6EFC/vBSX43+1O5w==
|
||||
dependencies:
|
||||
"@docusaurus/babel" "3.10.0"
|
||||
"@docusaurus/bundler" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/mdx-loader" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/babel" "3.10.1"
|
||||
"@docusaurus/bundler" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/mdx-loader" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
boxen "^6.2.1"
|
||||
chalk "^4.1.2"
|
||||
chokidar "^3.5.3"
|
||||
@@ -1668,22 +1668,22 @@
|
||||
webpack-dev-server "^5.2.2"
|
||||
webpack-merge "^6.0.1"
|
||||
|
||||
"@docusaurus/cssnano-preset@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.10.0.tgz#be1b435c33df09d743473d3fadda67b4568dfae3"
|
||||
integrity sha512-qzSshTO1DB3TYW+dPUal5KHM7XPc5YQfzF3Kdb2NDACJUyGbNcFtw3tGkCJlYwhNCRKbZcmwraKUS1i5dcHdGg==
|
||||
"@docusaurus/cssnano-preset@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.10.1.tgz#4b6bafeca8bb9423364d2fd6683c28e2f85a4665"
|
||||
integrity sha512-eNfHGcTKCSq6xmcavAkX3RRclHaE2xRCMParlDXLdXVP01/a2e/jKXMj/0ULnLFQSNwwuI62L0Ge8J+nZsR7UQ==
|
||||
dependencies:
|
||||
cssnano-preset-advanced "^6.1.2"
|
||||
postcss "^8.5.4"
|
||||
postcss-sort-media-queries "^5.2.0"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/faster@^3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/faster/-/faster-3.10.0.tgz#0758a93196f685537aa7700bde62faf926e6c817"
|
||||
integrity sha512-GNPtVH14ISjHfSwnHu3KiFGf86ICmJSQDeSv/QaanpBgiZGOtgZaslnC5q8WiguxM1EVkwcGxPuD8BXF4eggKw==
|
||||
"@docusaurus/faster@^3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/faster/-/faster-3.10.1.tgz#a63d89ae980c98e1eeab3ff15ee083f7c20ed353"
|
||||
integrity sha512-XTZhE5C1gZ/DaYYMlSk02dwP5vhpQON5QHVz1s3892mSESAywgWanURpXEDAvt4GvGuq7s+XP8rTWHZvfaJmdQ==
|
||||
dependencies:
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@rspack/core" "^1.7.10"
|
||||
"@swc/core" "^1.7.39"
|
||||
"@swc/html" "^1.13.5"
|
||||
@@ -1694,22 +1694,22 @@
|
||||
tslib "^2.6.0"
|
||||
webpack "^5.95.0"
|
||||
|
||||
"@docusaurus/logger@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.10.0.tgz#2bacbd004dd78e3da926dbe8f6fa9a930856575d"
|
||||
integrity sha512-9jrZzFuBH1LDRlZ7cznAhCLmAZ3HSDqgwdrSSZdGHq9SPUOQgXXu8mnxe2ZRB9NS1PCpMTIOVUqDtZPIhMafZg==
|
||||
"@docusaurus/logger@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.10.1.tgz#34c964e32e18f120e30f80171a38cfefe72cfb4b"
|
||||
integrity sha512-oPjNFnfJsRCkePVjkGrxWGq4MvJKRQT0r9jOP0eRBTZ7Wr9FAbzdP/Gjs0I2Ss6YRkPoEgygKG112OkE6skvJw==
|
||||
dependencies:
|
||||
chalk "^4.1.2"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/mdx-loader@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.10.0.tgz#1d4b050d751389ecf38dee48bcb61e53df8ffb82"
|
||||
integrity sha512-mQQV97080AH4PYNs087l202NMDqRopZA4mg5W76ZZyTFrmWhJ3mHg+8A+drJVENxw5/Q+wHMHLgsx+9z1nEs0A==
|
||||
"@docusaurus/mdx-loader@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.10.1.tgz#050ae9bc614158a4ec07a628aa75fa9ae90d7e82"
|
||||
integrity sha512-GRmeb/wQ+iXRrFwcHBfgQhrJxGElgCsoTWZYDhccjsZVne1p8MK/EpQVIloXttz76TCe78kKD5AEG9n1xc1oxQ==
|
||||
dependencies:
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
"@mdx-js/mdx" "^3.0.0"
|
||||
"@slorber/remark-comment" "^1.0.0"
|
||||
escape-html "^1.0.3"
|
||||
@@ -1732,12 +1732,12 @@
|
||||
vfile "^6.0.1"
|
||||
webpack "^5.88.1"
|
||||
|
||||
"@docusaurus/module-type-aliases@3.10.0", "@docusaurus/module-type-aliases@^3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.10.0.tgz#749928f104d563f11f046bf0c9ab6489a470c7c8"
|
||||
integrity sha512-/1O0Zg8w3DFrYX/I6Fbss7OJrtZw1QoyjDhegiFNHVi9A9Y0gQ3jUAytVxF6ywpAWpLyLxch8nN8H/V3XfzdJQ==
|
||||
"@docusaurus/module-type-aliases@3.10.1", "@docusaurus/module-type-aliases@^3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.10.1.tgz#22d39177c296786eb6e0d940699cd590cc93ca77"
|
||||
integrity sha512-YoOZKUdGlp8xSYhuAkGdSo5Ydkbq4V4eK3sD8v0a2hloxCWdQbNBhkc+Ko9QyjpESc0BYcIGM5iHVAy5hdFV6w==
|
||||
dependencies:
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
"@types/react-router-config" "*"
|
||||
@@ -1745,34 +1745,34 @@
|
||||
react-helmet-async "npm:@slorber/react-helmet-async@1.3.0"
|
||||
react-loadable "npm:@docusaurus/react-loadable@6.0.0"
|
||||
|
||||
"@docusaurus/plugin-client-redirects@^3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.10.0.tgz#4dd4619817fd69462d1e6d986580343aeb911111"
|
||||
integrity sha512-P+VLoLoZTc74so8+IbsaPZ33/mkf2BWL1CYXQpPRkl0v1QVCN2CgfsZY/8QtbYjQnx2upXUnv45abDhNcSggNw==
|
||||
"@docusaurus/plugin-client-redirects@^3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.10.1.tgz#e22ed20e5837b7c3a28258e3d1816c4239c82b36"
|
||||
integrity sha512-LHgd+YDvkhfOHMAE6XtUng3DQNzVM765RqVRrMJgHtzAvfopQhY6ieprqjxDVBdv21cLma6I0jHr+YCZH8fL9A==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
eta "^2.2.0"
|
||||
fs-extra "^11.1.1"
|
||||
lodash "^4.17.21"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/plugin-content-blog@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.10.0.tgz#10095291b637440847854ecb2c8afcd8746debd7"
|
||||
integrity sha512-RuTz68DhB7CL96QO5UsFbciD7GPYq6QV+YMfF9V0+N4ZgLhJIBgpVAr8GobrKF6NRe5cyWWETU5z5T834piG9g==
|
||||
"@docusaurus/plugin-content-blog@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.10.1.tgz#0bd8de700ccbd8e95d920df2613304ef59abe72b"
|
||||
integrity sha512-mmkgE6Q2+K74tnkou7tXlpDLvoCU/qkSa2GSQ3XUiHWvcebCoDQzS670RR3tO8PmaWlIyWWISYWzZLuMfxunRA==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/mdx-loader" "3.10.0"
|
||||
"@docusaurus/theme-common" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/mdx-loader" "3.10.1"
|
||||
"@docusaurus/theme-common" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
cheerio "1.0.0-rc.12"
|
||||
combine-promises "^1.1.0"
|
||||
feed "^4.2.2"
|
||||
@@ -1785,20 +1785,20 @@
|
||||
utility-types "^3.10.0"
|
||||
webpack "^5.88.1"
|
||||
|
||||
"@docusaurus/plugin-content-docs@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.0.tgz#9c4ea1d5a405340f28c281d2e4586c695a7c65a5"
|
||||
integrity sha512-9BjHhf15ct8Z7TThTC0xRndKDVvMKmVsAGAN7W9FpNRzfMdScOGcXtLmcCWtJGvAezjOJIm6CxOYCy3Io5+RnQ==
|
||||
"@docusaurus/plugin-content-docs@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.1.tgz#261e0e982e4a937c05b462e3c5729374f433b752"
|
||||
integrity sha512-2jRVrtzjf8LClGTHQlwlwuD3wQXRx3WEoF7XUarJ8Ou+0onV+SLtejsyfY9JLpfUh9hPhXM4pbBGkyAY4Bi3HQ==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/mdx-loader" "3.10.0"
|
||||
"@docusaurus/module-type-aliases" "3.10.0"
|
||||
"@docusaurus/theme-common" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/mdx-loader" "3.10.1"
|
||||
"@docusaurus/module-type-aliases" "3.10.1"
|
||||
"@docusaurus/theme-common" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
"@types/react-router-config" "^5.0.7"
|
||||
combine-promises "^1.1.0"
|
||||
fs-extra "^11.1.1"
|
||||
@@ -1809,142 +1809,142 @@
|
||||
utility-types "^3.10.0"
|
||||
webpack "^5.88.1"
|
||||
|
||||
"@docusaurus/plugin-content-pages@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.10.0.tgz#7670cbb3c849f434949f542bfdfded1580a13165"
|
||||
integrity sha512-5amX8kEJI+nIGtuLVjYk59Y5utEJ3CHETFOPEE4cooIRLA4xM4iBsA6zFgu4ljcopeYwvBzFEWf5g2I6Yb9SkA==
|
||||
"@docusaurus/plugin-content-pages@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.10.1.tgz#8c6ffc2079ed0262548ecc4df1dea6add6aa9673"
|
||||
integrity sha512-huJpaRPMl42nsFwuCXvV8bVDj2MazuwRJIUylI/RSlmZeJssVoZXeCjVf1y+1Drtpa9SKcdGn8yoJ76IRJijtw==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/mdx-loader" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/mdx-loader" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
fs-extra "^11.1.1"
|
||||
tslib "^2.6.0"
|
||||
webpack "^5.88.1"
|
||||
|
||||
"@docusaurus/plugin-css-cascade-layers@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.10.0.tgz#71e318d842be95f92be6c3dca00ceea4971d0edb"
|
||||
integrity sha512-6q1vtt5FJcg5osgkHeM1euErECNqEZ5Z1j69yiNx2luEBIso+nxCkS9nqj8w+MK5X7rvKEToGhFfOFWncs51pQ==
|
||||
"@docusaurus/plugin-css-cascade-layers@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.10.1.tgz#440578d95cbe1a6120936fa83df868d2626cd1d8"
|
||||
integrity sha512-r//fn+MNHkE1wCof8T29VAQezt1enGCpsFxoziBbvLgBM4JfXN2P3rxrBaavHmvLvm7lYkpJeitcDthwnmWCTw==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/plugin-debug@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.10.0.tgz#e77f924604e1e09d5d90fe0bdf23a3be8ea3307e"
|
||||
integrity sha512-XcljKN+G+nmmK69uQA1d9BlYU3ZftG3T3zpK8/7Hf/wrOlV7TA4Ampdrdwkg0jElKdKAoSnPhCO0/U3bQGsVQQ==
|
||||
"@docusaurus/plugin-debug@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.10.1.tgz#b8b7b24d9a7d185fd8a56a030f90145d3bfd8239"
|
||||
integrity sha512-9KqOpKNfAyqGZykRb9LhIT/vyRF6sm/ykhjj/39JvaJahDS+jZJE0Z1Wfz9q3DUNDTMNN0Q7u/kk4rKKU+IJuA==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
fs-extra "^11.1.1"
|
||||
react-json-view-lite "^2.3.0"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/plugin-google-analytics@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.10.0.tgz#22c7e976fe4d970c7cd1c73c9723d9a5786c6e37"
|
||||
integrity sha512-hTEoodatpBZnUat5nFExbuTGA1lhWGy7vZGuTew5Q3QDtGKFpSJLYmZJhdTjvCFwv1+qQ67hgAVlKdJOB8TXow==
|
||||
"@docusaurus/plugin-google-analytics@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.10.1.tgz#ac15afc77386e0352edb8a1698d993aa5de36ffc"
|
||||
integrity sha512-8o0P1KtmgdYQHH+oInitPpRWI0Of5XednAX4+DMhQNSmGSRNrsEEHg1ebv35m9AgRClfAytCJ5jA9KvcASTyuA==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/plugin-google-gtag@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.10.0.tgz#c38a2ba638257851cc845b934506b80c08d47f96"
|
||||
integrity sha512-iB/Zzjv/eelJRbdULZqzWCbgMgJ7ht4ONVjXtN3+BI/muil6S87gQ1OJyPwlXD+ELdKkitC7bWv5eJdYOZLhrQ==
|
||||
"@docusaurus/plugin-google-gtag@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.10.1.tgz#0482b83b9bc411aa99a432be2b39d2e53a00e2e0"
|
||||
integrity sha512-pu3xIUo5o/zCMLfUY9BO5KOwSH0zIsAGyFRPvXHayFSA5XIhCU/SFuB0g0ZNjFn9niZLCaNvoeAuOGFJZq0fdw==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
"@types/gtag.js" "^0.0.20"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/plugin-google-tag-manager@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.10.0.tgz#5469c923cc1ad4608399d0b17e5fcacd8e030d56"
|
||||
integrity sha512-FEjZxqKgLHa+Wez/EgKxRwvArNCWIScfyEQD95rot7jkxp6nonjI5XIbGfO/iYhM5Qinwe8aIEQHP2KZtpqVuA==
|
||||
"@docusaurus/plugin-google-tag-manager@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.10.1.tgz#eaf5765d6f82b4fb661d92a793d1883f9d1ec106"
|
||||
integrity sha512-f6fyGHiCm7kJHBtAisGQS5oNBnpnMTYQZxDXeVrnw/3zWU+LMA22pr6UHGYkBKDbN+qPC5QHG3NuOfzQLq3+Lw==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/plugin-sitemap@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.0.tgz#35d59d46803f279f22aa64fc1bd18c048f12662b"
|
||||
integrity sha512-DVTSLjB97hIjmayGnGcBfognCeI7ZuUKgEnU7Oz81JYqXtVg94mVTthDjq3QHTylYNeCUbkaW8VF0FDLcc8pPw==
|
||||
"@docusaurus/plugin-sitemap@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.1.tgz#66a6974bb2fd1b9d8f5cb0f3c5ecd2201c118565"
|
||||
integrity sha512-C26MbmmqgdjkDq1htaZ3aD7LzEDKFWXfpyQpt0EOUThuq5nV77zDaedV20yHcVo9p+3ey9aZ4pbHA0D3QcZTzg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
fs-extra "^11.1.1"
|
||||
sitemap "^7.1.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/plugin-svgr@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-svgr/-/plugin-svgr-3.10.0.tgz#8ada2e6dd8318d20206a9b044fc091a5794ba3f0"
|
||||
integrity sha512-lNljBESaETZqVBMPqkrGchr+UPT1eZzEPLmJhz8I76BxbjqgsUnRvrq6lQJ9sYjgmgX52KB7kkgczqd2yzoswQ==
|
||||
"@docusaurus/plugin-svgr@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-svgr/-/plugin-svgr-3.10.1.tgz#c217c24d6d23fd2bc6f54d44c040635b49d6b36e"
|
||||
integrity sha512-6SFxsmjWFkVLDmBUvFK6i72QjUwqyQFe4Ovz+SUJophJjOyVG3ZZG5IQpBC/kX/Gfv1yWeU9nWauH6F6Q7QX/Q==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
"@svgr/core" "8.1.0"
|
||||
"@svgr/webpack" "^8.1.0"
|
||||
tslib "^2.6.0"
|
||||
webpack "^5.88.1"
|
||||
|
||||
"@docusaurus/preset-classic@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.10.0.tgz#74b6facdaf568bcd41ec90cae9aebb7ca0ac8619"
|
||||
integrity sha512-kw/Ye02Hc6xP1OdTswy8yxQEHg0fdPpyWAQRxr5b2x3h7LlG2Zgbb5BDFROnXDDMpUxB7YejlocJIE5HIEfpNA==
|
||||
"@docusaurus/preset-classic@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.10.1.tgz#faf330d96aedc9083a59bec09d966ae4dfc8b2fb"
|
||||
integrity sha512-YO/FL8v1zmbxoTso6mjMz/RDjhaTJxb1UpFFTDdY5847LLDCeyYiYlrhyTbgN1RIN3xnkLKZ9Lj1x8hUzI4JOg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/plugin-content-blog" "3.10.0"
|
||||
"@docusaurus/plugin-content-docs" "3.10.0"
|
||||
"@docusaurus/plugin-content-pages" "3.10.0"
|
||||
"@docusaurus/plugin-css-cascade-layers" "3.10.0"
|
||||
"@docusaurus/plugin-debug" "3.10.0"
|
||||
"@docusaurus/plugin-google-analytics" "3.10.0"
|
||||
"@docusaurus/plugin-google-gtag" "3.10.0"
|
||||
"@docusaurus/plugin-google-tag-manager" "3.10.0"
|
||||
"@docusaurus/plugin-sitemap" "3.10.0"
|
||||
"@docusaurus/plugin-svgr" "3.10.0"
|
||||
"@docusaurus/theme-classic" "3.10.0"
|
||||
"@docusaurus/theme-common" "3.10.0"
|
||||
"@docusaurus/theme-search-algolia" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/plugin-content-blog" "3.10.1"
|
||||
"@docusaurus/plugin-content-docs" "3.10.1"
|
||||
"@docusaurus/plugin-content-pages" "3.10.1"
|
||||
"@docusaurus/plugin-css-cascade-layers" "3.10.1"
|
||||
"@docusaurus/plugin-debug" "3.10.1"
|
||||
"@docusaurus/plugin-google-analytics" "3.10.1"
|
||||
"@docusaurus/plugin-google-gtag" "3.10.1"
|
||||
"@docusaurus/plugin-google-tag-manager" "3.10.1"
|
||||
"@docusaurus/plugin-sitemap" "3.10.1"
|
||||
"@docusaurus/plugin-svgr" "3.10.1"
|
||||
"@docusaurus/theme-classic" "3.10.1"
|
||||
"@docusaurus/theme-common" "3.10.1"
|
||||
"@docusaurus/theme-search-algolia" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
|
||||
"@docusaurus/theme-classic@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.10.0.tgz#d937915c691189f27ced649c822994d839ea565b"
|
||||
integrity sha512-9msCAsRdN+UG+RwPwCFb0uKy4tGoPh5YfBozXeGUtIeAgsMdn6f3G/oY861luZ3t8S2ET8S9Y/1GnpJAGWytww==
|
||||
"@docusaurus/theme-classic@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.10.1.tgz#deed8cf73cc0f56113e53775cbb3b168c3c61566"
|
||||
integrity sha512-VU1RK0qb2pab0si4r7HFK37cYco8VzqLj3u1PspVipSr/z/GPVKHO4/HXbnePqHoWDk8urjyGSeatH0NIMBM1A==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/mdx-loader" "3.10.0"
|
||||
"@docusaurus/module-type-aliases" "3.10.0"
|
||||
"@docusaurus/plugin-content-blog" "3.10.0"
|
||||
"@docusaurus/plugin-content-docs" "3.10.0"
|
||||
"@docusaurus/plugin-content-pages" "3.10.0"
|
||||
"@docusaurus/theme-common" "3.10.0"
|
||||
"@docusaurus/theme-translations" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/mdx-loader" "3.10.1"
|
||||
"@docusaurus/module-type-aliases" "3.10.1"
|
||||
"@docusaurus/plugin-content-blog" "3.10.1"
|
||||
"@docusaurus/plugin-content-docs" "3.10.1"
|
||||
"@docusaurus/plugin-content-pages" "3.10.1"
|
||||
"@docusaurus/theme-common" "3.10.1"
|
||||
"@docusaurus/theme-translations" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
"@mdx-js/react" "^3.0.0"
|
||||
clsx "^2.0.0"
|
||||
copy-text-to-clipboard "^3.2.0"
|
||||
@@ -1959,15 +1959,15 @@
|
||||
tslib "^2.6.0"
|
||||
utility-types "^3.10.0"
|
||||
|
||||
"@docusaurus/theme-common@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.10.0.tgz#70b419ccfdf62f092299354a72d1692e81be597d"
|
||||
integrity sha512-Dkp1YXKn16ByCJAdIjbDIOpVb4Z66MsVD694/ilX1vAAHaVEMrVsf/NPd9VgreyFx08rJ9GqV1MtzsbTcU73Kg==
|
||||
"@docusaurus/theme-common@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.10.1.tgz#cbfec82b1b107be5c229811ed9caae14a501361c"
|
||||
integrity sha512-0YtmIeoNo1fIw65LO8+/1dPgmDV86UmhMkow37gzjytuiCSQm9xob6PJy0L4kuQEMTLfUOGvkXvZr7GPrHquMA==
|
||||
dependencies:
|
||||
"@docusaurus/mdx-loader" "3.10.0"
|
||||
"@docusaurus/module-type-aliases" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/mdx-loader" "3.10.1"
|
||||
"@docusaurus/module-type-aliases" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
"@types/react-router-config" "*"
|
||||
@@ -1977,48 +1977,48 @@
|
||||
tslib "^2.6.0"
|
||||
utility-types "^3.10.0"
|
||||
|
||||
"@docusaurus/theme-live-codeblock@^3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.10.0.tgz#05a38c6bfac479fd698f18f27ca06ebb126633d9"
|
||||
integrity sha512-1Ycxu0dBAhEXzXPQ1dQW01aY1MNi7TCTUOBtIF0GcNrQBFj74XxhDqv/T6GxYBsaN+6QnIDs1T+D43iV2/r2hQ==
|
||||
"@docusaurus/theme-live-codeblock@^3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.10.1.tgz#29e6ddee467d816205ad611fd7bf10f00db5bdef"
|
||||
integrity sha512-MKG/0zreelS6YlupQAoKmS5nCw9RRKwDHihJg2FinsU1+rqbrOYNYVq//eQy+m649k9b8XCazEw9VUMTFhpCTg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/theme-common" "3.10.0"
|
||||
"@docusaurus/theme-translations" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/theme-common" "3.10.1"
|
||||
"@docusaurus/theme-translations" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
"@philpl/buble" "^0.19.7"
|
||||
clsx "^2.0.0"
|
||||
fs-extra "^11.1.1"
|
||||
react-live "^4.1.6"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/theme-mermaid@^3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-mermaid/-/theme-mermaid-3.10.0.tgz#6581ccf16d27e4c02fe8c7cf15488862f27be9c8"
|
||||
integrity sha512-Y2xrlwhIJ80oOZIO3PXL6A7J869splfcMI87E3NKpYsy3zJxOyV+BP1QMtGi59ajKgU868HPuyyn6J+6BZGOBg==
|
||||
"@docusaurus/theme-mermaid@^3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-mermaid/-/theme-mermaid-3.10.1.tgz#dada9c50c780524d246906234ace8a35446f26fc"
|
||||
integrity sha512-2gxpmln8Pc4EN1oWzshQEx2HTs67jk14v7MmgqGs8ZU7Nm8oihg+fTouof2u4vN8DtB3Fln4cDJu4UprSX1S3Q==
|
||||
dependencies:
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/module-type-aliases" "3.10.0"
|
||||
"@docusaurus/theme-common" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/module-type-aliases" "3.10.1"
|
||||
"@docusaurus/theme-common" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
mermaid ">=11.6.0"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/theme-search-algolia@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.10.0.tgz#0ff57fe58db6abde8f5ad2877e459cd2fa6e7464"
|
||||
integrity sha512-f5FPKI08e3JRG63vR/o4qeuUVHUHzFzM0nnF+AkB67soAZgNsKJRf2qmUZvlQkGwlV+QFkKe4D0ANMh1jToU3g==
|
||||
"@docusaurus/theme-search-algolia@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.10.1.tgz#6f422058711629ce8d7c2f17e1e54efa075c626e"
|
||||
integrity sha512-OTaARARVZj2GvkJQjB+1jOIxntRaXea+G+fMsNqrZBAU1O1vJKDW22R7kECOHW27oJCLFN9HKaZeRrfAUyviug==
|
||||
dependencies:
|
||||
"@algolia/autocomplete-core" "^1.19.2"
|
||||
"@docsearch/react" "^3.9.0 || ^4.3.2"
|
||||
"@docusaurus/core" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/plugin-content-docs" "3.10.0"
|
||||
"@docusaurus/theme-common" "3.10.0"
|
||||
"@docusaurus/theme-translations" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-validation" "3.10.0"
|
||||
"@docusaurus/core" "3.10.1"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/plugin-content-docs" "3.10.1"
|
||||
"@docusaurus/theme-common" "3.10.1"
|
||||
"@docusaurus/theme-translations" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-validation" "3.10.1"
|
||||
algoliasearch "^5.37.0"
|
||||
algoliasearch-helper "^3.26.0"
|
||||
clsx "^2.0.0"
|
||||
@@ -2028,23 +2028,23 @@
|
||||
tslib "^2.6.0"
|
||||
utility-types "^3.10.0"
|
||||
|
||||
"@docusaurus/theme-translations@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.10.0.tgz#8fdc23d29bd7f907db49c36cf65e2123d96be300"
|
||||
integrity sha512-L9IbFLwTc5+XdgH45iQYufLn0SVZd6BUNelDbKIFlH+E4hhjuj/XHWAFMX/w2K59rfy8wak9McOaei7BSUfRPA==
|
||||
"@docusaurus/theme-translations@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.10.1.tgz#c3119a015652290eea560ca45ac775963d6eb75b"
|
||||
integrity sha512-cLMyaKivjBVWKMJuWqyFVVgtqe8DPJNPkog0bn8W1MDVAKcPdxRFycBfC1We1RaNp7Rdk513bmtW78RR6OBxBw==
|
||||
dependencies:
|
||||
fs-extra "^11.1.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/tsconfig@^3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.10.0.tgz#f40a57248828f0503a5f355cf30aa59941c9baaa"
|
||||
integrity sha512-TXdC3WXuPrdQAexLvjUJfnYf3YKEgEqAs5nK0Q88pRBCW7t7oN4ILvWYb3A5Z1wlSXyXGWW/mCUmLEhdWsjnDQ==
|
||||
"@docusaurus/tsconfig@^3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.10.1.tgz#1db31b4a4a5c914bdffa80070a35b6365d34f2e8"
|
||||
integrity sha512-rYvB7yqkdqWIpAbDzQljGfM4cDBkLTbhmagZBEcsyj6oPUsz47lmW2pYdN1j+7sGFgltbAmQH62xfbrij4Eh6Q==
|
||||
|
||||
"@docusaurus/types@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.10.0.tgz#a69232bba74b738fcf4671fd5f0f079366dd3d13"
|
||||
integrity sha512-F0dOt3FOoO20rRaFK7whGFQZ3ggyrWEdQc/c8/UiRuzhtg4y1w9FspXH5zpCT07uMnJKBPGh+qNazbNlCQqvSw==
|
||||
"@docusaurus/types@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.10.1.tgz#d42837938ae43ca2be0ca47e63e00476b5eb94be"
|
||||
integrity sha512-XYMK8k1szDCFMw2V+Xyen0g7Kee1sP3dtFnl7vkGkZOkeAJ/oPDQPL8iz4HBKOo/cwU8QeV6onVjMqtP+tFzsw==
|
||||
dependencies:
|
||||
"@mdx-js/mdx" "^3.0.0"
|
||||
"@types/history" "^4.7.11"
|
||||
@@ -2057,36 +2057,36 @@
|
||||
webpack "^5.95.0"
|
||||
webpack-merge "^5.9.0"
|
||||
|
||||
"@docusaurus/utils-common@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.10.0.tgz#2a6dc76b312664fca7234d33607c085318ff1ae3"
|
||||
integrity sha512-JyL7sb9QVDgYvudIS81Dv0lsWm7le0vGZSDwsztxWam1SPBqrnkvBy9UYL/amh6pbybkyYTd3CMTkO24oMlCSw==
|
||||
"@docusaurus/utils-common@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.10.1.tgz#6350b4898691e765de750f90eade0e0fa7902d99"
|
||||
integrity sha512-5mFSgEADtnFxFH7RLw02QA5MpU5JVUCj0MPeIvi/aF4Fi45tQRIuTwXoXDqJ+1VfQJuYJGz3SI63wmGz4HvXzA==
|
||||
dependencies:
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/utils-validation@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.10.0.tgz#a2418d7f31980d991fd3a1f39c8aad8820b36812"
|
||||
integrity sha512-c+6n2+ZPOJtWWc8Bb/EYdpSDfjYEScdCu9fB/SNjOmSCf1IdVnGf2T53o0tsz0gDRtCL90tifTL0JE/oMuP1Mw==
|
||||
"@docusaurus/utils-validation@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.10.1.tgz#ddbcce997a5506424cdd16abf6845cc51692acae"
|
||||
integrity sha512-cRv1X69jwaWv47waglllgZVWzeBFLhl53XT/XED/83BerVBTC5FTP8WTcVl8Z6sZOegDSwitu/wpCSPCDOT6lg==
|
||||
dependencies:
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/utils" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/utils" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
fs-extra "^11.2.0"
|
||||
joi "^17.9.2"
|
||||
js-yaml "^4.1.0"
|
||||
lodash "^4.17.21"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/utils@3.10.0":
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.10.0.tgz#ea7d7b0d325b60f728decc00bb3908d00ef86faf"
|
||||
integrity sha512-T3B0WTigsIthe0D4LQa2k+7bJY+c3WS+Wq2JhcznOSpn1lSN64yNtHQXboCj3QnUs1EuAZszQG1SHKu5w5ZrlA==
|
||||
"@docusaurus/utils@3.10.1":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.10.1.tgz#535968caa2c9bff69f997a081b98b95b3c5d3785"
|
||||
integrity sha512-3ojeJry9xBYdJO6qoyyzqeJFSJBVx2mXhyDzSdjwL2+URFQMf+h25gG38iswGImicK0ELjTd1EL2xzk8hf3QPw==
|
||||
dependencies:
|
||||
"@docusaurus/logger" "3.10.0"
|
||||
"@docusaurus/types" "3.10.0"
|
||||
"@docusaurus/utils-common" "3.10.0"
|
||||
"@docusaurus/logger" "3.10.1"
|
||||
"@docusaurus/types" "3.10.1"
|
||||
"@docusaurus/utils-common" "3.10.1"
|
||||
escape-string-regexp "^4.0.0"
|
||||
execa "^5.1.1"
|
||||
file-loader "^6.2.0"
|
||||
@@ -13328,7 +13328,7 @@ renderkid@^3.0.0:
|
||||
|
||||
repeat-string@^1.5.2:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==
|
||||
|
||||
require-directory@^2.1.1:
|
||||
@@ -15368,7 +15368,7 @@ webpack@^5.106.2, webpack@^5.88.1, webpack@^5.95.0:
|
||||
watchpack "^2.5.1"
|
||||
webpack-sources "^3.3.4"
|
||||
|
||||
webpackbar@^6.0.1, webpackbar@^7.0.0:
|
||||
webpackbar@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-7.0.0.tgz#7228d32881af2392381b6514499ddea73cdf218a"
|
||||
integrity sha512-aS9soqSO2iCHgqHoCrj4LbfGQUboDCYJPSFOAchEK+9psIjNrfSWW4Y0YEz67MKURNvMmfo0ycOg9d/+OOf9/Q==
|
||||
|
||||
@@ -707,7 +707,7 @@ protobuf==4.25.8
|
||||
# proto-plus
|
||||
psutil==6.1.0
|
||||
# via apache-superset
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg2-binary==2.9.12
|
||||
# via apache-superset
|
||||
py-key-value-aio==0.4.4
|
||||
# via fastmcp
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
[project]
|
||||
name = "apache-superset-core"
|
||||
version = "0.1.0rc2"
|
||||
version = "0.1.0rc3"
|
||||
description = "Core Python package for building Apache Superset backend extensions and integrations"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
[project]
|
||||
name = "apache-superset-extensions-cli"
|
||||
version = "0.1.0rc2"
|
||||
version = "0.1.0rc3"
|
||||
description = "Official command-line interface for building, bundling, and managing Apache Superset extensions"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
|
||||
9655
superset-frontend/package-lock.json
generated
9655
superset-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -183,7 +183,7 @@
|
||||
"json-bigint": "^1.0.0",
|
||||
"json-stringify-pretty-compact": "^2.0.0",
|
||||
"lodash": "^4.18.1",
|
||||
"mapbox-gl": "^3.22.0",
|
||||
"mapbox-gl": "^3.23.0",
|
||||
"markdown-to-jsx": "^9.7.16",
|
||||
"match-sorter": "^8.3.0",
|
||||
"memoize-one": "^5.2.1",
|
||||
@@ -194,13 +194,13 @@
|
||||
"pretty-ms": "^9.3.0",
|
||||
"query-string": "9.3.1",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-arborist": "^3.5.0",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-diff-viewer-continued": "^4.2.2",
|
||||
"react-dnd": "^11.1.3",
|
||||
"react-dnd-html5-backend": "^11.1.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"react-intersection-observer": "^10.0.3",
|
||||
"react-json-tree": "^0.20.0",
|
||||
@@ -211,7 +211,6 @@
|
||||
"react-reverse-portal": "^2.3.0",
|
||||
"react-router-dom": "^5.3.4",
|
||||
"react-search-input": "^0.11.3",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"react-split": "^2.0.9",
|
||||
"react-table": "^7.8.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
@@ -244,14 +243,13 @@
|
||||
"@babel/plugin-transform-export-namespace-from": "^7.27.1",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
||||
"@babel/plugin-transform-runtime": "^7.29.0",
|
||||
"@babel/preset-env": "^7.29.2",
|
||||
"@babel/preset-env": "^7.29.3",
|
||||
"@babel/preset-react": "^7.28.5",
|
||||
"@babel/preset-typescript": "^7.28.5",
|
||||
"@babel/register": "^7.23.7",
|
||||
"@babel/runtime": "^7.29.2",
|
||||
"@babel/runtime-corejs3": "^7.29.2",
|
||||
"@babel/types": "^7.28.6",
|
||||
"@cypress/react": "^8.0.2",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/jest": "^11.14.2",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
@@ -273,10 +271,9 @@
|
||||
"@swc/core": "^1.15.32",
|
||||
"@swc/plugin-emotion": "^14.9.0",
|
||||
"@swc/plugin-transform-imports": "^12.5.0",
|
||||
"@testing-library/dom": "^8.20.1",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@types/content-disposition": "^0.5.9",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
@@ -286,8 +283,8 @@
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/react": "^17.0.83",
|
||||
"@types/react-dom": "^17.0.26",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@types/react-loadable": "^5.5.11",
|
||||
"@types/react-redux": "^7.1.10",
|
||||
"@types/react-resizable": "^3.0.8",
|
||||
@@ -306,7 +303,7 @@
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"baseline-browser-mapping": "^2.10.21",
|
||||
"baseline-browser-mapping": "^2.10.24",
|
||||
"cheerio": "1.2.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
@@ -373,7 +370,7 @@
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.3",
|
||||
"webpack-manifest-plugin": "^5.0.1",
|
||||
"webpack-sources": "^3.4.0",
|
||||
"webpack-sources": "^3.4.1",
|
||||
"webpack-visualizer-plugin2": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"cross-env": "^10.1.0",
|
||||
"fs-extra": "^11.3.4",
|
||||
"jest": "^30.3.0",
|
||||
"yeoman-test": "^11.3.1"
|
||||
"yeoman-test": "^11.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">= 4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@apache-superset/core",
|
||||
"version": "0.1.0-rc2",
|
||||
"version": "0.1.0-rc3",
|
||||
"description": "This package contains UI elements, APIs, and utility functions used by Superset.",
|
||||
"sideEffects": false,
|
||||
"main": "lib/index.js",
|
||||
@@ -75,16 +75,15 @@
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.28.6",
|
||||
"@babel/core": "^7.29.0",
|
||||
"@babel/preset-env": "^7.29.2",
|
||||
"@babel/preset-env": "^7.29.3",
|
||||
"@babel/preset-react": "^7.28.5",
|
||||
"@babel/preset-typescript": "^7.28.5",
|
||||
"typescript": "^5.0.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@testing-library/dom": "^8.20.1",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "*",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react-hooks": "*",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "*",
|
||||
"@types/react": "*",
|
||||
"@types/react-loadable": "*",
|
||||
@@ -98,8 +97,8 @@
|
||||
"@fontsource/ibm-plex-mono": "^5.2.7",
|
||||
"@fontsource/inter": "^5.2.6",
|
||||
"nanoid": "^5.0.9",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-loadable": "^5.5.0",
|
||||
"tinycolor2": "*",
|
||||
"lodash": "^4.18.1",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ReactElement } from 'react';
|
||||
import { render, RenderOptions } from '@testing-library/react';
|
||||
import { render, RenderOptions, RenderResult } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { themeObject } from './theme';
|
||||
|
||||
@@ -33,7 +33,7 @@ const Providers = ({ children }: { children: React.ReactNode }) => (
|
||||
const customRender = (
|
||||
ui: ReactElement,
|
||||
options?: Omit<RenderOptions, 'wrapper'>,
|
||||
) => render(ui, { wrapper: Providers, ...options });
|
||||
): RenderResult => render(ui, { wrapper: Providers, ...options });
|
||||
|
||||
export {
|
||||
createEvent,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { theme as antdTheme } from 'antd';
|
||||
import {
|
||||
|
||||
@@ -33,17 +33,16 @@
|
||||
"@ant-design/icons": "^5.6.1",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@superset-ui/core": "*",
|
||||
"@testing-library/dom": "^8.20.1",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "*",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react-hooks": "*",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "*",
|
||||
"ace-builds": "^1.4.14",
|
||||
"brace": "^0.11.1",
|
||||
"memoize-one": "^5.1.1",
|
||||
"react": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-dom": "^17.0.2"
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -91,10 +91,9 @@
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@testing-library/dom": "^8.20.1",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "*",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react-hooks": "*",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "*",
|
||||
"@types/react": "*",
|
||||
"@types/react-loadable": "*",
|
||||
@@ -102,8 +101,8 @@
|
||||
"@types/tinycolor2": "*",
|
||||
"antd": "^5.26.0",
|
||||
"nanoid": "^5.0.9",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-loadable": "^5.5.0",
|
||||
"tinycolor2": "*"
|
||||
},
|
||||
|
||||
@@ -17,19 +17,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import type { ReactElement } from 'react';
|
||||
import {
|
||||
Tooltip,
|
||||
type TooltipPlacement,
|
||||
type IconType,
|
||||
} from '@superset-ui/core/components';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { Tooltip, type TooltipPlacement } from '@superset-ui/core/components';
|
||||
import { css, useTheme } from '@apache-superset/core/theme';
|
||||
|
||||
export interface ActionProps {
|
||||
label: string;
|
||||
tooltip?: string | ReactElement;
|
||||
placement?: TooltipPlacement;
|
||||
icon: IconType;
|
||||
icon: ReactNode;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useJsonValidation } from './useJsonValidation';
|
||||
|
||||
describe('useJsonValidation', () => {
|
||||
|
||||
@@ -16,16 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
RefObject,
|
||||
forwardRef,
|
||||
ComponentType,
|
||||
ForwardRefExoticComponent,
|
||||
PropsWithoutRef,
|
||||
RefAttributes,
|
||||
} from 'react';
|
||||
import React, { useEffect, useState, forwardRef, ComponentType } from 'react';
|
||||
|
||||
import { Loading } from '../Loading';
|
||||
import type { PlaceholderProps } from './types';
|
||||
@@ -93,15 +84,16 @@ export function AsyncEsmComponent<
|
||||
return promise;
|
||||
}
|
||||
|
||||
type AsyncComponent = ForwardRefExoticComponent<
|
||||
PropsWithoutRef<FullProps> & RefAttributes<ComponentType<FullProps>>
|
||||
type AsyncComponent = React.ForwardRefExoticComponent<
|
||||
React.PropsWithoutRef<FullProps> & React.RefAttributes<unknown>
|
||||
> & {
|
||||
preload?: typeof waitForPromise;
|
||||
};
|
||||
|
||||
// @ts-expect-error -- generic forwardRef has PropsWithoutRef incompatibility with FullProps
|
||||
const AsyncComponent: AsyncComponent = forwardRef(function AsyncComponent(
|
||||
props: FullProps,
|
||||
ref: RefObject<ComponentType<FullProps>>,
|
||||
ref,
|
||||
) {
|
||||
const [loaded, setLoaded] = useState(component !== undefined);
|
||||
useEffect(() => {
|
||||
|
||||
@@ -24,7 +24,6 @@ import type {
|
||||
ButtonVariantType,
|
||||
ButtonColorType,
|
||||
} from 'antd/es/button';
|
||||
import { IconType } from '@superset-ui/core/components/Icons/types';
|
||||
import type { TooltipPlacement } from '../Tooltip/types';
|
||||
|
||||
export type { AntdButtonProps, ButtonType, ButtonVariantType, ButtonColorType };
|
||||
@@ -49,5 +48,5 @@ export type ButtonProps = Omit<AntdButtonProps, 'css'> & {
|
||||
buttonStyle?: ButtonStyle;
|
||||
cta?: boolean;
|
||||
showMarginRight?: boolean;
|
||||
icon?: IconType;
|
||||
icon?: ReactNode;
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ export const Component = (props: DropdownContainerProps) => {
|
||||
const [overflowingState, setOverflowingState] = useState<OverflowingState>();
|
||||
const containerRef = useRef<DropdownRef>(null);
|
||||
const onOverflowingStateChange = useCallback(
|
||||
value => {
|
||||
(value: OverflowingState) => {
|
||||
if (!isEqual(overflowingState, value)) {
|
||||
setItems(generateItems(value));
|
||||
setOverflowingState(value);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
* under the License.
|
||||
*/
|
||||
import type { CSSProperties, ReactElement, RefObject, ReactNode } from 'react';
|
||||
import { IconType } from '../Icons';
|
||||
|
||||
/**
|
||||
* Container item.
|
||||
@@ -70,7 +69,7 @@ export interface DropdownContainerProps {
|
||||
/**
|
||||
* Icon of the dropdown trigger.
|
||||
*/
|
||||
dropdownTriggerIcon?: IconType;
|
||||
dropdownTriggerIcon?: ReactNode;
|
||||
/**
|
||||
* Text of the dropdown trigger.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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 { fireEvent, render, screen, userEvent } from '@superset-ui/core/spec';
|
||||
import { useState } from 'react';
|
||||
import { DynamicEditableTitle } from '.';
|
||||
|
||||
const Harness = ({ initialTitle = 'Original' }: { initialTitle?: string }) => {
|
||||
const [title, setTitle] = useState(initialTitle);
|
||||
return (
|
||||
<DynamicEditableTitle
|
||||
title={title}
|
||||
placeholder="placeholder"
|
||||
canEdit
|
||||
label="Title"
|
||||
onSave={setTitle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
test('rapid typing then backspacing keeps every keystroke', async () => {
|
||||
render(<Harness />);
|
||||
const input = screen.getByRole('textbox') as HTMLInputElement;
|
||||
userEvent.click(input);
|
||||
await userEvent.type(input, 'abc', { delay: 1 });
|
||||
expect(input.value).toBe('Originalabc');
|
||||
await userEvent.type(input, '{backspace}{backspace}{backspace}', {
|
||||
delay: 1,
|
||||
});
|
||||
expect(input.value).toBe('Original');
|
||||
});
|
||||
|
||||
test('a change event that arrives before isEditing flips is not dropped', () => {
|
||||
// Reproduces the regression: the input is focused but `isEditing` is still
|
||||
// false because no click has been registered yet (e.g. focus arrived via
|
||||
// tab, autofocus, or programmatic focus). The pre-fix `handleChange`
|
||||
// bailed out with `!isEditing`, dropping the keystroke. Because the
|
||||
// input is controlled, antd's internal `useMergedState` then resyncs the
|
||||
// DOM value back to the (stale) `props.value`, so the user sees their
|
||||
// typed character disappear. This test fires a raw change event so it
|
||||
// doesn't go through userEvent's implicit click.
|
||||
const onSave = jest.fn();
|
||||
render(
|
||||
<DynamicEditableTitle
|
||||
title="Foo"
|
||||
placeholder="placeholder"
|
||||
canEdit
|
||||
label="Title"
|
||||
onSave={onSave}
|
||||
/>,
|
||||
);
|
||||
const input = screen.getByRole('textbox') as HTMLInputElement;
|
||||
fireEvent.change(input, { target: { value: 'FooX' } });
|
||||
expect(input.value).toBe('FooX');
|
||||
});
|
||||
|
||||
test('prop changes mid-edit do not clobber unsaved typing', async () => {
|
||||
// Rerender DynamicEditableTitle directly with a changed title prop so the
|
||||
// sync effect actually runs. Going through Harness would not exercise the
|
||||
// bug because Harness owns its own state and only reads initialTitle once.
|
||||
const onSave = jest.fn();
|
||||
const props = {
|
||||
placeholder: 'placeholder',
|
||||
canEdit: true,
|
||||
label: 'Title',
|
||||
onSave,
|
||||
};
|
||||
const { rerender } = render(<DynamicEditableTitle {...props} title="Foo" />);
|
||||
const input = screen.getByRole('textbox') as HTMLInputElement;
|
||||
userEvent.click(input);
|
||||
await userEvent.type(input, 'X', { delay: 1 });
|
||||
expect(input.value).toBe('FooX');
|
||||
rerender(<DynamicEditableTitle {...props} title="Bar" />);
|
||||
expect(input.value).toBe('FooX');
|
||||
});
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
@@ -30,6 +31,7 @@ import { css, SupersetTheme, useTheme } from '@apache-superset/core/theme';
|
||||
import { useResizeDetector } from 'react-resize-detector';
|
||||
import { Tooltip } from '../Tooltip';
|
||||
import { Input } from '../Input';
|
||||
import type { InputRef } from '../Input';
|
||||
import type { DynamicEditableTitleProps } from './types';
|
||||
|
||||
const titleStyles = (theme: SupersetTheme) => css`
|
||||
@@ -75,37 +77,53 @@ export const DynamicEditableTitle = memo(
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const [currentTitle, setCurrentTitle] = useState(title || '');
|
||||
const [inputWidth, setInputWidth] = useState<number>(0);
|
||||
|
||||
const { width: inputWidth, ref: sizerRef } = useResizeDetector();
|
||||
const sizerRef = useRef<HTMLSpanElement>(null);
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const { width: containerWidth, ref: containerRef } = useResizeDetector({
|
||||
refreshMode: 'debounce',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentTitle(title);
|
||||
// Don't overwrite in-flight user input when the parent re-renders with a
|
||||
// new title prop mid-edit. handleBlur already syncs currentTitle on commit;
|
||||
// re-running this effect when isEditing flips would resync to a stale
|
||||
// title prop, so isEditing is intentionally read via closure rather than
|
||||
// listed as a dep.
|
||||
if (!isEditing) {
|
||||
setCurrentTitle(title);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [title]);
|
||||
useEffect(() => {
|
||||
if (isEditing && sizerRef?.current) {
|
||||
if (isEditing) {
|
||||
// move cursor and scroll to the end
|
||||
if (sizerRef.current.setSelectionRange) {
|
||||
const { length } = sizerRef.current.value;
|
||||
sizerRef.current.setSelectionRange(length, length);
|
||||
sizerRef.current.scrollLeft = sizerRef.current.scrollWidth;
|
||||
const inputElement = inputRef.current?.input;
|
||||
if (inputElement) {
|
||||
const { length } = inputElement.value;
|
||||
inputElement.setSelectionRange(length, length);
|
||||
inputElement.scrollLeft = inputElement.scrollWidth;
|
||||
}
|
||||
}
|
||||
}, [isEditing]);
|
||||
|
||||
// a trick to make the input grow when user types text
|
||||
// we make additional span component, place it somewhere out of view and copy input
|
||||
// then we can measure the width of that span to resize the input element
|
||||
// we make an additional span component, place it somewhere out of view and
|
||||
// mirror the input value, then measure the span synchronously (pre-paint)
|
||||
// to resize the input element. Reading offsetWidth in a useLayoutEffect
|
||||
// forces a sync layout, so the input width updates in the same commit as
|
||||
// the value change — preventing a flicker frame where the input is shown
|
||||
// with new value but stale width.
|
||||
useLayoutEffect(() => {
|
||||
if (sizerRef?.current) {
|
||||
if (sizerRef.current) {
|
||||
sizerRef.current.textContent = currentTitle || placeholder;
|
||||
setInputWidth(sizerRef.current.offsetWidth);
|
||||
}
|
||||
}, [currentTitle, placeholder, sizerRef]);
|
||||
}, [currentTitle, placeholder]);
|
||||
|
||||
useEffect(() => {
|
||||
const inputElement = sizerRef.current?.input;
|
||||
const inputElement = inputRef.current?.input;
|
||||
|
||||
if (inputElement) {
|
||||
if (inputElement.scrollWidth > inputElement.clientWidth) {
|
||||
@@ -137,9 +155,17 @@ export const DynamicEditableTitle = memo(
|
||||
|
||||
const handleChange = useCallback(
|
||||
(ev: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!canEdit || !isEditing) {
|
||||
if (!canEdit) {
|
||||
return;
|
||||
}
|
||||
// Any change implies the user is editing. Ensure isEditing is true
|
||||
// even if the change event arrives before the click handler has
|
||||
// committed (e.g. focus via tab, autofocus, or batched click+type
|
||||
// events). Otherwise the keystroke would be dropped and the
|
||||
// controlled input would revert to the previous value.
|
||||
if (!isEditing) {
|
||||
setIsEditing(true);
|
||||
}
|
||||
setCurrentTitle(ev.target.value);
|
||||
},
|
||||
[canEdit, isEditing],
|
||||
@@ -168,6 +194,7 @@ export const DynamicEditableTitle = memo(
|
||||
}
|
||||
>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
data-test="editable-title-input"
|
||||
variant="borderless"
|
||||
aria-label={label ?? t('Title')}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
* under the License.
|
||||
*/
|
||||
import type { ReactNode, SyntheticEvent } from 'react';
|
||||
import type { IconType } from '@superset-ui/core/components';
|
||||
|
||||
export type EmptyStateSize = 'small' | 'medium' | 'large';
|
||||
|
||||
@@ -26,7 +25,7 @@ export type EmptyStateProps = {
|
||||
description?: ReactNode;
|
||||
image?: ReactNode | string;
|
||||
buttonText?: ReactNode;
|
||||
buttonIcon?: IconType;
|
||||
buttonIcon?: ReactNode;
|
||||
buttonAction?: (event: SyntheticEvent) => void;
|
||||
/** Controls image size. Defaults to 'medium'. */
|
||||
size?: EmptyStateSize;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { Form as AntdForm } from 'antd';
|
||||
import { FormProps } from './types';
|
||||
|
||||
function CustomForm(props: FormProps) {
|
||||
return <AntdForm {...props} />;
|
||||
return <AntdForm {...(props as any)} />;
|
||||
}
|
||||
|
||||
export const Form = Object.assign(CustomForm, {
|
||||
|
||||
@@ -41,7 +41,6 @@ test('renders with monospace prop', () => {
|
||||
|
||||
// test stories from the storybook!
|
||||
test('renders all the storybook gallery variants', () => {
|
||||
// @ts-expect-error: Suppress TypeScript error for LabelGallery usage
|
||||
const { container } = render(<LabelGallery />);
|
||||
const nonInteractiveLabelCount = 4;
|
||||
const renderedLabelCount = options.length * 2 + nonInteractiveLabelCount;
|
||||
|
||||
@@ -21,6 +21,7 @@ import type { BackgroundPosition } from './ImageLoader';
|
||||
|
||||
export interface LinkProps {
|
||||
to: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export interface ListViewCardProps {
|
||||
|
||||
@@ -194,7 +194,7 @@ const MetadataBar = ({ items, tooltipPlacement = 'top' }: MetadataBarProps) => {
|
||||
}
|
||||
|
||||
const onResize = useCallback(
|
||||
width => {
|
||||
(width: number | undefined) => {
|
||||
// Calculates the breakpoint width to collapse the bar.
|
||||
// The last item does not have a space, so we subtract SPACE_BETWEEN_ITEMS from the total.
|
||||
const breakpoint =
|
||||
|
||||
@@ -54,7 +54,7 @@ export function FormModal({
|
||||
}, [onSave, resetForm]);
|
||||
|
||||
const handleFormSubmit = useCallback(
|
||||
async values => {
|
||||
async (values: object) => {
|
||||
try {
|
||||
setIsSaving(true);
|
||||
await formSubmitHandler(values);
|
||||
|
||||
@@ -104,6 +104,9 @@ export const StyledModal = styled(BaseModal)<StyledModalProps>`
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
// Keep the close button clickable when modal body content uses
|
||||
// position: sticky with elevated z-index (e.g. DatabaseModal header).
|
||||
z-index: ${theme.zIndexPopupBase + 1};
|
||||
}
|
||||
|
||||
.ant-modal-close:hover {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import type { ModalFuncProps } from 'antd';
|
||||
import type { FormInstance, ModalFuncProps } from 'antd';
|
||||
import type { ResizableProps } from 're-resizable';
|
||||
import type { DraggableProps } from 'react-draggable';
|
||||
import { ButtonStyle } from '../Button/types';
|
||||
@@ -68,7 +68,8 @@ export interface StyledModalProps {
|
||||
|
||||
export type { ModalFuncProps };
|
||||
|
||||
export interface FormModalProps extends ModalProps {
|
||||
export interface FormModalProps extends Omit<ModalProps, 'children'> {
|
||||
children: ReactNode | ((form: FormInstance) => ReactNode);
|
||||
initialValues?: object;
|
||||
formSubmitHandler: (values: object) => Promise<void>;
|
||||
onSave: () => void;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ReactNode, ReactElement } from 'react';
|
||||
import { ReactNode, ReactElement, memo } from 'react';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { css, SupersetTheme, useTheme } from '@apache-superset/core/theme';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
@@ -118,62 +118,64 @@ export type PageHeaderWithActionsProps = {
|
||||
};
|
||||
};
|
||||
|
||||
export const PageHeaderWithActions = ({
|
||||
editableTitleProps,
|
||||
showTitlePanelItems,
|
||||
certificatiedBadgeProps,
|
||||
showFaveStar,
|
||||
faveStarProps,
|
||||
titlePanelAdditionalItems,
|
||||
rightPanelAdditionalItems,
|
||||
additionalActionsMenu,
|
||||
menuDropdownProps,
|
||||
showMenuDropdown = true,
|
||||
tooltipProps,
|
||||
}: PageHeaderWithActionsProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div css={headerStyles} className="header-with-actions">
|
||||
<div className="title-panel">
|
||||
<DynamicEditableTitle {...editableTitleProps} />
|
||||
{showTitlePanelItems && (
|
||||
<div css={buttonsStyles}>
|
||||
{certificatiedBadgeProps?.certifiedBy && (
|
||||
<CertifiedBadge {...certificatiedBadgeProps} />
|
||||
)}
|
||||
{showFaveStar && <FaveStar {...faveStarProps} />}
|
||||
{titlePanelAdditionalItems}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="right-button-panel">
|
||||
{rightPanelAdditionalItems}
|
||||
<div css={additionalActionsContainerStyles}>
|
||||
{showMenuDropdown && (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
popupRender={() => additionalActionsMenu}
|
||||
{...menuDropdownProps}
|
||||
>
|
||||
<span>
|
||||
<Button
|
||||
css={menuTriggerStyles}
|
||||
buttonStyle="tertiary"
|
||||
aria-label={t('Menu actions trigger')}
|
||||
tooltip={tooltipProps?.text}
|
||||
placement={tooltipProps?.placement}
|
||||
data-test="actions-trigger"
|
||||
>
|
||||
<Icons.EllipsisOutlined
|
||||
iconColor={theme.colorPrimary}
|
||||
iconSize="l"
|
||||
/>
|
||||
</Button>
|
||||
</span>
|
||||
</Dropdown>
|
||||
export const PageHeaderWithActions = memo(
|
||||
({
|
||||
editableTitleProps,
|
||||
showTitlePanelItems,
|
||||
certificatiedBadgeProps,
|
||||
showFaveStar,
|
||||
faveStarProps,
|
||||
titlePanelAdditionalItems,
|
||||
rightPanelAdditionalItems,
|
||||
additionalActionsMenu,
|
||||
menuDropdownProps,
|
||||
showMenuDropdown = true,
|
||||
tooltipProps,
|
||||
}: PageHeaderWithActionsProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div css={headerStyles} className="header-with-actions">
|
||||
<div className="title-panel">
|
||||
<DynamicEditableTitle {...editableTitleProps} />
|
||||
{showTitlePanelItems && (
|
||||
<div css={buttonsStyles}>
|
||||
{certificatiedBadgeProps?.certifiedBy && (
|
||||
<CertifiedBadge {...certificatiedBadgeProps} />
|
||||
)}
|
||||
{showFaveStar && <FaveStar {...faveStarProps} />}
|
||||
{titlePanelAdditionalItems}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="right-button-panel">
|
||||
{rightPanelAdditionalItems}
|
||||
<div css={additionalActionsContainerStyles}>
|
||||
{showMenuDropdown && (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
popupRender={() => additionalActionsMenu}
|
||||
{...menuDropdownProps}
|
||||
>
|
||||
<span>
|
||||
<Button
|
||||
css={menuTriggerStyles}
|
||||
buttonStyle="tertiary"
|
||||
aria-label={t('Menu actions trigger')}
|
||||
tooltip={tooltipProps?.text}
|
||||
placement={tooltipProps?.placement}
|
||||
data-test="actions-trigger"
|
||||
>
|
||||
<Icons.EllipsisOutlined
|
||||
iconColor={theme.colorPrimary}
|
||||
iconSize="l"
|
||||
/>
|
||||
</Button>
|
||||
</span>
|
||||
</Dropdown>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { render, screen, fireEvent } from '@superset-ui/core/spec';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { TableInstance, useTable } from 'react-table';
|
||||
import TableCollection from '.';
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ export function mapColumns<T extends object>(
|
||||
return columns.map(column => {
|
||||
const { isSorted, isSortedDesc } = getSortingInfo(headerGroups, column.id);
|
||||
return {
|
||||
title: column.Header,
|
||||
title: column.Header as ReactNode,
|
||||
dataIndex: column.id?.includes('.') ? column.id.split('.') : column.id,
|
||||
hidden: column.hidden,
|
||||
key: column.id,
|
||||
@@ -121,7 +121,7 @@ export function mapColumns<T extends object>(
|
||||
column,
|
||||
});
|
||||
}
|
||||
return val;
|
||||
return val as ReactNode;
|
||||
},
|
||||
className: column.className,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
import { render, screen, userEvent, waitFor } from '@superset-ui/core/spec';
|
||||
import { TableView, TableViewProps } from '.';
|
||||
|
||||
// Mock window.scrollTo to prevent jsdom "Not implemented" errors
|
||||
beforeAll(() => {
|
||||
window.scrollTo = jest.fn();
|
||||
});
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const mockedProps: TableViewProps = {
|
||||
columns: [
|
||||
{
|
||||
@@ -125,27 +133,25 @@ test('should change page when pagination is clicked', async () => {
|
||||
expect(screen.getByText('Emily')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Kate')).not.toBeInTheDocument();
|
||||
|
||||
const page2 = screen.getByRole('listitem', { name: '2' });
|
||||
await userEvent.click(page2);
|
||||
await userEvent.click(screen.getByTitle('Next Page'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByRole('cell')).toHaveLength(3);
|
||||
expect(screen.getByText('321')).toBeInTheDocument();
|
||||
expect(screen.getByText('10')).toBeInTheDocument();
|
||||
expect(screen.getByText('Kate')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Emily')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getAllByRole('cell')).toHaveLength(3);
|
||||
expect(screen.getByText('321')).toBeInTheDocument();
|
||||
expect(screen.getByText('10')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Emily')).not.toBeInTheDocument();
|
||||
|
||||
const page1 = screen.getByRole('listitem', { name: '1' });
|
||||
await userEvent.click(page1);
|
||||
await userEvent.click(screen.getByTitle('Previous Page'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByRole('cell')).toHaveLength(3);
|
||||
expect(screen.getByText('123')).toBeInTheDocument();
|
||||
expect(screen.getByText('27')).toBeInTheDocument();
|
||||
expect(screen.getByText('Emily')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Kate')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getAllByRole('cell')).toHaveLength(3);
|
||||
expect(screen.getByText('123')).toBeInTheDocument();
|
||||
expect(screen.getByText('27')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Kate')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should sort by age', async () => {
|
||||
@@ -240,8 +246,7 @@ test('should handle server-side pagination', async () => {
|
||||
render(<TableView {...serverPaginationProps} />);
|
||||
|
||||
// Click next page
|
||||
const page2 = screen.getByRole('listitem', { name: '2' });
|
||||
await userEvent.click(page2);
|
||||
await userEvent.click(screen.getByTitle('Next Page'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onServerPagination).toHaveBeenCalledWith({
|
||||
@@ -301,9 +306,7 @@ test('should scroll to top when scrollTopOnPagination is true', async () => {
|
||||
};
|
||||
render(<TableView {...scrollProps} />);
|
||||
|
||||
// Click next page
|
||||
const page2 = screen.getByRole('listitem', { name: '2' });
|
||||
await userEvent.click(page2);
|
||||
await userEvent.click(screen.getByTitle('Next Page'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(scrollToSpy).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
|
||||
@@ -324,9 +327,7 @@ test('should NOT scroll to top when scrollTopOnPagination is false', async () =>
|
||||
};
|
||||
render(<TableView {...scrollProps} />);
|
||||
|
||||
// Click next page
|
||||
const page2 = screen.getByRole('listitem', { name: '2' });
|
||||
await userEvent.click(page2);
|
||||
await userEvent.click(screen.getByTitle('Next Page'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('321')).toBeInTheDocument();
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { memo, useEffect, useRef, useMemo, useCallback } from 'react';
|
||||
import { memo, useEffect, useRef, useMemo, useCallback, useState } from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
import { styled } from '@apache-superset/core/theme';
|
||||
import { useFilters, usePagination, useSortBy, useTable } from 'react-table';
|
||||
import { useFilters, useSortBy, useTable } from 'react-table';
|
||||
import { Empty } from '@superset-ui/core/components';
|
||||
import TableCollection from '@superset-ui/core/components/TableCollection';
|
||||
import { TableSize } from '@superset-ui/core/components/Table';
|
||||
@@ -117,43 +117,45 @@ const RawTableView = ({
|
||||
...props
|
||||
}: TableViewProps) => {
|
||||
const tableRef = useRef<HTMLTableElement>(null);
|
||||
const effectivePageSize = initialPageSize ?? DEFAULT_PAGE_SIZE;
|
||||
const [pageIndex, setPageIndex] = useState(initialPageIndex ?? 0);
|
||||
|
||||
const initialState = useMemo(
|
||||
() => ({
|
||||
pageSize: initialPageSize ?? DEFAULT_PAGE_SIZE,
|
||||
pageIndex: initialPageIndex ?? 0,
|
||||
pageSize: effectivePageSize,
|
||||
pageIndex: 0,
|
||||
sortBy: initialSortBy,
|
||||
}),
|
||||
[initialPageSize, initialPageIndex, initialSortBy],
|
||||
[effectivePageSize, initialSortBy],
|
||||
);
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
page,
|
||||
rows,
|
||||
prepareRow,
|
||||
gotoPage,
|
||||
setSortBy,
|
||||
state: { pageIndex, sortBy },
|
||||
state: { sortBy },
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
initialState,
|
||||
manualPagination: serverPagination,
|
||||
manualPagination: true,
|
||||
manualSortBy: serverPagination,
|
||||
pageCount: serverPagination
|
||||
? Math.ceil(totalCount / initialState.pageSize)
|
||||
: undefined,
|
||||
autoResetSortBy: false,
|
||||
},
|
||||
useFilters,
|
||||
useSortBy,
|
||||
...(withPagination ? [usePagination] : []),
|
||||
);
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (!withPagination || serverPagination) return rows;
|
||||
const start = pageIndex * effectivePageSize;
|
||||
return rows.slice(start, start + effectivePageSize);
|
||||
}, [withPagination, serverPagination, rows, pageIndex, effectivePageSize]);
|
||||
|
||||
const EmptyWrapperComponent = useMemo(() => {
|
||||
switch (emptyWrapperType) {
|
||||
case EmptyWrapperType.Small:
|
||||
@@ -164,11 +166,6 @@ const RawTableView = ({
|
||||
}
|
||||
}, [emptyWrapperType]);
|
||||
|
||||
const content = useMemo(
|
||||
() => (withPagination ? page : rows),
|
||||
[withPagination, page, rows],
|
||||
);
|
||||
|
||||
const isEmpty = useMemo(
|
||||
() => !loading && content.length === 0,
|
||||
[loading, content.length],
|
||||
@@ -192,10 +189,9 @@ const RawTableView = ({
|
||||
const handlePageChange = useCallback(
|
||||
(p: number) => {
|
||||
if (scrollTopOnPagination) handleScrollToTop();
|
||||
|
||||
gotoPage(p);
|
||||
setPageIndex(p);
|
||||
},
|
||||
[scrollTopOnPagination, handleScrollToTop, gotoPage],
|
||||
[scrollTopOnPagination, handleScrollToTop],
|
||||
);
|
||||
|
||||
const paginationProps = useMemo(() => {
|
||||
@@ -211,7 +207,7 @@ const RawTableView = ({
|
||||
if (serverPagination) {
|
||||
return {
|
||||
pageIndex,
|
||||
pageSize: initialPageSize ?? DEFAULT_PAGE_SIZE,
|
||||
pageSize: effectivePageSize,
|
||||
totalCount,
|
||||
onPageChange: handlePageChange,
|
||||
};
|
||||
@@ -219,7 +215,7 @@ const RawTableView = ({
|
||||
|
||||
return {
|
||||
pageIndex,
|
||||
pageSize: initialPageSize ?? DEFAULT_PAGE_SIZE,
|
||||
pageSize: effectivePageSize,
|
||||
totalCount: data.length,
|
||||
onPageChange: handlePageChange,
|
||||
};
|
||||
@@ -227,28 +223,28 @@ const RawTableView = ({
|
||||
withPagination,
|
||||
serverPagination,
|
||||
pageIndex,
|
||||
initialPageSize,
|
||||
effectivePageSize,
|
||||
totalCount,
|
||||
data.length,
|
||||
handlePageChange,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (serverPagination && pageIndex !== initialState.pageIndex) {
|
||||
if (serverPagination && pageIndex !== (initialPageIndex ?? 0)) {
|
||||
onServerPagination({
|
||||
pageIndex,
|
||||
});
|
||||
}
|
||||
}, [initialState.pageIndex, onServerPagination, pageIndex, serverPagination]);
|
||||
}, [initialPageIndex, onServerPagination, pageIndex, serverPagination]);
|
||||
|
||||
useEffect(() => {
|
||||
if (serverPagination && !isEqual(sortBy, initialState.sortBy)) {
|
||||
if (serverPagination && !isEqual(sortBy, initialSortBy)) {
|
||||
onServerPagination({
|
||||
pageIndex: 0,
|
||||
sortBy,
|
||||
});
|
||||
}
|
||||
}, [initialState.sortBy, onServerPagination, serverPagination, sortBy]);
|
||||
}, [initialSortBy, onServerPagination, serverPagination, sortBy]);
|
||||
|
||||
return (
|
||||
<TableViewStyles {...props} ref={tableRef}>
|
||||
|
||||
@@ -97,8 +97,8 @@ const StyledPlus = styled.span`
|
||||
|
||||
export default function TruncatedList<ListItemType>({
|
||||
items,
|
||||
renderVisibleItem = item => item,
|
||||
renderTooltipItem = item => item,
|
||||
renderVisibleItem = item => item as ReactNode,
|
||||
renderTooltipItem = item => item as ReactNode,
|
||||
getKey = item => item as unknown as Key,
|
||||
maxLinks = 20,
|
||||
}: TruncatedListProps<ListItemType>) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useChangeEffect } from './useChangeEffect';
|
||||
|
||||
test('call callback the first time with undefined and value', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useComponentDidMount } from './useComponentDidMount';
|
||||
|
||||
test('the effect should only be executed on the first render', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useComponentDidUpdate } from './useComponentDidUpdate';
|
||||
|
||||
test('the effect should not be executed on the first render', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useElementOnScreen } from './useElementOnScreen';
|
||||
|
||||
const observeMock = jest.fn();
|
||||
@@ -46,10 +46,9 @@ test('should return isSticky as true when intersectionRatio < 1', async () => {
|
||||
useElementOnScreen({ rootMargin: '-50px 0px 0px 0px' }),
|
||||
);
|
||||
const callback = IntersectionObserverMock.mock.calls[0][0];
|
||||
const callBack = callback([{ isIntersecting: true, intersectionRatio: 0.5 }]);
|
||||
const observer = new IntersectionObserverMock(callBack, {});
|
||||
const newDiv = document.createElement('div');
|
||||
observer.observe(newDiv);
|
||||
act(() => {
|
||||
callback([{ isIntersecting: true, intersectionRatio: 0.5 }]);
|
||||
});
|
||||
expect(hook.result.current[1]).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -58,10 +57,9 @@ test('should return isSticky as false when intersectionRatio >= 1', async () =>
|
||||
useElementOnScreen({ rootMargin: '-50px 0px 0px 0px' }),
|
||||
);
|
||||
const callback = IntersectionObserverMock.mock.calls[0][0];
|
||||
const callBack = callback([{ isIntersecting: true, intersectionRatio: 1 }]);
|
||||
const observer = new IntersectionObserverMock(callBack, {});
|
||||
const newDiv = document.createElement('div');
|
||||
observer.observe(newDiv);
|
||||
act(() => {
|
||||
callback([{ isIntersecting: true, intersectionRatio: 1 }]);
|
||||
});
|
||||
expect(hook.result.current[1]).toEqual(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { usePrevious } from './usePrevious';
|
||||
|
||||
test('get undefined on the first render when initialValue is not defined', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import useCSSTextTruncation from './useCSSTextTruncation';
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RefObject } from 'react';
|
||||
import useChildElementTruncation from './useChildElementTruncation';
|
||||
|
||||
|
||||
@@ -249,7 +249,8 @@ export type Extensions = Partial<{
|
||||
'navbar.right-menu.item.icon': ComponentType<RightMenuItemIconProps>;
|
||||
'navbar.right': ComponentType;
|
||||
'report-modal.dropdown.item.icon': ComponentType;
|
||||
'root.context.provider': ComponentType;
|
||||
'root.context.provider': ComponentType<{ children?: ReactNode }>;
|
||||
|
||||
'welcome.message': ComponentType;
|
||||
'welcome.banner': ComponentType;
|
||||
'welcome.main.replacement': ComponentType;
|
||||
|
||||
@@ -143,7 +143,7 @@ describe('SuperChart', () => {
|
||||
);
|
||||
|
||||
expect(await screen.findByText('Custom Fallback!')).toBeInTheDocument();
|
||||
expect(CustomFallbackComponent).toHaveBeenCalledTimes(1);
|
||||
expect(CustomFallbackComponent).toHaveBeenCalled();
|
||||
});
|
||||
test('call onErrorBoundary', async () => {
|
||||
expectedErrors = 1;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@apache-superset/core": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -34,6 +34,6 @@
|
||||
"@apache-superset/core": "*",
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@apache-superset/core": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@apache-superset/core": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -36,6 +36,6 @@
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@apache-superset/core": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
"@superset-ui/core": "*",
|
||||
"@apache-superset/core": "*",
|
||||
"@testing-library/jest-dom": "*",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@apache-superset/core": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -39,6 +39,6 @@
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@apache-superset/core": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,6 @@
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"dayjs": "^1.11.19",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,14 +39,13 @@
|
||||
"@apache-superset/core": "*",
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@testing-library/dom": "^8.20.1",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "*",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react-hooks": "*",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "*",
|
||||
"@types/react": "*",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -247,7 +247,7 @@ const AgGridDataTable: FunctionComponent<AgGridTableProps> = memo(
|
||||
[serverPagination, debouncedSearch, searchId],
|
||||
);
|
||||
|
||||
const handleColSort = (colId: string, sortDir: string) => {
|
||||
const handleColSort = (colId: string, sortDir: string | null) => {
|
||||
const isSortable = shouldSort({
|
||||
colId,
|
||||
sortDir,
|
||||
@@ -301,10 +301,12 @@ const AgGridDataTable: FunctionComponent<AgGridTableProps> = memo(
|
||||
};
|
||||
|
||||
const handleColumnHeaderClick = useCallback(
|
||||
params => {
|
||||
(params: { column?: { colId?: string; sort?: string | null } }) => {
|
||||
const colId = params?.column?.colId;
|
||||
const sortDir = params?.column?.sort;
|
||||
handleColSort(colId, sortDir);
|
||||
if (colId && sortDir !== undefined) {
|
||||
handleColSort(colId, sortDir);
|
||||
}
|
||||
},
|
||||
[serverPagination, gridInitialState, percentMetrics, onSortChange],
|
||||
);
|
||||
|
||||
@@ -147,7 +147,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
|
||||
]);
|
||||
|
||||
const handleColumnStateChange = useCallback(
|
||||
agGridState => {
|
||||
(agGridState: Record<string, unknown>) => {
|
||||
if (onChartStateChange) {
|
||||
onChartStateChange(agGridState);
|
||||
}
|
||||
|
||||
@@ -70,5 +70,9 @@ export const TextCellRenderer = (params: CellRendererProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
return <div>{valueFormatted ?? value}</div>;
|
||||
return (
|
||||
<div>
|
||||
{valueFormatted ?? (value instanceof Date ? value.toISOString() : value)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ export const shouldSort = ({
|
||||
gridInitialState,
|
||||
}: {
|
||||
colId: string;
|
||||
sortDir: string;
|
||||
sortDir: string | null;
|
||||
percentMetrics: string[];
|
||||
serverPagination: boolean;
|
||||
gridInitialState: GridState;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import {
|
||||
supersetTheme,
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"geostyler-wfs-parser": "^3.0.1",
|
||||
"ol": "^10.8.0",
|
||||
"polished": "*",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import Layer from 'ol/layer/Layer';
|
||||
import { FrameState } from 'ol/Map';
|
||||
import { apply as applyTransform } from 'ol/transform';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
import { SupersetTheme } from '@apache-superset/core/theme';
|
||||
import { ChartConfig, ChartLayerOptions, ChartSizeValues } from '../types';
|
||||
import { createChartComponent } from '../util/chartUtil';
|
||||
@@ -31,7 +31,14 @@ import Loader from '../images/loading.gif';
|
||||
* Custom OpenLayers layer that displays charts on given locations.
|
||||
*/
|
||||
export class ChartLayer extends Layer {
|
||||
charts: any[] = [];
|
||||
charts: {
|
||||
htmlElement: HTMLDivElement;
|
||||
root: Root;
|
||||
coordinate: number[];
|
||||
width: number;
|
||||
height: number;
|
||||
feature: any;
|
||||
}[] = [];
|
||||
|
||||
chartConfigs: ChartConfig = {
|
||||
type: 'FeatureCollection',
|
||||
@@ -166,7 +173,7 @@ export class ChartLayer extends Layer {
|
||||
*/
|
||||
removeAllChartElements() {
|
||||
this.charts.forEach(chart => {
|
||||
ReactDOM.unmountComponentAtNode(chart.htmlElement);
|
||||
chart.root.unmount();
|
||||
chart.htmlElement.remove();
|
||||
});
|
||||
this.charts = [];
|
||||
@@ -191,10 +198,12 @@ export class ChartLayer extends Layer {
|
||||
this.theme,
|
||||
this.locale,
|
||||
);
|
||||
ReactDOM.render(chartComponent, container);
|
||||
const root = createRoot(container);
|
||||
root.render(chartComponent);
|
||||
|
||||
return {
|
||||
htmlElement: container,
|
||||
root,
|
||||
coordinate: getProjectedCoordinateFromPointGeoJson(feature.geometry),
|
||||
width: chartWidth,
|
||||
height: chartHeight,
|
||||
@@ -227,7 +236,7 @@ export class ChartLayer extends Layer {
|
||||
this.theme,
|
||||
this.locale,
|
||||
);
|
||||
ReactDOM.render(chartComponent, chart.htmlElement);
|
||||
chart.root.render(chartComponent);
|
||||
|
||||
return {
|
||||
...chart,
|
||||
|
||||
@@ -41,6 +41,11 @@ describe('ChartLayer', () => {
|
||||
chartLayer.charts = [
|
||||
{
|
||||
htmlElement: document.createElement('div'),
|
||||
root: { render: jest.fn(), unmount: jest.fn() } as any,
|
||||
coordinate: [0, 0],
|
||||
width: 100,
|
||||
height: 100,
|
||||
feature: {},
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"dayjs": "^1.11.19",
|
||||
"echarts": "*",
|
||||
"memoize-one": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function EchartsMixedTimeseries({
|
||||
);
|
||||
|
||||
const getCrossFilterDataMask = useCallback(
|
||||
(seriesName, seriesIndex) => {
|
||||
(seriesName: string, seriesIndex: number) => {
|
||||
const selected: string[] = Object.values(selectedValues || {});
|
||||
let values: string[];
|
||||
if (selected.includes(seriesName)) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
import { useCallback } from 'react';
|
||||
import Echart from '../components/Echart';
|
||||
import { NULL_STRING } from '../constants';
|
||||
import { EventHandlers } from '../types';
|
||||
import { EventHandlers, TreePathInfo } from '../types';
|
||||
import { extractTreePathInfo } from './constants';
|
||||
import { TreemapTransformedProps } from './types';
|
||||
import { formatSeriesName } from '../utils/series';
|
||||
@@ -46,7 +46,7 @@ export default function EchartsTreemap({
|
||||
coltypeMapping,
|
||||
}: TreemapTransformedProps) {
|
||||
const getCrossFilterDataMask = useCallback(
|
||||
(data, treePathInfo) => {
|
||||
(data: Record<string, unknown>, treePathInfo: TreePathInfo[]) => {
|
||||
if (data?.children) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ export default function EchartsTreemap({
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(data, treePathInfo) => {
|
||||
(data: Record<string, unknown>, treePathInfo: TreePathInfo[]) => {
|
||||
if (!emitCrossFilters || groupby.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,95 +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.
|
||||
-->
|
||||
|
||||
## @superset-ui/plugin-chart-handlebars
|
||||
|
||||
[](https://www.npmjs.com/package/@superset-ui/plugin-chart-handlebars)
|
||||
[](https://libraries.io/npm/@superset-ui%2Fplugin-chart-handlebars)
|
||||
|
||||
This plugin renders the data using a handlebars template.
|
||||
|
||||
### Usage
|
||||
|
||||
Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to
|
||||
lookup this chart throughout the app.
|
||||
|
||||
```js
|
||||
import HandlebarsChartPlugin from '@superset-ui/plugin-chart-handlebars';
|
||||
|
||||
new HandlebarsChartPlugin().configure({ key: 'handlebars' }).register();
|
||||
```
|
||||
|
||||
Then use it via `SuperChart`. See
|
||||
[storybook](https://apache-superset.github.io/superset-ui/?selectedKind=plugin-chart-handlebars) for
|
||||
more details.
|
||||
|
||||
```js
|
||||
<SuperChart
|
||||
chartType="handlebars"
|
||||
width={600}
|
||||
height={600}
|
||||
formData={...}
|
||||
queriesData={[{
|
||||
data: {...},
|
||||
}]}
|
||||
/>
|
||||
```
|
||||
|
||||
### File structure generated
|
||||
|
||||
```
|
||||
├── package.json
|
||||
├── README.md
|
||||
├── tsconfig.json
|
||||
├── src
|
||||
│ ├── Handlebars.tsx
|
||||
│ ├── images
|
||||
│ │ └── thumbnail.png
|
||||
│ ├── index.ts
|
||||
│ ├── plugin
|
||||
│ │ ├── buildQuery.ts
|
||||
│ │ ├── controlPanel.ts
|
||||
│ │ ├── index.ts
|
||||
│ │ └── transformProps.ts
|
||||
│ └── types.ts
|
||||
├── test
|
||||
│ └── index.test.ts
|
||||
└── types
|
||||
└── external.d.ts
|
||||
```
|
||||
|
||||
### Available Handlebars Helpers in Superset
|
||||
|
||||
Below, you will find a list of all currently registered helpers in the Handlebars plugin for Superset. These helpers are registered and managed in the file [`HandlebarsViewer.tsx`](./path/to/HandlebarsViewer.tsx).
|
||||
|
||||
#### List of Registered Helpers:
|
||||
|
||||
1. **`dateFormat`**: Formats a date using a specified format.
|
||||
- **Usage**: `{{dateFormat my_date format="MMMM YYYY"}}`
|
||||
- **Default format**: `YYYY-MM-DD`.
|
||||
|
||||
2. **`stringify`**: Converts an object into a JSON string or returns a string representation of non-object values.
|
||||
- **Usage**: `{{stringify myObj}}`.
|
||||
|
||||
3. **`formatNumber`**: Formats a number using locale-specific formatting.
|
||||
- **Usage**: `{{formatNumber number locale="en-US"}}`.
|
||||
- **Default locale**: `en-US`.
|
||||
|
||||
4. **`parseJson`**: Parses a JSON string into a JavaScript object.
|
||||
- **Usage**: `{{parseJson jsonString}}`.
|
||||
@@ -39,9 +39,9 @@
|
||||
"handlebars": "^4.7.8",
|
||||
"lodash": "^4.18.1",
|
||||
"dayjs": "^1.11.19",
|
||||
"react": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-dom": "^17.0.2"
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^30.0.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* 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
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { validateNonEmpty } from '@superset-ui/core';
|
||||
import { useTheme } from '@apache-superset/core/theme';
|
||||
import { InfoTooltip, SafeMarkdown } from '@superset-ui/core/components';
|
||||
import { InfoTooltip } from '@superset-ui/core/components';
|
||||
import { CodeEditor } from '../../components/CodeEditor/CodeEditor';
|
||||
import { ControlHeader } from '../../components/ControlHeader/controlHeader';
|
||||
import { debounceFunc } from '../../consts';
|
||||
@@ -37,44 +37,30 @@ const HandlebarsTemplateControl = (
|
||||
props: CustomControlConfig<HandlebarsCustomControlProps>,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const val = String(
|
||||
props?.value ? props?.value : props?.default ? props?.default : '',
|
||||
);
|
||||
|
||||
const helperDescriptionsHeader = t(
|
||||
'Available Handlebars Helpers in Superset:',
|
||||
);
|
||||
|
||||
const helperDescriptions = [
|
||||
{ key: 'dateFormat', descKey: 'Formats a date using a specified format.' },
|
||||
{ key: 'stringify', descKey: 'Converts an object to a JSON string.' },
|
||||
{
|
||||
key: 'formatNumber',
|
||||
descKey: 'Formats a number using locale-specific formatting.',
|
||||
},
|
||||
{
|
||||
key: 'parseJson',
|
||||
descKey: 'Parses a JSON string into a JavaScript object.',
|
||||
},
|
||||
];
|
||||
|
||||
const helpersTooltipContent = `
|
||||
${helperDescriptionsHeader}
|
||||
|
||||
${helperDescriptions
|
||||
.map(({ key, descKey }) => `- **${key}**: ${t(descKey)}`)
|
||||
.join('\n')}
|
||||
`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ControlHeader>
|
||||
<div>
|
||||
{props.label}
|
||||
{typeof props.label === 'function' ? null : props.label}
|
||||
<InfoTooltip
|
||||
iconStyle={{ marginLeft: theme.sizeUnit }}
|
||||
tooltip={<SafeMarkdown source={helpersTooltipContent} />}
|
||||
tooltip={
|
||||
<span>
|
||||
{t('See ')}{' '}
|
||||
<a
|
||||
href="https://superset.apache.org/docs/using-superset/handlebars-chart"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('the Handlebars chart documentation')}
|
||||
</a>{' '}
|
||||
{t('for a list of available helpers.')}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</ControlHeader>
|
||||
@@ -104,7 +90,6 @@ export const handlebarsTemplateControlSetItem: ControlSetItem = {
|
||||
isInt: false,
|
||||
renderTrigger: true,
|
||||
valueKey: null,
|
||||
|
||||
validators: [validateNonEmpty],
|
||||
mapStateToProps: ({ form_data }) => ({
|
||||
value: form_data?.handlebarsTemplate ?? form_data?.handlebars_template,
|
||||
|
||||
@@ -49,7 +49,7 @@ const StyleControl = (props: CustomControlConfig<StyleCustomControlProps>) => {
|
||||
<div>
|
||||
<ControlHeader>
|
||||
<div>
|
||||
{props.label}
|
||||
{typeof props.label === 'function' ? null : props.label}
|
||||
{htmlSanitization && (
|
||||
<InfoTooltip
|
||||
iconStyle={{ marginLeft: theme.sizeUnit }}
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
"@superset-ui/core": "*",
|
||||
"lodash": "^4.18.1",
|
||||
"prop-types": "*",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.29.0",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"mapbox-gl": "^3.22.0",
|
||||
"mapbox-gl": "^3.23.0",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"react-map-gl": "^8.1.0",
|
||||
"supercluster": "^8.0.1"
|
||||
@@ -36,8 +36,8 @@
|
||||
"@apache-superset/core": "*",
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"react": "^17.0.2 || ^19.0.0",
|
||||
"react-dom": "^17.0.2 || ^19.0.0"
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -39,15 +39,14 @@
|
||||
"@apache-superset/core": "*",
|
||||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"@testing-library/dom": "^8.20.1",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "*",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react-hooks": "*",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "*",
|
||||
"@types/react": "*",
|
||||
"match-sorter": "^8.2.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
getTimeFormatterForGranularity,
|
||||
BinaryQueryObjectFilterClause,
|
||||
extractTextFromHTML,
|
||||
TimeGranularity,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
styled,
|
||||
@@ -309,6 +310,67 @@ function SelectPageSize({
|
||||
const getNoResultsMessage = (filter: string) =>
|
||||
filter ? t('No matching records found') : t('No records found');
|
||||
|
||||
/**
|
||||
* Calculates the inclusive/exclusive temporal range for a bucket.
|
||||
* standard SQL range pattern: [start, end)
|
||||
*/
|
||||
function getTimeRangeFromGranularity(
|
||||
startTime: Date,
|
||||
granularity: TimeGranularity,
|
||||
): [Date, Date] {
|
||||
const time = startTime.getTime();
|
||||
const date = startTime.getUTCDate();
|
||||
const month = startTime.getUTCMonth();
|
||||
const year = startTime.getUTCFullYear();
|
||||
|
||||
// Constants
|
||||
const MS_IN_SECOND = 1000;
|
||||
const MS_IN_MINUTE = 60 * MS_IN_SECOND;
|
||||
const MS_IN_HOUR = 60 * MS_IN_MINUTE;
|
||||
|
||||
switch (granularity) {
|
||||
case TimeGranularity.SECOND:
|
||||
return [startTime, new Date(time + MS_IN_SECOND)];
|
||||
case TimeGranularity.MINUTE:
|
||||
return [startTime, new Date(time + MS_IN_MINUTE)];
|
||||
case TimeGranularity.FIVE_MINUTES:
|
||||
return [startTime, new Date(time + MS_IN_MINUTE * 5)];
|
||||
case TimeGranularity.TEN_MINUTES:
|
||||
return [startTime, new Date(time + MS_IN_MINUTE * 10)];
|
||||
case TimeGranularity.FIFTEEN_MINUTES:
|
||||
return [startTime, new Date(time + MS_IN_MINUTE * 15)];
|
||||
case TimeGranularity.THIRTY_MINUTES:
|
||||
return [startTime, new Date(time + MS_IN_MINUTE * 30)];
|
||||
case TimeGranularity.HOUR:
|
||||
return [startTime, new Date(time + MS_IN_HOUR)];
|
||||
case TimeGranularity.DAY:
|
||||
case TimeGranularity.DATE:
|
||||
return [startTime, new Date(Date.UTC(year, month, date + 1))];
|
||||
case TimeGranularity.WEEK:
|
||||
case TimeGranularity.WEEK_STARTING_SUNDAY:
|
||||
case TimeGranularity.WEEK_STARTING_MONDAY:
|
||||
return [startTime, new Date(Date.UTC(year, month, date + 7))];
|
||||
case TimeGranularity.WEEK_ENDING_SATURDAY:
|
||||
case TimeGranularity.WEEK_ENDING_SUNDAY:
|
||||
// Week-ending buckets are labeled by the bucket's final day.
|
||||
return [
|
||||
new Date(Date.UTC(year, month, date - 6)),
|
||||
new Date(Date.UTC(year, month, date + 1)),
|
||||
];
|
||||
case TimeGranularity.MONTH:
|
||||
return [startTime, new Date(Date.UTC(year, month + 1, 1))];
|
||||
case TimeGranularity.QUARTER:
|
||||
return [
|
||||
startTime,
|
||||
new Date(Date.UTC(year, Math.floor(month / 3) * 3 + 3, 1)),
|
||||
];
|
||||
case TimeGranularity.YEAR:
|
||||
return [startTime, new Date(Date.UTC(year + 1, 0, 1))];
|
||||
default:
|
||||
return [startTime, new Date(Date.UTC(year, month, date + 1))];
|
||||
}
|
||||
}
|
||||
|
||||
export default function TableChart<D extends DataRecord = DataRecord>(
|
||||
props: TableChartTransformedProps<D> & {
|
||||
sticky?: DataTableProps<D>['sticky'];
|
||||
@@ -471,7 +533,12 @@ export default function TableChart<D extends DataRecord = DataRecord>(
|
||||
// so that cross-filters work on the receiving chart
|
||||
const resolvedCol = columnLabelToNameMap[col] ?? col;
|
||||
const val = ensureIsArray(updatedFilters?.[col]);
|
||||
if (!val.length)
|
||||
if (
|
||||
!val.length ||
|
||||
val[0] === null ||
|
||||
(val[0] instanceof DateWithFormatter &&
|
||||
val[0].input === null)
|
||||
)
|
||||
return {
|
||||
col: resolvedCol,
|
||||
op: 'IS NULL' as const,
|
||||
@@ -578,15 +645,47 @@ export default function TableChart<D extends DataRecord = DataRecord>(
|
||||
const drillToDetailFilters: BinaryQueryObjectFilterClause[] = [];
|
||||
filteredColumnsMeta.forEach(col => {
|
||||
if (!col.isMetric) {
|
||||
let dataRecordValue = value[col.key];
|
||||
dataRecordValue = extractTextFromHTML(dataRecordValue);
|
||||
const dataRecordValue = value[col.key];
|
||||
|
||||
drillToDetailFilters.push({
|
||||
col: col.key,
|
||||
op: '==',
|
||||
val: dataRecordValue as string | number | boolean,
|
||||
formattedVal: formatColumnValue(col, dataRecordValue)[1],
|
||||
});
|
||||
// FIX: Explicitly handle NULL values for temporal and non-temporal columns
|
||||
// DateWithFormatter objects wrap nulls, so we must check both
|
||||
if (
|
||||
dataRecordValue == null ||
|
||||
(dataRecordValue instanceof DateWithFormatter &&
|
||||
dataRecordValue.input == null)
|
||||
) {
|
||||
drillToDetailFilters.push({
|
||||
col: col.key,
|
||||
op: 'IS NULL' as any,
|
||||
val: null,
|
||||
});
|
||||
} else if (col.dataType === GenericDataType.Temporal && timeGrain) {
|
||||
const startTime =
|
||||
dataRecordValue instanceof Date
|
||||
? dataRecordValue
|
||||
: new Date(dataRecordValue as string | number);
|
||||
|
||||
const [rangeStartTime, rangeEndTime] =
|
||||
getTimeRangeFromGranularity(startTime, timeGrain);
|
||||
const timeRangeValue = `${rangeStartTime.toISOString()} : ${rangeEndTime.toISOString()}`;
|
||||
|
||||
drillToDetailFilters.push({
|
||||
col: col.key,
|
||||
op: 'TEMPORAL_RANGE',
|
||||
val: timeRangeValue,
|
||||
grain: timeGrain,
|
||||
formattedVal: formatColumnValue(col, dataRecordValue)[1],
|
||||
});
|
||||
} else {
|
||||
// Non-temporal columns use exact match
|
||||
const sanitizedValue = extractTextFromHTML(dataRecordValue);
|
||||
drillToDetailFilters.push({
|
||||
col: col.key,
|
||||
op: '==',
|
||||
val: sanitizedValue as string | number | boolean,
|
||||
formattedVal: formatColumnValue(col, sanitizedValue)[1],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
onContextMenu(clientX, clientY, {
|
||||
@@ -600,7 +699,11 @@ export default function TableChart<D extends DataRecord = DataRecord>(
|
||||
filters: [
|
||||
{
|
||||
col: cellPoint.key,
|
||||
op: '==',
|
||||
op: (cellPoint.value == null ||
|
||||
(cellPoint.value instanceof DateWithFormatter &&
|
||||
cellPoint.value.input == null)
|
||||
? 'IS NULL'
|
||||
: '==') as any,
|
||||
val: extractTextFromHTML(cellPoint.value),
|
||||
},
|
||||
],
|
||||
@@ -615,6 +718,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
|
||||
isRawRecords,
|
||||
filteredColumnsMeta,
|
||||
getCrossFilterDataMask,
|
||||
timeGrain,
|
||||
]);
|
||||
|
||||
const getHeaderColumns = useCallback(
|
||||
|
||||
@@ -130,13 +130,12 @@ const processComparisonTotals = (
|
||||
Object.keys(totalRecord).forEach(key => {
|
||||
if (totalRecord[key] !== undefined && !key.includes(comparisonSuffix)) {
|
||||
transformedTotals[`Main ${key}`] =
|
||||
parseInt(transformedTotals[`Main ${key}`]?.toString() || '0', 10) +
|
||||
parseInt(totalRecord[key]?.toString() || '0', 10);
|
||||
parseFloat(transformedTotals[`Main ${key}`]?.toString() || '0') +
|
||||
parseFloat(totalRecord[key]?.toString() || '0');
|
||||
transformedTotals[`# ${key}`] =
|
||||
parseInt(transformedTotals[`# ${key}`]?.toString() || '0', 10) +
|
||||
parseInt(
|
||||
parseFloat(transformedTotals[`# ${key}`]?.toString() || '0') +
|
||||
parseFloat(
|
||||
totalRecord[`${key}__${comparisonSuffix}`]?.toString() || '0',
|
||||
10,
|
||||
);
|
||||
const { valueDifference, percentDifferenceNum } = calculateDifferences(
|
||||
transformedTotals[`Main ${key}`] as number,
|
||||
|
||||
@@ -2360,3 +2360,76 @@ describe('plugin-chart-table', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* DRILL-TO-DETAIL FIX VERIFICATION (#23847)
|
||||
*/
|
||||
describe('Drill-to-Detail Temporal Range Logic', () => {
|
||||
const renderChartAndOpenContextMenu = (
|
||||
timeGrain?: TimeGranularity,
|
||||
timestampValue?: string | number | null,
|
||||
) => {
|
||||
const onContextMenu = jest.fn();
|
||||
const data = cloneDeep(testData.basic);
|
||||
|
||||
if (timestampValue !== undefined) {
|
||||
data.queriesData[0].data[0].__timestamp = timestampValue;
|
||||
}
|
||||
|
||||
const props = transformProps({
|
||||
...data,
|
||||
rawFormData: {
|
||||
...data.rawFormData,
|
||||
...(timeGrain ? { time_grain_sqla: timeGrain } : {}),
|
||||
},
|
||||
hooks: { onAddFilter: jest.fn(), onContextMenu, setDataMask: jest.fn() },
|
||||
});
|
||||
render(<TableChart {...props} sticky={false} />);
|
||||
|
||||
const tbody = screen.getAllByRole('rowgroup')[1];
|
||||
fireEvent.contextMenu(tbody.querySelectorAll('td')[0]);
|
||||
|
||||
const [, , { drillToDetail }] = onContextMenu.mock.calls[0];
|
||||
return drillToDetail.find((f: any) => f.col === '__timestamp');
|
||||
};
|
||||
|
||||
test('uses TEMPORAL_RANGE for monthly grain', () => {
|
||||
const filter = renderChartAndOpenContextMenu(TimeGranularity.MONTH);
|
||||
|
||||
expect(filter.op).toBe('TEMPORAL_RANGE');
|
||||
expect(filter.val).toContain(
|
||||
'2020-01-01T12:34:56.000Z : 2020-02-01T00:00:00.000Z',
|
||||
);
|
||||
});
|
||||
|
||||
test('uses the full bucket for week ending sunday grain', () => {
|
||||
const filter = renderChartAndOpenContextMenu(
|
||||
TimeGranularity.WEEK_ENDING_SUNDAY,
|
||||
'2020-01-05T00:00:00',
|
||||
);
|
||||
|
||||
expect(filter.op).toBe('TEMPORAL_RANGE');
|
||||
expect(filter.val).toBe(
|
||||
'2019-12-30T00:00:00.000Z : 2020-01-06T00:00:00.000Z',
|
||||
);
|
||||
});
|
||||
|
||||
test('uses the full bucket for week ending saturday grain', () => {
|
||||
const filter = renderChartAndOpenContextMenu(
|
||||
TimeGranularity.WEEK_ENDING_SATURDAY,
|
||||
'2020-01-04T00:00:00',
|
||||
);
|
||||
|
||||
expect(filter.op).toBe('TEMPORAL_RANGE');
|
||||
expect(filter.val).toBe(
|
||||
'2019-12-29T00:00:00.000Z : 2020-01-05T00:00:00.000Z',
|
||||
);
|
||||
});
|
||||
|
||||
test('correctly handles NULL values by emitting IS NULL instead of 1970 timestamp', () => {
|
||||
const filter = renderChartAndOpenContextMenu(TimeGranularity.MONTH, null);
|
||||
|
||||
expect(filter.op).toBe('IS NULL');
|
||||
expect(filter.val).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@superset-ui/core": "*",
|
||||
"@types/lodash": "*",
|
||||
"@types/react": "*",
|
||||
"react": "^17.0.2"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3-cloud": "^1.2.9"
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
"@superset-ui/core": "*",
|
||||
"dayjs": "^1.11.19",
|
||||
"mapbox-gl": ">=1.0.0",
|
||||
"react": "^17.0.2 || ^19.0.0",
|
||||
"react-dom": "^17.0.2 || ^19.0.0"
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"mapbox-gl": {
|
||||
|
||||
@@ -139,7 +139,8 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
|
||||
const setTooltip = useCallback((tooltip: TooltipProps['tooltip']) => {
|
||||
const { current } = containerRef;
|
||||
if (current) {
|
||||
current.setTooltip(tooltip);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(current as any).setTooltip(tooltip);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -194,5 +194,5 @@ export const DeckGLContainerStyledWrapper = styled(DeckGLContainer)`
|
||||
`;
|
||||
|
||||
export type DeckGLContainerHandle = typeof DeckGLContainer & {
|
||||
setTooltip: (tooltip: ReactNode) => void;
|
||||
setTooltip: (tooltip: TooltipProps['tooltip']) => void;
|
||||
};
|
||||
|
||||
@@ -97,10 +97,10 @@ describe('getAggFunc', () => {
|
||||
});
|
||||
|
||||
describe('commonLayerProps', () => {
|
||||
const mockSetTooltip = jest.fn();
|
||||
const mockSetTooltip = jest.fn() as any;
|
||||
const mockSetTooltipContent = jest.fn(
|
||||
() => (o: JsonObject) => `Tooltip for ${o}`,
|
||||
);
|
||||
) as any;
|
||||
const mockOnSelect = jest.fn();
|
||||
|
||||
test('returns correct props when js_tooltip is provided', () => {
|
||||
|
||||
@@ -97,6 +97,7 @@ export function createWrapper(options?: Options) {
|
||||
}
|
||||
|
||||
if (useDnd) {
|
||||
// @ts-expect-error react-dnd types not updated for React 18
|
||||
result = <DndProvider backend={HTML5Backend}>{result}</DndProvider>;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ const ContentWrapper = styled.div`
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
const AppLayout: React.FC = ({ children }) => {
|
||||
const AppLayout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
||||
const queryEditorId = useSelector<SqlLabRootState, string>(
|
||||
({ sqlLab: { tabHistory } }) => tabHistory.slice(-1)[0],
|
||||
);
|
||||
|
||||
@@ -19,16 +19,14 @@
|
||||
import { isValidElement } from 'react';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
import ColumnElement from 'src/SqlLab/components/ColumnElement';
|
||||
import { mockedActions, table } from 'src/SqlLab/fixtures';
|
||||
import { table } from 'src/SqlLab/fixtures';
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
|
||||
describe('ColumnElement', () => {
|
||||
const mockedProps = {
|
||||
actions: mockedActions,
|
||||
column: table.columns[0],
|
||||
};
|
||||
test('is valid with props', () => {
|
||||
expect(isValidElement(<ColumnElement {...mockedProps} />)).toBe(true);
|
||||
expect(isValidElement(<ColumnElement column={table.columns[0]} />)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
test('renders a proper primary key', () => {
|
||||
const { container } = render(<ColumnElement column={table.columns[0]} />);
|
||||
|
||||
@@ -116,19 +116,31 @@ describe('EditorWrapper', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('skips rerendering for updating cursor position', () => {
|
||||
test('skips rerendering for updating cursor position', async () => {
|
||||
const store = createStore(initialState, reducerIndex);
|
||||
setup(defaultQueryEditor, store);
|
||||
|
||||
expect(MockEditorHost).toHaveBeenCalled();
|
||||
const renderCount = MockEditorHost.mock.calls.length;
|
||||
await waitFor(() => expect(MockEditorHost).toHaveBeenCalled());
|
||||
const renderCountBeforeCursor = MockEditorHost.mock.calls.length;
|
||||
const updatedCursorPosition = { row: 1, column: 9 };
|
||||
store.dispatch(
|
||||
queryEditorSetCursorPosition(defaultQueryEditor, updatedCursorPosition),
|
||||
act(() => {
|
||||
store.dispatch(
|
||||
queryEditorSetCursorPosition(defaultQueryEditor, updatedCursorPosition),
|
||||
);
|
||||
});
|
||||
// Cursor position change should NOT trigger a re-render
|
||||
expect(MockEditorHost).toHaveBeenCalledTimes(renderCountBeforeCursor);
|
||||
|
||||
const renderCountBeforeDb = MockEditorHost.mock.calls.length;
|
||||
act(() => {
|
||||
store.dispatch(queryEditorSetDb(defaultQueryEditor, 2));
|
||||
});
|
||||
// DB change SHOULD trigger a re-render
|
||||
await waitFor(() =>
|
||||
expect(MockEditorHost.mock.calls.length).toBeGreaterThan(
|
||||
renderCountBeforeDb,
|
||||
),
|
||||
);
|
||||
expect(MockEditorHost).toHaveBeenCalledTimes(renderCount);
|
||||
store.dispatch(queryEditorSetDb(defaultQueryEditor, 2));
|
||||
expect(MockEditorHost).toHaveBeenCalledTimes(renderCount + 1);
|
||||
});
|
||||
|
||||
test('clears selectedText when selection becomes empty', async () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { COMMON_ERR_MESSAGES } from '@superset-ui/core';
|
||||
import {
|
||||
createWrapper,
|
||||
@@ -121,7 +121,7 @@ test('skips fetching validation if validator is undefined', () => {
|
||||
});
|
||||
|
||||
test('returns validation if validator is configured', async () => {
|
||||
const { result, waitFor } = initialize(true);
|
||||
const { result } = initialize(true);
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.callHistory.calls(queryValidationApiRoute)).toHaveLength(
|
||||
1,
|
||||
@@ -143,7 +143,7 @@ test('returns server error description', async () => {
|
||||
fetchMock.post(queryValidationApiRoute, {
|
||||
throws: new Error(errorMessage),
|
||||
});
|
||||
const { result, waitFor } = initialize(true);
|
||||
const { result } = initialize(true);
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(result.current.data).toEqual([
|
||||
@@ -164,7 +164,7 @@ test('returns session expire description when CSRF token expired', async () => {
|
||||
fetchMock.post(queryValidationApiRoute, {
|
||||
throws: new Error(errorMessage),
|
||||
});
|
||||
const { result, waitFor } = initialize(true);
|
||||
const { result } = initialize(true);
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(result.current.data).toEqual([
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { getExtensionsRegistry } from '@superset-ui/core';
|
||||
import {
|
||||
createWrapper,
|
||||
@@ -104,7 +104,7 @@ test('returns keywords including fetched function_names data', async () => {
|
||||
const dbFunctionNamesApiRoute = `glob:*/api/v1/database/${expectDbId}/function_names/`;
|
||||
fetchMock.get(dbFunctionNamesApiRoute, fakeFunctionNamesApiResult);
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useKeywords({
|
||||
queryEditorId: 'testqueryid',
|
||||
@@ -241,7 +241,7 @@ test('returns column keywords among selected tables', async () => {
|
||||
);
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useKeywords({
|
||||
queryEditorId: expectQueryEditorId,
|
||||
@@ -302,7 +302,7 @@ test('returns long keywords with detail', async () => {
|
||||
),
|
||||
);
|
||||
});
|
||||
const { result, waitFor } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useKeywords({
|
||||
queryEditorId: 'testqueryid',
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { FC } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { styled, css } from '@apache-superset/core/theme';
|
||||
import { ModalTrigger } from '@superset-ui/core/components';
|
||||
@@ -92,7 +92,7 @@ const ShortcutCode = styled.code`
|
||||
padding: ${({ theme }) => `${theme.sizeUnit}px ${theme.sizeUnit * 2}px`};
|
||||
`;
|
||||
|
||||
const KeyboardShortcutButton: FC<{}> = ({ children }) => (
|
||||
const KeyboardShortcutButton: FC<{ children?: ReactNode }> = ({ children }) => (
|
||||
<ModalTrigger
|
||||
modalTitle={t('Keyboard shortcuts')}
|
||||
modalBody={
|
||||
|
||||
@@ -39,7 +39,9 @@ import getBootstrapData from 'src/utils/getBootstrapData';
|
||||
|
||||
const SQL_LAB_URL = '/sqllab';
|
||||
|
||||
const PopEditorTab: React.FC = ({ children }) => {
|
||||
const PopEditorTab: React.FC<{ children?: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [queryEditorId, setQueryEditorId] = useState<string>();
|
||||
const { requestedQuery } = useLocationState();
|
||||
|
||||
@@ -50,14 +50,23 @@ import { StaticPosition, StyledTooltip, ModalResultSetWrapper } from './styles';
|
||||
|
||||
interface QueryTableQuery extends Omit<
|
||||
QueryResponse,
|
||||
'state' | 'sql' | 'progress' | 'results' | 'duration' | 'started'
|
||||
| 'state'
|
||||
| 'sql'
|
||||
| 'progress'
|
||||
| 'results'
|
||||
| 'duration'
|
||||
| 'started'
|
||||
| 'user'
|
||||
| 'db'
|
||||
> {
|
||||
state?: Record<string, any>;
|
||||
sql?: Record<string, any>;
|
||||
progress?: Record<string, any>;
|
||||
results?: Record<string, any>;
|
||||
state?: ReactNode;
|
||||
sql?: ReactNode;
|
||||
progress?: ReactNode;
|
||||
results?: ReactNode;
|
||||
duration?: ReactNode;
|
||||
started?: ReactNode;
|
||||
user?: ReactNode;
|
||||
db?: ReactNode;
|
||||
}
|
||||
|
||||
interface QueryTableProps {
|
||||
@@ -249,7 +258,7 @@ const QueryTable = ({
|
||||
|
||||
return queries
|
||||
.map(query => {
|
||||
const { state, sql, progress, ...rest } = query;
|
||||
const { state, sql, progress, results: _results, ...rest } = query;
|
||||
const q = rest as QueryTableQuery;
|
||||
|
||||
const status = statusAttributes[state] || statusAttributes.error;
|
||||
@@ -265,7 +274,7 @@ const QueryTable = ({
|
||||
buttonStyle="link"
|
||||
onClick={() => onUserClicked(q.userId)}
|
||||
>
|
||||
{q.user}
|
||||
{q.user as ReactNode}
|
||||
</Button>
|
||||
);
|
||||
q.db = (
|
||||
@@ -274,7 +283,7 @@ const QueryTable = ({
|
||||
buttonStyle="link"
|
||||
onClick={() => onDbClicked(q.dbId)}
|
||||
>
|
||||
{q.db}
|
||||
{q.db as ReactNode}
|
||||
</Button>
|
||||
);
|
||||
q.started = (
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { useMemo, FC, ReactElement } from 'react';
|
||||
import { useMemo, FC, ReactElement, type ReactNode } from 'react';
|
||||
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { styled, useTheme, SupersetTheme } from '@apache-superset/core/theme';
|
||||
|
||||
import { Button, DropdownButton } from '@superset-ui/core/components';
|
||||
import { IconType, Icons } from '@superset-ui/core/components/Icons';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import { detectOS } from 'src/utils/common';
|
||||
import { QueryButtonProps } from 'src/SqlLab/types';
|
||||
import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
|
||||
@@ -45,9 +45,9 @@ const buildTextAndIcon = (
|
||||
shouldShowStopButton: boolean,
|
||||
selectedText: string | undefined,
|
||||
theme: SupersetTheme,
|
||||
): { text: string; icon?: IconType } => {
|
||||
): { text: string; icon?: ReactNode } => {
|
||||
let text = t('Run');
|
||||
let icon: IconType | undefined = <Icons.CaretRightOutlined />;
|
||||
let icon: ReactNode = <Icons.CaretRightOutlined />;
|
||||
if (selectedText) {
|
||||
text = t('Run selection');
|
||||
icon = <Icons.StepForwardOutlined />;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import * as reactRedux from 'react-redux';
|
||||
import { act } from 'react';
|
||||
import {
|
||||
cleanup,
|
||||
fireEvent,
|
||||
@@ -136,7 +137,7 @@ describe('SaveDatasetModal', () => {
|
||||
const overwriteRadioBtn = screen.getByRole('radio', {
|
||||
name: /overwrite existing/i,
|
||||
});
|
||||
userEvent.click(overwriteRadioBtn);
|
||||
await userEvent.click(overwriteRadioBtn);
|
||||
|
||||
// Overwrite confirmation button should be disabled at this point
|
||||
const overwriteConfirmationBtn = screen.getByRole('button', {
|
||||
@@ -146,15 +147,21 @@ describe('SaveDatasetModal', () => {
|
||||
|
||||
// Click the overwrite select component
|
||||
const select = screen.getByRole('combobox', { name: /existing dataset/i })!;
|
||||
userEvent.click(select);
|
||||
await userEvent.click(select);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.queryByText('Loading...')).not.toBeVisible(),
|
||||
);
|
||||
// Advance timers to flush debounced fetches in AsyncSelect
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const loading = screen.queryByText('Loading...');
|
||||
expect(loading === null || !loading.checkVisibility()).toBe(true);
|
||||
});
|
||||
|
||||
// Select the first "existing dataset" from the listbox
|
||||
const option = screen.getAllByText('coolest table 0')[1];
|
||||
userEvent.click(option);
|
||||
await userEvent.click(option);
|
||||
|
||||
// Overwrite button should now be enabled
|
||||
expect(overwriteConfirmationBtn).toBeEnabled();
|
||||
@@ -168,25 +175,31 @@ describe('SaveDatasetModal', () => {
|
||||
const overwriteRadioBtn = screen.getByRole('radio', {
|
||||
name: /overwrite existing/i,
|
||||
});
|
||||
userEvent.click(overwriteRadioBtn);
|
||||
await userEvent.click(overwriteRadioBtn);
|
||||
|
||||
// Click the overwrite select component
|
||||
const select = screen.getByRole('combobox', { name: /existing dataset/i });
|
||||
userEvent.click(select);
|
||||
await userEvent.click(select);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.queryByText('Loading...')).not.toBeVisible(),
|
||||
);
|
||||
// Advance timers to flush debounced fetches in AsyncSelect
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const loading = screen.queryByText('Loading...');
|
||||
expect(loading === null || !loading.checkVisibility()).toBe(true);
|
||||
});
|
||||
|
||||
// Select the first "existing dataset" from the listbox
|
||||
const option = screen.getAllByText('coolest table 0')[1];
|
||||
userEvent.click(option);
|
||||
await userEvent.click(option);
|
||||
|
||||
// Click the overwrite button to access the confirmation screen
|
||||
const overwriteConfirmationBtn = screen.getByRole('button', {
|
||||
name: /overwrite/i,
|
||||
});
|
||||
userEvent.click(overwriteConfirmationBtn);
|
||||
await userEvent.click(overwriteConfirmationBtn);
|
||||
|
||||
// Overwrite screen text
|
||||
expect(screen.getByText(/save or overwrite dataset/i)).toBeInTheDocument();
|
||||
|
||||
@@ -140,7 +140,7 @@ const SouthPane = ({
|
||||
logAction(LOG_ACTIONS_SQLLAB_SWITCH_SOUTH_PANE_TAB, { tab: id });
|
||||
};
|
||||
const removeTable = useCallback(
|
||||
(key, action) => {
|
||||
(key: string, action: string) => {
|
||||
if (action === 'remove') {
|
||||
const table = pinnedTables.find(
|
||||
({ dbId, catalog, schema, name }) =>
|
||||
|
||||
@@ -292,7 +292,10 @@ const SqlEditor: FC<Props> = ({
|
||||
const SqlFormExtension = extensionsRegistry.get('sqleditor.extension.form');
|
||||
|
||||
const startQuery = useCallback(
|
||||
(ctasArg = false, ctas_method = CtasEnum.Table) => {
|
||||
(
|
||||
ctasArg = false,
|
||||
ctas_method: (typeof CtasEnum)[keyof typeof CtasEnum] = CtasEnum.Table,
|
||||
) => {
|
||||
if (!database) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { createWrapper } from 'spec/helpers/testing-library';
|
||||
import { initialState, defaultQueryEditor } from 'src/SqlLab/fixtures';
|
||||
import * as localStorageHelpers from 'src/utils/localStorageHelpers';
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { initialState, defaultQueryEditor } from 'src/SqlLab/fixtures';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { createWrapper } from 'spec/helpers/testing-library';
|
||||
|
||||
import useQueryEditor from '.';
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { SupersetClient } from '@superset-ui/core';
|
||||
import { useDeckLayerMetadata } from './useDeckLayerMetadata';
|
||||
|
||||
@@ -52,15 +52,14 @@ test('fetches layer metadata successfully', async () => {
|
||||
};
|
||||
mockSupersetClientGet.mockResolvedValue(mockResponse);
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useDeckLayerMetadata([1, 2]),
|
||||
);
|
||||
const { result } = renderHook(() => useDeckLayerMetadata([1, 2]));
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.layers).toEqual([
|
||||
{ sliceId: 1, name: 'Layer 1', type: 'deck_scatter' },
|
||||
{ sliceId: 2, name: 'Layer 2', type: 'deck_arc' },
|
||||
@@ -75,13 +74,12 @@ test('handles API error and returns fallback layers', async () => {
|
||||
const errorMessage = 'Network error';
|
||||
mockSupersetClientGet.mockRejectedValue(new Error(errorMessage));
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useDeckLayerMetadata([1, 2, 3]),
|
||||
);
|
||||
const { result } = renderHook(() => useDeckLayerMetadata([1, 2, 3]));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.error).toBe(errorMessage);
|
||||
expect(result.current.layers).toEqual([
|
||||
{ sliceId: 1, name: 'Layer 1', type: 'unknown' },
|
||||
@@ -93,13 +91,12 @@ test('handles API error and returns fallback layers', async () => {
|
||||
test('handles non-Error object rejection', async () => {
|
||||
mockSupersetClientGet.mockRejectedValue('String error');
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useDeckLayerMetadata([1]),
|
||||
);
|
||||
const { result } = renderHook(() => useDeckLayerMetadata([1]));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.error).toBe('Unknown error');
|
||||
expect(result.current.layers).toEqual([
|
||||
{ sliceId: 1, name: 'Layer 1', type: 'unknown' },
|
||||
@@ -125,22 +122,25 @@ test('refetches when sliceIds change', async () => {
|
||||
.mockResolvedValueOnce(mockResponse1)
|
||||
.mockResolvedValueOnce(mockResponse2);
|
||||
|
||||
const { result, rerender, waitForNextUpdate } = renderHook(
|
||||
const { result, rerender } = renderHook(
|
||||
({ ids }) => useDeckLayerMetadata(ids),
|
||||
{
|
||||
initialProps: { ids: [1] },
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.layers).toHaveLength(1);
|
||||
expect(result.current.layers[0].sliceId).toBe(1);
|
||||
|
||||
rerender({ ids: [2, 3] });
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.layers).toHaveLength(2);
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.layers).toHaveLength(2);
|
||||
@@ -157,13 +157,12 @@ test('handles empty result from API', async () => {
|
||||
};
|
||||
mockSupersetClientGet.mockResolvedValue(mockResponse);
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useDeckLayerMetadata([1, 2]),
|
||||
);
|
||||
const { result } = renderHook(() => useDeckLayerMetadata([1, 2]));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.layers).toEqual([]);
|
||||
expect(result.current.error).toBe(null);
|
||||
});
|
||||
@@ -176,16 +175,17 @@ test('clears isLoading when sliceIds transitions from non-empty to empty', async
|
||||
};
|
||||
mockSupersetClientGet.mockResolvedValue(mockResponse);
|
||||
|
||||
const { result, rerender, waitForNextUpdate } = renderHook(
|
||||
const { result, rerender } = renderHook(
|
||||
({ ids }) => useDeckLayerMetadata(ids),
|
||||
{
|
||||
initialProps: { ids: [1] },
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.layers).toHaveLength(1);
|
||||
|
||||
act(() => {
|
||||
@@ -204,16 +204,16 @@ test('does not refetch when sliceIds array has same values', async () => {
|
||||
};
|
||||
mockSupersetClientGet.mockResolvedValue(mockResponse);
|
||||
|
||||
const { result, rerender, waitForNextUpdate } = renderHook(
|
||||
const { result, rerender } = renderHook(
|
||||
({ ids }) => useDeckLayerMetadata(ids),
|
||||
{
|
||||
initialProps: { ids: [1] },
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
const callCount = mockSupersetClientGet.mock.calls.length;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { PureComponent } from 'react';
|
||||
import { ErrorInfo, PureComponent } from 'react';
|
||||
import { logging } from '@apache-superset/core/utils';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import {
|
||||
@@ -189,10 +189,11 @@ const MessageSpan = styled.span`
|
||||
class Chart extends PureComponent<ChartProps, {}> {
|
||||
static defaultProps = defaultProps;
|
||||
|
||||
renderStartTime: any;
|
||||
renderStartTime: number;
|
||||
|
||||
constructor(props: ChartProps) {
|
||||
super(props);
|
||||
this.renderStartTime = Logger.getTimestamp();
|
||||
this.handleRenderContainerFailure =
|
||||
this.handleRenderContainerFailure.bind(this);
|
||||
}
|
||||
@@ -235,16 +236,13 @@ class Chart extends PureComponent<ChartProps, {}> {
|
||||
);
|
||||
}
|
||||
|
||||
handleRenderContainerFailure(
|
||||
error: Error,
|
||||
info: { componentStack: string } | null,
|
||||
) {
|
||||
handleRenderContainerFailure(error: Error, info: ErrorInfo) {
|
||||
const { actions, chartId } = this.props;
|
||||
logging.warn(error);
|
||||
actions.chartRenderingFailed(
|
||||
error.toString(),
|
||||
chartId,
|
||||
info ? info.componentStack : null,
|
||||
info?.componentStack ?? null,
|
||||
);
|
||||
|
||||
actions.logEvent(LOG_ACTIONS_RENDER_CHART, {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user