diff --git a/.changeset/beige-kangaroos-suffer.md b/.changeset/beige-kangaroos-suffer.md
new file mode 100644
index 0000000000..249fb5a4b6
--- /dev/null
+++ b/.changeset/beige-kangaroos-suffer.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-kubernetes': patch
+---
+
+Fix cyclical dependency in built output
diff --git a/.changeset/breezy-kids-obey.md b/.changeset/breezy-kids-obey.md
new file mode 100644
index 0000000000..131a1ec1f3
--- /dev/null
+++ b/.changeset/breezy-kids-obey.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-import': patch
+---
+
+Added analytics event for import entity button
diff --git a/.changeset/little-boats-think.md b/.changeset/little-boats-think.md
new file mode 100644
index 0000000000..d389bfbbb1
--- /dev/null
+++ b/.changeset/little-boats-think.md
@@ -0,0 +1,8 @@
+---
+'@backstage/plugin-catalog-react': minor
+---
+
+The `EntityOwnerPicker` component has undergone improvements to enhance its performance.
+
+The component now loads entities asynchronously, resulting in improved performance and responsiveness. Instead of loading all entities upfront, they are now loaded in batches as the user scrolls.
+The previous implementation inferred users and groups displayed by the `EntityOwnerPicker` component based on the entities available in the `EntityListContext`. The updated version no longer relies on the `EntityListContext` for inference, allowing for better decoupling and improved performance.
diff --git a/.changeset/long-lobsters-brake.md b/.changeset/long-lobsters-brake.md
new file mode 100644
index 0000000000..01ae3f4e93
--- /dev/null
+++ b/.changeset/long-lobsters-brake.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-jenkins-common': patch
+---
+
+Export list of permissions
diff --git a/.changeset/lucky-keys-behave.md b/.changeset/lucky-keys-behave.md
new file mode 100644
index 0000000000..a223f83fa6
--- /dev/null
+++ b/.changeset/lucky-keys-behave.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-jenkins-backend': patch
+---
+
+Expose permissions through the metadata endpoint.
diff --git a/.changeset/pre.json b/.changeset/pre.json
new file mode 100644
index 0000000000..2856f9e0af
--- /dev/null
+++ b/.changeset/pre.json
@@ -0,0 +1,230 @@
+{
+ "mode": "pre",
+ "tag": "next",
+ "initialVersions": {
+ "example-app": "0.2.83",
+ "@backstage/app-defaults": "1.3.1",
+ "example-backend": "0.2.83",
+ "@backstage/backend-app-api": "0.4.3",
+ "@backstage/backend-common": "0.18.5",
+ "@backstage/backend-defaults": "0.1.10",
+ "@backstage/backend-dev-utils": "0.1.1",
+ "example-backend-next": "0.0.11",
+ "@backstage/backend-openapi-utils": "0.0.2",
+ "@backstage/backend-plugin-api": "0.5.2",
+ "@backstage/backend-tasks": "0.5.2",
+ "@backstage/backend-test-utils": "0.1.37",
+ "@backstage/catalog-client": "1.4.1",
+ "@backstage/catalog-model": "1.3.0",
+ "@backstage/cli": "0.22.7",
+ "@backstage/cli-common": "0.1.12",
+ "@backstage/cli-node": "0.1.0",
+ "@backstage/codemods": "0.1.44",
+ "@backstage/config": "1.0.7",
+ "@backstage/config-loader": "1.3.0",
+ "@backstage/core-app-api": "1.8.0",
+ "@backstage/core-components": "0.13.1",
+ "@backstage/core-plugin-api": "1.5.1",
+ "@backstage/create-app": "0.5.1",
+ "@backstage/dev-utils": "1.0.15",
+ "e2e-test": "0.2.3",
+ "@backstage/errors": "1.1.5",
+ "@backstage/eslint-plugin": "0.1.3",
+ "@backstage/integration": "1.4.5",
+ "@backstage/integration-aws-node": "0.1.3",
+ "@backstage/integration-react": "1.1.13",
+ "@backstage/release-manifests": "0.0.9",
+ "@backstage/repo-tools": "0.3.0",
+ "@techdocs/cli": "1.4.2",
+ "techdocs-cli-embedded-app": "0.2.82",
+ "@backstage/test-utils": "1.3.1",
+ "@backstage/theme": "0.3.0",
+ "@backstage/types": "1.0.2",
+ "@backstage/version-bridge": "1.0.4",
+ "@backstage/plugin-adr": "0.6.0",
+ "@backstage/plugin-adr-backend": "0.3.3",
+ "@backstage/plugin-adr-common": "0.2.9",
+ "@backstage/plugin-airbrake": "0.3.18",
+ "@backstage/plugin-airbrake-backend": "0.2.18",
+ "@backstage/plugin-allure": "0.1.34",
+ "@backstage/plugin-analytics-module-ga": "0.1.29",
+ "@backstage/plugin-analytics-module-ga4": "0.1.0",
+ "@backstage/plugin-apache-airflow": "0.2.11",
+ "@backstage/plugin-api-docs": "0.9.3",
+ "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.2",
+ "@backstage/plugin-apollo-explorer": "0.1.11",
+ "@backstage/plugin-app-backend": "0.3.45",
+ "@backstage/plugin-auth-backend": "0.18.3",
+ "@backstage/plugin-auth-node": "0.2.14",
+ "@backstage/plugin-azure-devops": "0.3.0",
+ "@backstage/plugin-azure-devops-backend": "0.3.24",
+ "@backstage/plugin-azure-devops-common": "0.3.0",
+ "@backstage/plugin-azure-sites": "0.1.7",
+ "@backstage/plugin-azure-sites-backend": "0.1.7",
+ "@backstage/plugin-azure-sites-common": "0.1.0",
+ "@backstage/plugin-badges": "0.2.42",
+ "@backstage/plugin-badges-backend": "0.2.0",
+ "@backstage/plugin-bazaar": "0.2.8",
+ "@backstage/plugin-bazaar-backend": "0.2.8",
+ "@backstage/plugin-bitbucket-cloud-common": "0.2.6",
+ "@backstage/plugin-bitrise": "0.1.45",
+ "@backstage/plugin-catalog": "1.11.0",
+ "@backstage/plugin-catalog-backend": "1.9.1",
+ "@backstage/plugin-catalog-backend-module-aws": "0.2.0",
+ "@backstage/plugin-catalog-backend-module-azure": "0.1.16",
+ "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.12",
+ "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.12",
+ "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.10",
+ "@backstage/plugin-catalog-backend-module-gerrit": "0.1.13",
+ "@backstage/plugin-catalog-backend-module-github": "0.3.0",
+ "@backstage/plugin-catalog-backend-module-gitlab": "0.2.1",
+ "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.3.2",
+ "@backstage/plugin-catalog-backend-module-ldap": "0.5.12",
+ "@backstage/plugin-catalog-backend-module-msgraph": "0.5.4",
+ "@backstage/plugin-catalog-backend-module-openapi": "0.1.11",
+ "@backstage/plugin-catalog-backend-module-puppetdb": "0.1.2",
+ "@backstage/plugin-catalog-common": "1.0.13",
+ "@internal/plugin-catalog-customized": "0.0.10",
+ "@backstage/plugin-catalog-graph": "0.2.30",
+ "@backstage/plugin-catalog-graphql": "0.3.20",
+ "@backstage/plugin-catalog-import": "0.9.8",
+ "@backstage/plugin-catalog-node": "1.3.6",
+ "@backstage/plugin-catalog-react": "1.6.0",
+ "@backstage/plugin-cicd-statistics": "0.1.20",
+ "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.14",
+ "@backstage/plugin-circleci": "0.3.18",
+ "@backstage/plugin-cloudbuild": "0.3.18",
+ "@backstage/plugin-code-climate": "0.1.18",
+ "@backstage/plugin-code-coverage": "0.2.11",
+ "@backstage/plugin-code-coverage-backend": "0.2.11",
+ "@backstage/plugin-codescene": "0.1.13",
+ "@backstage/plugin-config-schema": "0.1.41",
+ "@backstage/plugin-cost-insights": "0.12.7",
+ "@backstage/plugin-cost-insights-common": "0.1.1",
+ "@backstage/plugin-devtools": "0.1.0",
+ "@backstage/plugin-devtools-backend": "0.1.0",
+ "@backstage/plugin-devtools-common": "0.1.0",
+ "@backstage/plugin-dynatrace": "5.0.0",
+ "@backstage/plugin-entity-feedback": "0.2.1",
+ "@backstage/plugin-entity-feedback-backend": "0.1.3",
+ "@backstage/plugin-entity-feedback-common": "0.1.1",
+ "@backstage/plugin-entity-validation": "0.1.3",
+ "@backstage/plugin-events-backend": "0.2.6",
+ "@backstage/plugin-events-backend-module-aws-sqs": "0.2.0",
+ "@backstage/plugin-events-backend-module-azure": "0.1.7",
+ "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.1.7",
+ "@backstage/plugin-events-backend-module-gerrit": "0.1.7",
+ "@backstage/plugin-events-backend-module-github": "0.1.7",
+ "@backstage/plugin-events-backend-module-gitlab": "0.1.7",
+ "@backstage/plugin-events-backend-test-utils": "0.1.7",
+ "@backstage/plugin-events-node": "0.2.6",
+ "@internal/plugin-todo-list": "1.0.13",
+ "@internal/plugin-todo-list-backend": "1.0.13",
+ "@internal/plugin-todo-list-common": "1.0.10",
+ "@backstage/plugin-explore": "0.4.3",
+ "@backstage/plugin-explore-backend": "0.0.7",
+ "@backstage/plugin-explore-common": "0.0.1",
+ "@backstage/plugin-explore-react": "0.0.28",
+ "@backstage/plugin-firehydrant": "0.2.2",
+ "@backstage/plugin-fossa": "0.2.50",
+ "@backstage/plugin-gcalendar": "0.3.14",
+ "@backstage/plugin-gcp-projects": "0.3.37",
+ "@backstage/plugin-git-release-manager": "0.3.31",
+ "@backstage/plugin-github-actions": "0.5.18",
+ "@backstage/plugin-github-deployments": "0.1.49",
+ "@backstage/plugin-github-issues": "0.2.7",
+ "@backstage/plugin-github-pull-requests-board": "0.1.12",
+ "@backstage/plugin-gitops-profiles": "0.3.36",
+ "@backstage/plugin-gocd": "0.1.24",
+ "@backstage/plugin-graphiql": "0.2.50",
+ "@backstage/plugin-graphql-backend": "0.1.35",
+ "@backstage/plugin-graphql-voyager": "0.1.3",
+ "@backstage/plugin-home": "0.5.2",
+ "@backstage/plugin-home-react": "0.0.0",
+ "@backstage/plugin-ilert": "0.2.7",
+ "@backstage/plugin-jenkins": "0.8.0",
+ "@backstage/plugin-jenkins-backend": "0.2.0",
+ "@backstage/plugin-jenkins-common": "0.1.15",
+ "@backstage/plugin-kafka": "0.3.18",
+ "@backstage/plugin-kafka-backend": "0.2.38",
+ "@backstage/plugin-kubernetes": "0.9.0",
+ "@backstage/plugin-kubernetes-backend": "0.11.0",
+ "@backstage/plugin-kubernetes-common": "0.6.3",
+ "@backstage/plugin-lighthouse": "0.4.3",
+ "@backstage/plugin-lighthouse-backend": "0.2.1",
+ "@backstage/plugin-lighthouse-common": "0.1.1",
+ "@backstage/plugin-linguist": "0.1.3",
+ "@backstage/plugin-linguist-backend": "0.2.2",
+ "@backstage/plugin-linguist-common": "0.1.0",
+ "@backstage/plugin-microsoft-calendar": "0.1.3",
+ "@backstage/plugin-newrelic": "0.3.36",
+ "@backstage/plugin-newrelic-dashboard": "0.2.11",
+ "@backstage/plugin-octopus-deploy": "0.2.0",
+ "@backstage/plugin-org": "0.6.8",
+ "@backstage/plugin-org-react": "0.1.7",
+ "@backstage/plugin-pagerduty": "0.5.11",
+ "@backstage/plugin-periskop": "0.1.16",
+ "@backstage/plugin-periskop-backend": "0.1.16",
+ "@backstage/plugin-permission-backend": "0.5.20",
+ "@backstage/plugin-permission-common": "0.7.5",
+ "@backstage/plugin-permission-node": "0.7.8",
+ "@backstage/plugin-permission-react": "0.4.12",
+ "@backstage/plugin-playlist": "0.1.9",
+ "@backstage/plugin-playlist-backend": "0.3.1",
+ "@backstage/plugin-playlist-common": "0.1.6",
+ "@backstage/plugin-proxy-backend": "0.2.39",
+ "@backstage/plugin-puppetdb": "0.1.1",
+ "@backstage/plugin-rollbar": "0.4.18",
+ "@backstage/plugin-rollbar-backend": "0.1.42",
+ "@backstage/plugin-scaffolder": "1.13.1",
+ "@backstage/plugin-scaffolder-backend": "1.14.0",
+ "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.1.2",
+ "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.21",
+ "@backstage/plugin-scaffolder-backend-module-gitlab": "0.2.0",
+ "@backstage/plugin-scaffolder-backend-module-rails": "0.4.14",
+ "@backstage/plugin-scaffolder-backend-module-sentry": "0.1.5",
+ "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.18",
+ "@backstage/plugin-scaffolder-common": "1.3.0",
+ "@backstage/plugin-scaffolder-node": "0.1.3",
+ "@backstage/plugin-scaffolder-react": "1.4.0",
+ "@backstage/plugin-search": "1.3.0",
+ "@backstage/plugin-search-backend": "1.3.1",
+ "@backstage/plugin-search-backend-module-catalog": "0.1.1",
+ "@backstage/plugin-search-backend-module-elasticsearch": "1.3.0",
+ "@backstage/plugin-search-backend-module-explore": "0.1.1",
+ "@backstage/plugin-search-backend-module-pg": "0.5.6",
+ "@backstage/plugin-search-backend-module-techdocs": "0.1.1",
+ "@backstage/plugin-search-backend-node": "1.2.1",
+ "@backstage/plugin-search-common": "1.2.3",
+ "@backstage/plugin-search-react": "1.6.0",
+ "@backstage/plugin-sentry": "0.5.3",
+ "@backstage/plugin-shortcuts": "0.3.10",
+ "@backstage/plugin-sonarqube": "0.6.7",
+ "@backstage/plugin-sonarqube-backend": "0.1.10",
+ "@backstage/plugin-sonarqube-react": "0.1.5",
+ "@backstage/plugin-splunk-on-call": "0.4.7",
+ "@backstage/plugin-stack-overflow": "0.1.15",
+ "@backstage/plugin-stack-overflow-backend": "0.2.1",
+ "@backstage/plugin-stackstorm": "0.1.2",
+ "@backstage/plugin-tech-insights": "0.3.10",
+ "@backstage/plugin-tech-insights-backend": "0.5.11",
+ "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.29",
+ "@backstage/plugin-tech-insights-common": "0.2.10",
+ "@backstage/plugin-tech-insights-node": "0.4.3",
+ "@backstage/plugin-tech-radar": "0.6.4",
+ "@backstage/plugin-techdocs": "1.6.2",
+ "@backstage/plugin-techdocs-addons-test-utils": "1.0.13",
+ "@backstage/plugin-techdocs-backend": "1.6.2",
+ "@backstage/plugin-techdocs-module-addons-contrib": "1.0.13",
+ "@backstage/plugin-techdocs-node": "1.7.1",
+ "@backstage/plugin-techdocs-react": "1.1.6",
+ "@backstage/plugin-todo": "0.2.20",
+ "@backstage/plugin-todo-backend": "0.1.42",
+ "@backstage/plugin-user-settings": "0.7.3",
+ "@backstage/plugin-user-settings-backend": "0.1.9",
+ "@backstage/plugin-vault": "0.1.12",
+ "@backstage/plugin-vault-backend": "0.3.1",
+ "@backstage/plugin-xcmetrics": "0.2.38"
+ },
+ "changesets": []
+}
diff --git a/.changeset/selfish-mugs-study.md b/.changeset/selfish-mugs-study.md
new file mode 100644
index 0000000000..0a2152aecc
--- /dev/null
+++ b/.changeset/selfish-mugs-study.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-kubernetes': patch
+---
+
+Add errors to PodDrawer
diff --git a/.changeset/weak-snails-spar.md b/.changeset/weak-snails-spar.md
new file mode 100644
index 0000000000..3cd930d5b0
--- /dev/null
+++ b/.changeset/weak-snails-spar.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-addons-test-utils': patch
+---
+
+Avoiding re-running tests on cleanup.
diff --git a/.changeset/weak-snails-spark.md b/.changeset/weak-snails-spark.md
new file mode 100644
index 0000000000..c82626ea39
--- /dev/null
+++ b/.changeset/weak-snails-spark.md
@@ -0,0 +1,5 @@
+---
+'@backstage/test-utils': minor
+---
+
+Test App Wrapper is now using `UnifiedThemeProvider` for supporting MUI v5 next to MUI v4 in tests.
diff --git a/.changeset/weak-snails-sparkl.md b/.changeset/weak-snails-sparkl.md
new file mode 100644
index 0000000000..9bbc7d843b
--- /dev/null
+++ b/.changeset/weak-snails-sparkl.md
@@ -0,0 +1,17 @@
+---
+'@backstage/app-defaults': minor
+'@backstage/theme': minor
+---
+
+**MUI v5 Support:** Adding platform-wide support for MUI v5 allowing a transition phase for migrating central plugins & components over. We still support v4 instances & plugins by adding a
+
+To allow the future support of plugins & components using MUI v5 you want to upgrade your `AppTheme`'s to using the `UnifiedThemeProvider`
+
+```diff
+ Provider: ({ children }) => (
+-
+- {children}
+-
++
+ ),
+```
diff --git a/.changeset/weak-snails-sparkle.md b/.changeset/weak-snails-sparkle.md
new file mode 100644
index 0000000000..95a2255e56
--- /dev/null
+++ b/.changeset/weak-snails-sparkle.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Enforcing MUI v5 specific linting to minimize bundle size.
diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml
index 6c01999506..87056957f5 100644
--- a/.github/workflows/verify_e2e-kubernetes.yml
+++ b/.github/workflows/verify_e2e-kubernetes.yml
@@ -35,7 +35,7 @@ jobs:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: bootstrap kind
- uses: helm/kind-action@v1.5.0
+ uses: helm/kind-action@v1.7.0
- name: kubernetes test
working-directory: packages/backend-common
diff --git a/ADOPTERS.md b/ADOPTERS.md
index b0b7d06f07..98e156c9d4 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -248,3 +248,4 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [Livelo](https://www.livelo.com.br) | [Felipe Nardon](https://www.linkedin.com/in/felipe-nardon-dos-santos/), [Yves Galvão](https://www.linkedin.com/in/yvesgalvao/), [Isaque Rocha](https://www.linkedin.com/in/isaque-rocha/) | Internal platform that aims to reduce the teams' cognitive load through self-service, abstracting processes that are now part of the teams' daily lives, enabling a centralized management of documentation, catalog, metrics and knowledge. |
| [Worten](https://www.worten.pt) | [@elisiariocouto](https://github.com/elisiariocouto) | We are proud to be an adopter of Backstage. Our goal is to make our Backstage installation the go-to location for every developer. To achieve this, we developed a custom plugin to populate Backstage's catalog with information from our internal deployment tool. As a result, we now have full visibility of all Worten systems, components, APIs, messaging topics, and documentation. We also migrated the internal deployment tool's Web UI to Backstage. Currently, we are working on Backstage's Software Templates feature. |
| [Inventa](https://www.inventa.shop) | [João Munhoz](https://github.com/joaovitormunhoz), [Lucca Fonte](https://github.com/luccafonte) | We use Backstage at Inventa to share knowledge between squads, in the form of technical documentation, API contracts, and service status/ownership info, fomenting a self-service and scalable engineering culture. We also developed a plugin that allows us to have a centralized view of all the engineering tools we have for each environment, making it easy to access each one of our tools and increasing productivity. |
+| [PITS Global Data Recovery Services](https://www.pitsdatarecovery.net/) | [@pheianox](https://github.com/pheianox) | The internal developer portal seeks to combine infrastructure tooling, services, and developer documentation into a single, user-friendly interface. By streamlining these components, the portal enhances productivity and simplifies the development process for the internal team. |
diff --git a/docs/local-dev/cli-build-system.md b/docs/local-dev/cli-build-system.md
index a5df84f8dc..61e94e9ef4 100644
--- a/docs/local-dev/cli-build-system.md
+++ b/docs/local-dev/cli-build-system.md
@@ -165,16 +165,17 @@ module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
The configuration factory also provides utilities for extending the configuration in ways that are otherwise very cumbersome to do with plain ESLint, particularly for rules like `no-restricted-syntax`. These are the extra keys that are available:
-| Key | Description |
-| ----------------------- | ------------------------------------------------------------------ |
-| `tsRules` | Additional rules to apply to TypeScript files |
-| `testRules` | Additional rules to apply to tests files |
-| `restrictedImports` | Additional paths to add to `no-restricted-imports` |
-| `restrictedSrcImports` | Additional paths to add to `no-restricted-imports` in src files |
-| `restrictedTestImports` | Additional paths to add to `no-restricted-imports` in test files |
-| `restrictedSyntax` | Additional patterns to add to `no-restricted-syntax` |
-| `restrictedSrcSyntax` | Additional patterns to add to `no-restricted-syntax` in src files |
-| `restrictedTestSyntax` | Additional patterns to add to `no-restricted-syntax` in test files |
+| Key | Description |
+| -------------------------- | ------------------------------------------------------------------ |
+| `tsRules` | Additional rules to apply to TypeScript files |
+| `testRules` | Additional rules to apply to tests files |
+| `restrictedImports` | Additional paths to add to `no-restricted-imports` |
+| `restrictedImportPatterns` | Additional patterns to add to `no-restricted-imports` |
+| `restrictedSrcImports` | Additional paths to add to `no-restricted-imports` in src files |
+| `restrictedTestImports` | Additional paths to add to `no-restricted-imports` in test files |
+| `restrictedSyntax` | Additional patterns to add to `no-restricted-syntax` |
+| `restrictedSrcSyntax` | Additional patterns to add to `no-restricted-syntax` in src files |
+| `restrictedTestSyntax` | Additional patterns to add to `no-restricted-syntax` in test files |
## Type Checking
diff --git a/microsite/yarn.lock b/microsite/yarn.lock
index fe1c123575..7097fb337e 100644
--- a/microsite/yarn.lock
+++ b/microsite/yarn.lock
@@ -2604,90 +2604,90 @@ __metadata:
languageName: node
linkType: hard
-"@swc/core-darwin-arm64@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-darwin-arm64@npm:1.3.51"
+"@swc/core-darwin-arm64@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-darwin-arm64@npm:1.3.59"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"@swc/core-darwin-x64@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-darwin-x64@npm:1.3.51"
+"@swc/core-darwin-x64@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-darwin-x64@npm:1.3.59"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"@swc/core-linux-arm-gnueabihf@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.51"
+"@swc/core-linux-arm-gnueabihf@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.59"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
-"@swc/core-linux-arm64-gnu@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm64-gnu@npm:1.3.51"
+"@swc/core-linux-arm64-gnu@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm64-gnu@npm:1.3.59"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
-"@swc/core-linux-arm64-musl@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm64-musl@npm:1.3.51"
+"@swc/core-linux-arm64-musl@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm64-musl@npm:1.3.59"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
-"@swc/core-linux-x64-gnu@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-x64-gnu@npm:1.3.51"
+"@swc/core-linux-x64-gnu@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-x64-gnu@npm:1.3.59"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
-"@swc/core-linux-x64-musl@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-x64-musl@npm:1.3.51"
+"@swc/core-linux-x64-musl@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-x64-musl@npm:1.3.59"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
-"@swc/core-win32-arm64-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-arm64-msvc@npm:1.3.51"
+"@swc/core-win32-arm64-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-arm64-msvc@npm:1.3.59"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
-"@swc/core-win32-ia32-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-ia32-msvc@npm:1.3.51"
+"@swc/core-win32-ia32-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-ia32-msvc@npm:1.3.59"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
-"@swc/core-win32-x64-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-x64-msvc@npm:1.3.51"
+"@swc/core-win32-x64-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-x64-msvc@npm:1.3.59"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@swc/core@npm:^1.3.46":
- version: 1.3.51
- resolution: "@swc/core@npm:1.3.51"
+ version: 1.3.59
+ resolution: "@swc/core@npm:1.3.59"
dependencies:
- "@swc/core-darwin-arm64": 1.3.51
- "@swc/core-darwin-x64": 1.3.51
- "@swc/core-linux-arm-gnueabihf": 1.3.51
- "@swc/core-linux-arm64-gnu": 1.3.51
- "@swc/core-linux-arm64-musl": 1.3.51
- "@swc/core-linux-x64-gnu": 1.3.51
- "@swc/core-linux-x64-musl": 1.3.51
- "@swc/core-win32-arm64-msvc": 1.3.51
- "@swc/core-win32-ia32-msvc": 1.3.51
- "@swc/core-win32-x64-msvc": 1.3.51
+ "@swc/core-darwin-arm64": 1.3.59
+ "@swc/core-darwin-x64": 1.3.59
+ "@swc/core-linux-arm-gnueabihf": 1.3.59
+ "@swc/core-linux-arm64-gnu": 1.3.59
+ "@swc/core-linux-arm64-musl": 1.3.59
+ "@swc/core-linux-x64-gnu": 1.3.59
+ "@swc/core-linux-x64-musl": 1.3.59
+ "@swc/core-win32-arm64-msvc": 1.3.59
+ "@swc/core-win32-ia32-msvc": 1.3.59
+ "@swc/core-win32-x64-msvc": 1.3.59
peerDependencies:
"@swc/helpers": ^0.5.0
dependenciesMeta:
@@ -2714,7 +2714,7 @@ __metadata:
peerDependenciesMeta:
"@swc/helpers":
optional: true
- checksum: f4e72d347b26353fbb48eae110a63c939922a38b20730ea010f5ae3dd15434f212dce582df05556ec35e677c089dfb6d00f5dd6fc655f8c9a7829458e4301469
+ checksum: 0283f39cf49cd12a6dc7062ef9ea4fa93e1a4f622ce46903a09605219f4f47ed5ad03925c6146d09ffe6ee02a89bb14e3c82eb86b4c60ecce4985ea6e078ffcf
languageName: node
linkType: hard
diff --git a/packages/app-defaults/src/defaults/themes.tsx b/packages/app-defaults/src/defaults/themes.tsx
index 397c160168..10368bd3bc 100644
--- a/packages/app-defaults/src/defaults/themes.tsx
+++ b/packages/app-defaults/src/defaults/themes.tsx
@@ -15,11 +15,12 @@
*/
import React from 'react';
-import { darkTheme, lightTheme } from '@backstage/theme';
+import {
+ UnifiedThemeProvider,
+ themes as builtinThemes,
+} from '@backstage/theme';
import DarkIcon from '@material-ui/icons/Brightness2';
import LightIcon from '@material-ui/icons/WbSunny';
-import { ThemeProvider } from '@material-ui/core/styles';
-import CssBaseline from '@material-ui/core/CssBaseline';
import { AppTheme } from '@backstage/core-plugin-api';
export const themes: AppTheme[] = [
@@ -29,9 +30,7 @@ export const themes: AppTheme[] = [
variant: 'light',
icon: ,
Provider: ({ children }) => (
-
- {children}
-
+
),
},
{
@@ -40,9 +39,7 @@ export const themes: AppTheme[] = [
variant: 'dark',
icon: ,
Provider: ({ children }) => (
-
- {children}
-
+
),
},
];
diff --git a/packages/cli/config/eslint-factory.js b/packages/cli/config/eslint-factory.js
index dde853fdff..36b51324fc 100644
--- a/packages/cli/config/eslint-factory.js
+++ b/packages/cli/config/eslint-factory.js
@@ -24,6 +24,7 @@ const { join: joinPath } = require('path');
* - `tsRules`: Additional ESLint rules to apply to TypeScript
* - `testRules`: Additional ESLint rules to apply to tests
* - `restrictedImports`: Additional paths to add to no-restricted-imports
+ * - `restrictedImportsPattern`: Additional patterns to add to no-restricted-imports
* - `restrictedSrcImports`: Additional paths to add to no-restricted-imports in src files
* - `restrictedTestImports`: Additional paths to add to no-restricted-imports in test files
* - `restrictedSyntax`: Additional patterns to add to no-restricted-syntax
@@ -44,6 +45,7 @@ function createConfig(dir, extraConfig = {}) {
testRules,
restrictedImports,
+ restrictedImportPatterns,
restrictedSrcImports,
restrictedTestImports,
restrictedSyntax,
@@ -114,6 +116,7 @@ function createConfig(dir, extraConfig = {}) {
'*.test*',
'**/__testUtils__/**',
'**/__mocks__/**',
+ ...(restrictedImportPatterns ?? []),
],
},
],
@@ -208,9 +211,19 @@ function createConfigForRole(dir, role, extraConfig = {}) {
name: '@material-ui/icons/', // because this is possible too ._.
message: "Please import '@material-ui/icons/' instead.",
},
+ {
+ // https://mui.com/material-ui/guides/minimizing-bundle-size/
+ name: '@mui/material',
+ message: "Please import '@mui/material/...' instead.",
+ },
...require('module').builtinModules,
...(extraConfig.restrictedImports ?? []),
],
+ // https://mui.com/material-ui/guides/minimizing-bundle-size/
+ restrictedImportPatterns: [
+ '@mui/*/*/*',
+ ...(extraConfig.restrictedImportPatterns ?? []),
+ ],
tsRules: {
'react/prop-types': 0,
...extraConfig.tsRules,
diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts
index 3d9005b0c0..c34407ca3a 100644
--- a/packages/core-app-api/src/app/types.ts
+++ b/packages/core-app-api/src/app/types.ts
@@ -236,21 +236,13 @@ export type AppOptions = {
* title: 'Light Theme',
* variant: 'light',
* icon: ,
- * Provider: ({ children }) => (
- *
- * {children}
- *
- * ),
+ * Provider: ({ children }) => ,
* }, {
* id: 'dark',
* title: 'Dark Theme',
* variant: 'dark',
* icon: ,
- * Provider: ({ children }) => (
- *
- * {children}
- *
- * ),
+ * Provider: ({ children }) => ,
* }]
* ```
*/
diff --git a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx
index 0159d8f503..8fdace3766 100644
--- a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx
+++ b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx
@@ -89,7 +89,11 @@ export function OAuthRequestDialog(_props: {}) {
classes={{ root: classes.title }}
id="oauth-req-dialog-title"
>
-
+
Login Required
{authRedirect ? (
diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx
index 8b644ad2d1..6545cc312a 100644
--- a/packages/test-utils/src/testUtils/appWrappers.tsx
+++ b/packages/test-utils/src/testUtils/appWrappers.tsx
@@ -17,9 +17,7 @@
import React, { ComponentType, ReactNode, ReactElement } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { Route } from 'react-router-dom';
-import { lightTheme } from '@backstage/theme';
-import { ThemeProvider } from '@material-ui/core/styles';
-import { CssBaseline } from '@material-ui/core';
+import { UnifiedThemeProvider, themes } from '@backstage/theme';
import MockIcon from '@material-ui/icons/AcUnit';
import { createSpecializedApp } from '@backstage/core-app-api';
import {
@@ -142,9 +140,9 @@ export function createTestAppWrapper(
title: 'Test App Theme',
variant: 'light',
Provider: ({ children }) => (
-
- {children}
-
+
+ {children}
+
),
},
],
diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md
index 863ab81b68..fe05ca6b4e 100644
--- a/packages/theme/api-report.md
+++ b/packages/theme/api-report.md
@@ -3,13 +3,19 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
+import type { ComponentsProps } from '@material-ui/core/styles/props';
import { Overrides } from '@material-ui/core/styles/overrides';
-import { Palette } from '@material-ui/core/styles/createPalette';
-import { PaletteOptions } from '@material-ui/core/styles/createPalette';
-import { Theme } from '@material-ui/core';
-import { ThemeOptions } from '@material-ui/core';
+import type { Palette } from '@material-ui/core/styles/createPalette';
+import type { PaletteOptions } from '@material-ui/core/styles/createPalette';
+import { PaletteOptions as PaletteOptions_2 } from '@mui/material/styles';
+import { ReactNode } from 'react';
+import { Theme } from '@mui/material/styles';
+import { Theme as Theme_2 } from '@material-ui/core';
+import { ThemeOptions } from '@mui/material/styles';
+import { ThemeOptions as ThemeOptions_2 } from '@material-ui/core/styles';
+import type { ThemeOptions as ThemeOptions_3 } from '@material-ui/core';
-// @public
+// @public @deprecated
export type BackstagePalette = Palette & BackstagePaletteAdditions;
// @public
@@ -75,12 +81,12 @@ export type BackstagePaletteAdditions = {
};
};
-// @public
+// @public @deprecated
export type BackstagePaletteOptions = PaletteOptions &
BackstagePaletteAdditions;
-// @public
-export interface BackstageTheme extends Theme {
+// @public @deprecated
+export interface BackstageTheme extends Theme_2 {
// (undocumented)
getPageTheme: (selector: PageThemeSelector) => PageTheme;
// (undocumented)
@@ -90,7 +96,13 @@ export interface BackstageTheme extends Theme {
}
// @public
-export interface BackstageThemeOptions extends ThemeOptions {
+export type BackstageThemeAdditions = {
+ page: PageTheme;
+ getPageTheme: (selector: PageThemeSelector) => PageTheme;
+};
+
+// @public @deprecated
+export interface BackstageThemeOptions extends ThemeOptions_3 {
// (undocumented)
getPageTheme: (selector: PageThemeSelector) => PageTheme;
// (undocumented)
@@ -99,22 +111,86 @@ export interface BackstageThemeOptions extends ThemeOptions {
palette: BackstagePaletteOptions;
}
+// @public
+export interface BaseThemeOptionsInput {
+ // (undocumented)
+ defaultPageTheme?: string;
+ // (undocumented)
+ fontFamily?: string;
+ // (undocumented)
+ htmlFontSize?: number;
+ // (undocumented)
+ pageTheme?: Record;
+ // (undocumented)
+ palette: PaletteOptions;
+}
+
// @public
export const colorVariants: Record;
// @public
-export function createTheme(options: SimpleThemeOptions): BackstageTheme;
+export function createBaseThemeOptions(
+ options: BaseThemeOptionsInput,
+): {
+ palette: PaletteOptions;
+ typography: {
+ htmlFontSize: number;
+ fontFamily: string;
+ h1: {
+ fontSize: number;
+ fontWeight: number;
+ marginBottom: number;
+ };
+ h2: {
+ fontSize: number;
+ fontWeight: number;
+ marginBottom: number;
+ };
+ h3: {
+ fontSize: number;
+ fontWeight: number;
+ marginBottom: number;
+ };
+ h4: {
+ fontWeight: number;
+ fontSize: number;
+ marginBottom: number;
+ };
+ h5: {
+ fontWeight: number;
+ fontSize: number;
+ marginBottom: number;
+ };
+ h6: {
+ fontWeight: number;
+ fontSize: number;
+ marginBottom: number;
+ };
+ };
+ page: PageTheme;
+ getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme;
+};
+
+// @public @deprecated
+export function createTheme(options: SimpleThemeOptions): Theme_2;
+
+// @public @deprecated
+export function createThemeOptions(options: SimpleThemeOptions): ThemeOptions_3;
+
+// @public @deprecated
+export function createThemeOverrides(theme: Theme_2): Overrides;
// @public
-export function createThemeOptions(
- options: SimpleThemeOptions,
-): BackstageThemeOptions;
+export function createUnifiedTheme(options: UnifiedThemeOptions): UnifiedTheme;
// @public
-export function createThemeOverrides(theme: BackstageTheme): Overrides;
+export function createUnifiedThemeFromV4(options: ThemeOptions_2): UnifiedTheme;
+
+// @public @deprecated
+export const darkTheme: Theme_2;
// @public
-export const darkTheme: BackstageTheme;
+export const defaultComponentThemes: ThemeOptions['components'];
// @public
export function genPageTheme(props: {
@@ -125,8 +201,8 @@ export function genPageTheme(props: {
};
}): PageTheme;
-// @public
-export const lightTheme: BackstageTheme;
+// @public @deprecated
+export const lightTheme: Theme_2;
// @public
export type PageTheme = {
@@ -145,14 +221,214 @@ export type PageThemeSelector = {
};
// @public
-export const shapes: Record;
+export const palettes: {
+ light: {
+ type: 'light';
+ mode: 'light';
+ background: {
+ default: string;
+ paper: string;
+ };
+ status: {
+ ok: string;
+ warning: string;
+ error: string;
+ running: string;
+ pending: string;
+ aborted: string;
+ };
+ bursts: {
+ fontColor: string;
+ slackChannelText: string;
+ backgroundColor: {
+ default: string;
+ };
+ gradient: {
+ linear: string;
+ };
+ };
+ primary: {
+ main: string;
+ };
+ banner: {
+ info: string;
+ error: string;
+ text: string;
+ link: string;
+ closeButtonColor: string;
+ warning: string;
+ };
+ border: string;
+ textContrast: string;
+ textVerySubtle: string;
+ textSubtle: string;
+ highlight: string;
+ errorBackground: string;
+ warningBackground: string;
+ infoBackground: string;
+ errorText: string;
+ infoText: string;
+ warningText: string;
+ linkHover: string;
+ link: string;
+ gold: string;
+ navigation: {
+ background: string;
+ indicator: string;
+ color: string;
+ selectedColor: string;
+ navItem: {
+ hoverBackground: string;
+ };
+ submenu: {
+ background: string;
+ };
+ };
+ pinSidebarButton: {
+ icon: string;
+ background: string;
+ };
+ tabbar: {
+ indicator: string;
+ };
+ };
+ dark: {
+ type: 'dark';
+ mode: 'dark';
+ background: {
+ default: string;
+ paper: string;
+ };
+ status: {
+ ok: string;
+ warning: string;
+ error: string;
+ running: string;
+ pending: string;
+ aborted: string;
+ };
+ bursts: {
+ fontColor: string;
+ slackChannelText: string;
+ backgroundColor: {
+ default: string;
+ };
+ gradient: {
+ linear: string;
+ };
+ };
+ primary: {
+ main: string;
+ dark: string;
+ };
+ secondary: {
+ main: string;
+ };
+ banner: {
+ info: string;
+ error: string;
+ text: string;
+ link: string;
+ closeButtonColor: string;
+ warning: string;
+ };
+ border: string;
+ textContrast: string;
+ textVerySubtle: string;
+ textSubtle: string;
+ highlight: string;
+ errorBackground: string;
+ warningBackground: string;
+ infoBackground: string;
+ errorText: string;
+ infoText: string;
+ warningText: string;
+ linkHover: string;
+ link: string;
+ gold: string;
+ navigation: {
+ background: string;
+ indicator: string;
+ color: string;
+ selectedColor: string;
+ navItem: {
+ hoverBackground: string;
+ };
+ submenu: {
+ background: string;
+ };
+ };
+ pinSidebarButton: {
+ icon: string;
+ background: string;
+ };
+ tabbar: {
+ indicator: string;
+ };
+ };
+};
// @public
+export const shapes: Record;
+
+// @public @deprecated
export type SimpleThemeOptions = {
- palette: BackstagePaletteOptions;
- defaultPageTheme: string;
+ palette: PaletteOptions;
+ defaultPageTheme?: string;
pageTheme?: Record;
fontFamily?: string;
htmlFontSize?: number;
};
+
+// @public
+export const themes: {
+ light: UnifiedTheme;
+ dark: UnifiedTheme;
+};
+
+// @public
+export function transformV5ComponentThemesToV4(
+ theme: Theme,
+ components?: ThemeOptions['components'],
+): {
+ overrides: Overrides;
+ props: ComponentsProps;
+};
+
+// @public
+export interface UnifiedTheme {
+ // (undocumented)
+ getTheme(version: string): unknown | undefined;
+}
+
+// @public
+export interface UnifiedThemeOptions {
+ // (undocumented)
+ components?: ThemeOptions['components'];
+ // (undocumented)
+ defaultPageTheme?: string;
+ // (undocumented)
+ fontFamily?: string;
+ // (undocumented)
+ htmlFontSize?: number;
+ // (undocumented)
+ pageTheme?: Record;
+ // (undocumented)
+ palette: PaletteOptions & PaletteOptions_2;
+}
+
+// @public
+export function UnifiedThemeProvider(
+ props: UnifiedThemeProviderProps,
+): JSX.Element;
+
+// @public
+export interface UnifiedThemeProviderProps {
+ // (undocumented)
+ children: ReactNode;
+ // (undocumented)
+ noCssBaseline?: boolean;
+ // (undocumented)
+ theme: UnifiedTheme;
+}
```
diff --git a/packages/theme/package.json b/packages/theme/package.json
index fbc767fb2f..52def665d6 100644
--- a/packages/theme/package.json
+++ b/packages/theme/package.json
@@ -32,9 +32,13 @@
"test": "backstage-cli package test"
},
"dependencies": {
- "@material-ui/core": "^4.12.2"
+ "@emotion/react": "^11.10.5",
+ "@emotion/styled": "^11.10.5",
+ "@mui/material": "^5.12.2"
},
"peerDependencies": {
+ "@material-ui/core": "^4.12.2",
+ "@types/react": "^16.13.1 || ^17.0.0",
"react": "^16.13.1 || ^17.0.0",
"react-dom": "^16.13.1 || ^17.0.0"
},
diff --git a/packages/theme/src/base/createBaseThemeOptions.ts b/packages/theme/src/base/createBaseThemeOptions.ts
new file mode 100644
index 0000000000..e54e920deb
--- /dev/null
+++ b/packages/theme/src/base/createBaseThemeOptions.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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 { PageTheme, PageThemeSelector } from './types';
+import { pageTheme as defaultPageThemes } from './pageTheme';
+
+const DEFAULT_HTML_FONT_SIZE = 16;
+const DEFAULT_FONT_FAMILY =
+ '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif';
+const DEFAULT_PAGE_THEME = 'home';
+
+/**
+ * Options for {@link createBaseThemeOptions}.
+ *
+ * @public
+ */
+export interface BaseThemeOptionsInput {
+ palette: PaletteOptions;
+ defaultPageTheme?: string;
+ pageTheme?: Record;
+ fontFamily?: string;
+ htmlFontSize?: number;
+}
+
+/**
+ * A helper for creating theme options.
+ *
+ * @public
+ */
+export function createBaseThemeOptions(
+ options: BaseThemeOptionsInput,
+) {
+ const {
+ palette,
+ htmlFontSize = DEFAULT_HTML_FONT_SIZE,
+ fontFamily = DEFAULT_FONT_FAMILY,
+ defaultPageTheme = DEFAULT_PAGE_THEME,
+ pageTheme = defaultPageThemes,
+ } = options;
+
+ if (!pageTheme[defaultPageTheme]) {
+ throw new Error(`${defaultPageTheme} is not defined in pageTheme.`);
+ }
+
+ return {
+ palette,
+ typography: {
+ htmlFontSize,
+ fontFamily,
+ h1: {
+ fontSize: 54,
+ fontWeight: 700,
+ marginBottom: 10,
+ },
+ h2: {
+ fontSize: 40,
+ fontWeight: 700,
+ marginBottom: 8,
+ },
+ h3: {
+ fontSize: 32,
+ fontWeight: 700,
+ marginBottom: 6,
+ },
+ h4: {
+ fontWeight: 700,
+ fontSize: 28,
+ marginBottom: 6,
+ },
+ h5: {
+ fontWeight: 700,
+ fontSize: 24,
+ marginBottom: 4,
+ },
+ h6: {
+ fontWeight: 700,
+ fontSize: 20,
+ marginBottom: 2,
+ },
+ },
+ page: pageTheme[defaultPageTheme],
+ getPageTheme: ({ themeId }: PageThemeSelector) =>
+ pageTheme[themeId] ?? pageTheme[defaultPageTheme],
+ };
+}
diff --git a/packages/theme/src/base/index.ts b/packages/theme/src/base/index.ts
new file mode 100644
index 0000000000..4bb80aa42b
--- /dev/null
+++ b/packages/theme/src/base/index.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { createBaseThemeOptions } from './createBaseThemeOptions';
+export type { BaseThemeOptionsInput } from './createBaseThemeOptions';
+export { colorVariants, genPageTheme, pageTheme, shapes } from './pageTheme';
+export { palettes } from './palettes';
+export type {
+ BackstageThemeAdditions,
+ BackstagePaletteAdditions,
+ PageTheme,
+ PageThemeSelector,
+} from './types';
diff --git a/packages/theme/src/pageTheme.ts b/packages/theme/src/base/pageTheme.ts
similarity index 99%
rename from packages/theme/src/pageTheme.ts
rename to packages/theme/src/base/pageTheme.ts
index dd36d7641b..12a73b6089 100644
--- a/packages/theme/src/pageTheme.ts
+++ b/packages/theme/src/base/pageTheme.ts
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import { PageTheme } from './types';
/**
diff --git a/packages/theme/src/themes.ts b/packages/theme/src/base/palettes.ts
similarity index 87%
rename from packages/theme/src/themes.ts
rename to packages/theme/src/base/palettes.ts
index 23438a1c14..5e70afd7b8 100644
--- a/packages/theme/src/themes.ts
+++ b/packages/theme/src/base/palettes.ts
@@ -14,20 +14,18 @@
* limitations under the License.
*/
-import { createTheme } from './baseTheme';
-import { pageTheme } from './pageTheme';
-import { yellow } from '@material-ui/core/colors';
-
/**
- * The default Backstage light theme.
+ * Built-in Backstage color palettes.
*
* @public
*/
-export const lightTheme = createTheme({
- palette: {
- type: 'light',
+export const palettes = {
+ light: {
+ type: 'light' as const,
+ mode: 'light' as const,
background: {
default: '#F8F8F8',
+ paper: '#FFFFFF',
},
status: {
ok: '#1DB954',
@@ -71,7 +69,7 @@ export const lightTheme = createTheme({
warningText: '#000000',
linkHover: '#2196F3',
link: '#0A6EBE',
- gold: yellow.A700,
+ gold: '#FFD600',
navigation: {
background: '#171717',
indicator: '#9BF0E1',
@@ -92,20 +90,12 @@ export const lightTheme = createTheme({
indicator: '#9BF0E1',
},
},
- defaultPageTheme: 'home',
- pageTheme,
-});
-
-/**
- * The default Backstage dark theme.
- *
- * @public
- */
-export const darkTheme = createTheme({
- palette: {
- type: 'dark',
+ dark: {
+ type: 'dark' as const,
+ mode: 'dark' as const,
background: {
default: '#333333',
+ paper: '#424242',
},
status: {
ok: '#71CF88',
@@ -153,7 +143,7 @@ export const darkTheme = createTheme({
warningText: '#000000',
linkHover: '#82BAFD',
link: '#9CC9FF',
- gold: yellow.A700,
+ gold: '#FFD600',
navigation: {
background: '#424242',
indicator: '#9BF0E1',
@@ -174,6 +164,4 @@ export const darkTheme = createTheme({
indicator: '#9BF0E1',
},
},
- defaultPageTheme: 'home',
- pageTheme,
-});
+};
diff --git a/packages/theme/src/types.ts b/packages/theme/src/base/types.ts
similarity index 63%
rename from packages/theme/src/types.ts
rename to packages/theme/src/base/types.ts
index 0295e491f1..8f71ce4292 100644
--- a/packages/theme/src/types.ts
+++ b/packages/theme/src/base/types.ts
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Backstage Authors
+ * Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-import { Theme, ThemeOptions } from '@material-ui/core';
-import {
- PaletteOptions,
- Palette,
-} from '@material-ui/core/styles/createPalette';
-
/**
* Backstage specific additions to the material-ui palette.
*
@@ -90,21 +84,6 @@ export type BackstagePaletteAdditions = {
};
};
-/**
- * The full Backstage palette.
- *
- * @public
- */
-export type BackstagePalette = Palette & BackstagePaletteAdditions;
-
-/**
- * The full Backstage palette options.
- *
- * @public
- */
-export type BackstagePaletteOptions = PaletteOptions &
- BackstagePaletteAdditions;
-
/**
* Selector for what page theme to use.
*
@@ -114,48 +93,6 @@ export type PageThemeSelector = {
themeId: string;
};
-/**
- * A Backstage theme.
- *
- * @public
- */
-export interface BackstageTheme extends Theme {
- palette: BackstagePalette;
- page: PageTheme;
- getPageTheme: (selector: PageThemeSelector) => PageTheme;
-}
-
-/**
- * Backstage theme options.
- *
- * @public
- * @remarks
- *
- * This is essentially a partial theme definition made by the user, that then
- * gets merged together with defaults and other values to form the final
- * {@link BackstageTheme}.
- *
- */
-export interface BackstageThemeOptions extends ThemeOptions {
- palette: BackstagePaletteOptions;
- page: PageTheme;
- getPageTheme: (selector: PageThemeSelector) => PageTheme;
-}
-
-/**
- * A simpler configuration for creating a new theme that just tweaks some parts
- * of the backstage one.
- *
- * @public
- */
-export type SimpleThemeOptions = {
- palette: BackstagePaletteOptions;
- defaultPageTheme: string;
- pageTheme?: Record;
- fontFamily?: string;
- htmlFontSize?: number;
-};
-
/**
* The theme definitions for a given layout page.
*
@@ -167,3 +104,13 @@ export type PageTheme = {
backgroundImage: string;
fontColor: string;
};
+
+/**
+ * Backstage specific additions to the material-ui theme.
+ *
+ * @public
+ */
+export type BackstageThemeAdditions = {
+ page: PageTheme;
+ getPageTheme: (selector: PageThemeSelector) => PageTheme;
+};
diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts
index 41f827bf43..b0e3590273 100644
--- a/packages/theme/src/index.ts
+++ b/packages/theme/src/index.ts
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Backstage Authors
+ * Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
* @packageDocumentation
*/
-export * from './themes';
-export * from './baseTheme';
-export * from './types';
-export * from './pageTheme';
+export * from './unified';
+export * from './base';
+export * from './v4';
+export * from './v5';
diff --git a/packages/theme/src/unified/MuiClassNameSetup.ts b/packages/theme/src/unified/MuiClassNameSetup.ts
new file mode 100644
index 0000000000..42b948af32
--- /dev/null
+++ b/packages/theme/src/unified/MuiClassNameSetup.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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 { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material/className';
+
+/**
+ * This API is introduced in @mui/material (v5.0.5) as a replacement of deprecated createGenerateClassName & only affects v5 MUI components from `@mui/*`
+ */
+ClassNameGenerator.configure(componentName => {
+ return `v5-${componentName}`;
+});
diff --git a/packages/theme/src/unified/UnifiedTheme.tsx b/packages/theme/src/unified/UnifiedTheme.tsx
new file mode 100644
index 0000000000..0c5f80357a
--- /dev/null
+++ b/packages/theme/src/unified/UnifiedTheme.tsx
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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 {
+ Theme as Mui4Theme,
+ ThemeOptions as ThemeOptionsV4,
+ createTheme,
+} from '@material-ui/core/styles';
+import type { PaletteOptions as PaletteOptionsV4 } from '@material-ui/core/styles/createPalette';
+import { PaletteOptions as PaletteOptionsV5 } from '@mui/material/styles';
+import {
+ adaptV4Theme,
+ Theme as Mui5Theme,
+ createTheme as createV5Theme,
+ ThemeOptions as ThemeOptionsV5,
+} from '@mui/material/styles';
+import { transformV5ComponentThemesToV4 } from './overrides';
+import { PageTheme } from '../base/types';
+import { defaultComponentThemes } from '../v5';
+import { createBaseThemeOptions } from '../base/createBaseThemeOptions';
+import { UnifiedTheme } from './types';
+
+export class UnifiedThemeHolder implements UnifiedTheme {
+ #themes = new Map();
+
+ constructor(v4?: Mui4Theme, v5?: Mui5Theme) {
+ this.#themes = new Map();
+ if (v4) {
+ this.#themes.set('v4', v4);
+ }
+ if (v5) {
+ this.#themes.set('v5', v5);
+ }
+ }
+
+ getTheme(version: string): unknown | undefined {
+ return this.#themes.get(version);
+ }
+}
+
+/**
+ * Options for creating a new {@link UnifiedTheme}.
+ *
+ * @public
+ */
+export interface UnifiedThemeOptions {
+ palette: PaletteOptionsV4 & PaletteOptionsV5;
+ defaultPageTheme?: string;
+ pageTheme?: Record;
+ fontFamily?: string;
+ htmlFontSize?: number;
+ components?: ThemeOptionsV5['components'];
+}
+
+/**
+ * Creates a new {@link UnifiedTheme} using the provided options.
+ *
+ * @public
+ */
+export function createUnifiedTheme(options: UnifiedThemeOptions): UnifiedTheme {
+ const themeOptions = createBaseThemeOptions(options);
+ const components = { ...defaultComponentThemes, ...options.components };
+ const v5Theme = createV5Theme({ ...themeOptions, components });
+
+ // TODO: Not super relevant in the beginning
+ /* const mui4Styles = maybeLoadMui4Styles();
+ if (!mui4Styles) {
+ return new UnifiedThemeHolder(undefined, v5Theme);
+ } */
+
+ const v4Overrides = transformV5ComponentThemesToV4(v5Theme, components);
+ const v4Theme = { ...createTheme(themeOptions), ...v4Overrides };
+ return new UnifiedThemeHolder(v4Theme, v5Theme);
+}
+
+/**
+ * Creates a new {@link UnifiedTheme} using MUI v4 theme options.
+ * Note that this uses `adaptV4Theme` from MUI v5, which is deprecated.
+ *
+ * @public
+ */
+export function createUnifiedThemeFromV4(
+ options: ThemeOptionsV4,
+): UnifiedTheme {
+ const v5Theme = adaptV4Theme(options as any);
+ const v4Theme = createTheme(options);
+ return new UnifiedThemeHolder(v4Theme, v5Theme);
+}
diff --git a/packages/theme/src/unified/UnifiedThemeProvider.tsx b/packages/theme/src/unified/UnifiedThemeProvider.tsx
new file mode 100644
index 0000000000..c7054e0e55
--- /dev/null
+++ b/packages/theme/src/unified/UnifiedThemeProvider.tsx
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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 React, { ReactNode } from 'react';
+import './MuiClassNameSetup';
+import { ThemeProvider } from '@material-ui/core/styles';
+import {
+ StyledEngineProvider,
+ ThemeProvider as Mui5Provider,
+} from '@mui/material/styles';
+import CSSBaseline from '@mui/material/CssBaseline';
+import { UnifiedTheme } from './types';
+
+/**
+ * Props for {@link UnifiedThemeProvider}.
+ *
+ * @public
+ */
+export interface UnifiedThemeProviderProps {
+ children: ReactNode;
+ theme: UnifiedTheme;
+ noCssBaseline?: boolean;
+}
+
+/**
+ * Provides themes for all MUI versions supported by the provided unified theme.
+ *
+ * @public
+ */
+export function UnifiedThemeProvider(
+ props: UnifiedThemeProviderProps,
+): JSX.Element {
+ const { children, theme, noCssBaseline = false } = props;
+
+ const v4Theme = theme.getTheme('v4');
+ const v5Theme = theme.getTheme('v5');
+
+ let cssBaseline: JSX.Element | undefined = undefined;
+ if (!noCssBaseline) {
+ cssBaseline = ;
+ }
+
+ let result = (
+ <>
+ {cssBaseline}
+ {children}
+ >
+ );
+
+ if (v4Theme) {
+ result = {result};
+ }
+
+ if (v5Theme) {
+ result = (
+
+ {result}
+
+ );
+ }
+
+ return result;
+}
diff --git a/packages/theme/src/unified/index.ts b/packages/theme/src/unified/index.ts
new file mode 100644
index 0000000000..f5c805d3ed
--- /dev/null
+++ b/packages/theme/src/unified/index.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { transformV5ComponentThemesToV4 } from './overrides';
+export { createUnifiedTheme, createUnifiedThemeFromV4 } from './UnifiedTheme';
+export type { UnifiedThemeOptions } from './UnifiedTheme';
+export { themes } from './themes';
+export { UnifiedThemeProvider } from './UnifiedThemeProvider';
+export type { UnifiedThemeProviderProps } from './UnifiedThemeProvider';
+export type { UnifiedTheme } from './types';
diff --git a/packages/theme/src/unified/overrides.test.ts b/packages/theme/src/unified/overrides.test.ts
new file mode 100644
index 0000000000..e84cedade6
--- /dev/null
+++ b/packages/theme/src/unified/overrides.test.ts
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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 { Theme } from '@mui/material/styles';
+import { transformV5ComponentThemesToV4 } from './overrides';
+
+describe('transformV5ComponentThemesToV4', () => {
+ const mockTheme = {
+ palette: {
+ primary: {
+ main: 'red',
+ },
+ },
+ } as unknown as Theme;
+ it('transforms empty component themes', () => {
+ expect(transformV5ComponentThemesToV4(mockTheme)).toEqual({
+ overrides: {},
+ props: {},
+ });
+ expect(transformV5ComponentThemesToV4(mockTheme, {})).toEqual({
+ overrides: {},
+ props: {},
+ });
+ expect(
+ transformV5ComponentThemesToV4(mockTheme, {
+ MuiButton: {
+ styleOverrides: undefined,
+ defaultProps: undefined,
+ },
+ }),
+ ).toEqual({ overrides: {}, props: {} });
+ expect(
+ transformV5ComponentThemesToV4(mockTheme, {
+ MuiButton: {
+ styleOverrides: {},
+ defaultProps: {},
+ },
+ }),
+ ).toEqual({ overrides: { MuiButton: {} }, props: { MuiButton: {} } });
+ });
+
+ it('transforms component themes', () => {
+ expect(
+ transformV5ComponentThemesToV4(mockTheme, {
+ MuiButton: {
+ styleOverrides: {
+ root: {
+ color: 'green',
+ },
+ },
+ defaultProps: {
+ disableRipple: true,
+ },
+ },
+ }),
+ ).toEqual({
+ overrides: {
+ MuiButton: {
+ root: {
+ color: 'green',
+ },
+ },
+ },
+ props: {
+ MuiButton: {
+ disableRipple: true,
+ },
+ },
+ });
+ expect(
+ transformV5ComponentThemesToV4(mockTheme, {
+ MuiButton: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ color: theme.palette.primary.main,
+ }),
+ },
+ },
+ }),
+ ).toEqual({
+ overrides: {
+ MuiButton: {
+ root: {
+ color: 'red',
+ },
+ },
+ },
+ props: {},
+ });
+ });
+
+ it('transforms CSSBaseline theme', () => {
+ expect(
+ transformV5ComponentThemesToV4(mockTheme, {
+ MuiCssBaseline: {
+ styleOverrides: theme => ({
+ html: {
+ color: theme.palette.primary.main,
+ },
+ }),
+ defaultProps: {
+ enableColorScheme: true,
+ },
+ },
+ }),
+ ).toEqual({
+ overrides: {
+ MuiCssBaseline: {
+ '@global': {
+ html: {
+ color: 'red',
+ },
+ },
+ },
+ },
+ props: {
+ MuiCssBaseline: {
+ enableColorScheme: true,
+ },
+ },
+ });
+ });
+
+ it('transform state styles', () => {
+ expect(
+ transformV5ComponentThemesToV4(mockTheme, {
+ MuiButton: {
+ styleOverrides: {
+ root: {
+ color: 'green',
+ '&.Mui-disabled': {
+ color: 'red',
+ },
+ },
+ },
+ },
+ }),
+ ).toEqual({
+ overrides: {
+ MuiButton: {
+ root: {
+ color: 'green',
+ },
+ disabled: {
+ color: 'red',
+ },
+ },
+ },
+ props: {},
+ });
+ });
+});
diff --git a/packages/theme/src/unified/overrides.ts b/packages/theme/src/unified/overrides.ts
new file mode 100644
index 0000000000..7417fccedc
--- /dev/null
+++ b/packages/theme/src/unified/overrides.ts
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { Overrides } from '@material-ui/core/styles/overrides';
+import type { ComponentsProps } from '@material-ui/core/styles/props';
+import { ComponentsOverrides, Theme, ThemeOptions } from '@mui/material/styles';
+import { CSSProperties } from 'react';
+
+type V5Override = ComponentsOverrides[Exclude<
+ keyof ComponentsOverrides,
+ 'MuiCssBaseline'
+>];
+type V4Override = Overrides[keyof Overrides];
+type StaticStyleRules = Record<
+ string,
+ CSSProperties | Record
+>;
+
+// Converts callback-based overrides to static styles, e.g.
+// { root: theme => ({ color: theme.color }) } -> { root: { color: 'red' } }
+function adaptV5CssBaselineOverride(
+ theme: Theme,
+ overrides: ComponentsOverrides['MuiCssBaseline'],
+): StaticStyleRules | undefined {
+ if (!overrides || typeof overrides === 'string') {
+ return undefined;
+ }
+
+ const styles = typeof overrides === 'function' ? overrides(theme) : overrides;
+ if (styles) {
+ return { '@global': styles } as StaticStyleRules;
+ }
+
+ return undefined;
+}
+
+// Converts callback-based overrides to static styles, e.g.
+// { root: theme => ({ color: theme.color }) } -> { root: { color: 'red' } }
+function adaptV5Override(
+ theme: Theme,
+ overrides: V5Override,
+): StaticStyleRules | undefined {
+ if (!overrides || typeof overrides === 'string') {
+ return undefined;
+ }
+ if (typeof overrides === 'object') {
+ return Object.fromEntries(
+ Object.entries(overrides).map(([className, style]) => {
+ if (typeof style === 'function') {
+ return [className, style({ theme })];
+ }
+ return [className, style];
+ }),
+ );
+ }
+ return overrides as StaticStyleRules;
+}
+
+const stateStyleKeyPattern = /^&.Mui-([\w-]+)$/;
+
+// Move state style overrides to the top level, e.g.
+// { root: { '&.Mui-active': { color: 'red' } } } -> { active: { color: 'red' } }
+function extractV5StateOverrides(
+ overrides: StaticStyleRules | undefined,
+): StaticStyleRules | undefined {
+ let output = overrides;
+ if (!overrides || typeof overrides !== 'object') {
+ return output;
+ }
+ for (const className of Object.keys(overrides)) {
+ const styles = overrides[className];
+ if (!styles || typeof styles !== 'object') {
+ continue;
+ }
+ for (const _styleKey of Object.keys(styles)) {
+ const styleKey = _styleKey as keyof typeof styles;
+ const match = styleKey.match(stateStyleKeyPattern);
+ if (match) {
+ const [, state] = match;
+ const { [styleKey]: stateStyles, ...restStyles } = styles;
+ if (stateStyles) {
+ output = {
+ ...output,
+ [className]: restStyles,
+ [state]: stateStyles,
+ };
+ }
+ }
+ }
+ }
+ return output;
+}
+
+/**
+ * Transform MUI v5 component themes into a v4 theme props and overrides.
+ *
+ * @public
+ */
+export function transformV5ComponentThemesToV4(
+ theme: Theme,
+ components: ThemeOptions['components'] = {},
+): { overrides: Overrides; props: ComponentsProps } {
+ const overrides: Record = {};
+ const props: Record = {};
+
+ for (const name of Object.keys(components)) {
+ const component = components[name as keyof typeof components];
+ if (!component) {
+ continue;
+ }
+ if ('styleOverrides' in component) {
+ if (name === 'MuiCssBaseline') {
+ overrides[name] = adaptV5CssBaselineOverride(
+ theme,
+ component.styleOverrides as ComponentsOverrides['MuiCssBaseline'],
+ );
+ } else {
+ overrides[name] = extractV5StateOverrides(
+ adaptV5Override(theme, component.styleOverrides as V5Override),
+ );
+ }
+ }
+ if ('defaultProps' in component) {
+ props[name] = component.defaultProps;
+ }
+ }
+
+ return { overrides, props };
+}
diff --git a/packages/theme/src/unified/themes.ts b/packages/theme/src/unified/themes.ts
new file mode 100644
index 0000000000..24e1b1026b
--- /dev/null
+++ b/packages/theme/src/unified/themes.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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 { palettes } from '../base';
+import { createUnifiedTheme } from './UnifiedTheme';
+
+/**
+ * Built-in Backstage MUI themes.
+ *
+ * @public
+ */
+export const themes = {
+ light: createUnifiedTheme({ palette: palettes.light }),
+ dark: createUnifiedTheme({ palette: palettes.dark }),
+};
diff --git a/packages/theme/src/unified/types.ts b/packages/theme/src/unified/types.ts
new file mode 100644
index 0000000000..f090a99b83
--- /dev/null
+++ b/packages/theme/src/unified/types.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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.
+ */
+
+/**
+ * A container of one theme for multiple different MUI versions.
+ *
+ * Currently known keys are 'v4' and 'v5'.
+ *
+ * @public
+ */
+export interface UnifiedTheme {
+ getTheme(version: string): unknown | undefined;
+}
diff --git a/packages/theme/src/v4/baseTheme.ts b/packages/theme/src/v4/baseTheme.ts
new file mode 100644
index 0000000000..8143183ec0
--- /dev/null
+++ b/packages/theme/src/v4/baseTheme.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed 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 { Theme as Mui5Theme } from '@mui/material/styles';
+import { createTheme as createMuiTheme } from '@material-ui/core/styles';
+import type {
+ GridProps,
+ SwitchProps,
+ Theme,
+ ThemeOptions,
+} from '@material-ui/core';
+import { Overrides } from '@material-ui/core/styles/overrides';
+import { SimpleThemeOptions } from './types';
+import { createBaseThemeOptions } from '../base';
+import { defaultComponentThemes } from '../v5';
+import { transformV5ComponentThemesToV4 } from '../unified/overrides';
+
+/**
+ * An old helper for creating MUI v4 theme options.
+ *
+ * @public
+ * @deprecated Use {@link createBaseThemeOptions} instead.
+ */
+export function createThemeOptions(options: SimpleThemeOptions): ThemeOptions {
+ return {
+ props: {
+ MuiGrid: defaultComponentThemes?.MuiGrid
+ ?.defaultProps as Partial,
+ MuiSwitch: defaultComponentThemes?.MuiSwitch
+ ?.defaultProps as Partial,
+ },
+ ...createBaseThemeOptions(options),
+ };
+}
+
+/**
+ * * An old helper for creating MUI v4 theme overrides.
+ *
+ * @public
+ * @deprecated Use {@link defaultComponentThemes} with {@link transformV5ComponentThemesToV4} instead.
+ */
+export function createThemeOverrides(theme: Theme): Overrides {
+ return transformV5ComponentThemesToV4(
+ // Safe but we have to make sure we don't use mui5 specific stuff in the default component themes
+ theme as unknown as Mui5Theme,
+ defaultComponentThemes,
+ ).overrides;
+}
+
+/**
+ * The old method to create a Backstage MUI v4 theme using a palette.
+ * The theme is created with the common Backstage options and component styles.
+ *
+ * @public
+ * @deprecated Use {@link createUnifiedTheme} instead.
+ */
+export function createTheme(options: SimpleThemeOptions): Theme {
+ const themeOptions = createThemeOptions(options);
+ const baseTheme = createMuiTheme(themeOptions);
+ const overrides = createThemeOverrides(baseTheme);
+ const theme = { ...baseTheme, overrides };
+ return theme;
+}
diff --git a/packages/theme/src/v4/index.ts b/packages/theme/src/v4/index.ts
new file mode 100644
index 0000000000..fad349b849
--- /dev/null
+++ b/packages/theme/src/v4/index.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { darkTheme, lightTheme } from './themes';
+export {
+ createTheme,
+ createThemeOptions,
+ createThemeOverrides,
+} from './baseTheme';
+export type {
+ BackstagePalette,
+ BackstagePaletteOptions,
+ BackstageTheme,
+ BackstageThemeOptions,
+ SimpleThemeOptions,
+} from './types';
diff --git a/packages/theme/src/v4/themes.ts b/packages/theme/src/v4/themes.ts
new file mode 100644
index 0000000000..488ee58831
--- /dev/null
+++ b/packages/theme/src/v4/themes.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed 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 { createTheme } from './baseTheme';
+import { palettes } from '../base';
+
+/**
+ * The old MUI v4 Backstage light theme.
+ *
+ * @public
+ * @deprecated Use {@link themes.light} instead.
+ */
+export const lightTheme = createTheme({
+ palette: palettes.light,
+});
+
+/**
+ * The old MUI v4 Backstage dark theme.
+ *
+ * @public
+ * @deprecated Use {@link themes.dark} instead.
+ */
+export const darkTheme = createTheme({
+ palette: palettes.dark,
+});
diff --git a/packages/theme/src/v4/types.ts b/packages/theme/src/v4/types.ts
new file mode 100644
index 0000000000..8bca5b8c3b
--- /dev/null
+++ b/packages/theme/src/v4/types.ts
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type {
+ Theme as MuiTheme,
+ ThemeOptions as MuiThemeOptions,
+} from '@material-ui/core';
+import type {
+ PaletteOptions as MuiPaletteOptions,
+ Palette as MuiPalette,
+} from '@material-ui/core/styles/createPalette';
+import {
+ BackstagePaletteAdditions,
+ BackstageThemeAdditions,
+ PageTheme,
+ PageThemeSelector,
+} from '../base/types';
+
+/**
+ * The full Backstage palette.
+ *
+ * @public
+ * @deprecated This type is deprecated, the MUI Palette type is now always extended instead.
+ */
+export type BackstagePalette = MuiPalette & BackstagePaletteAdditions;
+
+/**
+ * The full Backstage palette options.
+ *
+ * @public
+ * @deprecated This type is deprecated, the MUI PaletteOptions type is now always extended instead.
+ */
+export type BackstagePaletteOptions = MuiPaletteOptions &
+ BackstagePaletteAdditions;
+
+/**
+ * Backstage theme options.
+ *
+ * @public
+ * @deprecated This type is deprecated, the MUI ThemeOptions type is now always extended instead.
+ * @remarks
+ *
+ * This is essentially a partial theme definition made by the user, that then
+ * gets merged together with defaults and other values to form the final
+ * {@link BackstageTheme}.
+ *
+ */
+export interface BackstageThemeOptions extends MuiThemeOptions {
+ palette: BackstagePaletteOptions;
+ page: PageTheme;
+ getPageTheme: (selector: PageThemeSelector) => PageTheme;
+}
+
+/**
+ * A Backstage theme.
+ *
+ * @public
+ * @deprecated This type is deprecated, the MUI Theme type is now always extended instead.
+ */
+export interface BackstageTheme extends MuiTheme {
+ palette: BackstagePalette;
+ page: PageTheme;
+ getPageTheme: (selector: PageThemeSelector) => PageTheme;
+}
+
+/**
+ * A simpler configuration for creating a new theme that just tweaks some parts
+ * of the backstage one.
+ *
+ * @public
+ * @deprecated Use {@link BaseThemeOptionsInput} instead.
+ */
+export type SimpleThemeOptions = {
+ palette: MuiPaletteOptions;
+ defaultPageTheme?: string;
+ pageTheme?: Record;
+ fontFamily?: string;
+ htmlFontSize?: number;
+};
+
+declare module '@material-ui/core/styles/createPalette' {
+ interface Palette extends BackstagePaletteAdditions {}
+
+ interface PaletteOptions extends BackstagePaletteAdditions {}
+}
+
+declare module '@material-ui/core/styles/createTheme' {
+ interface Theme extends BackstageThemeAdditions {}
+
+ interface ThemeOptions extends BackstageThemeAdditions {}
+}
diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/v5/defaultComponentThemes.ts
similarity index 50%
rename from packages/theme/src/baseTheme.ts
rename to packages/theme/src/v5/defaultComponentThemes.ts
index c0a9022bd0..14656b007d 100644
--- a/packages/theme/src/baseTheme.ts
+++ b/packages/theme/src/v5/defaultComponentThemes.ts
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Backstage Authors
+ * Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,121 +14,51 @@
* limitations under the License.
*/
-import { createTheme as createMuiTheme } from '@material-ui/core/styles';
-import { darken, lighten } from '@material-ui/core/styles/colorManipulator';
-import { Overrides } from '@material-ui/core/styles/overrides';
-import {
- BackstageTheme,
- BackstageThemeOptions,
- SimpleThemeOptions,
-} from './types';
-import { pageTheme as defaultPageThemes } from './pageTheme';
-
-const DEFAULT_HTML_FONT_SIZE = 16;
-const DEFAULT_FONT_FAMILY =
- '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif';
-
-/**
- * A helper for creating theme options.
- *
- * @public
- */
-export function createThemeOptions(
- options: SimpleThemeOptions,
-): BackstageThemeOptions {
- const {
- palette,
- htmlFontSize = DEFAULT_HTML_FONT_SIZE,
- fontFamily = DEFAULT_FONT_FAMILY,
- defaultPageTheme,
- pageTheme = defaultPageThemes,
- } = options;
-
- if (!pageTheme[defaultPageTheme]) {
- throw new Error(`${defaultPageTheme} is not defined in pageTheme.`);
- }
-
- return {
- palette,
- props: {
- MuiGrid: {
- spacing: 2,
- },
- MuiSwitch: {
- color: 'primary',
- },
- },
- typography: {
- htmlFontSize,
- fontFamily,
- h6: {
- fontWeight: 700,
- fontSize: 20,
- marginBottom: 2,
- },
- h5: {
- fontWeight: 700,
- fontSize: 24,
- marginBottom: 4,
- },
- h4: {
- fontWeight: 700,
- fontSize: 28,
- marginBottom: 6,
- },
- h3: {
- fontSize: 32,
- fontWeight: 700,
- marginBottom: 6,
- },
- h2: {
- fontSize: 40,
- fontWeight: 700,
- marginBottom: 8,
- },
- h1: {
- fontSize: 54,
- fontWeight: 700,
- marginBottom: 10,
- },
- },
- page: pageTheme[defaultPageTheme],
- getPageTheme: ({ themeId }) =>
- pageTheme[themeId] ?? pageTheme[defaultPageTheme],
- };
-}
+import { darken, lighten, ThemeOptions } from '@mui/material/styles';
/**
* A helper for creating theme overrides.
*
* @public
*/
-export function createThemeOverrides(theme: BackstageTheme): Overrides {
- return {
- MuiCssBaseline: {
- '@global': {
- html: {
- height: '100%',
- fontFamily: theme.typography.fontFamily,
- },
- body: {
- height: '100%',
- fontFamily: theme.typography.fontFamily,
- 'overscroll-behavior-y': 'none',
- },
- a: {
- color: 'inherit',
- textDecoration: 'none',
- },
+export const defaultComponentThemes: ThemeOptions['components'] = {
+ MuiCssBaseline: {
+ styleOverrides: theme => ({
+ html: {
+ height: '100%',
+ fontFamily: theme.typography.fontFamily,
},
+ body: {
+ height: '100%',
+ fontFamily: theme.typography.fontFamily,
+ overscrollBehaviorY: 'none',
+ fontSize: '0.875rem',
+ lineHeight: 1.43,
+ },
+ a: {
+ color: 'inherit',
+ textDecoration: 'none',
+ },
+ }),
+ },
+ MuiGrid: {
+ defaultProps: {
+ spacing: 2,
},
- MuiTableRow: {
+ },
+ MuiSwitch: {
+ defaultProps: {
+ color: 'primary',
+ },
+ },
+ MuiTableRow: {
+ styleOverrides: {
// Alternating row backgrounds
- root: {
+ root: ({ theme }) => ({
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.background.default,
},
- },
+ }),
// Use pointer for hoverable rows
hover: {
'&:hover': {
@@ -136,15 +66,17 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides {
},
},
// Alternating head backgrounds
- head: {
+ head: ({ theme }) => ({
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.background.paper,
},
- },
+ }),
},
- // Tables are more dense than default mui tables
- MuiTableCell: {
- root: {
+ },
+ // Tables are more dense than default mui tables
+ MuiTableCell: {
+ styleOverrides: {
+ root: ({ theme }) => ({
wordBreak: 'break-word',
overflow: 'hidden',
verticalAlign: 'middle',
@@ -152,26 +84,31 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides {
margin: 0,
padding: theme.spacing(3, 2, 3, 2.5),
borderBottom: 0,
- },
- sizeSmall: {
+ }),
+ sizeSmall: ({ theme }) => ({
padding: theme.spacing(1.5, 2, 1.5, 2.5),
- },
- head: {
+ }),
+ head: ({ theme }) => ({
wordBreak: 'break-word',
overflow: 'hidden',
+ color: theme.palette.textSubtle,
fontWeight: 'normal',
lineHeight: '1',
- },
+ }),
},
- MuiTabs: {
+ },
+ MuiTabs: {
+ styleOverrides: {
// Tabs are smaller than default mui tab rows
root: {
minHeight: 24,
},
},
- MuiTab: {
+ },
+ MuiTab: {
+ styleOverrides: {
// Tabs are smaller and have a hover background
- root: {
+ root: ({ theme }) => ({
color: theme.palette.link,
minHeight: 24,
textTransform: 'initial',
@@ -185,25 +122,33 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides {
fontSize: theme.typography.pxToRem(14),
fontWeight: 500,
},
- },
- textColorPrimary: {
+ }),
+ textColorPrimary: ({ theme }) => ({
color: theme.palette.link,
- },
+ }),
},
- MuiTableSortLabel: {
+ },
+ MuiTableSortLabel: {
+ styleOverrides: {
// No color change on hover, just rely on the arrow showing up instead.
root: {
color: 'inherit',
'&:hover': {
color: 'inherit',
},
- },
- // Bold font for highlighting selected column
- active: {
- fontWeight: 'bold',
+ '&:focus': {
+ color: 'inherit',
+ },
+ // Bold font for highlighting selected column
+ '&.Mui-active': {
+ fontWeight: 'bold',
+ color: 'inherit',
+ },
},
},
- MuiListItemText: {
+ },
+ MuiListItemText: {
+ styleOverrides: {
dense: {
// Default dense list items to adding ellipsis for really long str...
whiteSpace: 'nowrap',
@@ -211,44 +156,50 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides {
textOverflow: 'ellipsis',
},
},
- MuiButton: {
+ },
+ MuiButton: {
+ styleOverrides: {
text: {
// Text buttons have less padding by default, but we want to keep the original padding
padding: undefined,
},
},
- MuiChip: {
- root: {
+ },
+ MuiChip: {
+ styleOverrides: {
+ root: ({ theme }) => ({
backgroundColor: '#D9D9D9',
// By default there's no margin, but it's usually wanted, so we add some trailing margin
marginRight: theme.spacing(1),
marginBottom: theme.spacing(1),
color: theme.palette.grey[900],
- },
- outlined: {
+ }),
+ outlined: ({ theme }) => ({
color: theme.palette.text.primary,
- },
- label: {
- lineHeight: `${theme.spacing(2.5)}px`,
+ }),
+ label: ({ theme }) => ({
+ lineHeight: theme.spacing(2.5),
fontWeight: theme.typography.fontWeightMedium,
- fontSize: `${theme.spacing(1.75)}px`,
- },
- labelSmall: {
- fontSize: `${theme.spacing(1.5)}px`,
- },
- deleteIcon: {
+ fontSize: theme.spacing(1.75),
+ }),
+ labelSmall: ({ theme }) => ({
+ fontSize: theme.spacing(1.5),
+ }),
+ deleteIcon: ({ theme }) => ({
color: theme.palette.grey[500],
- width: `${theme.spacing(3)}px`,
- height: `${theme.spacing(3)}px`,
- margin: `0 ${theme.spacing(0.75)}px 0 -${theme.spacing(0.75)}px`,
- },
- deleteIconSmall: {
- width: `${theme.spacing(2)}px`,
- height: `${theme.spacing(2)}px`,
- margin: `0 ${theme.spacing(0.5)}px 0 -${theme.spacing(0.5)}px`,
- },
+ width: theme.spacing(3),
+ height: theme.spacing(3),
+ margin: `0 ${theme.spacing(0.75)} 0 -${theme.spacing(0.75)}`,
+ }),
+ deleteIconSmall: ({ theme }) => ({
+ width: theme.spacing(2),
+ height: theme.spacing(2),
+ margin: `0 ${theme.spacing(0.5)} 0 -${theme.spacing(0.5)}`,
+ }),
},
- MuiCard: {
+ },
+ MuiCard: {
+ styleOverrides: {
root: {
// When cards have a forced size, such as when they are arranged in a
// CSS grid, the content needs to flex such that the actions (buttons
@@ -258,13 +209,17 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides {
flexDirection: 'column',
},
},
- MuiCardHeader: {
+ },
+ MuiCardHeader: {
+ styleOverrides: {
root: {
// Reduce padding between header and content
paddingBottom: 0,
},
},
- MuiCardContent: {
+ },
+ MuiCardContent: {
+ styleOverrides: {
root: {
// When cards have a forced size, such as when they are arranged in a
// CSS grid, the content needs to flex such that the actions (buttons
@@ -276,25 +231,13 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides {
},
},
},
- MuiCardActions: {
+ },
+ MuiCardActions: {
+ styleOverrides: {
root: {
// We default to putting the card actions at the end
justifyContent: 'flex-end',
},
},
- };
-}
-
-/**
- * Creates a Backstage MUI theme using a palette. The theme is created with the
- * common Backstage options and component styles.
- *
- * @public
- */
-export function createTheme(options: SimpleThemeOptions): BackstageTheme {
- const themeOptions = createThemeOptions(options);
- const baseTheme = createMuiTheme(themeOptions) as BackstageTheme;
- const overrides = createThemeOverrides(baseTheme);
- const theme = { ...baseTheme, overrides };
- return theme;
-}
+ },
+};
diff --git a/packages/theme/src/v5/index.ts b/packages/theme/src/v5/index.ts
new file mode 100644
index 0000000000..eee5d852b1
--- /dev/null
+++ b/packages/theme/src/v5/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export * from './types';
+export { defaultComponentThemes } from './defaultComponentThemes';
diff --git a/packages/theme/src/v5/types.ts b/packages/theme/src/v5/types.ts
new file mode 100644
index 0000000000..31fe72b5b9
--- /dev/null
+++ b/packages/theme/src/v5/types.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed 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 { Theme } from '@mui/material/styles';
+import {
+ BackstagePaletteAdditions,
+ BackstageThemeAdditions,
+} from '../base/types';
+
+declare module '@mui/material/styles' {
+ interface Palette extends BackstagePaletteAdditions {}
+
+ interface PaletteOptions extends BackstagePaletteAdditions {}
+}
+
+declare module '@mui/material/styles' {
+ interface Theme extends BackstageThemeAdditions {}
+
+ interface ThemeOptions extends BackstageThemeAdditions {}
+}
+
+declare module '@mui/private-theming/defaultTheme' {
+ interface DefaultTheme extends Theme {}
+}
diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json
index 7286f66095..e703c81beb 100644
--- a/plugins/catalog-backend-module-puppetdb/package.json
+++ b/plugins/catalog-backend-module-puppetdb/package.json
@@ -65,7 +65,7 @@
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@types/lodash": "^4.14.151",
- "msw": "^0.49.0"
+ "msw": "^1.0.0"
},
"files": [
"dist",
diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx
index 6592c6195e..cc47c8850b 100644
--- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx
+++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx
@@ -24,7 +24,7 @@ describe('', () => {
,
);
- expect(baseElement.querySelector('.MuiSvgIcon-root')).toBeInTheDocument();
+ expect(baseElement.querySelector('svg')).toBeInTheDocument();
});
it('renders without exploding for unknown kind', async () => {
@@ -32,6 +32,6 @@ describe('', () => {
,
);
- expect(baseElement.querySelector('.MuiSvgIcon-root')).toBeInTheDocument();
+ expect(baseElement.querySelector('svg')).toBeInTheDocument();
});
});
diff --git a/plugins/catalog-import/src/components/StepReviewLocation/StepReviewLocation.tsx b/plugins/catalog-import/src/components/StepReviewLocation/StepReviewLocation.tsx
index 7943b6e3b8..74ebd973d0 100644
--- a/plugins/catalog-import/src/components/StepReviewLocation/StepReviewLocation.tsx
+++ b/plugins/catalog-import/src/components/StepReviewLocation/StepReviewLocation.tsx
@@ -22,7 +22,7 @@ import { BackButton, NextButton } from '../Buttons';
import { EntityListComponent } from '../EntityListComponent';
import { PrepareResult, ReviewResult } from '../useImportState';
-import { configApiRef, useApi } from '@backstage/core-plugin-api';
+import { configApiRef, useAnalytics, useApi } from '@backstage/core-plugin-api';
import { Link } from '@backstage/core-components';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { assertError } from '@backstage/errors';
@@ -40,6 +40,7 @@ export const StepReviewLocation = ({
}: Props) => {
const catalogApi = useApi(catalogApiRef);
const configApi = useApi(configApiRef);
+ const analytics = useAnalytics();
const appTitle = configApi.getOptional('app.title') || 'Backstage';
@@ -52,6 +53,7 @@ export const StepReviewLocation = ({
: false;
const handleClick = useCallback(async () => {
setSubmitted(true);
+ analytics.captureEvent('click', 'import entity');
try {
let refreshed = new Array<{ target: string }>();
if (prepareResult.type === 'locations') {
@@ -108,7 +110,7 @@ export const StepReviewLocation = ({
setSubmitted(false);
}
}
- }, [prepareResult, onReview, catalogApi]);
+ }, [prepareResult, onReview, catalogApi, analytics]);
return (
<>
diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json
index 569e9d5d8a..2628d2dee9 100644
--- a/plugins/catalog-react/package.json
+++ b/plugins/catalog-react/package.json
@@ -60,6 +60,7 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.61",
+ "@react-hookz/web": "^23.0.0",
"@types/react": "^16.13.1 || ^17.0.0",
"classnames": "^2.2.6",
"jwt-decode": "^3.1.0",
diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx
index e4dc322c7c..e5497d022a 100644
--- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx
+++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
-import { fireEvent, screen } from '@testing-library/react';
+import { Entity } from '@backstage/catalog-model';
+import { fireEvent, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { MockEntityListContextProvider } from '../../testUtils/providers';
import { EntityOwnerFilter } from '../../filters';
@@ -28,8 +28,9 @@ import {
} from '@backstage/test-utils';
import { catalogApiRef, CatalogApi } from '../..';
import { errorApiRef } from '@backstage/core-plugin-api';
+import { QueryEntitiesCursorRequest } from '@backstage/catalog-client';
-const ownerEntities: Entity[] = [
+const ownerEntitiesBatch1: Entity[] = [
{
apiVersion: '1',
kind: 'Group',
@@ -68,64 +69,56 @@ const ownerEntities: Entity[] = [
},
];
-const sampleEntities: Entity[] = [
+const ownerEntitiesBatch2: Entity[] = [
{
apiVersion: '1',
- kind: 'Component',
+ kind: 'Group',
metadata: {
- name: 'component-1',
+ name: 'some-owner-batch-2',
},
- relations: [
- {
- type: 'ownedBy',
- targetRef: 'group:default/some-owner',
- },
- {
- type: 'ownedBy',
- targetRef: 'group:default/some-owner-2',
- },
- ],
},
{
apiVersion: '1',
- kind: 'Component',
+ kind: 'Group',
metadata: {
- name: 'component-2',
+ name: 'some-owner-2-batch-2',
},
- relations: [
- {
- type: 'ownedBy',
- targetRef: 'group:default/another-owner',
+ spec: {
+ profile: {
+ displayName: 'Some Owner Batch 2',
},
- {
- type: 'ownedBy',
- targetRef: 'group:test-namespace/another-owner-2',
- },
- ],
+ },
},
{
apiVersion: '1',
- kind: 'Component',
+ kind: 'Group',
metadata: {
- name: 'component-3',
+ name: 'another-owner-batch-2',
+ title: 'Another Owner Batch 2',
+ },
+ },
+ {
+ apiVersion: '1',
+ kind: 'Group',
+ metadata: {
+ namespace: 'test-namespace',
+ name: 'another-owner-2-batch-2',
+ title: 'Another Owner in Another Namespace Batch 2',
},
- relations: [
- {
- type: 'ownedBy',
- targetRef: 'group:default/some-owner',
- },
- ],
},
];
-const getEntitiesByRefs = jest.fn(async ({ entityRefs }) => ({
- items: entityRefs.map((e: string) =>
- ownerEntities.find(f => stringifyEntityRef(f) === e),
- ),
-}));
+const mockedQueryEntities: jest.MockedFn =
+ jest.fn();
+
+const mockedGetEntitiesByRef: jest.MockedFn =
+ jest.fn();
+
const mockCatalogApi: Partial = {
- getEntitiesByRefs,
+ queryEntities: mockedQueryEntities,
+ getEntitiesByRefs: mockedGetEntitiesByRef,
};
+
const mockErrorApi = new MockErrorApi();
describe('', () => {
@@ -134,12 +127,33 @@ describe('', () => {
[errorApiRef, mockErrorApi],
);
- it('renders all owners', async () => {
+ beforeEach(() => {
+ jest.resetAllMocks();
+
+ mockedQueryEntities.mockImplementation(async request => {
+ const totalItems =
+ ownerEntitiesBatch1.length + ownerEntitiesBatch2.length;
+ if ((request as QueryEntitiesCursorRequest).cursor) {
+ return {
+ items: ownerEntitiesBatch2,
+ pageInfo: {},
+ totalItems,
+ };
+ }
+ return {
+ items: ownerEntitiesBatch1,
+ pageInfo: {
+ nextCursor: 'nextCursor',
+ },
+ totalItems,
+ };
+ });
+ });
+
+ it('renders all users and groups', async () => {
await renderWithEffects(
-
+
,
@@ -147,36 +161,37 @@ describe('', () => {
expect(screen.getByText('Owner')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('owner-picker-expand'));
+
+ await waitFor(() =>
+ expect(screen.getByText('Another Owner')).toBeInTheDocument(),
+ );
+
[
- 'Another Owner',
'some-owner',
'Some Owner 2',
'Another Owner in Another Namespace',
].forEach(owner => {
expect(screen.getByText(owner)).toBeInTheDocument();
});
- });
- it('renders unique owners in alphabetical order', async () => {
- await renderWithEffects(
-
-
-
-
- ,
+ expect(mockedQueryEntities).toHaveBeenCalledTimes(1);
+ expect(mockedGetEntitiesByRef).not.toHaveBeenCalled();
+
+ fireEvent.scroll(screen.getByTestId('owner-picker-listbox'));
+
+ await waitFor(() =>
+ expect(screen.getByText('some-owner-batch-2')).toBeInTheDocument(),
);
- expect(screen.getByText('Owner')).toBeInTheDocument();
- fireEvent.click(screen.getByTestId('owner-picker-expand'));
+ [
+ 'some-owner-batch-2',
+ 'Some Owner Batch 2',
+ 'Another Owner in Another Namespace Batch 2',
+ ].forEach(owner => {
+ expect(screen.getByText(owner)).toBeInTheDocument();
+ });
- expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([
- 'Another Owner',
- 'Another Owner in Another Namespace',
- 'some-owner',
- 'Some Owner 2',
- ]);
+ expect(mockedQueryEntities).toHaveBeenCalledTimes(2);
});
it('respects the query parameter filter value', async () => {
@@ -186,8 +201,6 @@ describe('', () => {
', () => {
,
);
+ expect(mockedGetEntitiesByRef).toHaveBeenCalledWith({
+ entityRefs: ['another-owner'],
+ });
expect(updateFilters).toHaveBeenLastCalledWith({
owners: new EntityOwnerFilter(['group:default/another-owner']),
});
});
+ it('should display the selected owners as humanized entities', async () => {
+ const updateFilters = jest.fn();
+ const queryParameters = { owners: ['another-owner'] };
+
+ mockedGetEntitiesByRef.mockResolvedValue({
+ items: [
+ {
+ metadata: {
+ name: 'another-owner',
+ title: 'Beautiful display name',
+ namespace: 'default',
+ },
+ apiVersion: '1',
+ kind: 'group',
+ },
+ ],
+ });
+ await renderWithEffects(
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(
+ screen.getByRole('button', {
+ name: 'Beautiful display name',
+ }),
+ ).toBeInTheDocument(),
+ );
+
+ expect(mockedGetEntitiesByRef).toHaveBeenCalledWith({
+ entityRefs: ['another-owner'],
+ });
+
+ fireEvent.click(screen.getByTestId('owner-picker-expand'));
+ await waitFor(() => screen.getByText('Some Owner 2'));
+ fireEvent.click(screen.getByText('Some Owner 2'));
+
+ expect(mockedGetEntitiesByRef).toHaveBeenCalledTimes(1);
+
+ await waitFor(() =>
+ expect(
+ screen.getByRole('button', {
+ name: 'Some Owner 2',
+ }),
+ ).toBeInTheDocument(),
+ );
+ });
+
it('adds owners to filters', async () => {
const updateFilters = jest.fn();
await renderWithEffects(
@@ -217,11 +288,14 @@ describe('', () => {
,
);
+ expect(mockedGetEntitiesByRef).not.toHaveBeenCalled();
expect(updateFilters).toHaveBeenLastCalledWith({
owners: undefined,
});
fireEvent.click(screen.getByTestId('owner-picker-expand'));
+ await waitFor(() => screen.getByText('some-owner'));
+
fireEvent.click(screen.getByText('some-owner'));
expect(updateFilters).toHaveBeenLastCalledWith({
owners: new EntityOwnerFilter(['group:default/some-owner']),
@@ -234,8 +308,6 @@ describe('', () => {
', () => {
,
);
+ expect(mockedGetEntitiesByRef).toHaveBeenCalledWith({
+ entityRefs: ['group:default/some-owner'],
+ });
expect(updateFilters).toHaveBeenLastCalledWith({
owners: new EntityOwnerFilter(['group:default/some-owner']),
});
fireEvent.click(screen.getByTestId('owner-picker-expand'));
- expect(screen.getByLabelText('some-owner')).toBeChecked();
+ await waitFor(() =>
+ expect(screen.getByLabelText('some-owner')).toBeChecked(),
+ );
fireEvent.click(screen.getByLabelText('some-owner'));
expect(updateFilters).toHaveBeenLastCalledWith({
@@ -265,13 +342,15 @@ describe('', () => {
value={{
updateFilters,
queryParameters: { owners: ['team-a'] },
- backendEntities: sampleEntities,
}}
>
,
);
+ expect(mockedGetEntitiesByRef).toHaveBeenCalledWith({
+ entityRefs: ['team-a'],
+ });
expect(updateFilters).toHaveBeenLastCalledWith({
owners: new EntityOwnerFilter(['group:default/team-a']),
});
@@ -281,7 +360,6 @@ describe('', () => {
value={{
updateFilters,
queryParameters: { owners: ['team-b'] },
- backendEntities: sampleEntities,
}}
>
@@ -292,23 +370,4 @@ describe('', () => {
owners: new EntityOwnerFilter(['group:default/team-b']),
});
});
- it('removes owners from filters if there are none available', async () => {
- const updateFilters = jest.fn();
- await renderWithEffects(
-
-
-
-
- ,
- );
- expect(updateFilters).toHaveBeenLastCalledWith({
- owners: undefined,
- });
- });
});
diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx
index 8c4f2bfea0..8b223c50b1 100644
--- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx
+++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx
@@ -14,32 +14,30 @@
* limitations under the License.
*/
-import {
- Entity,
- parseEntityRef,
- RELATION_OWNED_BY,
- stringifyEntityRef,
-} from '@backstage/catalog-model';
+import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
import {
Box,
Checkbox,
FormControlLabel,
- makeStyles,
TextField,
Typography,
+ makeStyles,
} from '@material-ui/core';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { Autocomplete } from '@material-ui/lab';
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useEntityList } from '../../hooks/useEntityListProvider';
import { EntityOwnerFilter } from '../../filters';
-import { getEntityRelations } from '../../utils';
-import useAsync from 'react-use/lib/useAsync';
-import { errorApiRef, useApi } from '@backstage/core-plugin-api';
+import { useApi } from '@backstage/core-plugin-api';
import { catalogApiRef } from '../../api';
-import { humanizeEntity, humanizeEntityRef } from '../EntityRefLink/humanize';
+import useAsync from 'react-use/lib/useAsync';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+import { useDebouncedEffect } from '@react-hookz/web';
+import PersonIcon from '@material-ui/icons/Person';
+import GroupIcon from '@material-ui/icons/Group';
+import { humanizeEntity } from '../EntityRefLink/humanize';
/** @public */
export type CatalogReactEntityOwnerPickerClassKey = 'input';
@@ -61,12 +59,51 @@ export const EntityOwnerPicker = () => {
const classes = useStyles();
const {
updateFilters,
- backendEntities,
filters,
queryParameters: { owners: ownersParameter },
} = useEntityList();
+
const catalogApi = useApi(catalogApiRef);
- const errorApi = useApi(errorApiRef);
+ const [text, setText] = useState('');
+
+ const [{ value, loading }, handleFetch] = useAsyncFn(
+ async (request: { text: string } | { cursor: string; prev: Entity[] }) => {
+ const initialRequest = request as { text: string };
+ const cursorRequest = request as { cursor: string; prev: Entity[] };
+ const limit = 20;
+
+ if (cursorRequest.cursor) {
+ const response = await catalogApi.queryEntities({
+ cursor: cursorRequest.cursor,
+ limit,
+ });
+ return {
+ ...response,
+ items: [...cursorRequest.prev, ...response.items],
+ };
+ }
+
+ return catalogApi.queryEntities({
+ fullTextFilter: {
+ term: initialRequest.text || '',
+ fields: [
+ 'metadata.name',
+ 'kind',
+ 'spec.profile.displayname',
+ 'metadata.title',
+ ],
+ },
+ filter: { kind: ['User', 'Group'] },
+ orderFields: [{ field: 'metadata.name', order: 'asc' }],
+ limit,
+ });
+ },
+ [text],
+ );
+
+ useDebouncedEffect(() => handleFetch({ text }), [text], 250);
+
+ const availableOwners = value?.items || [];
const queryParamOwners = useMemo(
() => [ownersParameter].flat().filter(Boolean) as string[],
@@ -74,71 +111,10 @@ export const EntityOwnerPicker = () => {
);
const [selectedOwners, setSelectedOwners] = useState(
- queryParamOwners.length
- ? new EntityOwnerFilter(queryParamOwners).values
- : filters.owners?.values ?? [],
+ queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],
);
- const {
- loading,
- error,
- value: ownerEntities,
- } = useAsync(async () => {
- const ownerEntityRefs = [
- ...new Set(
- backendEntities
- .flatMap((e: Entity) =>
- getEntityRelations(e, RELATION_OWNED_BY).map(o =>
- stringifyEntityRef(o),
- ),
- )
- .filter(Boolean) as string[],
- ),
- ];
- const { items: ownerEntitiesOrNull } = await catalogApi.getEntitiesByRefs({
- entityRefs: ownerEntityRefs,
- fields: [
- 'kind',
- 'metadata.name',
- 'metadata.title',
- 'metadata.namespace',
- 'spec.profile.displayName',
- ],
- });
- const owners = ownerEntitiesOrNull.map((entity, index) => {
- if (entity) {
- return {
- label: humanizeEntity(entity, { defaultKind: 'Group' }),
- entityRef: stringifyEntityRef(entity),
- };
- }
- return {
- label: humanizeEntityRef(parseEntityRef(ownerEntityRefs[index]), {
- defaultKind: 'group',
- }),
- entityRef: ownerEntityRefs[index],
- };
- });
-
- return owners.sort((a, b) =>
- a.label.localeCompare(b.label, 'en-US', {
- ignorePunctuation: true,
- caseFirst: 'upper',
- }),
- );
- }, [backendEntities]);
-
- useEffect(() => {
- if (error) {
- errorApi.post(
- {
- ...error,
- message: `EntityOwnerPicker failed to initialize: ${error.message}`,
- },
- {},
- );
- }
- }, [error, errorApi]);
+ const { getEntity, setEntity } = useSelectedOwners(selectedOwners);
// Set selected owners on query parameter updates; this happens at initial page load and from
// external updates to the page location.
@@ -150,17 +126,20 @@ export const EntityOwnerPicker = () => {
}, [queryParamOwners]);
useEffect(() => {
- if (!loading && ownerEntities) {
- updateFilters({
- owners:
- selectedOwners.length && ownerEntities.length
- ? new EntityOwnerFilter(selectedOwners)
- : undefined,
- });
- }
- }, [selectedOwners, updateFilters, ownerEntities, loading]);
+ updateFilters({
+ owners: selectedOwners.length
+ ? new EntityOwnerFilter(selectedOwners)
+ : undefined,
+ });
+ }, [selectedOwners, updateFilters]);
- if (!loading && !ownerEntities?.length) return null;
+ if (
+ ['user', 'group'].includes(
+ filters.kind?.value.toLocaleLowerCase('en-US') || '',
+ )
+ ) {
+ return null;
+ }
return (
@@ -170,40 +149,130 @@ export const EntityOwnerPicker = () => {
multiple
disableCloseOnSelect
loading={loading}
- options={ownerEntities || []}
- value={
- ownerEntities?.filter(e =>
- selectedOwners.some((f: string) => f === e.entityRef),
- ) ?? []
- }
- onChange={(_: object, value: { entityRef: string }[]) =>
- setSelectedOwners(value.map(e => e.entityRef))
- }
- getOptionLabel={option => option.label}
- renderOption={(option, { selected }) => (
-
- }
- onClick={event => event.preventDefault()}
- label={option.label}
- />
- )}
+ options={availableOwners}
+ value={selectedOwners as unknown as Entity[]}
+ getOptionSelected={(o, v) => {
+ if (typeof v === 'string') {
+ return stringifyEntityRef(o) === v;
+ }
+ return o === v;
+ }}
+ getOptionLabel={o => {
+ const entity = typeof o === 'string' ? getEntity(o) || o : o;
+
+ return typeof entity === 'string'
+ ? entity
+ : humanizeEntity(entity, entity.metadata.name);
+ }}
+ onChange={(_: object, owners) => {
+ setText('');
+ setSelectedOwners(
+ owners.map(e => {
+ const entityRef =
+ typeof e === 'string' ? e : stringifyEntityRef(e);
+
+ if (typeof e !== 'string') {
+ setEntity(e);
+ }
+
+ return entityRef;
+ }),
+ );
+ }}
+ filterOptions={x => x}
+ renderOption={(entity, { selected }) => {
+ const isGroup = entity.kind === 'Group';
+
+ return (
+
+ }
+ onClick={event => event.preventDefault()}
+ label={
+
+ {isGroup ? (
+
+ ) : (
+
+ )}
+
+ {humanizeEntity(entity, entity.metadata.name)}
+
+ }
+ />
+ );
+ }}
size="small"
popupIcon={}
renderInput={params => (
{
+ setText(e.currentTarget.value);
+ }}
variant="outlined"
/>
)}
+ ListboxProps={{
+ onScroll: (e: React.MouseEvent) => {
+ const element = e.currentTarget;
+ const hasReachedEnd =
+ Math.abs(
+ element.scrollHeight -
+ element.clientHeight -
+ element.scrollTop,
+ ) < 1;
+
+ if (hasReachedEnd && value?.pageInfo.nextCursor) {
+ handleFetch({
+ cursor: value.pageInfo.nextCursor,
+ prev: value.items,
+ });
+ }
+ },
+ 'data-testid': 'owner-picker-listbox',
+ }}
/>
);
};
+
+/**
+ * Hook used for storing the full entity of the specified owners
+ * in order to display users and group using the information contained on each entity.
+ * When a component is rendered for the first time, it loads the content of the entities
+ * specified by `initialSelectedOwnersRefs` and export the `getEntity` and `setEntity`
+ * utilities, used to retrieve and modify the owners.
+ */
+function useSelectedOwners(initialSelectedOwnersRefs: string[]) {
+ const allEntities = useRef>({});
+ const catalogApi = useApi(catalogApiRef);
+
+ useAsync(async () => {
+ if (initialSelectedOwnersRefs.length === 0) {
+ return;
+ }
+ const initialSelectedEntities = await catalogApi.getEntitiesByRefs({
+ entityRefs: initialSelectedOwnersRefs,
+ });
+ initialSelectedEntities.items.forEach(e => {
+ if (e) {
+ allEntities.current[stringifyEntityRef(e)] = e;
+ }
+ });
+ }, []);
+
+ return {
+ getEntity: (entityRef: string) => allEntities.current[entityRef],
+ setEntity: (entity: Entity) => {
+ allEntities.current[stringifyEntityRef(entity)] = entity;
+ },
+ };
+}
diff --git a/plugins/catalog-react/src/components/EntityRefLink/humanize.test.ts b/plugins/catalog-react/src/components/EntityRefLink/humanize.test.ts
index e7d1649a29..1182a7778d 100644
--- a/plugins/catalog-react/src/components/EntityRefLink/humanize.test.ts
+++ b/plugins/catalog-react/src/components/EntityRefLink/humanize.test.ts
@@ -215,9 +215,12 @@ describe('humanizeEntityRef', () => {
describe('humanizeEntity', () => {
it('gives a readable name when one is provided at metadata.title', () => {
expect(
- humanizeEntity({
- metadata: { name: 'my-entity', title: 'My Title' },
- } as Entity),
+ humanizeEntity(
+ {
+ metadata: { name: 'my-entity', title: 'My Title' },
+ } as Entity,
+ 'default',
+ ),
).toBe('My Title');
});
@@ -257,13 +260,16 @@ describe('humanizeEntity', () => {
])(
'gives a readable name for kind %s when one is provided at spec.profile.displayName',
(_, entity: Entity, expected) => {
- expect(humanizeEntity(entity)).toBe(expected);
+ expect(humanizeEntity(entity, 'default')).toBe(expected);
},
);
it('should pass through to humanizeEntityRef when nothing matches', () => {
expect(
- humanizeEntity({ kind: 'Group', metadata: { name: 'test' } } as Entity),
- ).toBe('group:test');
+ humanizeEntity(
+ { kind: 'Group', metadata: { name: 'test' } } as Entity,
+ 'default',
+ ),
+ ).toBe('default');
});
});
diff --git a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts
index 7f1501280a..478f0dce9d 100644
--- a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts
+++ b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts
@@ -25,7 +25,8 @@ import get from 'lodash/get';
* @param defaultNamespace - if set to false then namespace is never omitted,
* if set to string which matches namespace of entity then omitted
*
- * @public */
+ * @public
+ **/
export function humanizeEntityRef(
entityRef: Entity | CompoundEntityRef,
opts?: {
@@ -73,27 +74,19 @@ export function humanizeEntityRef(
* If an entity is either User or Group, this will be its `spec.profile.displayName`.
* Otherwise, this is `metadata.title`.
*
- * If neither of those are found or populated, fallback to `humanizeEntityRef`.
+ * If neither of those are found or populated, fallback to `defaultName`.
*
* @param entity - Entity to convert.
- * @param opts - If entity readable name is not available, opts will be used to specify humanizeEntityRef options.
- * @returns Readable name, defaults to unique identifier.
+ * @param defaultName - If entity readable name is not available, `defaultName` will be returned.
+ * @returns Readable name, defaults to `defaultName`.
*
- * @public
*/
-export function humanizeEntity(
- entity: Entity,
- opts?: {
- defaultKind?: string;
- defaultNamespace?: string | false;
- },
-) {
+export function humanizeEntity(entity: Entity, defaultName: string) {
for (const path of ['spec.profile.displayName', 'metadata.title']) {
const value = get(entity, path);
if (value && typeof value === 'string') {
return value;
}
}
-
- return humanizeEntityRef(entity, opts);
+ return defaultName;
}
diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json
index 77eaf43901..286e45d9ee 100644
--- a/plugins/devtools-backend/package.json
+++ b/plugins/devtools-backend/package.json
@@ -52,7 +52,7 @@
"@types/ping": "^0.4.1",
"@types/supertest": "^2.0.8",
"@types/yarnpkg__lockfile": "^1.1.4",
- "msw": "^0.47.0",
+ "msw": "^1.0.0",
"supertest": "^6.2.4"
},
"files": [
diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json
index d5bd0c567a..a06bf780f9 100644
--- a/plugins/devtools/package.json
+++ b/plugins/devtools/package.json
@@ -56,7 +56,7 @@
"@testing-library/user-event": "^14.0.0",
"@types/node": "*",
"cross-fetch": "^3.1.5",
- "msw": "^0.47.0"
+ "msw": "^1.0.0"
},
"files": [
"dist"
diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json
index f45908c04e..e9630bfb45 100644
--- a/plugins/jenkins-backend/package.json
+++ b/plugins/jenkins-backend/package.json
@@ -32,6 +32,7 @@
"@backstage/plugin-auth-node": "workspace:^",
"@backstage/plugin-jenkins-common": "workspace:^",
"@backstage/plugin-permission-common": "workspace:^",
+ "@backstage/plugin-permission-node": "workspace:^",
"@types/express": "^4.17.6",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
diff --git a/plugins/jenkins-backend/src/service/router.ts b/plugins/jenkins-backend/src/service/router.ts
index 484bfa273f..eea067fab6 100644
--- a/plugins/jenkins-backend/src/service/router.ts
+++ b/plugins/jenkins-backend/src/service/router.ts
@@ -28,6 +28,8 @@ import {
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { stringifyError } from '@backstage/errors';
+import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';
+import { jenkinsPermissions } from '@backstage/plugin-jenkins-common';
/** @public */
export interface RouterOptions {
@@ -58,6 +60,11 @@ export async function createRouter(
const router = Router();
router.use(express.json());
+ router.use(
+ createPermissionIntegrationRouter({
+ permissions: jenkinsPermissions,
+ }),
+ );
router.get(
'/v1/entity/:namespace/:kind/:name/projects',
diff --git a/plugins/jenkins-common/api-report.md b/plugins/jenkins-common/api-report.md
index 771161e1cb..4db5a6c938 100644
--- a/plugins/jenkins-common/api-report.md
+++ b/plugins/jenkins-common/api-report.md
@@ -8,5 +8,8 @@ import { ResourcePermission } from '@backstage/plugin-permission-common';
// @public
export const jenkinsExecutePermission: ResourcePermission<'catalog-entity'>;
+// @public
+export const jenkinsPermissions: ResourcePermission<'catalog-entity'>[];
+
// (No @packageDocumentation comment for this package)
```
diff --git a/plugins/jenkins-common/src/permissions.ts b/plugins/jenkins-common/src/permissions.ts
index 311bf993cc..aba79b8493 100644
--- a/plugins/jenkins-common/src/permissions.ts
+++ b/plugins/jenkins-common/src/permissions.ts
@@ -28,3 +28,10 @@ export const jenkinsExecutePermission = createPermission({
},
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
});
+
+/**
+ * List of all Jenkins permissions
+ *
+ * @public
+ */
+export const jenkinsPermissions = [jenkinsExecutePermission];
diff --git a/plugins/kubernetes/api-report.md b/plugins/kubernetes/api-report.md
index 5529b7910f..823b9c89a2 100644
--- a/plugins/kubernetes/api-report.md
+++ b/plugins/kubernetes/api-report.md
@@ -22,6 +22,7 @@ import { OAuthApi } from '@backstage/core-plugin-api';
import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common';
import { OpenIdConnectApi } from '@backstage/core-plugin-api';
import { Pod } from 'kubernetes-models/v1';
+import { Pod as Pod_2 } from 'kubernetes-models/v1/Pod';
import { default as React_2 } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { V1ConfigMap } from '@kubernetes/client-node';
diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json
index e6a0b518a1..d0d32814a1 100644
--- a/plugins/kubernetes/package.json
+++ b/plugins/kubernetes/package.json
@@ -42,6 +42,7 @@
"@backstage/plugin-kubernetes-common": "workspace:^",
"@backstage/theme": "workspace:^",
"@kubernetes-models/apimachinery": "^1.1.0",
+ "@kubernetes-models/base": "^4.0.1",
"@kubernetes/client-node": "0.18.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
diff --git a/plugins/kubernetes/src/components/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent.tsx
index 63aa52f1ba..a465422095 100644
--- a/plugins/kubernetes/src/components/KubernetesContent.tsx
+++ b/plugins/kubernetes/src/components/KubernetesContent.tsx
@@ -24,6 +24,7 @@ import { Cluster } from './Cluster';
import EmptyStateImage from '../assets/emptystate.svg';
import { useKubernetesObjects } from '../hooks';
import { Content, Page, Progress } from '@backstage/core-components';
+import { DetectedErrorsContext } from '../hooks/useMatchingErrors';
type KubernetesContentProps = {
entity: Entity;
@@ -49,88 +50,92 @@ export const KubernetesContent = ({
: new Map();
return (
-
-
- {kubernetesObjects === undefined && error === undefined && }
+
+
+
+ {kubernetesObjects === undefined && error === undefined && (
+
+ )}
- {/* errors retrieved from the kubernetes clusters */}
- {clustersWithErrors.length > 0 && (
-
-
-
+ {/* errors retrieved from the kubernetes clusters */}
+ {clustersWithErrors.length > 0 && (
+
+
+
+
-
- )}
+ )}
- {/* other errors */}
- {error !== undefined && (
-
-
-
+ {/* other errors */}
+ {error !== undefined && (
+
+
+
+
-
- )}
+ )}
- {kubernetesObjects && (
-
-
-
-
-
- Your Clusters
-
-
- {kubernetesObjects?.items.length <= 0 && (
-
-
-
- No resources on any known clusters for{' '}
- {entity.metadata.name}
-
-
-
-
-
-
- )}
- {kubernetesObjects?.items.length > 0 &&
- kubernetesObjects?.items.map((item, i) => {
- const podsWithErrors = new Set(
- detectedErrors
- .get(item.cluster.name)
- ?.filter(de => de.sourceRef.kind === 'Pod')
- .map(de => de.sourceRef.name),
- );
-
- return (
-
-
+
+
+
+
+ Your Clusters
+
+
+ {kubernetesObjects?.items.length <= 0 && (
+
+
+
+ No resources on any known clusters for{' '}
+ {entity.metadata.name}
+
+
+
+
- );
- })}
+
+ )}
+ {kubernetesObjects?.items.length > 0 &&
+ kubernetesObjects?.items.map((item, i) => {
+ const podsWithErrors = new Set(
+ detectedErrors
+ .get(item.cluster.name)
+ ?.filter(de => de.sourceRef.kind === 'Pod')
+ .map(de => de.sourceRef.name),
+ );
+
+ return (
+
+
+
+ );
+ })}
+
-
- )}
-
-
+ )}
+
+
+
);
};
diff --git a/plugins/kubernetes/src/components/Pods/ErrorList/ErrorList.test.tsx b/plugins/kubernetes/src/components/Pods/ErrorList/ErrorList.test.tsx
new file mode 100644
index 0000000000..f28580b509
--- /dev/null
+++ b/plugins/kubernetes/src/components/Pods/ErrorList/ErrorList.test.tsx
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed 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 React from 'react';
+
+import { render } from '@testing-library/react';
+
+import { ErrorList } from './ErrorList';
+import { Pod } from 'kubernetes-models/v1/Pod';
+
+describe('ErrorList', () => {
+ it('error highlight should render', () => {
+ const { getByText } = render(
+ ,
+ );
+ expect(getByText('some-pod')).toBeInTheDocument();
+ expect(getByText('some error message')).toBeInTheDocument();
+ });
+});
diff --git a/plugins/kubernetes/src/components/Pods/ErrorList/ErrorList.tsx b/plugins/kubernetes/src/components/Pods/ErrorList/ErrorList.tsx
new file mode 100644
index 0000000000..b7b3418889
--- /dev/null
+++ b/plugins/kubernetes/src/components/Pods/ErrorList/ErrorList.tsx
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed 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 React from 'react';
+
+import {
+ List,
+ ListItem,
+ ListItemText,
+ Divider,
+ createStyles,
+ makeStyles,
+ Theme,
+ Paper,
+} from '@material-ui/core';
+import { PodAndErrors } from '../types';
+
+const useStyles = makeStyles((_theme: Theme) =>
+ createStyles({
+ root: {
+ overflow: 'auto',
+ },
+ list: {
+ width: '100%',
+ },
+ }),
+);
+
+interface ErrorListProps {
+ podAndErrors: PodAndErrors[];
+}
+
+export const ErrorList = ({ podAndErrors }: ErrorListProps) => {
+ const classes = useStyles();
+ return (
+
+
+ {podAndErrors
+ .filter(pae => pae.errors.length > 0)
+ .flatMap(onlyPodWithErrors => {
+ return onlyPodWithErrors.errors.map((error, i) => {
+ return (
+
+ {i > 0 && }
+
+
+
+
+ );
+ });
+ })}
+
+
+ );
+};
diff --git a/plugins/kubernetes/src/components/Pods/ErrorList/index.ts b/plugins/kubernetes/src/components/Pods/ErrorList/index.ts
new file mode 100644
index 0000000000..f40c0583c9
--- /dev/null
+++ b/plugins/kubernetes/src/components/Pods/ErrorList/index.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export * from './ErrorList';
diff --git a/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.test.tsx b/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.test.tsx
index fa4f9f7168..64fd38c8d7 100644
--- a/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.test.tsx
+++ b/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.test.tsx
@@ -33,7 +33,7 @@ describe('PodDrawer', () => {
clusterName: 'some-cluster-1',
pod: {
metadata: {
- name: 'ok-pod',
+ name: 'some-pod',
},
spec: {
containers: [
@@ -51,17 +51,40 @@ describe('PodDrawer', () => {
],
},
},
- errors: [],
+ errors: [
+ {
+ type: 'some-error',
+ severity: 10,
+ message: 'some error message',
+ occuranceCount: 1,
+ sourceRef: {
+ name: 'some-pod',
+ namespace: 'some-namespace',
+ kind: 'Pod',
+ apiGroup: 'v1',
+ },
+ proposedFix: [
+ {
+ type: 'logs',
+ container: 'some-container',
+ errorType: 'some error type',
+ rootCauseExplanation: 'some root cause',
+ possibleFixes: ['fix1', 'fix2'],
+ },
+ ],
+ },
+ ],
},
} as any)}
/>,
),
);
- expect(getAllByText('ok-pod')).toHaveLength(2);
+ expect(getAllByText('some-pod')).toHaveLength(3);
expect(getByText('Pod (127.0.0.1)')).toBeInTheDocument();
expect(getByText('YAML')).toBeInTheDocument();
expect(getByText('Containers')).toBeInTheDocument();
expect(getByText('some-container')).toBeInTheDocument();
+ expect(getByText('some error message')).toBeInTheDocument();
});
});
diff --git a/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.tsx b/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.tsx
index a78f08683c..5e49410dd4 100644
--- a/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.tsx
+++ b/plugins/kubernetes/src/components/Pods/PodDrawer/PodDrawer.tsx
@@ -32,6 +32,7 @@ import { ContainerCard } from './ContainerCard';
import { PodAndErrors } from '../types';
import { KubernetesDrawer } from '../../KubernetesDrawer';
import { PendingPodContent } from './PendingPodContent';
+import { ErrorList } from '../ErrorList';
const useDrawerContentStyles = makeStyles((_theme: Theme) =>
createStyles({
@@ -116,6 +117,16 @@ export const PodDrawer = ({ podAndErrors, open }: PodDrawerProps) => {
)}
+ {podAndErrors.errors.length > 0 && (
+
+ Errors:
+
+ )}
+ {podAndErrors.errors.length > 0 && (
+
+
+
+ )}
)}
diff --git a/plugins/kubernetes/src/components/Pods/PodLogs/usePodLogs.ts b/plugins/kubernetes/src/components/Pods/PodLogs/usePodLogs.ts
index 9f3af5cb3b..446969f509 100644
--- a/plugins/kubernetes/src/components/Pods/PodLogs/usePodLogs.ts
+++ b/plugins/kubernetes/src/components/Pods/PodLogs/usePodLogs.ts
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { kubernetesProxyApiRef } from '@backstage/plugin-kubernetes';
import useAsync from 'react-use/lib/useAsync';
import { ContainerScope } from './types';
import { useApi } from '@backstage/core-plugin-api';
+import { kubernetesProxyApiRef } from '../../../api';
interface PodLogsOptions {
podScope: ContainerScope;
diff --git a/plugins/kubernetes/src/components/Pods/PodsTable.tsx b/plugins/kubernetes/src/components/Pods/PodsTable.tsx
index 4b7b4b93ed..620e91ce72 100644
--- a/plugins/kubernetes/src/components/Pods/PodsTable.tsx
+++ b/plugins/kubernetes/src/components/Pods/PodsTable.tsx
@@ -15,7 +15,6 @@
*/
import React, { useContext } from 'react';
-import { V1Pod } from '@kubernetes/client-node';
import { PodDrawer } from './PodDrawer';
import {
containersReady,
@@ -27,18 +26,21 @@ import {
import { Table, TableColumn } from '@backstage/core-components';
import { PodNamesWithMetricsContext } from '../../hooks/PodNamesWithMetrics';
import { ClusterContext } from '../../hooks/Cluster';
+import { useMatchingErrors } from '../../hooks/useMatchingErrors';
+import { Pod } from 'kubernetes-models/v1/Pod';
+import { V1Pod } from '@kubernetes/client-node';
export const READY_COLUMNS: PodColumns = 'READY';
export const RESOURCE_COLUMNS: PodColumns = 'RESOURCE';
export type PodColumns = 'READY' | 'RESOURCE';
type PodsTablesProps = {
- pods: V1Pod[];
+ pods: Pod | V1Pod[];
extraColumns?: PodColumns[];
children?: React.ReactNode;
};
-const READY: TableColumn[] = [
+const READY: TableColumn[] = [
{
title: 'containers ready',
align: 'center',
@@ -54,26 +56,37 @@ const READY: TableColumn[] = [
},
];
+const PodDrawerTrigger = ({ pod }: { pod: Pod }) => {
+ const cluster = useContext(ClusterContext);
+ const errors = useMatchingErrors({
+ kind: 'Pod',
+ apiVersion: 'v1',
+ metadata: pod.metadata,
+ });
+ return (
+
+ );
+};
+
export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => {
const podNamesWithMetrics = useContext(PodNamesWithMetricsContext);
- const cluster = useContext(ClusterContext);
- const defaultColumns: TableColumn[] = [
+ const defaultColumns: TableColumn[] = [
{
title: 'name',
highlight: true,
- render: (pod: V1Pod) => (
-
- ),
+ render: (pod: Pod) => {
+ return ;
+ },
},
{
title: 'phase',
- render: (pod: V1Pod) => pod.status?.phase ?? 'unknown',
+ render: (pod: Pod) => pod.status?.phase ?? 'unknown',
width: 'auto',
},
{
@@ -81,16 +94,16 @@ export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => {
render: containerStatuses,
},
];
- const columns: TableColumn[] = [...defaultColumns];
+ const columns: TableColumn[] = [...defaultColumns];
if (extraColumns.includes(READY_COLUMNS)) {
columns.push(...READY);
}
if (extraColumns.includes(RESOURCE_COLUMNS)) {
- const resourceColumns: TableColumn[] = [
+ const resourceColumns: TableColumn[] = [
{
title: 'CPU usage %',
- render: (pod: V1Pod) => {
+ render: (pod: Pod) => {
const metrics = podNamesWithMetrics.get(pod.metadata?.name ?? '');
if (!metrics) {
@@ -103,7 +116,7 @@ export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => {
},
{
title: 'Memory usage %',
- render: (pod: V1Pod) => {
+ render: (pod: Pod) => {
const metrics = podNamesWithMetrics.get(pod.metadata?.name ?? '');
if (!metrics) {
@@ -123,13 +136,11 @@ export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => {
width: '100%',
};
- const usePods = pods.map(p => ({ ...p, id: p.metadata?.uid }));
-
return (
diff --git a/plugins/kubernetes/src/components/Pods/types.ts b/plugins/kubernetes/src/components/Pods/types.ts
index 195f0b9135..ac707c2a57 100644
--- a/plugins/kubernetes/src/components/Pods/types.ts
+++ b/plugins/kubernetes/src/components/Pods/types.ts
@@ -14,9 +14,10 @@
* limitations under the License.
*/
import { Pod } from 'kubernetes-models/v1';
+import { DetectedError } from '../../error-detection';
export interface PodAndErrors {
clusterName: string;
pod: Pod;
- errors: any[];
+ errors: DetectedError[];
}
diff --git a/plugins/kubernetes/src/hooks/useMatchingErrors.test.tsx b/plugins/kubernetes/src/hooks/useMatchingErrors.test.tsx
new file mode 100644
index 0000000000..b5fe588e86
--- /dev/null
+++ b/plugins/kubernetes/src/hooks/useMatchingErrors.test.tsx
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed 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 React from 'react';
+import { renderHook } from '@testing-library/react-hooks';
+import { DetectedErrorsContext, useMatchingErrors } from './useMatchingErrors';
+import { DetectedError } from '../error-detection';
+import { ResourceRef } from '../error-detection/types';
+
+const genericErrorWithRef = (resourceRef: ResourceRef): DetectedError => {
+ return {
+ type: 'some-error',
+ severity: 10,
+ message: 'some error message',
+ occuranceCount: 1,
+ sourceRef: resourceRef,
+ proposedFix: [
+ {
+ type: 'logs',
+ container: 'some-container',
+ errorType: 'some error type',
+ rootCauseExplanation: 'some root cause',
+ possibleFixes: ['fix1', 'fix2'],
+ },
+ ],
+ };
+};
+
+describe('useMatchingErrors', () => {
+ it('should filter non-matching resources', () => {
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
+
+ {children}
+
+ );
+
+ const { result } = renderHook(
+ () =>
+ useMatchingErrors({
+ metadata: {
+ name: 'some-name',
+ namespace: 'some-namespace',
+ },
+ kind: 'some-kind',
+ apiVersion: 'some-apiGroup',
+ }),
+ { wrapper },
+ );
+ expect(result.current).toStrictEqual([
+ genericErrorWithRef({
+ name: 'some-name',
+ namespace: 'some-namespace',
+ kind: 'some-kind',
+ apiGroup: 'some-apiGroup',
+ }),
+ ]);
+ });
+});
diff --git a/plugins/kubernetes/src/hooks/useMatchingErrors.ts b/plugins/kubernetes/src/hooks/useMatchingErrors.ts
new file mode 100644
index 0000000000..623a32bdf8
--- /dev/null
+++ b/plugins/kubernetes/src/hooks/useMatchingErrors.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed 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 React, { useContext } from 'react';
+import { DetectedError, ResourceRef } from '../error-detection/types';
+import { TypeMeta } from '@kubernetes-models/base';
+import { IIoK8sApimachineryPkgApisMetaV1ObjectMeta as V1ObjectMeta } from '@kubernetes-models/apimachinery/apis/meta/v1/ObjectMeta';
+
+export const DetectedErrorsContext = React.createContext([]);
+
+type Matcher = {
+ metadata?: V1ObjectMeta;
+} & TypeMeta;
+
+export const useMatchingErrors = (matcher: Matcher): DetectedError[] => {
+ const targetRef: ResourceRef = {
+ name: matcher.metadata?.name ?? '',
+ namespace: matcher.metadata?.namespace ?? '',
+ kind: matcher.kind,
+ apiGroup: matcher.apiVersion,
+ };
+
+ const errors = useContext(DetectedErrorsContext);
+
+ return errors.filter(e => {
+ const r = e.sourceRef;
+ return (
+ targetRef.apiGroup === r.apiGroup &&
+ targetRef.kind === r.kind &&
+ targetRef.name === r.name &&
+ targetRef.namespace === r.namespace
+ );
+ });
+};
diff --git a/plugins/kubernetes/src/utils/pod.tsx b/plugins/kubernetes/src/utils/pod.tsx
index 2f657e7658..ce556fbc8d 100644
--- a/plugins/kubernetes/src/utils/pod.tsx
+++ b/plugins/kubernetes/src/utils/pod.tsx
@@ -28,6 +28,7 @@ import {
SubvalueCell,
} from '@backstage/core-components';
import { ClientPodStatus } from '@backstage/plugin-kubernetes-common';
+import { Pod } from 'kubernetes-models/v1/Pod';
export const imageChips = (pod: V1Pod): ReactNode => {
const containerStatuses = pod.status?.containerStatuses ?? [];
@@ -38,19 +39,19 @@ export const imageChips = (pod: V1Pod): ReactNode => {
return {images}
;
};
-export const containersReady = (pod: V1Pod): string => {
+export const containersReady = (pod: Pod): string => {
const containerStatuses = pod.status?.containerStatuses ?? [];
const containersReadyItem = containerStatuses.filter(cs => cs.ready).length;
return `${containersReadyItem}/${containerStatuses.length}`;
};
-export const totalRestarts = (pod: V1Pod): number => {
+export const totalRestarts = (pod: Pod): number => {
const containerStatuses = pod.status?.containerStatuses ?? [];
return containerStatuses?.reduce((a, b) => a + b.restartCount, 0);
};
-export const containerStatuses = (pod: V1Pod): ReactNode => {
+export const containerStatuses = (pod: Pod): ReactNode => {
const containerStatusesItem = pod.status?.containerStatuses ?? [];
const errors = containerStatusesItem.reduce((accum, next) => {
if (next.state === undefined) {
diff --git a/plugins/techdocs-addons-test-utils/src/test-utils.tsx b/plugins/techdocs-addons-test-utils/src/test-utils.tsx
index e60a96402c..1a921437b1 100644
--- a/plugins/techdocs-addons-test-utils/src/test-utils.tsx
+++ b/plugins/techdocs-addons-test-utils/src/test-utils.tsx
@@ -278,7 +278,7 @@ export class TechDocsAddonTester {
render(this.build());
});
- const shadowHost = screen.getByTestId('techdocs-native-shadowroot');
+ const shadowHost = await screen.findByTestId('techdocs-native-shadowroot');
return {
...screen,
diff --git a/storybook/yarn.lock b/storybook/yarn.lock
index a4d5837ba5..8be840ef57 100644
--- a/storybook/yarn.lock
+++ b/storybook/yarn.lock
@@ -2943,90 +2943,90 @@ __metadata:
languageName: node
linkType: hard
-"@swc/core-darwin-arm64@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-darwin-arm64@npm:1.3.51"
+"@swc/core-darwin-arm64@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-darwin-arm64@npm:1.3.59"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"@swc/core-darwin-x64@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-darwin-x64@npm:1.3.51"
+"@swc/core-darwin-x64@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-darwin-x64@npm:1.3.59"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"@swc/core-linux-arm-gnueabihf@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.51"
+"@swc/core-linux-arm-gnueabihf@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.59"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
-"@swc/core-linux-arm64-gnu@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm64-gnu@npm:1.3.51"
+"@swc/core-linux-arm64-gnu@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm64-gnu@npm:1.3.59"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
-"@swc/core-linux-arm64-musl@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm64-musl@npm:1.3.51"
+"@swc/core-linux-arm64-musl@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm64-musl@npm:1.3.59"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
-"@swc/core-linux-x64-gnu@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-x64-gnu@npm:1.3.51"
+"@swc/core-linux-x64-gnu@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-x64-gnu@npm:1.3.59"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
-"@swc/core-linux-x64-musl@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-x64-musl@npm:1.3.51"
+"@swc/core-linux-x64-musl@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-x64-musl@npm:1.3.59"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
-"@swc/core-win32-arm64-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-arm64-msvc@npm:1.3.51"
+"@swc/core-win32-arm64-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-arm64-msvc@npm:1.3.59"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
-"@swc/core-win32-ia32-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-ia32-msvc@npm:1.3.51"
+"@swc/core-win32-ia32-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-ia32-msvc@npm:1.3.59"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
-"@swc/core-win32-x64-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-x64-msvc@npm:1.3.51"
+"@swc/core-win32-x64-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-x64-msvc@npm:1.3.59"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@swc/core@npm:^1.3.46":
- version: 1.3.51
- resolution: "@swc/core@npm:1.3.51"
+ version: 1.3.59
+ resolution: "@swc/core@npm:1.3.59"
dependencies:
- "@swc/core-darwin-arm64": 1.3.51
- "@swc/core-darwin-x64": 1.3.51
- "@swc/core-linux-arm-gnueabihf": 1.3.51
- "@swc/core-linux-arm64-gnu": 1.3.51
- "@swc/core-linux-arm64-musl": 1.3.51
- "@swc/core-linux-x64-gnu": 1.3.51
- "@swc/core-linux-x64-musl": 1.3.51
- "@swc/core-win32-arm64-msvc": 1.3.51
- "@swc/core-win32-ia32-msvc": 1.3.51
- "@swc/core-win32-x64-msvc": 1.3.51
+ "@swc/core-darwin-arm64": 1.3.59
+ "@swc/core-darwin-x64": 1.3.59
+ "@swc/core-linux-arm-gnueabihf": 1.3.59
+ "@swc/core-linux-arm64-gnu": 1.3.59
+ "@swc/core-linux-arm64-musl": 1.3.59
+ "@swc/core-linux-x64-gnu": 1.3.59
+ "@swc/core-linux-x64-musl": 1.3.59
+ "@swc/core-win32-arm64-msvc": 1.3.59
+ "@swc/core-win32-ia32-msvc": 1.3.59
+ "@swc/core-win32-x64-msvc": 1.3.59
peerDependencies:
"@swc/helpers": ^0.5.0
dependenciesMeta:
@@ -3053,7 +3053,7 @@ __metadata:
peerDependenciesMeta:
"@swc/helpers":
optional: true
- checksum: f4e72d347b26353fbb48eae110a63c939922a38b20730ea010f5ae3dd15434f212dce582df05556ec35e677c089dfb6d00f5dd6fc655f8c9a7829458e4301469
+ checksum: 0283f39cf49cd12a6dc7062ef9ea4fa93e1a4f622ce46903a09605219f4f47ed5ad03925c6146d09ffe6ee02a89bb14e3c82eb86b4c60ecce4985ea6e078ffcf
languageName: node
linkType: hard
diff --git a/yarn.lock b/yarn.lock
index ebd5852444..6c30cf1524 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2248,7 +2248,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.0, @babel/helper-module-imports@npm:^7.18.6":
+"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.0, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.18.6":
version: 7.18.6
resolution: "@babel/helper-module-imports@npm:7.18.6"
dependencies:
@@ -2730,7 +2730,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/plugin-syntax-jsx@npm:^7.0.0, @babel/plugin-syntax-jsx@npm:^7.18.6, @babel/plugin-syntax-jsx@npm:^7.7.2":
+"@babel/plugin-syntax-jsx@npm:^7.0.0, @babel/plugin-syntax-jsx@npm:^7.17.12, @babel/plugin-syntax-jsx@npm:^7.18.6, @babel/plugin-syntax-jsx@npm:^7.7.2":
version: 7.18.6
resolution: "@babel/plugin-syntax-jsx@npm:7.18.6"
dependencies:
@@ -3472,7 +3472,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
+"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.21.0
resolution: "@babel/runtime@npm:7.21.0"
dependencies:
@@ -5471,7 +5471,7 @@ __metadata:
"@types/lodash": ^4.14.151
lodash: ^4.17.21
luxon: ^3.0.0
- msw: ^0.49.0
+ msw: ^1.0.0
node-fetch: ^2.6.7
uuid: ^8.0.0
winston: ^3.2.1
@@ -5697,6 +5697,7 @@ __metadata:
"@material-ui/core": ^4.12.2
"@material-ui/icons": ^4.9.1
"@material-ui/lab": 4.0.0-alpha.61
+ "@react-hookz/web": ^23.0.0
"@testing-library/dom": ^8.0.0
"@testing-library/jest-dom": ^5.10.1
"@testing-library/react": ^12.1.3
@@ -6138,7 +6139,7 @@ __metadata:
express-promise-router: ^4.1.0
fs-extra: ^10.0.0
lodash: ^4.17.21
- msw: ^0.47.0
+ msw: ^1.0.0
node-fetch: ^2.6.7
ping: ^0.4.1
semver: ^7.3.2
@@ -6181,7 +6182,7 @@ __metadata:
"@testing-library/user-event": ^14.0.0
"@types/node": "*"
cross-fetch: ^3.1.5
- msw: ^0.47.0
+ msw: ^1.0.0
react-json-view: ^1.21.3
react-use: ^17.2.4
peerDependencies:
@@ -7161,6 +7162,7 @@ __metadata:
"@backstage/plugin-auth-node": "workspace:^"
"@backstage/plugin-jenkins-common": "workspace:^"
"@backstage/plugin-permission-common": "workspace:^"
+ "@backstage/plugin-permission-node": "workspace:^"
"@types/express": ^4.17.6
"@types/jenkins": ^0.23.1
"@types/supertest": ^2.0.8
@@ -7356,6 +7358,7 @@ __metadata:
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"
"@kubernetes-models/apimachinery": ^1.1.0
+ "@kubernetes-models/base": ^4.0.1
"@kubernetes/client-node": 0.18.1
"@material-ui/core": ^4.12.2
"@material-ui/icons": ^4.9.1
@@ -9625,9 +9628,13 @@ __metadata:
resolution: "@backstage/theme@workspace:packages/theme"
dependencies:
"@backstage/cli": "workspace:^"
- "@material-ui/core": ^4.12.2
+ "@emotion/react": ^11.10.5
+ "@emotion/styled": ^11.10.5
+ "@mui/material": ^5.12.2
"@types/react": ^16.13.1 || ^17.0.0
peerDependencies:
+ "@material-ui/core": ^4.12.2
+ "@types/react": ^16.13.1 || ^17.0.0
react: ^16.13.1 || ^17.0.0
react-dom: ^16.13.1 || ^17.0.0
languageName: unknown
@@ -10155,6 +10162,41 @@ __metadata:
languageName: node
linkType: hard
+"@emotion/babel-plugin@npm:^11.10.5":
+ version: 11.10.5
+ resolution: "@emotion/babel-plugin@npm:11.10.5"
+ dependencies:
+ "@babel/helper-module-imports": ^7.16.7
+ "@babel/plugin-syntax-jsx": ^7.17.12
+ "@babel/runtime": ^7.18.3
+ "@emotion/hash": ^0.9.0
+ "@emotion/memoize": ^0.8.0
+ "@emotion/serialize": ^1.1.1
+ babel-plugin-macros: ^3.1.0
+ convert-source-map: ^1.5.0
+ escape-string-regexp: ^4.0.0
+ find-root: ^1.1.0
+ source-map: ^0.5.7
+ stylis: 4.1.3
+ peerDependencies:
+ "@babel/core": ^7.0.0
+ checksum: e3353499c76c4422d6e900c0dfab73607056d9da86161a3f27c3459c193c4908050c5d252c68fcde231e13f02a9d8e0dc07d260317ae0e5206841e331cc4caae
+ languageName: node
+ linkType: hard
+
+"@emotion/cache@npm:^11.10.5, @emotion/cache@npm:^11.10.7":
+ version: 11.10.7
+ resolution: "@emotion/cache@npm:11.10.7"
+ dependencies:
+ "@emotion/memoize": ^0.8.0
+ "@emotion/sheet": ^1.2.1
+ "@emotion/utils": ^1.2.0
+ "@emotion/weak-memoize": ^0.3.0
+ stylis: 4.1.3
+ checksum: 6b1efed2dffc93dac419409d91f6d57a200d858ec5ffa4b7c30080fdbd93db431ff86bb779c5b8830b8373f3c5dd754d9beb386604ed2667c7d55608ff653dfc
+ languageName: node
+ linkType: hard
+
"@emotion/hash@npm:^0.8.0":
version: 0.8.0
resolution: "@emotion/hash@npm:0.8.0"
@@ -10162,19 +10204,93 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/is-prop-valid@npm:^1.1.0":
- version: 1.1.2
- resolution: "@emotion/is-prop-valid@npm:1.1.2"
- dependencies:
- "@emotion/memoize": ^0.7.4
- checksum: 58b1f2d429a589f8f5bc2c33a8732cbb7bbcb17131a103511ef9a94ac754d7eeb53d627f947da480cd977f9d419fd92e244991680292f3287204159652745707
+"@emotion/hash@npm:^0.9.0":
+ version: 0.9.0
+ resolution: "@emotion/hash@npm:0.9.0"
+ checksum: b63428f7c8186607acdca5d003700cecf0ded519d0b5c5cc3b3154eafcad6ff433f8361bd2bac8882715b557e6f06945694aeb6ba8b25c6095d7a88570e2e0bb
languageName: node
linkType: hard
-"@emotion/memoize@npm:^0.7.4":
- version: 0.7.5
- resolution: "@emotion/memoize@npm:0.7.5"
- checksum: 83da8d4a7649a92c72f960817692bc6be13cc13e107b9f7e878d63766525ed4402881bfeb3cda61145c050281e7e260f114a0a2870515527346f2ef896b915b3
+"@emotion/is-prop-valid@npm:^1.1.0, @emotion/is-prop-valid@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "@emotion/is-prop-valid@npm:1.2.0"
+ dependencies:
+ "@emotion/memoize": ^0.8.0
+ checksum: cc7a19850a4c5b24f1514665289442c8c641709e6f7711067ad550e05df331da0692a16148e85eda6f47e31b3261b64d74c5e25194d053223be16231f969d633
+ languageName: node
+ linkType: hard
+
+"@emotion/memoize@npm:^0.8.0":
+ version: 0.8.0
+ resolution: "@emotion/memoize@npm:0.8.0"
+ checksum: c87bb110b829edd8e1c13b90a6bc37cebc39af29c7599a1e66a48e06f9bec43e8e53495ba86278cc52e7589549492c8dfdc81d19f4fdec0cee6ba13d2ad2c928
+ languageName: node
+ linkType: hard
+
+"@emotion/react@npm:^11.10.5":
+ version: 11.10.5
+ resolution: "@emotion/react@npm:11.10.5"
+ dependencies:
+ "@babel/runtime": ^7.18.3
+ "@emotion/babel-plugin": ^11.10.5
+ "@emotion/cache": ^11.10.5
+ "@emotion/serialize": ^1.1.1
+ "@emotion/use-insertion-effect-with-fallbacks": ^1.0.0
+ "@emotion/utils": ^1.2.0
+ "@emotion/weak-memoize": ^0.3.0
+ hoist-non-react-statics: ^3.3.1
+ peerDependencies:
+ "@babel/core": ^7.0.0
+ react: ">=16.8.0"
+ peerDependenciesMeta:
+ "@babel/core":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: 32b67b28e9b6d6c53b970072680697f04c2521441050bdeb19a1a7f0164af549b4dad39ff375eda1b6a3cf1cc86ba2c6fa55460ec040e6ebbca3e9ec58353cf7
+ languageName: node
+ linkType: hard
+
+"@emotion/serialize@npm:^1.1.1":
+ version: 1.1.1
+ resolution: "@emotion/serialize@npm:1.1.1"
+ dependencies:
+ "@emotion/hash": ^0.9.0
+ "@emotion/memoize": ^0.8.0
+ "@emotion/unitless": ^0.8.0
+ "@emotion/utils": ^1.2.0
+ csstype: ^3.0.2
+ checksum: 24cfd5b16e6f2335c032ca33804a876e0442aaf8f9c94d269d23735ebd194fb1ed142542dd92191a3e6ef8bad5bd560dfc5aaf363a1b70954726dbd4dd93085c
+ languageName: node
+ linkType: hard
+
+"@emotion/sheet@npm:^1.2.1":
+ version: 1.2.1
+ resolution: "@emotion/sheet@npm:1.2.1"
+ checksum: ce78763588ea522438156344d9f592203e2da582d8d67b32e1b0b98eaba26994c6c270f8c7ad46442fc9c0a9f048685d819cd73ca87e544520fd06f0e24a1562
+ languageName: node
+ linkType: hard
+
+"@emotion/styled@npm:^11.10.5":
+ version: 11.10.5
+ resolution: "@emotion/styled@npm:11.10.5"
+ dependencies:
+ "@babel/runtime": ^7.18.3
+ "@emotion/babel-plugin": ^11.10.5
+ "@emotion/is-prop-valid": ^1.2.0
+ "@emotion/serialize": ^1.1.1
+ "@emotion/use-insertion-effect-with-fallbacks": ^1.0.0
+ "@emotion/utils": ^1.2.0
+ peerDependencies:
+ "@babel/core": ^7.0.0
+ "@emotion/react": ^11.0.0-rc.0
+ react: ">=16.8.0"
+ peerDependenciesMeta:
+ "@babel/core":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: 1cec5f6aeb227a7255141031e8594f38ad83902413472aae0a46c27e5f9769c01e23c1ad39adee408d8a2168a697464314d1a0c4f50b31a5d25ea506b2d7bbc8
languageName: node
linkType: hard
@@ -10192,6 +10308,36 @@ __metadata:
languageName: node
linkType: hard
+"@emotion/unitless@npm:^0.8.0":
+ version: 0.8.0
+ resolution: "@emotion/unitless@npm:0.8.0"
+ checksum: 176141117ed23c0eb6e53a054a69c63e17ae532ec4210907a20b2208f91771821835f1c63dd2ec63e30e22fcc984026d7f933773ee6526dd038e0850919fae7a
+ languageName: node
+ linkType: hard
+
+"@emotion/use-insertion-effect-with-fallbacks@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.0.0"
+ peerDependencies:
+ react: ">=16.8.0"
+ checksum: 4f06a3b48258c832aa8022a262572061a31ff078d377e9164cccc99951309d70f4466e774fe704461b2f8715007a82ed625a54a5c7a127c89017d3ce3187d4f1
+ languageName: node
+ linkType: hard
+
+"@emotion/utils@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "@emotion/utils@npm:1.2.0"
+ checksum: 55457a49ddd4db6a014ea0454dc09eaa23eedfb837095c8ff90470cb26a303f7ceb5fcc1e2190ef64683e64cfd33d3ba3ca3109cd87d12bc9e379e4195c9a4dd
+ languageName: node
+ linkType: hard
+
+"@emotion/weak-memoize@npm:^0.3.0":
+ version: 0.3.0
+ resolution: "@emotion/weak-memoize@npm:0.3.0"
+ checksum: f43ef4c8b7de70d9fa5eb3105921724651e4188e895beb71f0c5919dc899a7b8743e1fdd99d38b9092dd5722c7be2312ebb47fbdad0c4e38bea58f6df5885cc0
+ languageName: node
+ linkType: hard
+
"@esbuild-kit/cjs-loader@npm:^2.4.1":
version: 2.4.2
resolution: "@esbuild-kit/cjs-loader@npm:2.4.2"
@@ -10552,27 +10698,27 @@ __metadata:
languageName: node
linkType: hard
-"@eslint/eslintrc@npm:^2.0.2":
- version: 2.0.2
- resolution: "@eslint/eslintrc@npm:2.0.2"
+"@eslint/eslintrc@npm:^2.0.3":
+ version: 2.0.3
+ resolution: "@eslint/eslintrc@npm:2.0.3"
dependencies:
ajv: ^6.12.4
debug: ^4.3.2
- espree: ^9.5.1
+ espree: ^9.5.2
globals: ^13.19.0
ignore: ^5.2.0
import-fresh: ^3.2.1
js-yaml: ^4.1.0
minimatch: ^3.1.2
strip-json-comments: ^3.1.1
- checksum: cfcf5e12c7b2c4476482e7f12434e76eae16fcd163ee627309adb10b761e5caa4a4e52ed7be464423320ff3d11eca5b50de5bf8be3e25834222470835dd5c801
+ checksum: ddc51f25f8524d8231db9c9bf03177e503d941a332e8d5ce3b10b09241be4d5584a378a529a27a527586bfbccf3031ae539eb891352033c340b012b4d0c81d92
languageName: node
linkType: hard
-"@eslint/js@npm:8.38.0":
- version: 8.38.0
- resolution: "@eslint/js@npm:8.38.0"
- checksum: 1f28987aa8c9cd93e23384e16c7220863b39b5dc4b66e46d7cdbccce868040f455a98d24cd8b567a884f26545a0555b761f7328d4a00c051e7ef689cbea5fce1
+"@eslint/js@npm:8.41.0":
+ version: 8.41.0
+ resolution: "@eslint/js@npm:8.41.0"
+ checksum: af013d70fe8d0429cdf5cd8b5dcc6fc384ed026c1eccb0cfe30f5849b968ab91645111373fd1b83282b38955b1bdfbe667c1a7dbda3b06cae753521223cad775
languageName: node
linkType: hard
@@ -10635,27 +10781,27 @@ __metadata:
languageName: node
linkType: hard
-"@frsource/base64@npm:1.0.5":
- version: 1.0.5
- resolution: "@frsource/base64@npm:1.0.5"
- checksum: ba526dd81df4e91b0abbcc34fc6818c8ff132e5230c23a4448a365ba934f782a86abb2a8566050f9450905851e467a7160f01f3d321fbb0fa3c8c516c5a82b9b
+"@frsource/base64@npm:1.0.14":
+ version: 1.0.14
+ resolution: "@frsource/base64@npm:1.0.14"
+ checksum: 9a5b718e4ac12fd1b5bfdefcdf669fb0a125599fb6cd55564da3beeccd7a5707f58bc41f572268a8120af15ccca32355e8384341561f738397521020e0c2564c
languageName: node
linkType: hard
"@frsource/cypress-plugin-visual-regression-diff@npm:^3.2.8":
- version: 3.2.14
- resolution: "@frsource/cypress-plugin-visual-regression-diff@npm:3.2.14"
+ version: 3.3.1
+ resolution: "@frsource/cypress-plugin-visual-regression-diff@npm:3.3.1"
dependencies:
- "@frsource/base64": 1.0.5
+ "@frsource/base64": 1.0.14
glob: 8.0.3
- meta-png: 1.0.5
+ meta-png: 1.0.6
move-file: 2.1.0
pixelmatch: 5.3.0
- pngjs: 6.0.0
+ pngjs: 7.0.0
sharp: 0.31.2
peerDependencies:
cypress: ">=4.5.0"
- checksum: 1650eded6db559ef13d0d8b3aba8b19e703dc103a9e806c87cb49ce495a37e296c041530f509f4c0372c15217c8b54a17f51b034261b5b109f4503ab24a8181f
+ checksum: fae1e9de89fcc0f75a71dd7cc78cb870fa4d0989a0dd2e1084eec2fdc95f9ff503a124f05ea88f87b52c3d03463a79e96439e5620a6c0f24ac39135c81af9edc
languageName: node
linkType: hard
@@ -12732,6 +12878,162 @@ __metadata:
languageName: node
linkType: hard
+"@mui/base@npm:5.0.0-alpha.127":
+ version: 5.0.0-alpha.127
+ resolution: "@mui/base@npm:5.0.0-alpha.127"
+ dependencies:
+ "@babel/runtime": ^7.21.0
+ "@emotion/is-prop-valid": ^1.2.0
+ "@mui/types": ^7.2.4
+ "@mui/utils": ^5.12.0
+ "@popperjs/core": ^2.11.7
+ clsx: ^1.2.1
+ prop-types: ^15.8.1
+ react-is: ^18.2.0
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 330baeef1fdaa513707f295e07a3436af24d0831a18109ed877f76b86cf1f392309833d33a2c7ca250db79fdf53f5e88c2618f876f50dbbbf9323bbf08e367e7
+ languageName: node
+ linkType: hard
+
+"@mui/core-downloads-tracker@npm:^5.12.2":
+ version: 5.12.2
+ resolution: "@mui/core-downloads-tracker@npm:5.12.2"
+ checksum: d748bdc56df6fdfe6712550a94263cf094c53ddcb70f6a8f633caf23927edf5ac88b1f888429d895fe2a5045b27eb7aa9826ccee5b917e31973667d604ed87fe
+ languageName: node
+ linkType: hard
+
+"@mui/material@npm:^5.12.2":
+ version: 5.12.2
+ resolution: "@mui/material@npm:5.12.2"
+ dependencies:
+ "@babel/runtime": ^7.21.0
+ "@mui/base": 5.0.0-alpha.127
+ "@mui/core-downloads-tracker": ^5.12.2
+ "@mui/system": ^5.12.1
+ "@mui/types": ^7.2.4
+ "@mui/utils": ^5.12.0
+ "@types/react-transition-group": ^4.4.5
+ clsx: ^1.2.1
+ csstype: ^3.1.2
+ prop-types: ^15.8.1
+ react-is: ^18.2.0
+ react-transition-group: ^4.4.5
+ peerDependencies:
+ "@emotion/react": ^11.5.0
+ "@emotion/styled": ^11.3.0
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: a3464dfbcc496164967e134d6f1d40e060ee02fc2ddb5fcf648ca85580efdbd5eff5aab6a73486d0a7f329f7299127b52501a13993bb131cbd4e310c7f2e633f
+ languageName: node
+ linkType: hard
+
+"@mui/private-theming@npm:^5.12.0":
+ version: 5.12.0
+ resolution: "@mui/private-theming@npm:5.12.0"
+ dependencies:
+ "@babel/runtime": ^7.21.0
+ "@mui/utils": ^5.12.0
+ prop-types: ^15.8.1
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 761bc7a57e1643c2c4c327886882fa5efc7bacae1c6fffe6be4197f49d337261c916a883d96996445efcdedc0672251725c1e5b264b6250d6c9527fd0cafcc62
+ languageName: node
+ linkType: hard
+
+"@mui/styled-engine@npm:^5.12.0":
+ version: 5.12.0
+ resolution: "@mui/styled-engine@npm:5.12.0"
+ dependencies:
+ "@babel/runtime": ^7.21.0
+ "@emotion/cache": ^11.10.7
+ csstype: ^3.1.2
+ prop-types: ^15.8.1
+ peerDependencies:
+ "@emotion/react": ^11.4.1
+ "@emotion/styled": ^11.3.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ checksum: 4a415473cf62aa05012f667dd2e9b1dc2fb175be5b0c4d0b8df541e2dac3d7db410e920e0d5910c8e2b8996a4fb51f74d79483e2878a9b5c0d334498f5537d74
+ languageName: node
+ linkType: hard
+
+"@mui/system@npm:^5.12.1":
+ version: 5.12.1
+ resolution: "@mui/system@npm:5.12.1"
+ dependencies:
+ "@babel/runtime": ^7.21.0
+ "@mui/private-theming": ^5.12.0
+ "@mui/styled-engine": ^5.12.0
+ "@mui/types": ^7.2.4
+ "@mui/utils": ^5.12.0
+ clsx: ^1.2.1
+ csstype: ^3.1.2
+ prop-types: ^15.8.1
+ peerDependencies:
+ "@emotion/react": ^11.5.0
+ "@emotion/styled": ^11.3.0
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: b951959bf5e5af581319354c044f441d97849ff120cfec111d2ed5e7fa05efd63721acff96f48093b8e2bdeb5ccbe35c48613fb925be2b58c596447d10dbed3e
+ languageName: node
+ linkType: hard
+
+"@mui/types@npm:^7.2.4":
+ version: 7.2.4
+ resolution: "@mui/types@npm:7.2.4"
+ peerDependencies:
+ "@types/react": "*"
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 16bea0547492193a22fd1794382f314698a114f6c673825314c66b56766c3a9d305992cc495684722b7be16a1ecf7e6e48a79caa64f90c439b530e8c02611a61
+ languageName: node
+ linkType: hard
+
+"@mui/utils@npm:^5.12.0":
+ version: 5.12.0
+ resolution: "@mui/utils@npm:5.12.0"
+ dependencies:
+ "@babel/runtime": ^7.21.0
+ "@types/prop-types": ^15.7.5
+ "@types/react-is": ^16.7.1 || ^17.0.0
+ prop-types: ^15.8.1
+ react-is: ^18.2.0
+ peerDependencies:
+ react: ^17.0.0 || ^18.0.0
+ checksum: 87b2c7468803b083f50af28d7c215c45291e73fef16570848b596d0f1cde1fc613c20e8951f431217b31451de254744abd50eda5013dedec4982420b5bf1c6b6
+ languageName: node
+ linkType: hard
+
"@n1ru4l/push-pull-async-iterable-iterator@npm:^3.1.0":
version: 3.1.0
resolution: "@n1ru4l/push-pull-async-iterable-iterator@npm:3.1.0"
@@ -13643,6 +13945,13 @@ __metadata:
languageName: node
linkType: hard
+"@popperjs/core@npm:^2.11.7":
+ version: 2.11.7
+ resolution: "@popperjs/core@npm:2.11.7"
+ checksum: 5b6553747899683452a1d28898c1b39173a4efd780e74360bfcda8eb42f1c5e819602769c81a10920fc68c881d07fb40429604517d499567eac079cfa6470f19
+ languageName: node
+ linkType: hard
+
"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2":
version: 1.1.2
resolution: "@protobufjs/aspromise@npm:1.1.2"
@@ -13739,6 +14048,22 @@ __metadata:
languageName: node
linkType: hard
+"@react-hookz/web@npm:^23.0.0":
+ version: 23.0.0
+ resolution: "@react-hookz/web@npm:23.0.0"
+ dependencies:
+ "@react-hookz/deep-equal": ^1.0.4
+ peerDependencies:
+ js-cookie: ^3.0.1
+ react: ^16.8 || ^17 || ^18
+ react-dom: ^16.8 || ^17 || ^18
+ peerDependenciesMeta:
+ js-cookie:
+ optional: true
+ checksum: 230bff62291bcafa65e0b9adcc3f0769fd63fabc226c77149e7797d1ea67362e9ad581ceff3cdfe2949698798d2deefb223c05bdfed1e465b5d92298d48cf3e5
+ languageName: node
+ linkType: hard
+
"@remix-run/router@npm:1.3.2":
version: 1.3.2
resolution: "@remix-run/router@npm:1.3.2"
@@ -14833,90 +15158,90 @@ __metadata:
languageName: node
linkType: hard
-"@swc/core-darwin-arm64@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-darwin-arm64@npm:1.3.51"
+"@swc/core-darwin-arm64@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-darwin-arm64@npm:1.3.59"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"@swc/core-darwin-x64@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-darwin-x64@npm:1.3.51"
+"@swc/core-darwin-x64@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-darwin-x64@npm:1.3.59"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"@swc/core-linux-arm-gnueabihf@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.51"
+"@swc/core-linux-arm-gnueabihf@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.59"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
-"@swc/core-linux-arm64-gnu@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm64-gnu@npm:1.3.51"
+"@swc/core-linux-arm64-gnu@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm64-gnu@npm:1.3.59"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
-"@swc/core-linux-arm64-musl@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-arm64-musl@npm:1.3.51"
+"@swc/core-linux-arm64-musl@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-arm64-musl@npm:1.3.59"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
-"@swc/core-linux-x64-gnu@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-x64-gnu@npm:1.3.51"
+"@swc/core-linux-x64-gnu@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-x64-gnu@npm:1.3.59"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
-"@swc/core-linux-x64-musl@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-linux-x64-musl@npm:1.3.51"
+"@swc/core-linux-x64-musl@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-linux-x64-musl@npm:1.3.59"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
-"@swc/core-win32-arm64-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-arm64-msvc@npm:1.3.51"
+"@swc/core-win32-arm64-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-arm64-msvc@npm:1.3.59"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
-"@swc/core-win32-ia32-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-ia32-msvc@npm:1.3.51"
+"@swc/core-win32-ia32-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-ia32-msvc@npm:1.3.59"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
-"@swc/core-win32-x64-msvc@npm:1.3.51":
- version: 1.3.51
- resolution: "@swc/core-win32-x64-msvc@npm:1.3.51"
+"@swc/core-win32-x64-msvc@npm:1.3.59":
+ version: 1.3.59
+ resolution: "@swc/core-win32-x64-msvc@npm:1.3.59"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@swc/core@npm:^1.3.46":
- version: 1.3.51
- resolution: "@swc/core@npm:1.3.51"
+ version: 1.3.59
+ resolution: "@swc/core@npm:1.3.59"
dependencies:
- "@swc/core-darwin-arm64": 1.3.51
- "@swc/core-darwin-x64": 1.3.51
- "@swc/core-linux-arm-gnueabihf": 1.3.51
- "@swc/core-linux-arm64-gnu": 1.3.51
- "@swc/core-linux-arm64-musl": 1.3.51
- "@swc/core-linux-x64-gnu": 1.3.51
- "@swc/core-linux-x64-musl": 1.3.51
- "@swc/core-win32-arm64-msvc": 1.3.51
- "@swc/core-win32-ia32-msvc": 1.3.51
- "@swc/core-win32-x64-msvc": 1.3.51
+ "@swc/core-darwin-arm64": 1.3.59
+ "@swc/core-darwin-x64": 1.3.59
+ "@swc/core-linux-arm-gnueabihf": 1.3.59
+ "@swc/core-linux-arm64-gnu": 1.3.59
+ "@swc/core-linux-arm64-musl": 1.3.59
+ "@swc/core-linux-x64-gnu": 1.3.59
+ "@swc/core-linux-x64-musl": 1.3.59
+ "@swc/core-win32-arm64-msvc": 1.3.59
+ "@swc/core-win32-ia32-msvc": 1.3.59
+ "@swc/core-win32-x64-msvc": 1.3.59
peerDependencies:
"@swc/helpers": ^0.5.0
dependenciesMeta:
@@ -14943,16 +15268,16 @@ __metadata:
peerDependenciesMeta:
"@swc/helpers":
optional: true
- checksum: f4e72d347b26353fbb48eae110a63c939922a38b20730ea010f5ae3dd15434f212dce582df05556ec35e677c089dfb6d00f5dd6fc655f8c9a7829458e4301469
+ checksum: 0283f39cf49cd12a6dc7062ef9ea4fa93e1a4f622ce46903a09605219f4f47ed5ad03925c6146d09ffe6ee02a89bb14e3c82eb86b4c60ecce4985ea6e078ffcf
languageName: node
linkType: hard
"@swc/helpers@npm:^0.5.0":
- version: 0.5.0
- resolution: "@swc/helpers@npm:0.5.0"
+ version: 0.5.1
+ resolution: "@swc/helpers@npm:0.5.1"
dependencies:
tslib: ^2.4.0
- checksum: 61c9c7dddb707deb58b85328cfe9d211887145f1311ae6a6e6c0fa9f781fb29916a8669a7d479e46e26a32a89d6ef4f293a22dee4925e009c84051e9dd10e8b7
+ checksum: 71e0e27234590435e4c62b97ef5e796f88e786841a38c7116a5e27a3eafa7b9ead7cdec5249b32165902076de78446945311c973e59bddf77c1e24f33a8f272a
languageName: node
linkType: hard
@@ -16161,9 +16486,9 @@ __metadata:
linkType: hard
"@types/marked@npm:^4.0.0":
- version: 4.0.8
- resolution: "@types/marked@npm:4.0.8"
- checksum: 68278fa7acaa5d920cdc239d675b5daf842e0ad4779e4848cd617d9baf2ac1afccb5a264c331e37d80031d647e1640cb983cd31e73d45b28552670b4853fad8e
+ version: 4.3.1
+ resolution: "@types/marked@npm:4.3.1"
+ checksum: eb6891b925d53ea53057f088b28f9232016499e62639cbb4a67f7d34f288ec6a8e40aae4836dbd06bd0fc3c541fca1175cd50d9e8d405547b6be7b7781e6a2a5
languageName: node
linkType: hard
@@ -16471,7 +16796,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.3":
+"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.3, @types/prop-types@npm:^15.7.5":
version: 15.7.5
resolution: "@types/prop-types@npm:15.7.5"
checksum: 5b43b8b15415e1f298243165f1d44390403bb2bd42e662bca3b5b5633fdd39c938e91b7fce3a9483699db0f7a715d08cef220c121f723a634972fdf596aec980
@@ -16537,6 +16862,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/react-is@npm:^16.7.1 || ^17.0.0":
+ version: 17.0.3
+ resolution: "@types/react-is@npm:17.0.3"
+ dependencies:
+ "@types/react": "*"
+ checksum: 6abb7c47d54f012272650df8a962a28bd82f219291e5ef8b4dfa7fe0bb98ae243b060bf9fbe8ceff6213141794855a006db194b490b00ffd15842ae19d0ce1f0
+ languageName: node
+ linkType: hard
+
"@types/react-redux@npm:^7.1.16":
version: 7.1.19
resolution: "@types/react-redux@npm:7.1.19"
@@ -16585,12 +16919,12 @@ __metadata:
languageName: node
linkType: hard
-"@types/react-transition-group@npm:^4.2.0":
- version: 4.2.4
- resolution: "@types/react-transition-group@npm:4.2.4"
+"@types/react-transition-group@npm:^4.2.0, @types/react-transition-group@npm:^4.4.5":
+ version: 4.4.5
+ resolution: "@types/react-transition-group@npm:4.4.5"
dependencies:
"@types/react": "*"
- checksum: a8abb3ad3c6e0575c630b591503a78ae3a94e21d1285905a49c36168e5d3de68b0886e2c6d75b751e30f2dc6e4245351cde30e4b3ff2649aec345f2088961e2c
+ checksum: 265f1c74061556708ffe8d15559e35c60d6c11478c9950d3735575d2c116ca69f461d85effa06d73a613eb8b73c84fd32682feb57cf7c5f9e4284021dbca25b0
languageName: node
linkType: hard
@@ -18647,6 +18981,17 @@ __metadata:
languageName: node
linkType: hard
+"babel-plugin-macros@npm:^3.1.0":
+ version: 3.1.0
+ resolution: "babel-plugin-macros@npm:3.1.0"
+ dependencies:
+ "@babel/runtime": ^7.12.5
+ cosmiconfig: ^7.0.0
+ resolve: ^1.19.0
+ checksum: 765de4abebd3e4688ebdfbff8571ddc8cd8061f839bb6c3e550b0344a4027b04c60491f843296ce3f3379fb356cc873d57a9ee6694262547eb822c14a25be9a6
+ languageName: node
+ linkType: hard
+
"babel-plugin-polyfill-corejs2@npm:^0.3.1":
version: 0.3.1
resolution: "babel-plugin-polyfill-corejs2@npm:0.3.1"
@@ -18916,13 +19261,13 @@ __metadata:
linkType: hard
"better-sqlite3@npm:^8.0.0":
- version: 8.3.0
- resolution: "better-sqlite3@npm:8.3.0"
+ version: 8.4.0
+ resolution: "better-sqlite3@npm:8.4.0"
dependencies:
bindings: ^1.5.0
node-gyp: latest
prebuild-install: ^7.1.0
- checksum: 00fc9f12058d2d157f56fe57b0f5c8ba705aee22a1dfe33ef8f60755531eda8f809cdb2377af314f6ed9396ad036094576e0596649bd6ca5bcaec172613e2dc9
+ checksum: f8b180c26428a2d381482e83b4519d0d81a918e00f92cafd255f42eb9583f49b2fed1015121ad49ebe1f3f790cc8c6f4697d1e805193d65e70dacf2e8abbd6af
languageName: node
linkType: hard
@@ -20102,7 +20447,7 @@ __metadata:
languageName: node
linkType: hard
-"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.1.1":
+"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.1.1, clsx@npm:^1.2.1":
version: 1.2.1
resolution: "clsx@npm:1.2.1"
checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12
@@ -20685,12 +21030,10 @@ __metadata:
languageName: node
linkType: hard
-"convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0":
- version: 1.7.0
- resolution: "convert-source-map@npm:1.7.0"
- dependencies:
- safe-buffer: ~5.1.1
- checksum: bcd2e3ea7d37f96b85a6e362c8a89402ccc73757256e3ee53aa2c22fe915adb854c66b1f81111be815a3a6a6ce3c58e8001858e883c9d5b4fe08a853fa865967
+"convert-source-map@npm:^1.5.0, convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0":
+ version: 1.9.0
+ resolution: "convert-source-map@npm:1.9.0"
+ checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8
languageName: node
linkType: hard
@@ -21310,10 +21653,10 @@ __metadata:
languageName: node
linkType: hard
-"csstype@npm:^3.0.2, csstype@npm:^3.0.6":
- version: 3.0.7
- resolution: "csstype@npm:3.0.7"
- checksum: 2f30c993be570c6d0de334b979a718370ee9bca9569c90340f13e05e542146c55b22e87372b858c061f9f8ded494da7a4715957882f9356cf9993ba11ab6f09c
+"csstype@npm:^3.0.2, csstype@npm:^3.0.6, csstype@npm:^3.1.2":
+ version: 3.0.9
+ resolution: "csstype@npm:3.0.9"
+ checksum: 199f9af7e673f9f188525c3102a329d637ff46c52f6385a4427ff5cb17adcb736189150170a7af7c5701d18d7704bdad130273f4aa7e44c6c4f9967e6115dc93
languageName: node
linkType: hard
@@ -23376,13 +23719,13 @@ __metadata:
linkType: hard
"eslint-plugin-testing-library@npm:^5.9.1":
- version: 5.10.3
- resolution: "eslint-plugin-testing-library@npm:5.10.3"
+ version: 5.11.0
+ resolution: "eslint-plugin-testing-library@npm:5.11.0"
dependencies:
"@typescript-eslint/utils": ^5.58.0
peerDependencies:
eslint: ^7.5.0 || ^8.0.0
- checksum: 3033121a0040b98280cd41856273ad1b268f56083759401e7af72ac8a96dcb213892f1248983aa9a18988b44913263e93f0d182dbb0c5b27b00242bfffc9cdcc
+ checksum: 7f19d3dedd7788b411ca3d9045de682feb26025b9c26d97d4e2f0bf62f5eaa276147d946bd5d0cd967b822e546a954330fdb7ef80485301264f646143f011a02
languageName: node
linkType: hard
@@ -23396,13 +23739,13 @@ __metadata:
languageName: node
linkType: hard
-"eslint-scope@npm:^7.1.1":
- version: 7.1.1
- resolution: "eslint-scope@npm:7.1.1"
+"eslint-scope@npm:^7.2.0":
+ version: 7.2.0
+ resolution: "eslint-scope@npm:7.2.0"
dependencies:
esrecurse: ^4.3.0
estraverse: ^5.2.0
- checksum: 9f6e974ab2db641ca8ab13508c405b7b859e72afe9f254e8131ff154d2f40c99ad4545ce326fd9fde3212ff29707102562a4834f1c48617b35d98c71a97fbf3e
+ checksum: 64591a2d8b244ade9c690b59ef238a11d5c721a98bcee9e9f445454f442d03d3e04eda88e95a4daec558220a99fa384309d9faae3d459bd40e7a81b4063980ae
languageName: node
linkType: hard
@@ -23424,10 +23767,10 @@ __metadata:
languageName: node
linkType: hard
-"eslint-visitor-keys@npm:^3.0.0, eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.0":
- version: 3.4.0
- resolution: "eslint-visitor-keys@npm:3.4.0"
- checksum: 33159169462d3989321a1ec1e9aaaf6a24cc403d5d347e9886d1b5bfe18ffa1be73bdc6203143a28a606b142b1af49787f33cff0d6d0813eb5f2e8d2e1a6043c
+"eslint-visitor-keys@npm:^3.0.0, eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1":
+ version: 3.4.1
+ resolution: "eslint-visitor-keys@npm:3.4.1"
+ checksum: f05121d868202736b97de7d750847a328fcfa8593b031c95ea89425333db59676ac087fa905eba438d0a3c5769632f828187e0c1a0d271832a2153c1d3661c2c
languageName: node
linkType: hard
@@ -23448,13 +23791,13 @@ __metadata:
linkType: hard
"eslint@npm:^8.33.0, eslint@npm:^8.6.0":
- version: 8.38.0
- resolution: "eslint@npm:8.38.0"
+ version: 8.41.0
+ resolution: "eslint@npm:8.41.0"
dependencies:
"@eslint-community/eslint-utils": ^4.2.0
"@eslint-community/regexpp": ^4.4.0
- "@eslint/eslintrc": ^2.0.2
- "@eslint/js": 8.38.0
+ "@eslint/eslintrc": ^2.0.3
+ "@eslint/js": 8.41.0
"@humanwhocodes/config-array": ^0.11.8
"@humanwhocodes/module-importer": ^1.0.1
"@nodelib/fs.walk": ^1.2.8
@@ -23464,9 +23807,9 @@ __metadata:
debug: ^4.3.2
doctrine: ^3.0.0
escape-string-regexp: ^4.0.0
- eslint-scope: ^7.1.1
- eslint-visitor-keys: ^3.4.0
- espree: ^9.5.1
+ eslint-scope: ^7.2.0
+ eslint-visitor-keys: ^3.4.1
+ espree: ^9.5.2
esquery: ^1.4.2
esutils: ^2.0.2
fast-deep-equal: ^3.1.3
@@ -23474,13 +23817,12 @@ __metadata:
find-up: ^5.0.0
glob-parent: ^6.0.2
globals: ^13.19.0
- grapheme-splitter: ^1.0.4
+ graphemer: ^1.4.0
ignore: ^5.2.0
import-fresh: ^3.0.0
imurmurhash: ^0.1.4
is-glob: ^4.0.0
is-path-inside: ^3.0.3
- js-sdsl: ^4.1.4
js-yaml: ^4.1.0
json-stable-stringify-without-jsonify: ^1.0.1
levn: ^0.4.1
@@ -23493,7 +23835,7 @@ __metadata:
text-table: ^0.2.0
bin:
eslint: bin/eslint.js
- checksum: 73b6d9b650d0434aa7c07d0a1802f099b086ee70a8d8ba7be730439a26572a5eb71def12125c82942be2ec8ee5be38a6f1b42a13e40d4b67f11a148ec9e263eb
+ checksum: 09979a6f8451dcc508a7005b6670845c8a518376280b3fd96657a406b8b6ef29d0e480d1ba11b4eb48da93d607e0c55c9b877676fe089d09973ec152354e23b2
languageName: node
linkType: hard
@@ -23504,14 +23846,14 @@ __metadata:
languageName: node
linkType: hard
-"espree@npm:^9.0.0, espree@npm:^9.5.1":
- version: 9.5.1
- resolution: "espree@npm:9.5.1"
+"espree@npm:^9.0.0, espree@npm:^9.5.2":
+ version: 9.5.2
+ resolution: "espree@npm:9.5.2"
dependencies:
acorn: ^8.8.0
acorn-jsx: ^5.3.2
- eslint-visitor-keys: ^3.4.0
- checksum: cdf6e43540433d917c4f2ee087c6e987b2063baa85a1d9cdaf51533d78275ebd5910c42154e7baf8e3e89804b386da0a2f7fad2264d8f04420e7506bf87b3b88
+ eslint-visitor-keys: ^3.4.1
+ checksum: 6506289d6eb26471c0b383ee24fee5c8ae9d61ad540be956b3127be5ce3bf687d2ba6538ee5a86769812c7c552a9d8239e8c4d150f9ea056c6d5cbe8399c03c1
languageName: node
linkType: hard
@@ -25483,6 +25825,13 @@ __metadata:
languageName: node
linkType: hard
+"graphemer@npm:^1.4.0":
+ version: 1.4.0
+ resolution: "graphemer@npm:1.4.0"
+ checksum: bab8f0be9b568857c7bec9fda95a89f87b783546d02951c40c33f84d05bb7da3fd10f863a9beb901463669b6583173a8c8cc6d6b306ea2b9b9d5d3d943c3a673
+ languageName: node
+ linkType: hard
+
"graphiql@npm:^1.5.12, graphiql@npm:^1.8.8":
version: 1.11.5
resolution: "graphiql@npm:1.11.5"
@@ -25939,7 +26288,7 @@ __metadata:
languageName: node
linkType: hard
-"hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.2.1, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
+"hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.2.1, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2":
version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2"
dependencies:
@@ -26478,15 +26827,6 @@ __metadata:
languageName: node
linkType: hard
-"indefinite-observable@npm:^2.0.1":
- version: 2.0.1
- resolution: "indefinite-observable@npm:2.0.1"
- dependencies:
- symbol-observable: 1.2.0
- checksum: b4305c999b7596901f8798e569b8faa030361dfb77f8b720e229558a29f544826f7e8987c55cc2a84c37ff2723594a965b96a40a6c71daae92b918f3eb393077
- languageName: node
- linkType: hard
-
"indent-string@npm:^4.0.0":
version: 4.0.0
resolution: "indent-string@npm:4.0.0"
@@ -27023,7 +27363,7 @@ __metadata:
languageName: node
linkType: hard
-"is-node-process@npm:^1.0.1, is-node-process@npm:^1.2.0":
+"is-node-process@npm:^1.2.0":
version: 1.2.0
resolution: "is-node-process@npm:1.2.0"
checksum: 930765cdc6d81ab8f1bbecbea4a8d35c7c6d88a3ff61f3630e0fc7f22d624d7661c1df05c58547d0eb6a639dfa9304682c8e342c4113a6ed51472b704cee2928
@@ -28238,13 +28578,6 @@ __metadata:
languageName: node
linkType: hard
-"js-sdsl@npm:^4.1.4":
- version: 4.1.4
- resolution: "js-sdsl@npm:4.1.4"
- checksum: 1977cea4ab18e0e03e28bdf0371d8b443fad65ca0988e0faa216406faf6bb943714fe8f7cc7a5bfe5f35ba3d94ddae399f4d10200f547f2c3320688b0670d726
- languageName: node
- linkType: hard
-
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
@@ -28827,76 +29160,76 @@ __metadata:
linkType: hard
"jss-plugin-camel-case@npm:^10.5.1":
- version: 10.6.0
- resolution: "jss-plugin-camel-case@npm:10.6.0"
+ version: 10.9.2
+ resolution: "jss-plugin-camel-case@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
hyphenate-style-name: ^1.0.3
- jss: 10.6.0
- checksum: 75c23826deef63c3603cfb47331d1eaf9feeafd097bd115a076f2ee9fb696855d3f18b3f41455a046120fddf0ea11985259c1b8b8a37b3556f4de1a1c83affd5
+ jss: 10.9.2
+ checksum: 5fa617b23ce9718244691c59ace6a0d1271dbcb4430ce3e13b851ee1879c1db8ecab7e941c33802bea763a0f0e2b609d004b8a975b2063f213cdd639cdd384d2
languageName: node
linkType: hard
"jss-plugin-default-unit@npm:^10.5.1":
- version: 10.6.0
- resolution: "jss-plugin-default-unit@npm:10.6.0"
+ version: 10.9.2
+ resolution: "jss-plugin-default-unit@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
- jss: 10.6.0
- checksum: 7ae36fadf4f206a16e49c40806ef92ba1f0e60395488ab8ad4df96796b408013b38ea5ed975155ecd7833a42a96c8d5cb1e90aef011279816633e60f807ec330
+ jss: 10.9.2
+ checksum: 48d8d836d36dd15513d98de11fba6be373ac29e6fd5702eb2edd143c815fb9e2f9969b2af6b1b964e9b8a052828690887042f6bcb34836836d5c359e52702d0f
languageName: node
linkType: hard
"jss-plugin-global@npm:^10.5.1":
- version: 10.6.0
- resolution: "jss-plugin-global@npm:10.6.0"
+ version: 10.9.2
+ resolution: "jss-plugin-global@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
- jss: 10.6.0
- checksum: 7aab58408c77071c571dd2c2132edad8d40b683513a8de534f0ec7f63926fd9d9fcf6bdda82103eb541452a723b992f061720f8e5cf2014acb7f6bbbde111f56
+ jss: 10.9.2
+ checksum: 9b29b0c1f169d5a1033890875df072d76364a902d0f6470f448544669a388612a9a4d51844fb2bcb6d25a1c43d67c1637f11a162c2cdd9f4b6b0a8f9c94f6090
languageName: node
linkType: hard
"jss-plugin-nested@npm:^10.5.1":
- version: 10.6.0
- resolution: "jss-plugin-nested@npm:10.6.0"
+ version: 10.9.2
+ resolution: "jss-plugin-nested@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
- jss: 10.6.0
+ jss: 10.9.2
tiny-warning: ^1.0.2
- checksum: 38095127e8b5ddb0a33ccaf61a4d89dc1b0f78e6d06d079a47a14bca4aed4e885f1957d83f11c5da08b4249b2c629e8bc62b2996f1674ca1302575a7b2b11171
+ checksum: ee08df07f3d553931b48037674842a8314bbc7857cc954a52f962a516bfc4b2d4e9871578b06b8fa3981edf5a927cea00021fd368d4ce315870065b7647f7b57
languageName: node
linkType: hard
"jss-plugin-props-sort@npm:^10.5.1":
- version: 10.6.0
- resolution: "jss-plugin-props-sort@npm:10.6.0"
+ version: 10.9.2
+ resolution: "jss-plugin-props-sort@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
- jss: 10.6.0
- checksum: 2917ff301e667f75efa15df865a860a50d4319d415d1169c213e66b464edc2b37b6f428be2d974ccdde7c8d61c50123f18e788143a96eacc2a1c42ad250ae375
+ jss: 10.9.2
+ checksum: 70bd181a458a6078f19ad4d7350570c78d26b9aabc25a1fbde673839edcc19825af7b636861b208a38aa17e551e68d0ea38599480716b4aec08e353bbe737222
languageName: node
linkType: hard
"jss-plugin-rule-value-function@npm:^10.5.1":
- version: 10.6.0
- resolution: "jss-plugin-rule-value-function@npm:10.6.0"
+ version: 10.9.2
+ resolution: "jss-plugin-rule-value-function@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
- jss: 10.6.0
+ jss: 10.9.2
tiny-warning: ^1.0.2
- checksum: 709e1ce0afdc39a52cb78e371fef626f933e99992790b15771d85056176bdf69365d62cfed6ce95434752cc1a1e7e05a6f2548e4cd5999808d50a28174315be3
+ checksum: b1a03209d0249f13ea6de766d3ee14c1769cd1f67d8c543c7d1ce6178c32cf15507c021ecb3e3b7585a8a7a2425dddbe0bdae02f4135c4598725a4152bebfc99
languageName: node
linkType: hard
"jss-plugin-vendor-prefixer@npm:^10.5.1":
- version: 10.6.0
- resolution: "jss-plugin-vendor-prefixer@npm:10.6.0"
+ version: 10.9.2
+ resolution: "jss-plugin-vendor-prefixer@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
css-vendor: ^2.0.8
- jss: 10.6.0
- checksum: 9db048022ee2fb79a223a9f1be5976e131715057e3c0200cfcead9b9f8625023942534afa261e3c967cee6ad716bfd7d4a2f356556178f60f4eddcf83bcd8856
+ jss: 10.9.2
+ checksum: a5c352a500fea82e8a782a090cc9815f6331259f1a331158ed74ed77c750fb45750f5ae95f07d27922742830b45d4c3592cfaab194b3ba4a50591acbdeab04d8
languageName: node
linkType: hard
@@ -28920,16 +29253,15 @@ __metadata:
languageName: node
linkType: hard
-"jss@npm:10.6.0":
- version: 10.6.0
- resolution: "jss@npm:10.6.0"
+"jss@npm:10.9.2":
+ version: 10.9.2
+ resolution: "jss@npm:10.9.2"
dependencies:
"@babel/runtime": ^7.3.1
csstype: ^3.0.2
- indefinite-observable: ^2.0.1
is-in-browser: ^1.1.3
tiny-warning: ^1.0.2
- checksum: 529edd871ddf059beef55ee0f974ad8436fbcf050d6538e6d2d7edab53ce2c09983d2f863e42a1473dd0e42a0447e9912bce6706bb0e539226d92cdaad128d46
+ checksum: 7ae5cd2f8602bf197ec90251d774b9f10d55eb2db0854ac78dc7fb6983828c202e8eb0d5c8c59c73b2f64718ebd33d6063afa799d625a995986a22dc1cc27230
languageName: node
linkType: hard
@@ -30451,10 +30783,10 @@ __metadata:
languageName: node
linkType: hard
-"meta-png@npm:1.0.5":
- version: 1.0.5
- resolution: "meta-png@npm:1.0.5"
- checksum: b8c92a696d5e78ef7f0ab53eb9039f108ee0c55cdac9d70e92e5fe2187b090176c0489426d58e594c7d1096481dac37913dd1265f28dfa39926db4d702a5e21d
+"meta-png@npm:1.0.6":
+ version: 1.0.6
+ resolution: "meta-png@npm:1.0.6"
+ checksum: aed14b3b53189c9d32002d76c40926bd93dd9d439bae3ad23ffb2645c375c9772c160879aa8af59ac85a2f43ae8bbe06b9648a53fe27815f4aeab238f4e9b79d
languageName: node
linkType: hard
@@ -31229,75 +31561,6 @@ __metadata:
languageName: node
linkType: hard
-"msw@npm:^0.47.0":
- version: 0.47.4
- resolution: "msw@npm:0.47.4"
- dependencies:
- "@mswjs/cookies": ^0.2.2
- "@mswjs/interceptors": ^0.17.5
- "@open-draft/until": ^1.0.3
- "@types/cookie": ^0.4.1
- "@types/js-levenshtein": ^1.1.1
- chalk: 4.1.1
- chokidar: ^3.4.2
- cookie: ^0.4.2
- graphql: ^15.0.0 || ^16.0.0
- headers-polyfill: ^3.1.0
- inquirer: ^8.2.0
- is-node-process: ^1.0.1
- js-levenshtein: ^1.1.6
- node-fetch: ^2.6.7
- outvariant: ^1.3.0
- path-to-regexp: ^6.2.0
- statuses: ^2.0.0
- strict-event-emitter: ^0.2.6
- type-fest: ^2.19.0
- yargs: ^17.3.1
- peerDependencies:
- typescript: ">= 4.2.x <= 4.8.x"
- peerDependenciesMeta:
- typescript:
- optional: true
- bin:
- msw: cli/index.js
- checksum: 10ff632641d40384d6622abf4df6399e4ae649db0f676b5d1ee2d0a515ec96f33abe9d4fecba08cdba4b2e43255af419da9eefc020d40a7e10669d0906457197
- languageName: node
- linkType: hard
-
-"msw@npm:^0.49.0":
- version: 0.49.3
- resolution: "msw@npm:0.49.3"
- dependencies:
- "@mswjs/cookies": ^0.2.2
- "@mswjs/interceptors": ^0.17.5
- "@open-draft/until": ^1.0.3
- "@types/cookie": ^0.4.1
- "@types/js-levenshtein": ^1.1.1
- chalk: 4.1.1
- chokidar: ^3.4.2
- cookie: ^0.4.2
- graphql: ^15.0.0 || ^16.0.0
- headers-polyfill: ^3.1.0
- inquirer: ^8.2.0
- is-node-process: ^1.0.1
- js-levenshtein: ^1.1.6
- node-fetch: ^2.6.7
- outvariant: ^1.3.0
- path-to-regexp: ^6.2.0
- strict-event-emitter: ^0.4.3
- type-fest: ^2.19.0
- yargs: ^17.3.1
- peerDependencies:
- typescript: ">= 4.4.x <= 4.9.x"
- peerDependenciesMeta:
- typescript:
- optional: true
- bin:
- msw: cli/index.js
- checksum: 8322cd42cd69f289c05517d02bde22fc2f10e86fc2d0d209d9df54bd03d10b8123723c5587a2654dcd2cd0f314a016f9eccac88cffa30fafd1f9fead16db639e
- languageName: node
- linkType: hard
-
"msw@npm:^1.0.0, msw@npm:^1.0.1, msw@npm:^1.2.1":
version: 1.2.1
resolution: "msw@npm:1.2.1"
@@ -32440,7 +32703,7 @@ __metadata:
languageName: node
linkType: hard
-"outvariant@npm:^1.2.1, outvariant@npm:^1.3.0, outvariant@npm:^1.4.0":
+"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0":
version: 1.4.0
resolution: "outvariant@npm:1.4.0"
checksum: ec32dfc315c464bb6e4906b2f450d259ce0b86caf70b70b249054359d9af21a7fccf53a8b6aa232f8d718449e31c1cfa594e6ebffaafe7bf908b502495256d7b
@@ -33466,7 +33729,14 @@ __metadata:
languageName: node
linkType: hard
-"pngjs@npm:6.0.0, pngjs@npm:^6.0.0":
+"pngjs@npm:7.0.0":
+ version: 7.0.0
+ resolution: "pngjs@npm:7.0.0"
+ checksum: b19a018930d27de26229c1b3ff250b3a25d09caa22cbb0b0459987d91eb0a560a18ab5d67da45a38ed7514140f26d1db58de83c31159ec101f2bb270a3c707f1
+ languageName: node
+ linkType: hard
+
+"pngjs@npm:^6.0.0":
version: 6.0.0
resolution: "pngjs@npm:6.0.0"
checksum: ab6c285086060087097eab9fe6b5a528a24f9e79c03dea2b4fd6264ed4fdb5beff4a3257eeeaf2a9dc18249b539609c2a4e4013c567164a1f6b5ba2c974d5ecb
@@ -35269,9 +35539,9 @@ __metadata:
languageName: node
linkType: hard
-"react-transition-group@npm:^4.0.0, react-transition-group@npm:^4.4.0":
- version: 4.4.1
- resolution: "react-transition-group@npm:4.4.1"
+"react-transition-group@npm:^4.0.0, react-transition-group@npm:^4.4.0, react-transition-group@npm:^4.4.5":
+ version: 4.4.5
+ resolution: "react-transition-group@npm:4.4.5"
dependencies:
"@babel/runtime": ^7.5.5
dom-helpers: ^5.0.1
@@ -35280,7 +35550,7 @@ __metadata:
peerDependencies:
react: ">=16.6.0"
react-dom: ">=16.6.0"
- checksum: 0bcd8af483709832e318dcef84c26ebddeb866bf4f58010286367ef0c1e7c5106e00cfc65688b9102414cb3d572c63909c2eb7ea972b4420fc70a78c10b6e8ad
+ checksum: 75602840106aa9c6545149d6d7ae1502fb7b7abadcce70a6954c4b64a438ff1cd16fc77a0a1e5197cdd72da398f39eb929ea06f9005c45b132ed34e056ebdeb1
languageName: node
linkType: hard
@@ -37274,6 +37544,13 @@ __metadata:
languageName: node
linkType: hard
+"source-map@npm:^0.5.7":
+ version: 0.5.7
+ resolution: "source-map@npm:0.5.7"
+ checksum: 5dc2043b93d2f194142c7f38f74a24670cd7a0063acdaf4bf01d2964b402257ae843c2a8fa822ad5b71013b5fcafa55af7421383da919752f22ff488bc553f4d
+ languageName: node
+ linkType: hard
+
"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1":
version: 0.6.1
resolution: "source-map@npm:0.6.1"
@@ -37618,7 +37895,7 @@ __metadata:
languageName: node
linkType: hard
-"statuses@npm:2.0.1, statuses@npm:^2.0.0":
+"statuses@npm:2.0.1":
version: 2.0.1
resolution: "statuses@npm:2.0.1"
checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb
@@ -37720,7 +37997,7 @@ __metadata:
languageName: node
linkType: hard
-"strict-event-emitter@npm:^0.2.4, strict-event-emitter@npm:^0.2.6":
+"strict-event-emitter@npm:^0.2.4":
version: 0.2.8
resolution: "strict-event-emitter@npm:0.2.8"
dependencies:
@@ -38075,10 +38352,10 @@ __metadata:
languageName: node
linkType: hard
-"stylis@npm:^4.0.6":
- version: 4.0.7
- resolution: "stylis@npm:4.0.7"
- checksum: 4f9fdc4b131b54cdc5988aaf23e796fcf77927c9d2f3147f6b23b8f3a9568eb38ce91b338b9d1b50a50391da32969c34ff68ff971c453fc3a5af89941c60ab06
+"stylis@npm:4.1.3, stylis@npm:^4.0.6":
+ version: 4.1.3
+ resolution: "stylis@npm:4.1.3"
+ checksum: d04dbffcb9bf2c5ca8d8dc09534203c75df3bf711d33973ea22038a99cc475412a350b661ebd99cbc01daa50d7eedcf0d130d121800eb7318759a197023442a6
languageName: node
linkType: hard